users

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 25, 2026 License: AGPL-3.0 Imports: 11 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
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

type BridgeStopper interface {
	RemoveBridgesByOwner(ctx context.Context, ownerID uuid.UUID) error
}

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

type CreateRequest struct {
	Email       string
	DisplayName string
	TenantRole  string
}

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

func New(d *db.DB, bridges BridgeStopper, logger *zap.Logger) *Service

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

func (s *Service) Delete(ctx context.Context, p authz.Principal, targetID uuid.UUID) error

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

func (s *Service) Get(ctx context.Context, p authz.Principal, userID uuid.UUID) (Summary, error)

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

func (s *Service) List(ctx context.Context, p authz.Principal) ([]Summary, error)

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

func (s *Service) ListDetail(ctx context.Context, p authz.Principal) ([]Detail, error)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL