Documentation
¶
Overview ¶
Package users owns the tenant-wide user directory: reads (List / ListDetail / Get / Lookup) at TenantUserView, self-service display-name updates at TenantSelfProfileUpdate, and admin mutators (Create / UpdateRole / Delete) at TenantUserManage. Every method gates through authz.Authorize so the policy table is the one place the access matrix is editable.
Index ¶
- Variables
- type BridgeStopper
- type CreateRequest
- type Detail
- type Service
- func (s *Service) Create(ctx context.Context, p authz.Principal, req CreateRequest) (Detail, string, error)
- func (s *Service) Delete(ctx context.Context, p authz.Principal, targetID uuid.UUID) error
- func (s *Service) Get(ctx context.Context, p authz.Principal, userID uuid.UUID) (Summary, error)
- func (s *Service) List(ctx context.Context, p authz.Principal) ([]Summary, error)
- func (s *Service) ListDetail(ctx context.Context, p authz.Principal) ([]Detail, error)
- func (s *Service) Lookup(ctx context.Context, p authz.Principal, identifier string) (Summary, error)
- func (s *Service) UpdateOwnDisplayName(ctx context.Context, p authz.Principal, displayName string) error
- func (s *Service) UpdateRole(ctx context.Context, p authz.Principal, targetID uuid.UUID, role string) error
- type Summary
Constants ¶
This section is empty.
Variables ¶
var ErrLastAdmin = fmt.Errorf("cannot remove the last admin: %w", service.ErrInvalidInput)
ErrLastAdmin — the one named guard Delete enforces beyond the generic ones. Wraps ErrInvalidInput so HTTPStatus maps to 400.
Functions ¶
This section is empty.
Types ¶
type BridgeStopper ¶
BridgeStopper is the narrow surface Delete uses to pre-cancel the BridgeManager pollers for a user's bridges before the DB CASCADE removes the rows. Defined as an interface to avoid importing trigger (cycle risk) and to keep the users service testable.
type CreateRequest ¶
CreateRequest is the input to Create. Role "" defaults to "user".
type Detail ¶
type Detail struct {
Summary
OIDCSub string `json:"oidc_sub"`
MustChangePassword bool `json:"must_change_password"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
Detail is the full admin-visible user row — adds the OIDC subject, timestamps, and password-reset flag. Returned only by admin-gated methods (ListDetail / GetDetail) because oidc_sub leaks SSO identity to other tenants if surfaced widely.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
func New ¶
New wires the users service. bridges may be nil for tests that don't exercise the Delete cascade path; production wires it to the BridgeManager.
func (*Service) Create ¶
func (s *Service) Create(ctx context.Context, p authz.Principal, req CreateRequest) (Detail, string, error)
Create provisions a user with a server-generated temporary password and must_change_password=true, so the recipient is forced to secure the account (set a strong password or register a passkey) on first login. It returns the one-time temp password for the admin to hand off; it is never stored in plaintext or retrievable again. Admin-gated (TenantUserManage). ErrConflict on duplicate email.
func (*Service) Delete ¶
Delete removes a user. Admin-gated. Refuses self-deletion and refuses deletion of the last remaining admin (would lock the tenant out of TenantUserManage).
func (*Service) Get ¶
Get returns one user by id. ErrNotFound for an unknown id. Same auth shape as List — any authenticated user can look up another by id (e.g. whoami expanding the principal's email field).
func (*Service) List ¶
List returns every user in the tenant in stable order (by created_at, matching the underlying ListUsers query). Available to any authenticated user — the same access level the member-picker dropdown uses, since invite flows need to see candidate users.
func (*Service) ListDetail ¶
ListDetail returns every user with the full admin-visible Detail shape (Summary + oidc_sub + timestamps + must_change_password). Gated on TenantUserManage — only tenant admins should see the SSO subject claim or password-reset state.
func (*Service) Lookup ¶
func (s *Service) Lookup(ctx context.Context, p authz.Principal, identifier string) (Summary, error)
Lookup resolves a user identifier (UUID OR email) to a Summary. Used by tools like add_agent_member where the operator may type either form. ErrInvalidInput for empty input; ErrNotFound when the identifier matches no user.
func (*Service) UpdateOwnDisplayName ¶
func (s *Service) UpdateOwnDisplayName(ctx context.Context, p authz.Principal, displayName string) error
UpdateOwnDisplayName changes the authenticated user's display name without changing credentials, authorization state, or sessions.
func (*Service) UpdateRole ¶
func (s *Service) UpdateRole(ctx context.Context, p authz.Principal, targetID uuid.UUID, role string) error
UpdateRole changes a user's tenant role. Admin-gated. Refuses the self-change case (callers can't demote themselves out of admin) and refuses to demote the last admin. ErrInvalidInput on unknown role; ErrNotFound when the user is gone.
type Summary ¶
type Summary struct {
ID uuid.UUID `json:"id"`
Email string `json:"email"`
DisplayName string `json:"display_name"`
TenantRole string `json:"tenant_role"`
}
Summary is the slim user shape every caller needs — id, identity fields, and tenant role. No password hash, no oidc_sub, no timestamps; the wider Detail type carries those for admin-only callers.