user_hierarchy.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. package service
  2. import (
  3. "context"
  4. infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
  5. "github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
  6. )
  7. type userManagementActorKey struct{}
  8. // WithUserManagementActor carries the server-authenticated actor to the final
  9. // repository transaction. It is never populated from request JSON.
  10. func WithUserManagementActor(ctx context.Context, actorID int64) context.Context {
  11. return context.WithValue(ctx, userManagementActorKey{}, actorID)
  12. }
  13. func UserManagementActor(ctx context.Context) (int64, bool) {
  14. id, ok := ctx.Value(userManagementActorKey{}).(int64)
  15. return id, ok
  16. }
  17. func (s *adminServiceImpl) authorizeManagedUserResource(ctx context.Context, targetID int64) error {
  18. actorID, managed := UserManagementActor(ctx)
  19. if !managed {
  20. return nil
  21. } // Non-HTTP internal callers retain their own authority.
  22. if s.userRepo == nil || actorID <= 0 {
  23. return infraerrors.Forbidden("USER_RESOURCE_FORBIDDEN", "User resource authorization unavailable")
  24. }
  25. actor, err := s.userRepo.GetByID(ctx, actorID)
  26. if err != nil {
  27. return err
  28. }
  29. target, err := s.userRepo.GetByID(ctx, targetID)
  30. if err != nil {
  31. return err
  32. }
  33. if !actor.IsActive() || !actor.IsAdmin() || (target.IsAdmin() && !actor.IsSuperAdmin()) {
  34. return infraerrors.Forbidden("SUPER_ADMIN_REQUIRED", "只有超级管理员可以管理管理员账号的密钥和订阅")
  35. }
  36. return nil
  37. }
  38. // Only a live super administrator may grant roles or change another
  39. // administrator. Actor identity comes from authenticated server context.
  40. func (s *adminServiceImpl) authorizeUserRoleChange(ctx context.Context, actorID int64, target *User, requestedRole string) error {
  41. roleChange := target == nil && IsAdminRole(requestedRole) || target != nil && requestedRole != "" && requestedRole != target.Role
  42. protectedTarget := target != nil && target.IsAdmin()
  43. if !roleChange && !protectedTarget {
  44. return nil
  45. }
  46. if actorID <= 0 {
  47. return infraerrors.Forbidden("SUPER_ADMIN_REQUIRED", "只有超级管理员可以修改角色或管理管理员账号")
  48. }
  49. actor, err := s.userRepo.GetByID(ctx, actorID)
  50. if err != nil {
  51. return err
  52. }
  53. if !actor.IsActive() || !actor.IsSuperAdmin() {
  54. return infraerrors.Forbidden("SUPER_ADMIN_REQUIRED", "只有超级管理员可以修改角色或管理管理员账号")
  55. }
  56. if target != nil && target.ID == actorID && requestedRole != "" && requestedRole != actor.Role {
  57. return infraerrors.Forbidden("SELF_DEMOTION_FORBIDDEN", "不能修改自己的管理员角色")
  58. }
  59. return nil
  60. }
  61. func (s *adminServiceImpl) ensureNotLastSuperAdmin(ctx context.Context) error {
  62. noSubs := false
  63. _, result, err := s.userRepo.ListWithFilters(ctx, pagination.PaginationParams{Page: 1, PageSize: 1}, UserListFilters{Role: RoleSuperAdmin, IncludeSubscriptions: &noSubs})
  64. if err != nil {
  65. return err
  66. }
  67. if result == nil || result.Total <= 1 {
  68. return infraerrors.Forbidden("LAST_SUPER_ADMIN", "不能降级最后一个超级管理员")
  69. }
  70. return nil
  71. }