From 0f7c755d5382948969af63fe5b391b46c09ed296 Mon Sep 17 00:00:00 2001 From: Bryan Frimin Date: Sat, 20 Dec 2025 14:14:31 +0100 Subject: [PATCH] Rename user into identity Signed-off-by: Bryan Frimin --- pkg/coredata/api_key_membership.go | 92 ++++---- pkg/coredata/entity_type_reg.go | 22 +- pkg/coredata/{user.go => identity.go} | 153 ++++++------ ...order_field.go => identity_order_field.go} | 14 +- pkg/coredata/membership.go | 102 ++++---- pkg/coredata/migrations/20251220T104530Z.sql | 8 + pkg/coredata/organization.go | 50 ++-- .../{user_api_key.go => personal_api_key.go} | 70 +++--- ...eld.go => personal_api_key_order_field.go} | 14 +- pkg/coredata/session.go | 38 +-- pkg/iam/access_management_service.go | 66 ++---- pkg/iam/account_service.go | 222 ++++++++---------- pkg/iam/api_key.go | 12 +- pkg/iam/auth_service.go | 100 ++++---- pkg/iam/authorizer.go | 4 +- pkg/iam/errors.go | 48 ++-- pkg/iam/organization_service.go | 16 +- pkg/iam/permissions.go | 2 +- pkg/iam/saml/service.go | 36 +-- pkg/iam/session_service.go | 24 +- .../api/connect/v1/api_key_middleware.go | 22 +- pkg/server/api/connect/v1/graphql_handler.go | 2 +- .../api/connect/v1/session_middleware.go | 16 +- pkg/server/api/connect/v1/types/identity.go | 2 +- pkg/server/api/connect/v1/types/membership.go | 2 +- .../api/connect/v1/types/personal_api_key.go | 8 +- pkg/server/api/connect/v1/types/session.go | 2 +- pkg/server/api/connect/v1/v1_resolver.go | 52 ++-- pkg/server/api/console/v1/resolver.go | 6 +- pkg/server/api/console/v1/v1_resolver.go | 19 +- pkg/server/api/mcp/v1/middleware.go | 2 +- pkg/server/api/mcp/v1/resolver.go | 2 +- pkg/server/api/mcp/v1/schema.resolvers.go | 4 +- pkg/server/api/trust/v1/trustauth/auth.go | 10 +- pkg/server/api/trust/v1/v1_resolver.go | 28 +-- 35 files changed, 606 insertions(+), 664 deletions(-) rename pkg/coredata/{user.go => identity.go} (56%) rename pkg/coredata/{user_order_field.go => identity_order_field.go} (73%) create mode 100644 pkg/coredata/migrations/20251220T104530Z.sql rename pkg/coredata/{user_api_key.go => personal_api_key.go} (70%) rename pkg/coredata/{user_api_key_order_field.go => personal_api_key_order_field.go} (70%) diff --git a/pkg/coredata/api_key_membership.go b/pkg/coredata/api_key_membership.go index b3e115494..7b9498c85 100644 --- a/pkg/coredata/api_key_membership.go +++ b/pkg/coredata/api_key_membership.go @@ -26,9 +26,9 @@ import ( ) type ( - UserAPIKeyMembership struct { + PersonalAPIKeyMembership struct { ID gid.GID `db:"id"` - UserAPIKeyID gid.GID `db:"auth_user_api_key_id"` + PersonalAPIKeyID gid.GID `db:"auth_personal_api_key_id"` MembershipID gid.GID `db:"membership_id"` Role APIRole `db:"role"` OrganizationID gid.GID `db:"organization_id"` @@ -37,21 +37,21 @@ type ( UpdatedAt time.Time `db:"updated_at"` } - UserAPIKeyMemberships []*UserAPIKeyMembership + PersonalAPIKeyMemberships []*PersonalAPIKeyMembership ) -func (a *UserAPIKeyMembership) Insert( +func (a *PersonalAPIKeyMembership) Insert( ctx context.Context, conn pg.Conn, scope Scoper, ) error { q := ` INSERT INTO - authz_api_keys_memberships (id, tenant_id, auth_user_api_key_id, membership_id, role, organization_id, created_at, updated_at) + authz_api_keys_memberships (id, tenant_id, auth_personal_api_key_id, membership_id, role, organization_id, created_at, updated_at) VALUES ( @id, @tenant_id, - @auth_user_api_key_id, + @auth_personal_api_key_id, @membership_id, @role, @organization_id, @@ -61,34 +61,34 @@ VALUES ( ` args := pgx.StrictNamedArgs{ - "id": a.ID, - "tenant_id": scope.GetTenantID(), - "auth_user_api_key_id": a.UserAPIKeyID, - "membership_id": a.MembershipID, - "role": a.Role, - "organization_id": a.OrganizationID, - "created_at": a.CreatedAt, - "updated_at": a.UpdatedAt, + "id": a.ID, + "tenant_id": scope.GetTenantID(), + "auth_personal_api_key_id": a.PersonalAPIKeyID, + "membership_id": a.MembershipID, + "role": a.Role, + "organization_id": a.OrganizationID, + "created_at": a.CreatedAt, + "updated_at": a.UpdatedAt, } _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot insert user api key membership: %w", err) + return fmt.Errorf("cannot insert personal api key membership: %w", err) } return nil } -func (a *UserAPIKeyMemberships) LoadByUserAPIKeyID( +func (a *PersonalAPIKeyMemberships) LoadByPersonalAPIKeyID( ctx context.Context, conn pg.Conn, scope Scoper, - userAPIKeyID gid.GID, + personalAPIKeyID gid.GID, ) error { q := ` SELECT akm.id, - akm.auth_user_api_key_id, + akm.auth_personal_api_key_id, akm.membership_id, akm.role, akm.created_at, @@ -102,7 +102,7 @@ JOIN JOIN organizations o ON m.organization_id = o.id WHERE - akm.auth_user_api_key_id = @auth_user_api_key_id + akm.auth_personal_api_key_id = @auth_personal_api_key_id AND m.%s ORDER BY akm.created_at DESC ` @@ -110,18 +110,18 @@ ORDER BY akm.created_at DESC q = fmt.Sprintf(q, scope.SQLFragment()) args := pgx.StrictNamedArgs{ - "auth_user_api_key_id": userAPIKeyID, + "auth_personal_api_key_id": personalAPIKeyID, } maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query user api key memberships: %w", err) + return fmt.Errorf("cannot query personal api key memberships: %w", err) } - memberships, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[UserAPIKeyMembership]) + memberships, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[PersonalAPIKeyMembership]) if err != nil { - return fmt.Errorf("cannot collect user api key memberships: %w", err) + return fmt.Errorf("cannot collect personal api key memberships: %w", err) } *a = memberships @@ -130,7 +130,7 @@ ORDER BY akm.created_at DESC } // LoadRoleByAPIKeyAndEntityID loads an API key's role by querying any entity to extract its organization_id -func (a *UserAPIKeyMembership) LoadRoleByAPIKeyAndEntityID( +func (a *PersonalAPIKeyMembership) LoadRoleByAPIKeyAndEntityID( ctx context.Context, conn pg.Conn, scope Scoper, @@ -152,7 +152,7 @@ func (a *UserAPIKeyMembership) LoadRoleByAPIKeyAndEntityID( query := fmt.Sprintf(` SELECT akm.id, - akm.auth_user_api_key_id, + akm.auth_personal_api_key_id, akm.membership_id, akm.role, akm.created_at, @@ -163,7 +163,7 @@ FROM INNER JOIN %s e ON e.id = @entity_id WHERE %s - AND akm.auth_user_api_key_id = @api_key_id + AND akm.auth_personal_api_key_id = @api_key_id AND m.organization_id = e.organization_id LIMIT 1; `, tableName, scope.SQLFragment()) @@ -184,10 +184,10 @@ LIMIT 1; return fmt.Errorf("API key membership not found for key %s and entity %s", apiKeyID, entityID) } - var membership UserAPIKeyMembership + var membership PersonalAPIKeyMembership err = rows.Scan( &membership.ID, - &membership.UserAPIKeyID, + &membership.PersonalAPIKeyID, &membership.MembershipID, &membership.Role, &membership.CreatedAt, @@ -202,7 +202,7 @@ LIMIT 1; return nil } -func (a *UserAPIKeyMembership) LoadByAPIKeyIDAndOrganizationID( +func (a *PersonalAPIKeyMembership) LoadByAPIKeyIDAndOrganizationID( ctx context.Context, conn pg.Conn, scope Scoper, @@ -212,7 +212,7 @@ func (a *UserAPIKeyMembership) LoadByAPIKeyIDAndOrganizationID( q := ` SELECT akm.id, - akm.auth_user_api_key_id, + akm.auth_personal_api_key_id, akm.membership_id, akm.role, akm.created_at, @@ -226,7 +226,7 @@ JOIN JOIN organizations o ON m.organization_id = o.id WHERE - akm.auth_user_api_key_id = @api_key_id + akm.auth_personal_api_key_id = @api_key_id AND m.organization_id = @organization_id AND m.%s ` @@ -241,22 +241,22 @@ WHERE rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query user api key membership: %w", err) + return fmt.Errorf("cannot query personal api key membership: %w", err) } - membership, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[UserAPIKeyMembership]) + membership, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[PersonalAPIKeyMembership]) if err != nil { if err == pgx.ErrNoRows { return fmt.Errorf("API key does not have access to organization") } - return fmt.Errorf("cannot collect user api key membership: %w", err) + return fmt.Errorf("cannot collect personal api key membership: %w", err) } *a = membership return nil } -func (a *UserAPIKeyMembership) Delete( +func (a *PersonalAPIKeyMembership) Delete( ctx context.Context, conn pg.Conn, scope Scoper, @@ -278,13 +278,13 @@ WHERE _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot delete user api key membership: %w", err) + return fmt.Errorf("cannot delete personal api key membership: %w", err) } return nil } -func (a *UserAPIKeyMemberships) LoadByMembershipID( +func (a *PersonalAPIKeyMemberships) LoadByMembershipID( ctx context.Context, conn pg.Conn, scope Scoper, @@ -293,7 +293,7 @@ func (a *UserAPIKeyMemberships) LoadByMembershipID( q := ` SELECT akm.id, - akm.auth_user_api_key_id, + akm.auth_personal_api_key_id, akm.membership_id, akm.role, akm.created_at, @@ -321,12 +321,12 @@ ORDER BY akm.created_at DESC rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query user api key memberships by membership id: %w", err) + return fmt.Errorf("cannot query personal api key memberships by membership id: %w", err) } - memberships, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[UserAPIKeyMembership]) + memberships, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[PersonalAPIKeyMembership]) if err != nil { - return fmt.Errorf("cannot collect user api key memberships: %w", err) + return fmt.Errorf("cannot collect personal api key memberships: %w", err) } *a = memberships @@ -334,25 +334,25 @@ ORDER BY akm.created_at DESC return nil } -func DeleteAllUserAPIKeyMembershipsByUserAPIKeyID( +func DeleteAllPersonalAPIKeyMembershipsByPersonalAPIKeyID( ctx context.Context, conn pg.Conn, - userAPIKeyID gid.GID, + personalAPIKeyID gid.GID, ) error { q := ` DELETE FROM authz_api_keys_memberships WHERE - auth_user_api_key_id = @auth_user_api_key_id + auth_personal_api_key_id = @auth_personal_api_key_id ` args := pgx.StrictNamedArgs{ - "auth_user_api_key_id": userAPIKeyID, + "auth_personal_api_key_id": personalAPIKeyID, } _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot delete user api key memberships: %w", err) + return fmt.Errorf("cannot delete personal api key memberships: %w", err) } return nil diff --git a/pkg/coredata/entity_type_reg.go b/pkg/coredata/entity_type_reg.go index 1f93bab6d..e5f40d8c8 100644 --- a/pkg/coredata/entity_type_reg.go +++ b/pkg/coredata/entity_type_reg.go @@ -32,7 +32,7 @@ const ( PeopleEntityType uint16 = 8 VendorComplianceReportEntityType uint16 = 9 DocumentEntityType uint16 = 10 - UserEntityType uint16 = 11 + IdentityEntityType uint16 = 11 SessionEntityType uint16 = 12 EmailEntityType uint16 = 13 ControlEntityType uint16 = 14 @@ -64,8 +64,8 @@ const ( SlackMessageEntityType uint16 = 40 TrustCenterFileEntityType uint16 = 41 SAMLConfigurationEntityType uint16 = 42 - UserAPIKeyEntityType uint16 = 43 - UserAPIKeyMembershipEntityType uint16 = 44 + PersonalAPIKeyEntityType uint16 = 43 + PersonalAPIKeyMembershipEntityType uint16 = 44 MeetingEntityType uint16 = 45 DataProtectionImpactAssessmentEntityType uint16 = 46 TransferImpactAssessmentEntityType uint16 = 47 @@ -124,9 +124,9 @@ var entityRegistry = map[uint16]EntityInfo{ Model: "Document", Table: "documents", }, - UserEntityType: { - Model: "User", - Table: "auth_users", + IdentityEntityType: { + Model: "Identity", + Table: "identities", }, SessionEntityType: { Model: "Session", @@ -252,12 +252,12 @@ var entityRegistry = map[uint16]EntityInfo{ Model: "SAMLConfiguration", Table: "auth_saml_configurations", }, - UserAPIKeyEntityType: { - Model: "UserAPIKey", - Table: "auth_user_api_keys", + PersonalAPIKeyEntityType: { + Model: "PersonalAPIKey", + Table: "auth_personal_api_keys", }, - UserAPIKeyMembershipEntityType: { - Model: "UserAPIKeyMembership", + PersonalAPIKeyMembershipEntityType: { + Model: "PersonalAPIKeyMembership", Table: "authz_api_keys_memberships", }, MeetingEntityType: { diff --git a/pkg/coredata/user.go b/pkg/coredata/identity.go similarity index 56% rename from pkg/coredata/user.go rename to pkg/coredata/identity.go index 600ec3f03..7c1388cfb 100644 --- a/pkg/coredata/user.go +++ b/pkg/coredata/identity.go @@ -31,34 +31,34 @@ import ( ) type ( - User struct { + Identity struct { ID gid.GID `db:"id"` EmailAddress mail.Addr `db:"email_address"` - HashedPassword []byte `db:"hashed_password"` - FullName string `db:"fullname"` - EmailAddressVerified bool `db:"email_address_verified"` - SAMLSubject *string `db:"saml_subject"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` + HashedPassword []byte `db:"hashed_password"` + FullName string `db:"fullname"` + EmailAddressVerified bool `db:"email_address_verified"` + SAMLSubject *string `db:"saml_subject"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` } - Users []*User + Identities []*Identity ) -func (u User) CursorKey(orderBy UserOrderField) page.CursorKey { +func (i Identity) CursorKey(orderBy IdentityOrderField) page.CursorKey { switch orderBy { - case UserOrderFieldCreatedAt: - return page.NewCursorKey(u.ID, u.CreatedAt) + case IdentityOrderFieldCreatedAt: + return page.NewCursorKey(i.ID, i.CreatedAt) } panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } -func (u *Users) LoadByOrganizationID( +func (i *Identities) LoadByOrganizationID( ctx context.Context, conn pg.Conn, organizationID gid.GID, - cursor *page.Cursor[UserOrderField], + cursor *page.Cursor[IdentityOrderField], ) error { q := ` SELECT @@ -70,10 +70,10 @@ SELECT created_at, updated_at FROM - users + identities WHERE id IN ( - SELECT user_id FROM authz_memberships WHERE organization_id = @organization_id + SELECT identity_id FROM authz_memberships WHERE organization_id = @organization_id ) AND %s ` @@ -85,20 +85,20 @@ WHERE rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query users: %w", err) + return fmt.Errorf("cannot query identities: %w", err) } - users, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[User]) + identities, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Identity]) if err != nil { - return fmt.Errorf("cannot collect users: %w", err) + return fmt.Errorf("cannot collect identities: %w", err) } - *u = users + *i = identities return nil } -func (u *Users) CountByOrganizationID( +func (i *Identities) CountByOrganizationID( ctx context.Context, conn pg.Conn, scope Scoper, @@ -108,10 +108,10 @@ func (u *Users) CountByOrganizationID( SELECT COUNT(*) FROM - users + identities WHERE id IN ( - SELECT user_id FROM authz_memberships WHERE organization_id = @organization_id AND %s + SELECT identity_id FROM authz_memberships WHERE organization_id = @organization_id AND %s ) ` @@ -125,14 +125,14 @@ WHERE var count int err := row.Scan(&count) if err != nil { - return 0, fmt.Errorf("cannot count users: %w", err) + return 0, fmt.Errorf("cannot count identities: %w", err) } return count, nil } -// Tenant id scope is not applied because we want to access users across all tenants for authentication purposes. -func (u *User) LoadByEmail( +// Tenant id scope is not applied because we want to access identities across all tenants for authentication purposes. +func (i *Identity) LoadByEmail( ctx context.Context, conn pg.Conn, email mail.Addr, @@ -148,38 +148,38 @@ SELECT created_at, updated_at FROM - users + identities WHERE - email_address = @user_email + email_address = @identity_email LIMIT 1; ` - args := pgx.StrictNamedArgs{"user_email": email} + args := pgx.StrictNamedArgs{"identity_email": email} rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query user: %w", err) + return fmt.Errorf("cannot query identity: %w", err) } - user, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[User]) + identity, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Identity]) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return ErrResourceNotFound } - return fmt.Errorf("cannot collect user: %w", err) + return fmt.Errorf("cannot collect identity: %w", err) } - *u = user + *i = identity return nil } -// Tenant id scope is not applied because we want to access users across all tenants for authentication purposes. -func (u *User) LoadByID( +// Tenant id scope is not applied because we want to access identities across all tenants for authentication purposes. +func (i *Identity) LoadByID( ctx context.Context, conn pg.Conn, - userID gid.GID, + identityID gid.GID, ) error { q := ` SELECT @@ -192,42 +192,42 @@ SELECT created_at, updated_at FROM - users + identities WHERE - id = @user_id + id = @identity_id LIMIT 1; ` - args := pgx.StrictNamedArgs{"user_id": userID} + args := pgx.StrictNamedArgs{"identity_id": identityID} rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query user: %w", err) + return fmt.Errorf("cannot query identity: %w", err) } - user, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[User]) + identity, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Identity]) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return ErrResourceNotFound } - return fmt.Errorf("cannot collect user: %w", err) + return fmt.Errorf("cannot collect identity: %w", err) } - *u = user + *i = identity return nil } -func (u *User) Insert( +func (i *Identity) Insert( ctx context.Context, conn pg.Conn, ) error { q := ` INSERT INTO - users (id, email_address, hashed_password, email_address_verified, fullname, saml_subject, created_at, updated_at) + identities (id, email_address, hashed_password, email_address_verified, fullname, saml_subject, created_at, updated_at) VALUES ( - @user_id, + @identity_id, @email_address, @hashed_password, @email_address_verified, @@ -239,14 +239,14 @@ VALUES ( ` args := pgx.StrictNamedArgs{ - "user_id": u.ID, - "email_address": u.EmailAddress, - "hashed_password": u.HashedPassword, - "fullname": u.FullName, - "saml_subject": u.SAMLSubject, - "created_at": u.CreatedAt, - "updated_at": u.UpdatedAt, - "email_address_verified": u.EmailAddressVerified, + "identity_id": i.ID, + "email_address": i.EmailAddress, + "hashed_password": i.HashedPassword, + "fullname": i.FullName, + "saml_subject": i.SAMLSubject, + "created_at": i.CreatedAt, + "updated_at": i.UpdatedAt, + "email_address_verified": i.EmailAddressVerified, } _, err := conn.Exec(ctx, q, args) @@ -265,10 +265,10 @@ VALUES ( return nil } -func (u *User) Update(ctx context.Context, conn pg.Conn) error { +func (i *Identity) Update(ctx context.Context, conn pg.Conn) error { q := ` UPDATE - users + identities SET email_address = @email_address, email_address_verified = @email_address_verified, @@ -277,22 +277,22 @@ SET hashed_password = @hashed_password, updated_at = @updated_at WHERE - id = @user_id + id = @identity_id ` args := pgx.StrictNamedArgs{ - "user_id": u.ID, - "email_address": u.EmailAddress, - "email_address_verified": u.EmailAddressVerified, - "saml_subject": u.SAMLSubject, - "updated_at": u.UpdatedAt, - "fullname": u.FullName, - "hashed_password": u.HashedPassword, + "identity_id": i.ID, + "email_address": i.EmailAddress, + "email_address_verified": i.EmailAddressVerified, + "saml_subject": i.SAMLSubject, + "updated_at": i.UpdatedAt, + "fullname": i.FullName, + "hashed_password": i.HashedPassword, } result, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot update user: %w", err) + return fmt.Errorf("cannot update identity: %w", err) } if result.RowsAffected() == 0 { @@ -302,8 +302,8 @@ WHERE return nil } -// LoadBySAMLSubject loads a user by their SAML subject (NameID) -func (u *User) LoadBySAMLSubject( +// LoadBySAMLSubject loads an identity by their SAML subject (NameID) +func (i *Identity) LoadBySAMLSubject( ctx context.Context, conn pg.Conn, samlSubject string, @@ -319,7 +319,7 @@ SELECT created_at, updated_at FROM - users + identities WHERE saml_subject = @saml_subject LIMIT 1; @@ -329,24 +329,24 @@ LIMIT 1; rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query user by SAML subject: %w", err) + return fmt.Errorf("cannot query identity by SAML subject: %w", err) } - user, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[User]) + identity, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Identity]) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return ErrResourceNotFound } - return fmt.Errorf("cannot collect user: %w", err) + return fmt.Errorf("cannot collect identity: %w", err) } - *u = user + *i = identity return nil } -func (u *User) CountMemberships( +func (i *Identity) CountMemberships( ctx context.Context, conn pg.Conn, ) (int, error) { @@ -356,19 +356,16 @@ SELECT FROM authz_memberships WHERE - user_id = @user_id + identity_id = @identity_id ` - args := pgx.StrictNamedArgs{"user_id": u.ID} + args := pgx.StrictNamedArgs{"identity_id": i.ID} var count int err := conn.QueryRow(ctx, q, args).Scan(&count) if err != nil { - return 0, fmt.Errorf("cannot count user memberships: %w", err) + return 0, fmt.Errorf("cannot count identity memberships: %w", err) } return count, nil } - -// ConvertToTenantUser method removed -// All users are now global (no tenant conversion needed) diff --git a/pkg/coredata/user_order_field.go b/pkg/coredata/identity_order_field.go similarity index 73% rename from pkg/coredata/user_order_field.go rename to pkg/coredata/identity_order_field.go index ab99a38f5..14121e9ea 100644 --- a/pkg/coredata/user_order_field.go +++ b/pkg/coredata/identity_order_field.go @@ -15,26 +15,26 @@ package coredata type ( - UserOrderField string + IdentityOrderField string ) const ( - UserOrderFieldCreatedAt UserOrderField = "CREATED_AT" + IdentityOrderFieldCreatedAt IdentityOrderField = "CREATED_AT" ) -func (p UserOrderField) Column() string { +func (p IdentityOrderField) Column() string { return string(p) } -func (p UserOrderField) String() string { +func (p IdentityOrderField) String() string { return string(p) } -func (p UserOrderField) MarshalText() ([]byte, error) { +func (p IdentityOrderField) MarshalText() ([]byte, error) { return []byte(p.String()), nil } -func (p *UserOrderField) UnmarshalText(text []byte) error { - *p = UserOrderField(text) +func (p *IdentityOrderField) UnmarshalText(text []byte) error { + *p = IdentityOrderField(text) return nil } diff --git a/pkg/coredata/membership.go b/pkg/coredata/membership.go index 4458bb5e2..de0e1df2c 100644 --- a/pkg/coredata/membership.go +++ b/pkg/coredata/membership.go @@ -33,7 +33,7 @@ import ( type ( Membership struct { ID gid.GID `db:"id"` - UserID gid.GID `db:"user_id"` + IdentityID gid.GID `db:"identity_id"` OrganizationID gid.GID `db:"organization_id"` Role MembershipRole `db:"role"` FullName string `db:"full_name"` @@ -60,11 +60,11 @@ func (m Membership) CursorKey(orderBy MembershipOrderField) page.CursorKey { panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } -func (m *Membership) LoadByUserInOrganization(ctx context.Context, conn pg.Conn, userID gid.GID, organizationID gid.GID) error { +func (m *Membership) LoadByIdentityInOrganization(ctx context.Context, conn pg.Conn, identityID gid.GID, organizationID gid.GID) error { q := ` SELECT id, - user_id, + identity_id, organization_id, role, created_at, @@ -72,12 +72,12 @@ SELECT FROM authz_memberships WHERE - user_id = @user_id + identity_id = @identity_id AND organization_id = @organization_id ` args := pgx.StrictNamedArgs{ - "user_id": userID, + "identity_id": identityID, "organization_id": organizationID, } @@ -105,7 +105,7 @@ INSERT INTO authz_memberships ( tenant_id, id, - user_id, + identity_id, organization_id, role, created_at, @@ -114,7 +114,7 @@ INSERT INTO VALUES ( @tenant_id, @id, - @user_id, + @identity_id, @organization_id, @role, @created_at, @@ -125,7 +125,7 @@ VALUES ( args := pgx.StrictNamedArgs{ "tenant_id": scope.GetTenantID(), "id": m.ID, - "user_id": m.UserID, + "identity_id": m.IdentityID, "organization_id": m.OrganizationID, "role": m.Role, "created_at": m.CreatedAt, @@ -159,7 +159,7 @@ func (m *Membership) LoadByID( WITH mbr AS ( SELECT id, - user_id, + identity_id, organization_id, role, created_at, @@ -172,17 +172,17 @@ WITH mbr AS ( ) SELECT mbr.id, - mbr.user_id, + mbr.identity_id, mbr.organization_id, mbr.role, - u.fullname as full_name, - u.email_address, + i.fullname as full_name, + i.email_address, mbr.created_at, mbr.updated_at FROM mbr JOIN - users u ON mbr.user_id = u.id + identities i ON mbr.identity_id = i.id ` query = fmt.Sprintf(query, scope.SQLFragment()) @@ -210,19 +210,19 @@ JOIN return nil } -// LoadRoleByUserAndEntityID loads a user's role by querying any entity to extract its organization_id -func (m *Membership) LoadRoleByUserAndEntityID( +// LoadRoleByIdentityAndEntityID loads an identity's role by querying any entity to extract its organization_id +func (m *Membership) LoadRoleByIdentityAndEntityID( ctx context.Context, conn pg.Conn, scope Scoper, - userID gid.GID, + identityID gid.GID, entityID gid.GID, ) error { entityType := entityID.EntityType() // For organization, the entity ID is the organization ID if entityType == OrganizationEntityType { - return m.LoadByUserAndOrg(ctx, conn, scope, userID, entityID) + return m.LoadByIdentityAndOrg(ctx, conn, scope, identityID, entityID) } tableName, ok := EntityTable(entityType) @@ -238,7 +238,7 @@ func (m *Membership) LoadRoleByUserAndEntityID( query := fmt.Sprintf(` SELECT m.id, - m.user_id, + m.identity_id, m.organization_id, m.role, m.created_at, @@ -248,14 +248,14 @@ FROM INNER JOIN %s e ON e.id = @entity_id WHERE %s - AND m.user_id = @user_id + AND m.identity_id = @identity_id AND m.organization_id = e.organization_id LIMIT 1; `, tableName, scopeFragment) args := pgx.NamedArgs{ - "user_id": userID, - "entity_id": entityID, + "identity_id": identityID, + "entity_id": entityID, } maps.Copy(args, scope.SQLArguments()) @@ -272,7 +272,7 @@ LIMIT 1; var membership Membership err = rows.Scan( &membership.ID, - &membership.UserID, + &membership.IdentityID, &membership.OrganizationID, &membership.Role, &membership.CreatedAt, @@ -287,18 +287,18 @@ LIMIT 1; return nil } -func (m *Membership) LoadByUserAndOrg( +func (m *Membership) LoadByIdentityAndOrg( ctx context.Context, conn pg.Conn, scope Scoper, - userID gid.GID, + identityID gid.GID, organizationID gid.GID, ) error { q := ` WITH mbr AS ( SELECT am.id, - am.user_id, + am.identity_id, am.organization_id, am.role, am.created_at, @@ -306,29 +306,29 @@ WITH mbr AS ( FROM authz_memberships am WHERE - am.user_id = @user_id + am.identity_id = @identity_id AND am.organization_id = @organization_id AND %s ) SELECT mbr.id, - mbr.user_id, + mbr.identity_id, mbr.organization_id, mbr.role, - u.fullname as full_name, - u.email_address, + i.fullname as full_name, + i.email_address, mbr.created_at, mbr.updated_at FROM mbr JOIN - users u ON mbr.user_id = u.id + identities i ON mbr.identity_id = i.id ` q = fmt.Sprintf(q, scope.SQLFragment()) args := pgx.StrictNamedArgs{ - "user_id": userID, + "identity_id": identityID, "organization_id": organizationID, } maps.Copy(args, scope.SQLArguments()) @@ -412,18 +412,18 @@ WHERE return nil } -func (m *Memberships) LoadByUserID( +func (m *Memberships) LoadByIdentityID( ctx context.Context, conn pg.Conn, scope Scoper, - userID gid.GID, + identityID gid.GID, cursor *page.Cursor[MembershipOrderField], ) error { query := ` WITH mbr AS ( SELECT id, - user_id, + identity_id, organization_id, role, created_at, @@ -431,24 +431,24 @@ WITH mbr AS ( FROM authz_memberships WHERE - user_id = @user_id + identity_id = @identity_id AND %s ORDER BY created_at DESC ) SELECT mbr.id, - mbr.user_id, + mbr.identity_id, mbr.organization_id, mbr.role, - u.fullname as full_name, - u.email_address, + i.fullname as full_name, + i.email_address, mbr.created_at, mbr.updated_at FROM mbr JOIN - users u ON mbr.user_id = u.id + identities i ON mbr.identity_id = i.id ORDER BY mbr.created_at DESC ` @@ -456,7 +456,7 @@ ORDER BY query = fmt.Sprintf(query, scope.SQLFragment()) args := pgx.StrictNamedArgs{ - "user_id": userID, + "identity_id": identityID, } maps.Copy(args, scope.SQLArguments()) @@ -485,7 +485,7 @@ func (m *Memberships) LoadByOrganizationID( WITH mbr AS ( SELECT id, - user_id, + identity_id, organization_id, role, created_at, @@ -498,7 +498,7 @@ WITH mbr AS ( ) SELECT id, - user_id, + identity_id, organization_id, role, full_name, @@ -508,18 +508,18 @@ SELECT FROM ( SELECT mbr.id, - mbr.user_id, + mbr.identity_id, mbr.organization_id, mbr.role, - u.fullname as full_name, - u.email_address, + i.fullname as full_name, + i.email_address, mbr.created_at, mbr.updated_at FROM mbr JOIN - users u ON mbr.user_id = u.id -) AS membership_with_user + identities i ON mbr.identity_id = i.id +) AS membership_with_identity WHERE %s ` @@ -573,10 +573,10 @@ WHERE return count, nil } -func (m *Memberships) CountByUserID( +func (m *Memberships) CountByIdentityID( ctx context.Context, conn pg.Conn, - userID gid.GID, + identityID gid.GID, ) (int, error) { query := ` SELECT @@ -584,10 +584,10 @@ SELECT FROM authz_memberships WHERE - user_id = @user_id + identity_id = @identity_id ` args := pgx.StrictNamedArgs{ - "user_id": userID, + "identity_id": identityID, } row := conn.QueryRow(ctx, query, args) diff --git a/pkg/coredata/migrations/20251220T104530Z.sql b/pkg/coredata/migrations/20251220T104530Z.sql new file mode 100644 index 000000000..745b4bc1b --- /dev/null +++ b/pkg/coredata/migrations/20251220T104530Z.sql @@ -0,0 +1,8 @@ +ALTER TABLE users RENAME TO identities; +ALTER TABLE sessions RENAME COLUMN user_id TO identity_id; +ALTER TABLE authz_memberships RENAME COLUMN user_id TO identity_id; +ALTER TABLE peoples RENAME COLUMN user_id TO identity_id; +ALTER TABLE auth_user_api_keys RENAME TO auth_personal_api_keys; +ALTER TABLE auth_personal_api_keys RENAME COLUMN user_id TO identity_id; +ALTER TABLE authz_api_keys_memberships RENAME COLUMN auth_user_api_key_id TO auth_personal_api_key_id; + diff --git a/pkg/coredata/organization.go b/pkg/coredata/organization.go index 723cbf5bf..c86ed0e55 100644 --- a/pkg/coredata/organization.go +++ b/pkg/coredata/organization.go @@ -111,21 +111,21 @@ LIMIT 1; return nil } -func (o *Organizations) LoadByUserID( +func (o *Organizations) LoadByIdentityID( ctx context.Context, conn pg.Conn, scope Scoper, - userID gid.GID, + identityID gid.GID, cursor *page.Cursor[OrganizationOrderField], ) error { q := ` -WITH user_org AS ( +WITH identity_org AS ( SELECT organization_id FROM authz_memberships WHERE - user_id = @user_id + identity_id = @identity_id ) SELECT tenant_id, @@ -143,7 +143,7 @@ SELECT FROM organizations INNER JOIN - user_org ON organizations.id = user_org.organization_id + identity_org ON organizations.id = identity_org.organization_id WHERE %s AND %s @@ -151,7 +151,7 @@ WHERE q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) - args := pgx.StrictNamedArgs{"user_id": userID} + args := pgx.StrictNamedArgs{"identity_id": identityID} maps.Copy(args, cursor.SQLArguments()) rows, err := conn.Query(ctx, q, args) @@ -169,19 +169,19 @@ WHERE return nil } -func (o *Organizations) LoadAllByUserID( +func (o *Organizations) LoadAllByIdentityID( ctx context.Context, conn pg.Conn, - userID gid.GID, + identityID gid.GID, ) error { q := ` -WITH user_org AS ( +WITH identity_org AS ( SELECT organization_id FROM authz_memberships WHERE - user_id = @user_id + identity_id = @identity_id ) SELECT tenant_id, @@ -199,12 +199,12 @@ SELECT FROM organizations INNER JOIN - user_org ON organizations.id = user_org.organization_id + identity_org ON organizations.id = identity_org.organization_id ORDER BY name ASC ` - args := pgx.StrictNamedArgs{"user_id": userID} + args := pgx.StrictNamedArgs{"identity_id": identityID} rows, err := conn.Query(ctx, q, args) if err != nil { @@ -221,20 +221,20 @@ ORDER BY return nil } -func (o *Organizations) LoadAllByUserIDWithRole( +func (o *Organizations) LoadAllByIdentityIDWithRole( ctx context.Context, conn pg.Conn, - userID gid.GID, + identityID gid.GID, role MembershipRole, ) error { q := ` -WITH user_org AS ( +WITH identity_org AS ( SELECT organization_id FROM authz_memberships WHERE - user_id = @user_id + identity_id = @identity_id AND role = @role ) SELECT @@ -253,14 +253,14 @@ SELECT FROM organizations INNER JOIN - user_org ON organizations.id = user_org.organization_id + identity_org ON organizations.id = identity_org.organization_id ORDER BY name ASC ` args := pgx.StrictNamedArgs{ - "user_id": userID, - "role": role, + "identity_id": identityID, + "role": role, } rows, err := conn.Query(ctx, q, args) @@ -278,13 +278,13 @@ ORDER BY return nil } -func (o *Organizations) LoadAllByUserAPIKeyID( +func (o *Organizations) LoadAllByPersonalAPIKeyID( ctx context.Context, conn pg.Conn, - userAPIKeyID gid.GID, + personalAPIKeyID gid.GID, ) error { q := ` -WITH user_api_key_org AS ( +WITH personal_api_key_org AS ( SELECT am.organization_id FROM @@ -292,7 +292,7 @@ WITH user_api_key_org AS ( INNER JOIN authz_memberships am ON akm.membership_id = am.id WHERE - akm.auth_user_api_key_id = @auth_user_api_key_id + akm.auth_personal_api_key_id = @auth_personal_api_key_id ) SELECT tenant_id, @@ -310,12 +310,12 @@ SELECT FROM organizations INNER JOIN - user_api_key_org ON organizations.id = user_api_key_org.organization_id + personal_api_key_org ON organizations.id = personal_api_key_org.organization_id ORDER BY name ASC ` - args := pgx.StrictNamedArgs{"auth_user_api_key_id": userAPIKeyID} + args := pgx.StrictNamedArgs{"auth_personal_api_key_id": personalAPIKeyID} rows, err := conn.Query(ctx, q, args) if err != nil { diff --git a/pkg/coredata/user_api_key.go b/pkg/coredata/personal_api_key.go similarity index 70% rename from pkg/coredata/user_api_key.go rename to pkg/coredata/personal_api_key.go index 5586157d1..f6eb90b26 100644 --- a/pkg/coredata/user_api_key.go +++ b/pkg/coredata/personal_api_key.go @@ -27,9 +27,9 @@ import ( ) type ( - UserAPIKey struct { + PersonalAPIKey struct { ID gid.GID `db:"id"` - UserID gid.GID `db:"user_id"` + IdentityID gid.GID `db:"identity_id"` Name string `db:"name"` ExpiresAt time.Time `db:"expires_at"` ExpireReason *ExpireReason `db:"expire_reason"` @@ -37,19 +37,19 @@ type ( UpdatedAt time.Time `db:"updated_at"` } - UserAPIKeys []*UserAPIKey + PersonalAPIKeys []*PersonalAPIKey ) -func (a *UserAPIKey) CursorKey(orderBy UserAPIKeyOrderField) page.CursorKey { +func (a *PersonalAPIKey) CursorKey(orderBy PersonalAPIKeyOrderField) page.CursorKey { switch orderBy { - case UserAPIKeyOrderFieldCreatedAt: + case PersonalAPIKeyOrderFieldCreatedAt: return page.NewCursorKey(a.ID, a.CreatedAt) } panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } -func (a *UserAPIKey) LoadByID( +func (a *PersonalAPIKey) LoadByID( ctx context.Context, conn pg.Conn, apiKeyID gid.GID, @@ -57,14 +57,14 @@ func (a *UserAPIKey) LoadByID( q := ` SELECT id, - user_id, + identity_id, name, expires_at, expire_reason, created_at, updated_at FROM - auth_user_api_keys + auth_personal_api_keys WHERE id = @api_key_id LIMIT 1; @@ -74,16 +74,16 @@ LIMIT 1; rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query user api key: %w", err) + return fmt.Errorf("cannot query personal api key: %w", err) } - apiKey, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[UserAPIKey]) + apiKey, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[PersonalAPIKey]) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return ErrResourceNotFound } - return fmt.Errorf("cannot collect user api key: %w", err) + return fmt.Errorf("cannot collect personal api key: %w", err) } *a = apiKey @@ -91,37 +91,37 @@ LIMIT 1; return nil } -func (a *UserAPIKeys) LoadByUserID( +func (a *PersonalAPIKeys) LoadByIdentityID( ctx context.Context, conn pg.Conn, - userID gid.GID, + identityID gid.GID, ) error { q := ` SELECT id, - user_id, + identity_id, name, expires_at, expire_reason, created_at, updated_at FROM - auth_user_api_keys + auth_personal_api_keys WHERE - user_id = @user_id + identity_id = @identity_id ORDER BY created_at DESC; ` - args := pgx.StrictNamedArgs{"user_id": userID} + args := pgx.StrictNamedArgs{"identity_id": identityID} rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query user api keys: %w", err) + return fmt.Errorf("cannot query personal api keys: %w", err) } - apiKeys, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[UserAPIKey]) + apiKeys, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[PersonalAPIKey]) if err != nil { - return fmt.Errorf("cannot collect user api keys: %w", err) + return fmt.Errorf("cannot collect personal api keys: %w", err) } *a = apiKeys @@ -129,18 +129,18 @@ ORDER BY created_at DESC; return nil } -func (a *UserAPIKeys) CountByUserID(ctx context.Context, conn pg.Conn, userID gid.GID) (int, error) { +func (a *PersonalAPIKeys) CountByIdentityID(ctx context.Context, conn pg.Conn, identityID gid.GID) (int, error) { q := ` SELECT COUNT(*) FROM - auth_user_api_keys + auth_personal_api_keys WHERE - user_id = @user_id + identity_id = @identity_id ORDER BY created_at DESC; ` - args := pgx.StrictNamedArgs{"user_id": userID} + args := pgx.StrictNamedArgs{"identity_id": identityID} row := conn.QueryRow(ctx, q, args) var count int if err := row.Scan(&count); err != nil { @@ -150,16 +150,16 @@ ORDER BY created_at DESC; return count, nil } -func (a *UserAPIKey) Insert( +func (a *PersonalAPIKey) Insert( ctx context.Context, conn pg.Conn, ) error { q := ` INSERT INTO - auth_user_api_keys (id, user_id, name, expires_at, expire_reason, created_at, updated_at) + auth_personal_api_keys (id, identity_id, name, expires_at, expire_reason, created_at, updated_at) VALUES ( @api_key_id, - @user_id, + @identity_id, @name, @expires_at, @expire_reason, @@ -170,7 +170,7 @@ VALUES ( args := pgx.StrictNamedArgs{ "api_key_id": a.ID, - "user_id": a.UserID, + "identity_id": a.IdentityID, "name": a.Name, "expires_at": a.ExpiresAt, "expire_reason": a.ExpireReason, @@ -180,19 +180,19 @@ VALUES ( _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot insert user api key: %w", err) + return fmt.Errorf("cannot insert personal api key: %w", err) } return nil } -func (a *UserAPIKey) Update( +func (a *PersonalAPIKey) Update( ctx context.Context, conn pg.Conn, ) error { q := ` UPDATE - auth_user_api_keys + auth_personal_api_keys SET name = @name, expires_at = @expires_at, @@ -212,19 +212,19 @@ WHERE _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot update user api key: %w", err) + return fmt.Errorf("cannot update personal api key: %w", err) } return nil } -func (a *UserAPIKey) Delete( +func (a *PersonalAPIKey) Delete( ctx context.Context, conn pg.Conn, ) error { q := ` DELETE FROM - auth_user_api_keys + auth_personal_api_keys WHERE id = @api_key_id ` @@ -233,7 +233,7 @@ WHERE _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot delete user api key: %w", err) + return fmt.Errorf("cannot delete personal api key: %w", err) } return nil diff --git a/pkg/coredata/user_api_key_order_field.go b/pkg/coredata/personal_api_key_order_field.go similarity index 70% rename from pkg/coredata/user_api_key_order_field.go rename to pkg/coredata/personal_api_key_order_field.go index aee261450..d1df1d0ee 100644 --- a/pkg/coredata/user_api_key_order_field.go +++ b/pkg/coredata/personal_api_key_order_field.go @@ -15,26 +15,26 @@ package coredata type ( - UserAPIKeyOrderField string + PersonalAPIKeyOrderField string ) const ( - UserAPIKeyOrderFieldCreatedAt UserAPIKeyOrderField = "CREATED_AT" + PersonalAPIKeyOrderFieldCreatedAt PersonalAPIKeyOrderField = "CREATED_AT" ) -func (p UserAPIKeyOrderField) Column() string { +func (p PersonalAPIKeyOrderField) Column() string { return string(p) } -func (p UserAPIKeyOrderField) String() string { +func (p PersonalAPIKeyOrderField) String() string { return string(p) } -func (p UserAPIKeyOrderField) MarshalText() ([]byte, error) { +func (p PersonalAPIKeyOrderField) MarshalText() ([]byte, error) { return []byte(p.String()), nil } -func (p *UserAPIKeyOrderField) UnmarshalText(text []byte) error { - *p = UserAPIKeyOrderField(text) +func (p *PersonalAPIKeyOrderField) UnmarshalText(text []byte) error { + *p = PersonalAPIKeyOrderField(text) return nil } diff --git a/pkg/coredata/session.go b/pkg/coredata/session.go index f50f7d7c8..ccde17956 100644 --- a/pkg/coredata/session.go +++ b/pkg/coredata/session.go @@ -31,7 +31,7 @@ import ( type ( Session struct { ID gid.GID `db:"id"` - UserID gid.GID `db:"user_id"` + IdentityID gid.GID `db:"identity_id"` TenantID *gid.TenantID `db:"tenant_id"` MembershipID *gid.GID `db:"membership_id"` ParentSessionID *gid.GID `db:"parent_session_id"` @@ -58,11 +58,11 @@ const ( AuthMethodSAML AuthMethod = "SAML" ) -func NewRootSession(userID gid.GID, method AuthMethod, duration time.Duration) *Session { +func NewRootSession(identityID gid.GID, method AuthMethod, duration time.Duration) *Session { now := time.Now() return &Session{ ID: gid.New(gid.NilTenant, SessionEntityType), - UserID: userID, + IdentityID: identityID, ExpiredAt: now.Add(duration), AuthMethod: method, AuthenticatedAt: now, @@ -100,7 +100,7 @@ func (s *Session) LoadByID( q := ` SELECT id, - user_id, + identity_id, tenant_id, membership_id, data, @@ -146,10 +146,10 @@ func (s *Session) Insert( ) error { q := ` INSERT INTO - sessions (id, user_id, tenant_id, membership_id, data, parent_session_id, auth_method, authenticated_at, expire_reason, user_agent, ip_address, expired_at, created_at, updated_at) + sessions (id, identity_id, tenant_id, membership_id, data, parent_session_id, auth_method, authenticated_at, expire_reason, user_agent, ip_address, expired_at, created_at, updated_at) VALUES ( @session_id, - @user_id, + @identity_id, @tenant_id, @membership_id, @data, @@ -167,7 +167,7 @@ VALUES ( args := pgx.StrictNamedArgs{ "session_id": s.ID, - "user_id": s.UserID, + "identity_id": s.IdentityID, "tenant_id": s.TenantID, "membership_id": s.MembershipID, "data": s.Data, @@ -225,11 +225,11 @@ WHERE return nil } -func (s *Sessions) LoadByUserID(ctx context.Context, conn pg.Conn, userID gid.GID, cursor *page.Cursor[SessionOrderField]) error { +func (s *Sessions) LoadByIdentityID(ctx context.Context, conn pg.Conn, identityID gid.GID, cursor *page.Cursor[SessionOrderField]) error { q := ` SELECT id, - user_id, + identity_id, tenant_id, membership_id, data, @@ -245,13 +245,13 @@ SELECT FROM sessions WHERE - user_id = @user_id + identity_id = @identity_id AND %s ` q = fmt.Sprintf(q, cursor.SQLFragment()) - args := pgx.StrictNamedArgs{"user_id": userID} + args := pgx.StrictNamedArgs{"identity_id": identityID} maps.Copy(args, cursor.SQLArguments()) rows, err := conn.Query(ctx, q, args) @@ -269,17 +269,17 @@ WHERE return nil } -func (s *Sessions) CountByUserID(ctx context.Context, conn pg.Conn, userID gid.GID) (int, error) { +func (s *Sessions) CountByIdentityID(ctx context.Context, conn pg.Conn, identityID gid.GID) (int, error) { q := ` SELECT COUNT(*) FROM sessions WHERE - user_id = @user_id + identity_id = @identity_id ` - args := pgx.StrictNamedArgs{"user_id": userID} + args := pgx.StrictNamedArgs{"identity_id": identityID} row := conn.QueryRow(ctx, q, args) @@ -291,7 +291,7 @@ WHERE return count, nil } -func (s *Sessions) ExpireAllForUserExceptOneSession(ctx context.Context, conn pg.Conn, userID gid.GID, sessionID gid.GID) (int64, error) { +func (s *Sessions) ExpireAllForIdentityExceptOneSession(ctx context.Context, conn pg.Conn, identityID gid.GID, sessionID gid.GID) (int64, error) { q := ` UPDATE sessions SET @@ -300,13 +300,13 @@ SET expire_reason = 'revoked' WHERE id != @session_id - AND user_id = @user_id + AND identity_id = @identity_id AND expire_reason IS NULL ` args := pgx.StrictNamedArgs{ - "session_id": sessionID, - "user_id": userID, + "session_id": sessionID, + "identity_id": identityID, } result, err := conn.Exec(ctx, q, args) @@ -321,7 +321,7 @@ func (s *Session) LoadByRootSessionIDAndMembershipID(ctx context.Context, conn p q := ` SELECT id, - user_id, + identity_id, tenant_id, membership_id, data, diff --git a/pkg/iam/access_management_service.go b/pkg/iam/access_management_service.go index 0676eea83..32a4c2d6a 100644 --- a/pkg/iam/access_management_service.go +++ b/pkg/iam/access_management_service.go @@ -38,12 +38,12 @@ func NewAccessManagementService(svc *Service) *AccessManagementService { } // Authorize implements Model 2 authorization: -// - principalID is the actor (User now; later service accounts) -// - credentialID is an optional credential (UserAPIKey now) +// - principalID is the actor (Identity now; later service accounts) +// - credentialID is an optional credential (PersonalAPIKey now) // - intersection semantics: actor must be allowed AND credential (if present) must be allowed. // // Entity scope: -// - Global/self-owned entities (User/Session/UserAPIKey) are authorized via ownership checks only (no global admin). +// - Global/self-owned entities (Identity/Session/PersonalAPIKey) are authorized via ownership checks only (no global admin). // - Organization-scoped entities are authorized via membership lookups that derive organization_id from entityID. func (s *AccessManagementService) Authorize(ctx context.Context, principalID gid.GID, credentialID *gid.GID, entityID gid.GID, action Action) error { requiredRoles := GetPermissionsForAction(entityID.EntityType(), action) @@ -53,7 +53,7 @@ func (s *AccessManagementService) Authorize(ctx context.Context, principalID gid } switch principalID.EntityType() { - case coredata.UserEntityType: + case coredata.IdentityEntityType: // ok default: return NewUnsupportedPrincipalTypeError(principalID.EntityType()) @@ -62,7 +62,7 @@ func (s *AccessManagementService) Authorize(ctx context.Context, principalID gid return s.pg.WithConn(ctx, func(conn pg.Conn) error { // Global/self-owned path switch entityID.EntityType() { - case coredata.UserEntityType: + case coredata.IdentityEntityType: if entityID != principalID { return NewInsufficientPermissionsError(principalID, entityID, action) } @@ -73,17 +73,17 @@ func (s *AccessManagementService) Authorize(ctx context.Context, principalID gid if err := sess.LoadByID(ctx, conn, entityID); err != nil { return NewInsufficientPermissionsError(principalID, entityID, action) } - if sess.UserID != principalID { + if sess.IdentityID != principalID { return NewInsufficientPermissionsError(principalID, entityID, action) } return nil - case coredata.UserAPIKeyEntityType: - key := &coredata.UserAPIKey{} + case coredata.PersonalAPIKeyEntityType: + key := &coredata.PersonalAPIKey{} if err := key.LoadByID(ctx, conn, entityID); err != nil { return NewInsufficientPermissionsError(principalID, entityID, action) } - if key.UserID != principalID { + if key.IdentityID != principalID { return NewInsufficientPermissionsError(principalID, entityID, action) } return nil @@ -92,7 +92,7 @@ func (s *AccessManagementService) Authorize(ctx context.Context, principalID gid // Organization-scoped path (derive org via joins) scope := coredata.NewScope(entityID.TenantID()) - actorRoleName, err := s.loadUserRoleForEntity(ctx, conn, scope, principalID, entityID) + actorRoleName, err := s.loadIdentityRoleForEntity(ctx, conn, scope, principalID, entityID) if err != nil || !requiredRoleNamesContain(actorRoleName, requiredRoles) { return NewInsufficientPermissionsError(principalID, entityID, action) } @@ -100,13 +100,13 @@ func (s *AccessManagementService) Authorize(ctx context.Context, principalID gid // Optional credential restriction (intersection) if credentialID != nil { switch credentialID.EntityType() { - case coredata.UserAPIKeyEntityType: + case coredata.PersonalAPIKeyEntityType: // Defensive check: credential must belong to actor - apiKey := &coredata.UserAPIKey{} + apiKey := &coredata.PersonalAPIKey{} if err := apiKey.LoadByID(ctx, conn, *credentialID); err != nil { return NewInsufficientPermissionsError(principalID, entityID, action) } - if apiKey.UserID != principalID { + if apiKey.IdentityID != principalID { return NewInsufficientPermissionsError(principalID, entityID, action) } @@ -123,15 +123,15 @@ func (s *AccessManagementService) Authorize(ctx context.Context, principalID gid }) } -func (s *AccessManagementService) loadUserRoleForEntity( +func (s *AccessManagementService) loadIdentityRoleForEntity( ctx context.Context, conn pg.Conn, scope coredata.Scoper, - userID gid.GID, + identityID gid.GID, entityID gid.GID, ) (Role, error) { var m coredata.Membership - if err := m.LoadRoleByUserAndEntityID(ctx, conn, scope, userID, entityID); err != nil { + if err := m.LoadRoleByIdentityAndEntityID(ctx, conn, scope, identityID, entityID); err != nil { // Do not leak existence details if errors.Is(err, coredata.ErrResourceNotFound) { return "", err @@ -148,7 +148,7 @@ func (s *AccessManagementService) loadAPIKeyRoleForEntity( apiKeyID gid.GID, entityID gid.GID, ) (Role, error) { - var akm coredata.UserAPIKeyMembership + var akm coredata.PersonalAPIKeyMembership if err := akm.LoadRoleByAPIKeyAndEntityID(ctx, conn, scope, apiKeyID, entityID); err != nil { return "", err } @@ -173,35 +173,3 @@ func requiredRoleNamesContain(roleName Role, required []Role) bool { } return false } - -// func (s AccountService) AllAccessibleTenants(ctx context.Context, identityID gid.GID) ([]gid.TenantID, error) { -// var tenants []gid.TenantID - -// err := s.pg.WithConn( -// ctx, -// func(conn pg.Conn) error { -// memberships := coredata.Memberships{} -// orderBy := page.OrderBy[coredata.MembershipOrderField]{ -// Field: coredata.MembershipOrderFieldCreatedAt, -// Direction: page.OrderDirectionDesc, -// } -// cursor := page.NewCursor(1000, nil, page.Head, orderBy) - -// err := memberships.LoadByUserID(ctx, conn, coredata.NewNoScope(), identityID, cursor) -// if err != nil { -// return fmt.Errorf("cannot load memberships: %w", err) -// } - -// for _, membership := range memberships { -// tenants = append(tenants, membership.ID.TenantID()) -// } -// return nil -// }, -// ) - -// if err != nil { -// return nil, err -// } - -// return tenants, nil -// } diff --git a/pkg/iam/account_service.go b/pkg/iam/account_service.go index 7d54570e9..bfd64a963 100644 --- a/pkg/iam/account_service.go +++ b/pkg/iam/account_service.go @@ -35,7 +35,7 @@ type ( *Service } - UserAPIKeyTokenData struct { + PersonalAPIKeyTokenData struct { Version int `json:"v"` KeyID gid.GID `json:"kid"` PrincipalID gid.GID `json:"pid"` @@ -43,8 +43,8 @@ type ( } EmailConfirmationData struct { - UserID gid.GID `json:"uid"` - Email mail.Addr `json:"email"` + IdentityID gid.GID `json:"uid"` + Email mail.Addr `json:"email"` } ) @@ -78,7 +78,7 @@ func (s AccountService) ChangeEmail(ctx context.Context, identityID gid.GID, req s.tokenSecret, TokenTypeEmailConfirmation, 24*time.Hour, - EmailConfirmationData{UserID: identityID, Email: req.NewEmail}, + EmailConfirmationData{IdentityID: identityID, Email: req.NewEmail}, ) if err != nil { return fmt.Errorf("cannot generate confirmation token: %w", err) @@ -97,17 +97,17 @@ func (s AccountService) ChangeEmail(ctx context.Context, identityID gid.GID, req return s.pg.WithTx( ctx, func(tx pg.Conn) error { - user := &coredata.User{} - err := user.LoadByID(ctx, tx, identityID) + identity := &coredata.Identity{} + err := identity.LoadByID(ctx, tx, identityID) if err != nil { if err == coredata.ErrResourceNotFound { - return NewUserNotFoundError(identityID) + return NewIdentityNotFoundError(identityID) } - return fmt.Errorf("cannot load user: %w", err) + return fmt.Errorf("cannot load identity: %w", err) } - isPasswordMatch, err := s.hp.ComparePasswordAndHash([]byte(req.Password), user.HashedPassword) + isPasswordMatch, err := s.hp.ComparePasswordAndHash([]byte(req.Password), identity.HashedPassword) if err != nil { return fmt.Errorf("cannot compare password: %w", err) } @@ -116,18 +116,18 @@ func (s AccountService) ChangeEmail(ctx context.Context, identityID gid.GID, req return NewInvalidPasswordError("invalid password") } - user.EmailAddress = req.NewEmail - user.EmailAddressVerified = false - user.UpdatedAt = time.Now() + identity.EmailAddress = req.NewEmail + identity.EmailAddressVerified = false + identity.UpdatedAt = time.Now() - err = user.Update(ctx, tx) + err = identity.Update(ctx, tx) if err != nil { - return fmt.Errorf("cannot update user: %w", err) + return fmt.Errorf("cannot update identity: %w", err) } subject, textBody, htmlBody, err := emails.RenderConfirmEmail( s.baseURL, - user.FullName, + identity.FullName, confirmationUrl, ) if err != nil { @@ -135,8 +135,8 @@ func (s AccountService) ChangeEmail(ctx context.Context, identityID gid.GID, req } confirmationEmail := coredata.NewEmail( - user.FullName, - user.EmailAddress, + identity.FullName, + identity.EmailAddress, subject, textBody, htmlBody, @@ -161,30 +161,30 @@ func (s AccountService) VerifyEmail(ctx context.Context, token string) error { return s.pg.WithTx( ctx, func(tx pg.Conn) error { - user := &coredata.User{} - err := user.LoadByID(ctx, tx, payload.Data.UserID) + identity := &coredata.Identity{} + err := identity.LoadByID(ctx, tx, payload.Data.IdentityID) if err != nil { if err == coredata.ErrResourceNotFound { - return NewUserNotFoundError(payload.Data.UserID) + return NewIdentityNotFoundError(payload.Data.IdentityID) } - return fmt.Errorf("cannot load user: %w", err) + return fmt.Errorf("cannot load identity: %w", err) } - if user.EmailAddress != payload.Data.Email { + if identity.EmailAddress != payload.Data.Email { return NewEmailVerificationMismatchError() } - if user.EmailAddressVerified { + if identity.EmailAddressVerified { return NewEmailAlreadyVerifiedError() } - user.EmailAddressVerified = true - user.UpdatedAt = time.Now() + identity.EmailAddressVerified = true + identity.UpdatedAt = time.Now() - err = user.Update(ctx, tx) + err = identity.Update(ctx, tx) if err != nil { - return fmt.Errorf("cannot update user: %w", err) + return fmt.Errorf("cannot update identity: %w", err) } return nil @@ -205,16 +205,16 @@ func (s *AccountService) AcceptInvitation( err := s.pg.WithTx( ctx, func(tx pg.Conn) error { - user := coredata.User{} + identity := coredata.Identity{} invitation := coredata.Invitation{} - err := user.LoadByID(ctx, tx, identityID) + err := identity.LoadByID(ctx, tx, identityID) if err != nil { if err == coredata.ErrResourceNotFound { - return NewUserNotFoundError(identityID) + return NewIdentityNotFoundError(identityID) } - return fmt.Errorf("cannot load user: %w", err) + return fmt.Errorf("cannot load identity: %w", err) } err = invitation.LoadByID(ctx, tx, coredata.NewNoScope(), invitationID) @@ -226,7 +226,7 @@ func (s *AccountService) AcceptInvitation( return fmt.Errorf("cannot load invitation: %w", err) } - if invitation.Email != user.EmailAddress { + if invitation.Email != identity.EmailAddress { return NewInvitationNotFoundError(invitationID) } @@ -243,7 +243,7 @@ func (s *AccountService) AcceptInvitation( membership = &coredata.Membership{ ID: gid.New(tenantID, coredata.MembershipEntityType), - UserID: identityID, + IdentityID: identityID, OrganizationID: invitation.OrganizationID, Role: invitation.Role, CreatedAt: now, @@ -286,11 +286,11 @@ func (s *AccountService) ListPendingInvitations( err := s.pg.WithConn( ctx, func(conn pg.Conn) error { - identity := coredata.User{} + identity := coredata.Identity{} err := identity.LoadByID(ctx, conn, identityID) if err != nil { if err == coredata.ErrResourceNotFound { - return NewUserNotFoundError(identityID) + return NewIdentityNotFoundError(identityID) } return fmt.Errorf("cannot load identity: %w", err) @@ -323,7 +323,7 @@ func (s *AccountService) CountPendingInvitations( err := s.pg.WithConn( ctx, func(conn pg.Conn) error { - identity := coredata.User{} + identity := coredata.Identity{} err := identity.LoadByID(ctx, conn, identityID) if err != nil { return fmt.Errorf("cannot load identity: %w", err) @@ -357,7 +357,7 @@ func (s *AccountService) ListMemberships( err := s.pg.WithConn( ctx, func(conn pg.Conn) error { - err := memberships.LoadByUserID(ctx, conn, coredata.NewNoScope(), identityID, cursor) + err := memberships.LoadByIdentityID(ctx, conn, coredata.NewNoScope(), identityID, cursor) if err != nil { return fmt.Errorf("cannot load memberships: %w", err) } @@ -383,7 +383,7 @@ func (s *AccountService) CountMemberships( ctx, func(conn pg.Conn) (err error) { memberships := coredata.Memberships{} - count, err = memberships.CountByUserID(ctx, conn, identityID) + count, err = memberships.CountByIdentityID(ctx, conn, identityID) if err != nil { return fmt.Errorf("cannot count memberships: %w", err) } @@ -407,17 +407,17 @@ func (s AccountService) ChangePassword(ctx context.Context, identityID gid.GID, return s.pg.WithTx( ctx, func(tx pg.Conn) error { - user := &coredata.User{} - err := user.LoadByID(ctx, tx, identityID) + identity := &coredata.Identity{} + err := identity.LoadByID(ctx, tx, identityID) if err != nil { if err == coredata.ErrResourceNotFound { - return NewUserNotFoundError(identityID) + return NewIdentityNotFoundError(identityID) } - return fmt.Errorf("cannot load user: %w", err) + return fmt.Errorf("cannot load identity: %w", err) } - isLegacyPasswordMatch, err := s.hp.ComparePasswordAndHash([]byte(req.CurrentPassword), user.HashedPassword) + isLegacyPasswordMatch, err := s.hp.ComparePasswordAndHash([]byte(req.CurrentPassword), identity.HashedPassword) if err != nil { return fmt.Errorf("cannot compare legacy password: %w", err) } @@ -431,15 +431,15 @@ func (s AccountService) ChangePassword(ctx context.Context, identityID gid.GID, return fmt.Errorf("cannot hash new password: %w", err) } - user.HashedPassword = newPasswordHash - user.UpdatedAt = time.Now() + identity.HashedPassword = newPasswordHash + identity.UpdatedAt = time.Now() - err = user.Update(ctx, tx) + err = identity.Update(ctx, tx) if err != nil { - return fmt.Errorf("cannot update user: %w", err) + return fmt.Errorf("cannot update identity: %w", err) } - // TODO: email to notify user that their password has been changed + // TODO: email to notify identity that their password has been changed return nil }, @@ -453,7 +453,7 @@ func (s AccountService) CountSessions(ctx context.Context, identityID gid.GID) ( ctx, func(conn pg.Conn) (err error) { sessions := coredata.Sessions{} - count, err = sessions.CountByUserID(ctx, conn, identityID) + count, err = sessions.CountByIdentityID(ctx, conn, identityID) if err != nil { return fmt.Errorf("cannot count sessions: %w", err) } @@ -475,7 +475,7 @@ func (s AccountService) ListSessions( err := s.pg.WithConn( ctx, func(conn pg.Conn) error { - err := sessions.LoadByUserID(ctx, conn, identityID, cursor) + err := sessions.LoadByIdentityID(ctx, conn, identityID, cursor) if err != nil { return fmt.Errorf("cannot load sessions: %w", err) } @@ -491,19 +491,19 @@ func (s AccountService) ListSessions( return page.NewPage(sessions, cursor), nil } -func (s AccountService) GetIdentity(ctx context.Context, identityID gid.GID) (*coredata.User, error) { - user := &coredata.User{} +func (s AccountService) GetIdentity(ctx context.Context, identityID gid.GID) (*coredata.Identity, error) { + identity := &coredata.Identity{} err := s.pg.WithConn( ctx, func(conn pg.Conn) error { - err := user.LoadByID(ctx, conn, identityID) + err := identity.LoadByID(ctx, conn, identityID) if err != nil { if err == coredata.ErrResourceNotFound { - return NewUserNotFoundError(identityID) + return NewIdentityNotFoundError(identityID) } - return fmt.Errorf("cannot load user: %w", err) + return fmt.Errorf("cannot load identity: %w", err) } return nil @@ -513,20 +513,20 @@ func (s AccountService) GetIdentity(ctx context.Context, identityID gid.GID) (*c return nil, err } - return user, nil + return identity, nil } func (s AccountService) ListPersonalAPIKeys( ctx context.Context, identityID gid.GID, - cursor *page.Cursor[coredata.UserAPIKeyOrderField], -) (*page.Page[*coredata.UserAPIKey, coredata.UserAPIKeyOrderField], error) { - var personalAccessTokens coredata.UserAPIKeys + cursor *page.Cursor[coredata.PersonalAPIKeyOrderField], +) (*page.Page[*coredata.PersonalAPIKey, coredata.PersonalAPIKeyOrderField], error) { + var personalAccessTokens coredata.PersonalAPIKeys err := s.pg.WithConn( ctx, func(conn pg.Conn) error { - err := personalAccessTokens.LoadByUserID(ctx, conn, identityID) + err := personalAccessTokens.LoadByIdentityID(ctx, conn, identityID) if err != nil { return fmt.Errorf("cannot load personal access tokens: %w", err) } @@ -548,8 +548,8 @@ func (s AccountService) CountPersonalAPIKeys(ctx context.Context, identityID gid err := s.pg.WithConn( ctx, func(conn pg.Conn) (err error) { - personalAccessTokens := coredata.UserAPIKeys{} - count, err = personalAccessTokens.CountByUserID(ctx, conn, identityID) + personalAccessTokens := coredata.PersonalAPIKeys{} + count, err = personalAccessTokens.CountByIdentityID(ctx, conn, identityID) if err != nil { return fmt.Errorf("cannot count personal access tokens: %w", err) } @@ -561,10 +561,10 @@ func (s AccountService) CountPersonalAPIKeys(ctx context.Context, identityID gid return count, err } -func (s AccountService) GetIdentityForMembership(ctx context.Context, membershipID gid.GID) (*coredata.User, error) { +func (s AccountService) GetIdentityForMembership(ctx context.Context, membershipID gid.GID) (*coredata.Identity, error) { var ( scope = coredata.NewScopeFromObjectID(membershipID) - identity = &coredata.User{} + identity = &coredata.Identity{} ) err := s.pg.WithConn( @@ -580,10 +580,10 @@ func (s AccountService) GetIdentityForMembership(ctx context.Context, membership return fmt.Errorf("cannot load membership: %w", err) } - err = identity.LoadByID(ctx, conn, membership.UserID) + err = identity.LoadByID(ctx, conn, membership.IdentityID) if err != nil { if err == coredata.ErrResourceNotFound { - return NewUserNotFoundError(membership.UserID) + return NewIdentityNotFoundError(membership.IdentityID) } return fmt.Errorf("cannot load identity: %w", err) @@ -605,10 +605,10 @@ func (s *AccountService) CreatePersonalAPIKey( identityID gid.GID, name string, expiresAt time.Time, -) (*coredata.UserAPIKey, string, error) { +) (*coredata.PersonalAPIKey, string, error) { var ( - userAPIKey *coredata.UserAPIKey - token string + personalAPIKey *coredata.PersonalAPIKey + token string ) err := s.pg.WithTx( @@ -616,33 +616,33 @@ func (s *AccountService) CreatePersonalAPIKey( func(tx pg.Conn) (err error) { now := time.Now() - userAPIKey = &coredata.UserAPIKey{ - ID: gid.New(gid.NilTenant, coredata.UserAPIKeyEntityType), - UserID: identityID, - Name: name, - ExpiresAt: expiresAt, - CreatedAt: now, - UpdatedAt: now, + personalAPIKey = &coredata.PersonalAPIKey{ + ID: gid.New(gid.NilTenant, coredata.PersonalAPIKeyEntityType), + IdentityID: identityID, + Name: name, + ExpiresAt: expiresAt, + CreatedAt: now, + UpdatedAt: now, } - if err := userAPIKey.Insert(ctx, tx); err != nil { - return fmt.Errorf("cannot insert user api key: %w", err) + if err := personalAPIKey.Insert(ctx, tx); err != nil { + return fmt.Errorf("cannot insert personal api key: %w", err) } token, err = statelesstoken.NewDeterministicToken( s.tokenSecret, TokenTypeAPIKey, - userAPIKey.ExpiresAt, - userAPIKey.CreatedAt, - UserAPIKeyTokenData{ + personalAPIKey.ExpiresAt, + personalAPIKey.CreatedAt, + PersonalAPIKeyTokenData{ Version: 2, - KeyID: userAPIKey.ID, + KeyID: personalAPIKey.ID, PrincipalID: identityID, - IssuedAt: userAPIKey.CreatedAt, + IssuedAt: personalAPIKey.CreatedAt, }, ) if err != nil { - return fmt.Errorf("cannot generate user api key token: %w", err) + return fmt.Errorf("cannot generate personal api key token: %w", err) } return nil @@ -653,34 +653,34 @@ func (s *AccountService) CreatePersonalAPIKey( return nil, "", err } - return userAPIKey, token, nil + return personalAPIKey, token, nil } func (s *AccountService) DeletePersonalAPIKey( ctx context.Context, identityID gid.GID, - userAPIKeyID gid.GID, + personalAPIKeyID gid.GID, ) error { return s.pg.WithTx( ctx, func(tx pg.Conn) error { - userAPIKey := &coredata.UserAPIKey{} - err := userAPIKey.LoadByID(ctx, tx, userAPIKeyID) + personalAPIKey := &coredata.PersonalAPIKey{} + err := personalAPIKey.LoadByID(ctx, tx, personalAPIKeyID) if err != nil { if err == coredata.ErrResourceNotFound { - return NewUserAPIKeyNotFoundError(userAPIKeyID) + return NewPersonalAPIKeyNotFoundError(personalAPIKeyID) } - return fmt.Errorf("cannot load user api key: %w", err) + return fmt.Errorf("cannot load personal api key: %w", err) } - if userAPIKey.UserID != identityID { - return NewUserAPIKeyNotFoundError(userAPIKeyID) + if personalAPIKey.IdentityID != identityID { + return NewPersonalAPIKeyNotFoundError(personalAPIKeyID) } - err = userAPIKey.Delete(ctx, tx) + err = personalAPIKey.Delete(ctx, tx) if err != nil { - return fmt.Errorf("cannot delete user api key: %w", err) + return fmt.Errorf("cannot delete personal api key: %w", err) } return nil @@ -699,7 +699,7 @@ func (s AccountService) ListOrganizations(ctx context.Context, identityID gid.GI err := s.pg.WithConn( ctx, func(conn pg.Conn) error { - err := organizations.LoadByUserID(ctx, conn, coredata.NewNoScope(), identityID, cursor) + err := organizations.LoadByIdentityID(ctx, conn, coredata.NewNoScope(), identityID, cursor) if err != nil { return fmt.Errorf("cannot load organizations: %w", err) } @@ -714,35 +714,3 @@ func (s AccountService) ListOrganizations(ctx context.Context, identityID gid.GI return organizations, nil } - -// func (s AccountService) AllAccessibleTenants(ctx context.Context, identityID gid.GID) ([]gid.TenantID, error) { -// var tenants []gid.TenantID - -// err := s.pg.WithConn( -// ctx, -// func(conn pg.Conn) error { -// memberships := coredata.Memberships{} -// orderBy := page.OrderBy[coredata.MembershipOrderField]{ -// Field: coredata.MembershipOrderFieldCreatedAt, -// Direction: page.OrderDirectionDesc, -// } -// cursor := page.NewCursor(1000, nil, page.Head, orderBy) - -// err := memberships.LoadByUserID(ctx, conn, coredata.NewNoScope(), identityID, cursor) -// if err != nil { -// return fmt.Errorf("cannot load memberships: %w", err) -// } - -// for _, membership := range memberships { -// tenants = append(tenants, membership.ID.TenantID()) -// } -// return nil -// }, -// ) - -// if err != nil { -// return nil, err -// } - -// return tenants, nil -// } diff --git a/pkg/iam/api_key.go b/pkg/iam/api_key.go index 0faa7c3e6..736ffbc1e 100644 --- a/pkg/iam/api_key.go +++ b/pkg/iam/api_key.go @@ -35,9 +35,9 @@ func NewAPIKeyService(svc *Service) *APIKeyService { return &APIKeyService{Service: svc} } -func (s *APIKeyService) GetAPIKey(ctx context.Context, keyID gid.GID) (*coredata.UserAPIKey, error) { +func (s *APIKeyService) GetAPIKey(ctx context.Context, keyID gid.GID) (*coredata.PersonalAPIKey, error) { var ( - apiKey = &coredata.UserAPIKey{} + apiKey = &coredata.PersonalAPIKey{} now = time.Now() ) @@ -46,12 +46,12 @@ func (s *APIKeyService) GetAPIKey(ctx context.Context, keyID gid.GID) (*coredata func(tx pg.Conn) error { if err := apiKey.LoadByID(ctx, tx, keyID); err != nil { if err == coredata.ErrResourceNotFound { - return NewUserAPIKeyNotFoundError(keyID) + return NewPersonalAPIKeyNotFoundError(keyID) } } if apiKey.ExpireReason != nil { - return NewUserAPIKeyExpiredError(keyID) + return NewPersonalAPIKeyExpiredError(keyID) } if now.After(apiKey.ExpiresAt) { @@ -60,10 +60,10 @@ func (s *APIKeyService) GetAPIKey(ctx context.Context, keyID gid.GID) (*coredata apiKey.UpdatedAt = now if err := apiKey.Update(ctx, tx); err != nil { - return fmt.Errorf("cannot update user api key: %w", err) + return fmt.Errorf("cannot update personal api key: %w", err) } - return NewUserAPIKeyExpiredError(keyID) + return NewPersonalAPIKeyExpiredError(keyID) } return nil diff --git a/pkg/iam/auth_service.go b/pkg/iam/auth_service.go index c8303e765..e1daea7fb 100644 --- a/pkg/iam/auth_service.go +++ b/pkg/iam/auth_service.go @@ -110,7 +110,7 @@ func (req CreateIdentityWithPasswordRequest) Validate() error { func (s *AuthService) CreateIdentityFromInvitation( ctx context.Context, req *CreateIdentityFromInvitationRequest, -) (*coredata.User, *coredata.Session, error) { +) (*coredata.Identity, *coredata.Session, error) { if err := req.Validate(); err != nil { return nil, nil, fmt.Errorf("invalid request: %w", err) } @@ -123,7 +123,7 @@ func (s *AuthService) CreateIdentityFromInvitation( var ( scope = coredata.NewScopeFromObjectID(payload.Data.InvitationID) invitation = &coredata.Invitation{} - user = &coredata.User{} + identity = &coredata.Identity{} session = &coredata.Session{} now = time.Now() ) @@ -153,8 +153,8 @@ func (s *AuthService) CreateIdentityFromInvitation( return NewInvitationExpiredError(payload.Data.InvitationID) } - user = &coredata.User{ - ID: gid.New(gid.NilTenant, coredata.UserEntityType), + identity = &coredata.Identity{ + ID: gid.New(gid.NilTenant, coredata.IdentityEntityType), EmailAddress: invitation.Email, HashedPassword: hashedPassword, EmailAddressVerified: true, @@ -163,16 +163,16 @@ func (s *AuthService) CreateIdentityFromInvitation( UpdatedAt: now, } - err = user.Insert(ctx, tx) + err = identity.Insert(ctx, tx) if err != nil { if err == coredata.ErrResourceAlreadyExists { - return NewUserAlreadyExistsError(invitation.Email) + return NewIdentityAlreadyExistsError(invitation.Email) } - return fmt.Errorf("cannot insert user: %w", err) + return fmt.Errorf("cannot insert identity: %w", err) } - session = coredata.NewRootSession(user.ID, coredata.AuthMethodPassword, s.sessionDuration) + session = coredata.NewRootSession(identity.ID, coredata.AuthMethodPassword, s.sessionDuration) err = session.Insert(ctx, tx) if err != nil { return fmt.Errorf("cannot insert session: %w", err) @@ -186,7 +186,7 @@ func (s *AuthService) CreateIdentityFromInvitation( return nil, nil, err } - return user, session, nil + return identity, session, nil } func (s AuthService) ResetPassword( @@ -210,26 +210,26 @@ func (s AuthService) ResetPassword( return s.pg.WithTx( ctx, func(tx pg.Conn) error { - user := &coredata.User{} - err := user.LoadByEmail(ctx, tx, payload.Data.Email) + identity := &coredata.Identity{} + err := identity.LoadByEmail(ctx, tx, payload.Data.Email) if err != nil { if err == coredata.ErrResourceNotFound { - return nil // Don't leak information about non-existent users + return nil // Don't leak information about non-existent identities } - return fmt.Errorf("cannot load user: %w", err) + return fmt.Errorf("cannot load identity: %w", err) } - user.HashedPassword = hashedPassword - user.UpdatedAt = time.Now() + identity.HashedPassword = hashedPassword + identity.UpdatedAt = time.Now() - err = user.Update(ctx, tx) + err = identity.Update(ctx, tx) if err != nil { if err == coredata.ErrResourceNotFound { - return nil // Don't leak information about non-existent users + return nil // Don't leak information about non-existent identities } - return fmt.Errorf("cannot update user: %w", err) + return fmt.Errorf("cannot update identity: %w", err) } return nil @@ -264,18 +264,18 @@ func (s AuthService) SendPasswordResetInstructionByEmail( return s.pg.WithTx( ctx, func(tx pg.Conn) error { - user := &coredata.User{} - if err := user.LoadByEmail(ctx, tx, email); err != nil { + identity := &coredata.Identity{} + if err := identity.LoadByEmail(ctx, tx, email); err != nil { if err == coredata.ErrResourceNotFound { - return nil // Don't leak information about non-existent users + return nil // Don't leak information about non-existent identities } - return fmt.Errorf("cannot load user: %w", err) + return fmt.Errorf("cannot load identity: %w", err) } subject, textBody, htmlBody, err := emails.RenderPasswordReset( s.baseURL, - user.FullName, + identity.FullName, resetPasswordUrl, ) if err != nil { @@ -283,8 +283,8 @@ func (s AuthService) SendPasswordResetInstructionByEmail( } passwordResetEmail := coredata.NewEmail( - user.FullName, - user.EmailAddress, + identity.FullName, + identity.EmailAddress, subject, textBody, htmlBody, @@ -303,7 +303,7 @@ func (s AuthService) SendPasswordResetInstructionByEmail( func (s AuthService) CreateIdentityWithPassword( ctx context.Context, req *CreateIdentityWithPasswordRequest, -) (*coredata.User, *coredata.Session, error) { +) (*coredata.Identity, *coredata.Session, error) { if s.disableSignup { // TODO Rename this one to disableSignup return nil, nil, NewErrSignupDisabled() } @@ -320,8 +320,8 @@ func (s AuthService) CreateIdentityWithPassword( var ( now = time.Now() - user = &coredata.User{ - ID: gid.New(gid.NilTenant, coredata.UserEntityType), + identity = &coredata.Identity{ + ID: gid.New(gid.NilTenant, coredata.IdentityEntityType), EmailAddress: req.Email, HashedPassword: hashedPassword, EmailAddressVerified: false, @@ -330,14 +330,14 @@ func (s AuthService) CreateIdentityWithPassword( UpdatedAt: now, } - session = coredata.NewRootSession(user.ID, coredata.AuthMethodPassword, 24*time.Hour*7) + session = coredata.NewRootSession(identity.ID, coredata.AuthMethodPassword, 24*time.Hour*7) ) confirmationToken, err := statelesstoken.NewToken( s.tokenSecret, TokenTypeEmailConfirmation, 24*time.Hour, - EmailConfirmationData{UserID: user.ID, Email: user.EmailAddress}, + EmailConfirmationData{IdentityID: identity.ID, Email: identity.EmailAddress}, ) if err != nil { return nil, nil, fmt.Errorf("cannot generate confirmation token: %w", err) @@ -358,7 +358,7 @@ func (s AuthService) CreateIdentityWithPassword( subject, textBody, htmlBody, err := emails.RenderConfirmEmail( s.baseURL, - user.FullName, + identity.FullName, confirmationUrl, ) if err != nil { @@ -366,8 +366,8 @@ func (s AuthService) CreateIdentityWithPassword( } confirmationEmail := coredata.NewEmail( - user.FullName, - user.EmailAddress, + identity.FullName, + identity.EmailAddress, subject, textBody, htmlBody, @@ -376,13 +376,13 @@ func (s AuthService) CreateIdentityWithPassword( err = s.pg.WithTx( ctx, func(tx pg.Conn) error { - err := user.Insert(ctx, tx) + err := identity.Insert(ctx, tx) if err != nil { if err == coredata.ErrResourceAlreadyExists { - return NewUserAlreadyExistsError(user.EmailAddress) + return NewIdentityAlreadyExistsError(identity.EmailAddress) } - return fmt.Errorf("cannot insert user: %w", err) + return fmt.Errorf("cannot insert identity: %w", err) } if err := confirmationEmail.Insert(ctx, tx); err != nil { @@ -397,16 +397,16 @@ func (s AuthService) CreateIdentityWithPassword( }, ) - return user, session, err + return identity, session, err } -func (s AuthService) OpenSessionWithSAML(ctx context.Context, userID gid.GID, organizationID gid.GID) (*coredata.Session, error) { +func (s AuthService) OpenSessionWithSAML(ctx context.Context, identityID gid.GID, organizationID gid.GID) (*coredata.Session, error) { session := &coredata.Session{} err := s.pg.WithTx( ctx, func(conn pg.Conn) (err error) { - session = coredata.NewRootSession(userID, coredata.AuthMethodSAML, s.sessionDuration) + session = coredata.NewRootSession(identityID, coredata.AuthMethodSAML, s.sessionDuration) err = session.Insert(ctx, conn) if err != nil { return fmt.Errorf("cannot insert session: %w", err) @@ -423,7 +423,7 @@ func (s AuthService) OpenSessionWithSAML(ctx context.Context, userID gid.GID, or return session, nil } -func (s AuthService) OpenSessionWithPassword(ctx context.Context, email mail.Addr, password string) (*coredata.User, *coredata.Session, error) { +func (s AuthService) OpenSessionWithPassword(ctx context.Context, email mail.Addr, password string) (*coredata.Identity, *coredata.Session, error) { v := validator.New() v.Check(password, "password", PasswordValidator()) @@ -433,29 +433,29 @@ func (s AuthService) OpenSessionWithPassword(ctx context.Context, email mail.Add } var ( - user = &coredata.User{} - session = &coredata.Session{} + identity = &coredata.Identity{} + session = &coredata.Session{} ) err = s.pg.WithTx( ctx, func(conn pg.Conn) error { - err := user.LoadByEmail(ctx, conn, email) + err := identity.LoadByEmail(ctx, conn, email) if err != nil { - // Do not leak information about non-existent users + // Do not leak information about non-existent identities if err != coredata.ErrResourceNotFound { - return fmt.Errorf("cannot load user by email: %w", err) + return fmt.Errorf("cannot load identity by email: %w", err) } } - // Perform a password comparison even when the user does not exist to mitigate timing attacks + // Perform a password comparison even when the identity does not exist to mitigate timing attacks // and prevent revealing account existence. - if user.ID == gid.Nil { + if identity.ID == gid.Nil { s.hp.ComparePasswordAndHash([]byte(password+"qwertyuiop1234567890"), []byte("qwertyuiop1234567890")) return NewInvalidCredentialsError("invalid email or password") } - isPasswordMatch, err := s.hp.ComparePasswordAndHash([]byte(password), user.HashedPassword) + isPasswordMatch, err := s.hp.ComparePasswordAndHash([]byte(password), identity.HashedPassword) if err != nil { return fmt.Errorf("cannot verify password: %w", err) } @@ -464,7 +464,7 @@ func (s AuthService) OpenSessionWithPassword(ctx context.Context, email mail.Add return NewInvalidCredentialsError("invalid email or password") } - session = coredata.NewRootSession(user.ID, coredata.AuthMethodPassword, s.sessionDuration) + session = coredata.NewRootSession(identity.ID, coredata.AuthMethodPassword, s.sessionDuration) err = session.Insert(ctx, conn) if err != nil { return fmt.Errorf("cannot insert session: %w", err) @@ -474,5 +474,5 @@ func (s AuthService) OpenSessionWithPassword(ctx context.Context, email mail.Add }, ) - return user, session, err + return identity, session, err } diff --git a/pkg/iam/authorizer.go b/pkg/iam/authorizer.go index 3c7048588..7ee7ade9d 100644 --- a/pkg/iam/authorizer.go +++ b/pkg/iam/authorizer.go @@ -70,7 +70,7 @@ type AuthorizeParams struct { // It combines self-management policies with role-based policies. func (a *Authorizer) Authorize(ctx context.Context, params AuthorizeParams) error { // Validate principal type - if params.Principal.EntityType() != coredata.UserEntityType { + if params.Principal.EntityType() != coredata.IdentityEntityType { return NewUnsupportedPrincipalTypeError(params.Principal.EntityType()) } @@ -135,7 +135,7 @@ func (a *Authorizer) loadRolePolicies(ctx context.Context, principalID gid.GID, scope := coredata.NewScope(resourceID.TenantID()) var m coredata.Membership - if err := m.LoadRoleByUserAndEntityID(ctx, conn, scope, principalID, resourceID); err != nil { + if err := m.LoadRoleByIdentityAndEntityID(ctx, conn, scope, principalID, resourceID); err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return nil // No membership = no role-based policies } diff --git a/pkg/iam/errors.go b/pkg/iam/errors.go index c44ec4a5e..25db318d8 100644 --- a/pkg/iam/errors.go +++ b/pkg/iam/errors.go @@ -61,14 +61,14 @@ func (e ErrInvitationExpired) Error() string { return fmt.Sprintf("invitation %q expired", e.InvitationID) } -type ErrUserAlreadyExists struct{ EmailAddress mail.Addr } +type ErrIdentityAlreadyExists struct{ EmailAddress mail.Addr } -func NewUserAlreadyExistsError(emailAddress mail.Addr) error { - return &ErrUserAlreadyExists{EmailAddress: emailAddress} +func NewIdentityAlreadyExistsError(emailAddress mail.Addr) error { + return &ErrIdentityAlreadyExists{EmailAddress: emailAddress} } -func (e ErrUserAlreadyExists) Error() string { - return fmt.Sprintf("user %q already exists", e.EmailAddress.String()) +func (e ErrIdentityAlreadyExists) Error() string { + return fmt.Sprintf("identity %q already exists", e.EmailAddress.String()) } type ErrEmailAlreadyVerified struct{ message string } @@ -81,14 +81,14 @@ func (e ErrEmailAlreadyVerified) Error() string { return e.message } -type ErrUserNotFound struct{ UserID gid.GID } +type ErrIdentityNotFound struct{ IdentityID gid.GID } -func NewUserNotFoundError(userID gid.GID) error { - return &ErrUserNotFound{userID} +func NewIdentityNotFoundError(identityID gid.GID) error { + return &ErrIdentityNotFound{identityID} } -func (e ErrUserNotFound) Error() string { - return fmt.Sprintf("user %q not found", e.UserID) +func (e ErrIdentityNotFound) Error() string { + return fmt.Sprintf("identity %q not found", e.IdentityID) } type ErrInvalidPassword struct{ message string } @@ -168,16 +168,16 @@ func (e ErrSessionExpired) Error() string { } type ErrMembershipAlreadyExists struct { - UserID gid.GID + IdentityID gid.GID OrganizationID gid.GID } -func NewMembershipAlreadyExistsError(userID gid.GID, organizationID gid.GID) error { - return &ErrMembershipAlreadyExists{UserID: userID, OrganizationID: organizationID} +func NewMembershipAlreadyExistsError(identityID gid.GID, organizationID gid.GID) error { + return &ErrMembershipAlreadyExists{IdentityID: identityID, OrganizationID: organizationID} } func (e ErrMembershipAlreadyExists) Error() string { - return fmt.Sprintf("membership already exists for user %q in organization %q", e.UserID, e.OrganizationID) + return fmt.Sprintf("membership already exists for identity %q in organization %q", e.IdentityID, e.OrganizationID) } type ErrSAMLConfigurationNotFound struct{ ConfigID gid.GID } @@ -190,24 +190,24 @@ func (e ErrSAMLConfigurationNotFound) Error() string { return fmt.Sprintf("SAML configuration %q not found", e.ConfigID) } -type ErrUserAPIKeyNotFound struct{ UserAPIKeyID gid.GID } +type ErrPersonalAPIKeyNotFound struct{ PersonalAPIKeyID gid.GID } -func NewUserAPIKeyNotFoundError(userAPIKeyID gid.GID) error { - return &ErrUserAPIKeyNotFound{UserAPIKeyID: userAPIKeyID} +func NewPersonalAPIKeyNotFoundError(personalAPIKeyID gid.GID) error { + return &ErrPersonalAPIKeyNotFound{PersonalAPIKeyID: personalAPIKeyID} } -func (e ErrUserAPIKeyNotFound) Error() string { - return fmt.Sprintf("user API key %q not found", e.UserAPIKeyID) +func (e ErrPersonalAPIKeyNotFound) Error() string { + return fmt.Sprintf("personal API key %q not found", e.PersonalAPIKeyID) } -type ErrUserAPIKeyExpired struct{ UserAPIKeyID gid.GID } +type ErrPersonalAPIKeyExpired struct{ PersonalAPIKeyID gid.GID } -func NewUserAPIKeyExpiredError(userAPIKeyID gid.GID) error { - return &ErrUserAPIKeyExpired{UserAPIKeyID: userAPIKeyID} +func NewPersonalAPIKeyExpiredError(personalAPIKeyID gid.GID) error { + return &ErrPersonalAPIKeyExpired{PersonalAPIKeyID: personalAPIKeyID} } -func (e ErrUserAPIKeyExpired) Error() string { - return fmt.Sprintf("user API key %q expired", e.UserAPIKeyID) +func (e ErrPersonalAPIKeyExpired) Error() string { + return fmt.Sprintf("personal API key %q expired", e.PersonalAPIKeyID) } type ErrSAMLConfigurationDomainNotVerified struct{ ConfigID gid.GID } diff --git a/pkg/iam/organization_service.go b/pkg/iam/organization_service.go index 1bb8faa67..6382745a9 100644 --- a/pkg/iam/organization_service.go +++ b/pkg/iam/organization_service.go @@ -307,22 +307,22 @@ func (s *OrganizationService) InviteMember( return fmt.Errorf("cannot load organization: %w", err) } - user := &coredata.User{} - err = user.LoadByEmail(ctx, tx, emailAddress) + identity := &coredata.Identity{} + err = identity.LoadByEmail(ctx, tx, emailAddress) if err != nil && err != coredata.ErrResourceNotFound { - return fmt.Errorf("cannot load user: %w", err) + return fmt.Errorf("cannot load identity: %w", err) } - userExists := user.ID != gid.Nil - if userExists { + identityExists := identity.ID != gid.Nil + if identityExists { membership := &coredata.Membership{} - err = membership.LoadByUserAndOrg(ctx, tx, scope, user.ID, organizationID) + err = membership.LoadByIdentityAndOrg(ctx, tx, scope, identity.ID, organizationID) if err != nil && err != coredata.ErrResourceNotFound { return fmt.Errorf("cannot load membership: %w", err) } if membership.ID != gid.Nil { - return NewMembershipAlreadyExistsError(user.ID, organizationID) + return NewMembershipAlreadyExistsError(identity.ID, organizationID) } } @@ -506,7 +506,7 @@ func (s *OrganizationService) CreateOrganization( membership := &coredata.Membership{ ID: gid.New(tenantID, coredata.MembershipEntityType), - UserID: identityID, + IdentityID: identityID, OrganizationID: organizationID, Role: coredata.MembershipRoleOwner, CreatedAt: now, diff --git a/pkg/iam/permissions.go b/pkg/iam/permissions.go index fb6fa91bd..11de98ab2 100644 --- a/pkg/iam/permissions.go +++ b/pkg/iam/permissions.go @@ -413,7 +413,7 @@ var Permissions = map[uint16]map[Action][]Role{ ActionGetTrustCenterFile: EditRoles, ActionDeleteTrustCenterFile: EditRoles, }, - coredata.UserEntityType: { + coredata.IdentityEntityType: { ActionGet: NonEmployeeRoles, }, coredata.MembershipEntityType: { diff --git a/pkg/iam/saml/service.go b/pkg/iam/saml/service.go index 9050346fc..2e92237b6 100644 --- a/pkg/iam/saml/service.go +++ b/pkg/iam/saml/service.go @@ -171,10 +171,10 @@ func (s *Service) HandleAssertion( ctx context.Context, samlResponse string, configID gid.GID, -) (*coredata.User, *coredata.Membership, error) { +) (*coredata.Identity, *coredata.Membership, error) { var ( now = time.Now() - user = &coredata.User{} + identity = &coredata.Identity{} membership = &coredata.Membership{} ) @@ -251,12 +251,12 @@ func (s *Service) HandleAssertion( return NewEmailDomainMismatchError(email, config.EmailDomain) } - err = user.LoadByEmail(ctx, tx, email) + err = identity.LoadByEmail(ctx, tx, email) if err == coredata.ErrResourceNotFound && !config.AutoSignupEnabled { return NewSAMLAutoSignupDisabledError(config.ID) } else if err == coredata.ErrResourceNotFound && config.AutoSignupEnabled { - *user = coredata.User{ - ID: gid.New(gid.NilTenant, coredata.UserEntityType), + *identity = coredata.Identity{ + ID: gid.New(gid.NilTenant, coredata.IdentityEntityType), EmailAddress: email, HashedPassword: nil, EmailAddressVerified: true, @@ -265,26 +265,26 @@ func (s *Service) HandleAssertion( UpdatedAt: now, } - err := user.Insert(ctx, tx) + err := identity.Insert(ctx, tx) if err != nil { - return fmt.Errorf("cannot insert user: %w", err) + return fmt.Errorf("cannot insert identity: %w", err) } } else if err != nil { - return fmt.Errorf("cannot load user: %w", err) + return fmt.Errorf("cannot load identity: %w", err) } else { - user.SAMLSubject = &assertion.Subject.NameID.Value - user.FullName = fullname - user.EmailAddress = email - user.EmailAddressVerified = true - user.UpdatedAt = now + identity.SAMLSubject = &assertion.Subject.NameID.Value + identity.FullName = fullname + identity.EmailAddress = email + identity.EmailAddressVerified = true + identity.UpdatedAt = now - err = user.Update(ctx, tx) + err = identity.Update(ctx, tx) if err != nil { - return fmt.Errorf("cannot update user: %w", err) + return fmt.Errorf("cannot update identity: %w", err) } } - err = membership.LoadByUserAndOrg(ctx, tx, coredata.NewNoScope(), user.ID, config.OrganizationID) + err = membership.LoadByIdentityAndOrg(ctx, tx, coredata.NewNoScope(), identity.ID, config.OrganizationID) if err != nil && err != coredata.ErrResourceNotFound { return fmt.Errorf("cannot load membership: %w", err) } @@ -293,7 +293,7 @@ func (s *Service) HandleAssertion( if !isMember { membership = &coredata.Membership{ ID: gid.New(config.ID.TenantID(), coredata.MembershipEntityType), - UserID: user.ID, + IdentityID: identity.ID, OrganizationID: config.OrganizationID, Role: coredata.MembershipRoleViewer, CreatedAt: now, @@ -324,7 +324,7 @@ func (s *Service) HandleAssertion( return nil, nil, err } - return user, membership, nil + return identity, membership, nil } func (s *Service) validateAssertion(assertion *saml.Assertion, config *coredata.SAMLConfiguration, now time.Time) error { diff --git a/pkg/iam/session_service.go b/pkg/iam/session_service.go index 391d57597..c4fab04ee 100644 --- a/pkg/iam/session_service.go +++ b/pkg/iam/session_service.go @@ -129,14 +129,14 @@ func (s SessionService) RevokeSession(ctx context.Context, identityID gid.GID, s return s.pg.WithTx( ctx, func(tx pg.Conn) error { - user := &coredata.User{} - err := user.LoadByID(ctx, tx, identityID) + identity := &coredata.Identity{} + err := identity.LoadByID(ctx, tx, identityID) if err != nil { if err == coredata.ErrResourceNotFound { - return NewUserNotFoundError(identityID) + return NewIdentityNotFoundError(identityID) } - return fmt.Errorf("cannot load user: %w", err) + return fmt.Errorf("cannot load identity: %w", err) } session := &coredata.Session{} @@ -150,7 +150,7 @@ func (s SessionService) RevokeSession(ctx context.Context, identityID gid.GID, s } // TODO: move to dedicated query instead of LoadByID - if session.UserID != identityID { + if session.IdentityID != identityID { return NewSessionNotFoundError(sessionID) } @@ -192,7 +192,7 @@ func (s SessionService) RevokeAllSessions(ctx context.Context, currentSessionID } sessions := coredata.Sessions{} - count, err = sessions.ExpireAllForUserExceptOneSession(ctx, tx, session.UserID, session.ID) + count, err = sessions.ExpireAllForIdentityExceptOneSession(ctx, tx, session.IdentityID, session.ID) if err != nil { return fmt.Errorf("cannot expire all sessions: %w", err) } @@ -323,7 +323,7 @@ func (s SessionService) AssumeOrganizationSession( var ( now = time.Now() rootSession = &coredata.Session{} - user = &coredata.User{} + identity = &coredata.Identity{} membership = &coredata.Membership{} childSession = &coredata.Session{} scope = coredata.NewScopeFromObjectID(organizationID) @@ -348,12 +348,12 @@ func (s SessionService) AssumeOrganizationSession( return NewSessionExpiredError(sessionID) } - err = user.LoadByID(ctx, tx, rootSession.UserID) + err = identity.LoadByID(ctx, tx, rootSession.IdentityID) if err != nil { - return fmt.Errorf("cannot load user: %w", err) + return fmt.Errorf("cannot load identity: %w", err) } - err = membership.LoadByUserInOrganization(ctx, tx, rootSession.UserID, organizationID) + err = membership.LoadByIdentityInOrganization(ctx, tx, rootSession.IdentityID, organizationID) if err != nil { if err == coredata.ErrResourceNotFound { return NewMembershipNotFoundError(organizationID) @@ -367,7 +367,7 @@ func (s SessionService) AssumeOrganizationSession( tx, scope, organizationID, - user.EmailAddress.Domain(), + identity.EmailAddress.Domain(), ) if err != nil && err != coredata.ErrResourceNotFound { return fmt.Errorf("cannot load SAML configuration: %w", err) @@ -389,7 +389,7 @@ func (s SessionService) AssumeOrganizationSession( tenantID := scope.GetTenantID() childSession = &coredata.Session{ ID: gid.New(tenantID, coredata.SessionEntityType), - UserID: rootSession.UserID, + IdentityID: rootSession.IdentityID, TenantID: &tenantID, MembershipID: &membership.ID, ParentSessionID: &rootSession.ID, diff --git a/pkg/server/api/connect/v1/api_key_middleware.go b/pkg/server/api/connect/v1/api_key_middleware.go index 13d683b1f..7bb604202 100644 --- a/pkg/server/api/connect/v1/api_key_middleware.go +++ b/pkg/server/api/connect/v1/api_key_middleware.go @@ -31,8 +31,8 @@ var ( apiKeyContextKey = &ctxKey{name: "api_key"} ) -func APIKeyFromContext(ctx context.Context) *coredata.UserAPIKey { - apiKey, _ := ctx.Value(apiKeyContextKey).(*coredata.UserAPIKey) +func APIKeyFromContext(ctx context.Context) *coredata.PersonalAPIKey { + apiKey, _ := ctx.Value(apiKeyContextKey).(*coredata.PersonalAPIKey) return apiKey } @@ -62,30 +62,30 @@ func NewAPIKeyMiddleware(svc *iam.Service) func(next http.Handler) http.Handler apiKey, err := svc.APIKeyService.GetAPIKey(ctx, keyID) if err != nil { - var errUserAPIKeyNotFound *iam.ErrUserAPIKeyNotFound - var errUserAPIKeyExpired *iam.ErrUserAPIKeyExpired + var errPersonalAPIKeyNotFound *iam.ErrPersonalAPIKeyNotFound + var errPersonalAPIKeyExpired *iam.ErrPersonalAPIKeyExpired - if errors.As(err, &errUserAPIKeyNotFound) || errors.As(err, &errUserAPIKeyExpired) { + if errors.As(err, &errPersonalAPIKeyNotFound) || errors.As(err, &errPersonalAPIKeyExpired) { next.ServeHTTP(w, r) return } - panic(fmt.Errorf("cannot get user API key: %w", err)) + panic(fmt.Errorf("cannot get personal API key: %w", err)) } - user, err := svc.AccountService.GetIdentity(ctx, apiKey.UserID) + identity, err := svc.AccountService.GetIdentity(ctx, apiKey.IdentityID) if err != nil { - var errUserNotFound *iam.ErrUserNotFound - if errors.As(err, &errUserNotFound) { + var errIdentityNotFound *iam.ErrIdentityNotFound + if errors.As(err, &errIdentityNotFound) { next.ServeHTTP(w, r) return } - panic(fmt.Errorf("cannot get user: %w", err)) + panic(fmt.Errorf("cannot get identity: %w", err)) } ctx = context.WithValue(ctx, apiKeyContextKey, apiKey) - ctx = context.WithValue(ctx, identityContextKey, user) + ctx = context.WithValue(ctx, identityContextKey, identity) next.ServeHTTP(w, r.WithContext(ctx)) }, diff --git a/pkg/server/api/connect/v1/graphql_handler.go b/pkg/server/api/connect/v1/graphql_handler.go index 2d1c0d733..fca63c532 100644 --- a/pkg/server/api/connect/v1/graphql_handler.go +++ b/pkg/server/api/connect/v1/graphql_handler.go @@ -70,7 +70,7 @@ func SessionDirective(ctx context.Context, obj any, next graphql.Resolver, requi } func IsViewerDirective(ctx context.Context, obj any, next graphql.Resolver) (any, error) { - identity := UserFromContext(ctx) + identity := IdentityFromContext(ctx) switch node := obj.(type) { case *types.Identity: diff --git a/pkg/server/api/connect/v1/session_middleware.go b/pkg/server/api/connect/v1/session_middleware.go index acc50d407..61a84f357 100644 --- a/pkg/server/api/connect/v1/session_middleware.go +++ b/pkg/server/api/connect/v1/session_middleware.go @@ -38,9 +38,9 @@ func SessionFromContext(ctx context.Context) *coredata.Session { return session } -func UserFromContext(ctx context.Context) *coredata.User { - user, _ := ctx.Value(identityContextKey).(*coredata.User) - return user +func IdentityFromContext(ctx context.Context) *coredata.Identity { + identity, _ := ctx.Value(identityContextKey).(*coredata.Identity) + return identity } func NewSessionMiddleware(svc *iam.Service, cookieConfig securecookie.Config) func(next http.Handler) http.Handler { @@ -82,16 +82,16 @@ func NewSessionMiddleware(svc *iam.Service, cookieConfig securecookie.Config) fu panic(fmt.Errorf("cannot get session: %w", err)) } - user, err := svc.AccountService.GetIdentity(ctx, session.UserID) + identity, err := svc.AccountService.GetIdentity(ctx, session.IdentityID) if err != nil { - var errUserNotFound *iam.ErrUserNotFound - if errors.As(err, &errUserNotFound) { + var errIdentityNotFound *iam.ErrIdentityNotFound + if errors.As(err, &errIdentityNotFound) { securecookie.Clear(w, cookieConfig) next.ServeHTTP(w, r) return } - panic(fmt.Errorf("cannot get user: %w", err)) + panic(fmt.Errorf("cannot get identity: %w", err)) } userAgent := r.UserAgent() @@ -109,7 +109,7 @@ func NewSessionMiddleware(svc *iam.Service, cookieConfig securecookie.Config) fu } ctx = context.WithValue(ctx, sessionContextKey, session) - ctx = context.WithValue(ctx, identityContextKey, user) + ctx = context.WithValue(ctx, identityContextKey, identity) next.ServeHTTP(w, r.WithContext(ctx)) diff --git a/pkg/server/api/connect/v1/types/identity.go b/pkg/server/api/connect/v1/types/identity.go index 770616502..6aa1936c5 100644 --- a/pkg/server/api/connect/v1/types/identity.go +++ b/pkg/server/api/connect/v1/types/identity.go @@ -16,7 +16,7 @@ package types import "go.probo.inc/probo/pkg/coredata" -func NewIdentity(identity *coredata.User) *Identity { +func NewIdentity(identity *coredata.Identity) *Identity { return &Identity{ ID: identity.ID, Email: identity.EmailAddress, diff --git a/pkg/server/api/connect/v1/types/membership.go b/pkg/server/api/connect/v1/types/membership.go index bed0448be..4f7e7c17f 100644 --- a/pkg/server/api/connect/v1/types/membership.go +++ b/pkg/server/api/connect/v1/types/membership.go @@ -62,7 +62,7 @@ func NewMembershipEdge(membership *coredata.Membership, orderField coredata.Memb func NewMembership(membership *coredata.Membership) *Membership { return &Membership{ ID: membership.ID, - IdentityID: membership.UserID, + IdentityID: membership.IdentityID, CreatedAt: membership.CreatedAt, // Permissions: membership.Permissions, // ProvisionedBy: membership.ProvisionedBy, diff --git a/pkg/server/api/connect/v1/types/personal_api_key.go b/pkg/server/api/connect/v1/types/personal_api_key.go index 1b6621f7f..50434047e 100644 --- a/pkg/server/api/connect/v1/types/personal_api_key.go +++ b/pkg/server/api/connect/v1/types/personal_api_key.go @@ -21,7 +21,7 @@ import ( ) type ( - PersonalAPIKeyOrderBy OrderBy[coredata.UserAPIKeyOrderField] + PersonalAPIKeyOrderBy OrderBy[coredata.PersonalAPIKeyOrderField] PersonalAPIKeyConnection struct { TotalCount int @@ -34,7 +34,7 @@ type ( ) func NewPersonalAPIKeyConnection( - p *page.Page[*coredata.UserAPIKey, coredata.UserAPIKeyOrderField], + p *page.Page[*coredata.PersonalAPIKey, coredata.PersonalAPIKeyOrderField], resolver any, parentID gid.GID, ) *PersonalAPIKeyConnection { @@ -52,14 +52,14 @@ func NewPersonalAPIKeyConnection( } } -func NewPersonalAPIKeyEdge(personalAPIKey *coredata.UserAPIKey, orderField coredata.UserAPIKeyOrderField) *PersonalAPIKeyEdge { +func NewPersonalAPIKeyEdge(personalAPIKey *coredata.PersonalAPIKey, orderField coredata.PersonalAPIKeyOrderField) *PersonalAPIKeyEdge { return &PersonalAPIKeyEdge{ Node: NewPersonalAPIKey(personalAPIKey), Cursor: personalAPIKey.CursorKey(orderField), } } -func NewPersonalAPIKey(personalAPIKey *coredata.UserAPIKey) *PersonalAPIKey { +func NewPersonalAPIKey(personalAPIKey *coredata.PersonalAPIKey) *PersonalAPIKey { return &PersonalAPIKey{ ID: personalAPIKey.ID, Name: personalAPIKey.Name, diff --git a/pkg/server/api/connect/v1/types/session.go b/pkg/server/api/connect/v1/types/session.go index fd90f2aa2..36b3d0f72 100644 --- a/pkg/server/api/connect/v1/types/session.go +++ b/pkg/server/api/connect/v1/types/session.go @@ -63,7 +63,7 @@ func NewSession(session *coredata.Session) *Session { return &Session{ ID: session.ID, IPAddress: session.IPAddress.String(), - IdentityID: session.UserID, + IdentityID: session.IdentityID, UserAgent: session.UserAgent, UpdatedAt: session.UpdatedAt, CreatedAt: session.CreatedAt, diff --git a/pkg/server/api/connect/v1/v1_resolver.go b/pkg/server/api/connect/v1/v1_resolver.go index 6e5e8161a..b5ce2b6a9 100644 --- a/pkg/server/api/connect/v1/v1_resolver.go +++ b/pkg/server/api/connect/v1/v1_resolver.go @@ -89,8 +89,8 @@ func (r *identityResolver) Sessions(ctx context.Context, obj *types.Identity, fi // PersonalAPIKeys is the resolver for the personalAPIKeys field. func (r *identityResolver) PersonalAPIKeys(ctx context.Context, obj *types.Identity, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.PersonalAPIKeyConnection, error) { - pageOrderBy := page.OrderBy[coredata.UserAPIKeyOrderField]{ - Field: coredata.UserAPIKeyOrderFieldCreatedAt, + pageOrderBy := page.OrderBy[coredata.PersonalAPIKeyOrderField]{ + Field: coredata.PersonalAPIKeyOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, } @@ -233,8 +233,8 @@ func (r *mutationResolver) SignUp(ctx context.Context, input types.SignUpInput) }, ) if err != nil { - var errUserAlreadyExists *iam.ErrUserAlreadyExists - if errors.As(err, &errUserAlreadyExists) { + var errIdentityAlreadyExists *iam.ErrIdentityAlreadyExists + if errors.As(err, &errIdentityAlreadyExists) { return nil, gqlutils.Invalid(err, nil) } @@ -287,7 +287,7 @@ func (r *mutationResolver) SignUpFromInvitation(ctx context.Context, input types errInvitationNotFound *iam.ErrInvitationNotFound errInvitationAlreadyAccepted *iam.ErrInvitationAlreadyAccepted errInvitationExpired *iam.ErrInvitationExpired - errUserAlreadyExists *iam.ErrUserAlreadyExists + errIdentityAlreadyExists *iam.ErrIdentityAlreadyExists isInvalidErr = errors.As(err, &errInvalidToken) || errors.As(err, &errInvitationNotFound) || @@ -299,7 +299,7 @@ func (r *mutationResolver) SignUpFromInvitation(ctx context.Context, input types return nil, gqlutils.Invalid(err, nil) } - if errors.As(err, &errUserAlreadyExists) { + if errors.As(err, &errIdentityAlreadyExists) { return nil, gqlutils.Conflict(err) } @@ -371,7 +371,7 @@ func (r *mutationResolver) VerifyEmail(ctx context.Context, input types.VerifyEm if err != nil { var ( errInvalidToken *iam.ErrInvalidToken - errUserNotFound *iam.ErrUserNotFound + errIdentityNotFound *iam.ErrIdentityNotFound errEmailAlreadyVerified *iam.ErrEmailAlreadyVerified errEmailVerificationMismatch *iam.ErrEmailVerificationMismatch @@ -387,7 +387,7 @@ func (r *mutationResolver) VerifyEmail(ctx context.Context, input types.VerifyEm return nil, gqlutils.Conflict(err) } - if errors.As(err, &errUserNotFound) { + if errors.As(err, &errIdentityNotFound) { return nil, gqlutils.NotFound(err) } @@ -402,7 +402,7 @@ func (r *mutationResolver) VerifyEmail(ctx context.Context, input types.VerifyEm // ChangePassword is the resolver for the changePassword field. func (r *mutationResolver) ChangePassword(ctx context.Context, input types.ChangePasswordInput) (*types.ChangePasswordPayload, error) { - identity := UserFromContext(ctx) + identity := IdentityFromContext(ctx) err := r.iam.AccountService.ChangePassword( ctx, @@ -414,15 +414,15 @@ func (r *mutationResolver) ChangePassword(ctx context.Context, input types.Chang ) if err != nil { var ( - errInvalidPassword *iam.ErrInvalidPassword - errUserNotFound *iam.ErrUserNotFound + errInvalidPassword *iam.ErrInvalidPassword + errIdentityNotFound *iam.ErrIdentityNotFound ) if errors.As(err, &errInvalidPassword) { return nil, gqlutils.Invalid(err, nil) } - if errors.As(err, &errUserNotFound) { + if errors.As(err, &errIdentityNotFound) { return nil, gqlutils.NotFound(err) } @@ -437,7 +437,7 @@ func (r *mutationResolver) ChangePassword(ctx context.Context, input types.Chang // ChangeEmail is the resolver for the changeEmail field. func (r *mutationResolver) ChangeEmail(ctx context.Context, input types.ChangeEmailInput) (*types.ChangeEmailPayload, error) { - identity := UserFromContext(ctx) + identity := IdentityFromContext(ctx) err := r.iam.AccountService.ChangeEmail( ctx, @@ -449,15 +449,15 @@ func (r *mutationResolver) ChangeEmail(ctx context.Context, input types.ChangeEm ) if err != nil { var ( - errInvalidPassword *iam.ErrInvalidPassword - errUserNotFound *iam.ErrUserNotFound + errInvalidPassword *iam.ErrInvalidPassword + errIdentityNotFound *iam.ErrIdentityNotFound ) if errors.As(err, &errInvalidPassword) { return nil, gqlutils.Invalid(err, nil) } - if errors.As(err, &errUserNotFound) { + if errors.As(err, &errIdentityNotFound) { return nil, gqlutils.NotFound(err) } @@ -522,7 +522,7 @@ func (r *mutationResolver) UpdateIdentityProfile(ctx context.Context, input type // RevokeSession is the resolver for the revokeSession field. func (r *mutationResolver) RevokeSession(ctx context.Context, input types.RevokeSessionInput) (*types.RevokeSessionPayload, error) { - identity := UserFromContext(ctx) + identity := IdentityFromContext(ctx) err := r.iam.SessionService.RevokeSession(ctx, identity.ID, input.SessionID) if err != nil { @@ -553,7 +553,7 @@ func (r *mutationResolver) RevokeAllSessions(ctx context.Context) (*types.Revoke // CreatePersonalAPIKey is the resolver for the createPersonalAPIKey field. func (r *mutationResolver) CreatePersonalAPIKey(ctx context.Context, input types.CreatePersonalAPIKeyInput) (*types.CreatePersonalAPIKeyPayload, error) { - identity := UserFromContext(ctx) + identity := IdentityFromContext(ctx) userAPIKey, token, err := r.iam.AccountService.CreatePersonalAPIKey( ctx, @@ -567,7 +567,7 @@ func (r *mutationResolver) CreatePersonalAPIKey(ctx context.Context, input types } return &types.CreatePersonalAPIKeyPayload{ - PersonalAPIKeyEdge: types.NewPersonalAPIKeyEdge(userAPIKey, coredata.UserAPIKeyOrderFieldCreatedAt), + PersonalAPIKeyEdge: types.NewPersonalAPIKeyEdge(userAPIKey, coredata.PersonalAPIKeyOrderFieldCreatedAt), Token: token, }, nil } @@ -579,7 +579,7 @@ func (r *mutationResolver) UpdatePersonalAPIKey(ctx context.Context, input types // RevokePersonalAPIKey is the resolver for the revokePersonalAPIKey field. func (r *mutationResolver) RevokePersonalAPIKey(ctx context.Context, input types.RevokePersonalAPIKeyInput) (*types.RevokePersonalAPIKeyPayload, error) { - identity := UserFromContext(ctx) + identity := IdentityFromContext(ctx) err := r.iam.AccountService.DeletePersonalAPIKey(ctx, identity.ID, input.TokenID) if err != nil { @@ -592,7 +592,7 @@ func (r *mutationResolver) RevokePersonalAPIKey(ctx context.Context, input types // CreateOrganization is the resolver for the createOrganization field. func (r *mutationResolver) CreateOrganization(ctx context.Context, input types.CreateOrganizationInput) (*types.CreateOrganizationPayload, error) { - identity := UserFromContext(ctx) + identity := IdentityFromContext(ctx) var ( logoFile *iam.UploadedFile @@ -735,7 +735,7 @@ func (r *mutationResolver) RemoveMember(ctx context.Context, input types.RemoveM // AcceptInvitation is the resolver for the acceptInvitation field. func (r *mutationResolver) AcceptInvitation(ctx context.Context, input types.AcceptInvitationInput) (*types.AcceptInvitationPayload, error) { - identity := UserFromContext(ctx) + identity := IdentityFromContext(ctx) membership, err := r.iam.AccountService.AcceptInvitation(ctx, identity.ID, input.InvitationID) if err != nil { @@ -928,7 +928,7 @@ func (r *personalAPIKeyConnectionResolver) TotalCount(ctx context.Context, obj * func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) { var ( loadNode func(ctx context.Context, id gid.GID) (types.Node, error) - user = UserFromContext(ctx) + user = IdentityFromContext(ctx) action string ) @@ -942,7 +942,7 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error } return types.NewOrganization(organization), nil } - case coredata.UserEntityType: + case coredata.IdentityEntityType: action = iam.ActionIAMIdentityGet loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) { identity, err := r.iam.AccountService.GetIdentity(ctx, id) @@ -1009,7 +1009,7 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error if err != nil { var ( errOrganizationNotFound *iam.ErrOrganizationNotFound - errIdentityNotFound *iam.ErrUserNotFound + errIdentityNotFound *iam.ErrIdentityNotFound errSessionNotFound *iam.ErrSessionNotFound errMembershipNotFound *iam.ErrMembershipNotFound errInvitationNotFound *iam.ErrInvitationNotFound @@ -1034,7 +1034,7 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error // Viewer is the resolver for the viewer field. func (r *queryResolver) Viewer(ctx context.Context) (*types.Identity, error) { - user := UserFromContext(ctx) + user := IdentityFromContext(ctx) return &types.Identity{ ID: user.ID, diff --git a/pkg/server/api/console/v1/resolver.go b/pkg/server/api/console/v1/resolver.go index 23c6e949b..3e7e38f9c 100644 --- a/pkg/server/api/console/v1/resolver.go +++ b/pkg/server/api/console/v1/resolver.go @@ -52,7 +52,7 @@ type ( ) func ensureAuthenticated(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler { - identity := connect_v1.UserFromContext(ctx) + identity := connect_v1.IdentityFromContext(ctx) if identity == nil { return func(ctx context.Context) *graphql.Response { @@ -228,7 +228,7 @@ func NewMux( panic(fmt.Errorf("cannot parse organization id: %w", err)) } - identity := connect_v1.UserFromContext(r.Context()) + identity := connect_v1.IdentityFromContext(r.Context()) apiKey := connect_v1.APIKeyFromContext(r.Context()) if identity == nil { httpserver.RenderError(w, http.StatusUnauthorized, fmt.Errorf("authentication required")) @@ -328,7 +328,7 @@ func GetTenantService(ctx context.Context, proboSvc *probo.Service, tenantID gid } func (r *Resolver) MustBeAuthorized(ctx context.Context, entityID gid.GID, action iam.Action) { - user := connect_v1.UserFromContext(ctx) + user := connect_v1.IdentityFromContext(ctx) apiKey := connect_v1.APIKeyFromContext(ctx) var credentialID *gid.GID diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index 4bf2bd7b5..958123bcb 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -967,7 +967,7 @@ func (r *documentVersionResolver) Signatures(ctx context.Context, obj *types.Doc func (r *documentVersionResolver) Signed(ctx context.Context, obj *types.DocumentVersion) (bool, error) { r.MustBeAuthorized(ctx, obj.ID, iam.ActionGetSigned) - identity := connect_v1.UserFromContext(ctx) + identity := connect_v1.IdentityFromContext(ctx) if identity == nil { panic(fmt.Errorf("user not found in context")) } @@ -1758,7 +1758,7 @@ func (r *mutationResolver) DeleteTrustCenterFile(ctx context.Context, input type // CreatePeople is the resolver for the createPeople field. func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreatePeopleInput) (*types.CreatePeoplePayload, error) { - user := connect_v1.UserFromContext(ctx) + user := connect_v1.IdentityFromContext(ctx) r.iam.Authorizer.Authorize(ctx, iam.AuthorizeParams{ Principal: user.ID, @@ -2165,7 +2165,7 @@ func (r *mutationResolver) ExportFramework(ctx context.Context, input types.Expo r.MustBeAuthorized(ctx, input.FrameworkID, iam.ActionExportFramework) prb := r.ProboService(ctx, input.FrameworkID.TenantID()) - identity := connect_v1.UserFromContext(ctx) + identity := connect_v1.IdentityFromContext(ctx) err, exportJobID := prb.Frameworks.RequestExport( ctx, @@ -3257,7 +3257,7 @@ func (r *mutationResolver) PublishDocumentVersion(ctx context.Context, input typ prb := r.ProboService(ctx, input.DocumentID.TenantID()) - identity := connect_v1.UserFromContext(ctx) + identity := connect_v1.IdentityFromContext(ctx) document, documentVersion, err := prb.Documents.PublishVersion(ctx, input.DocumentID, identity.ID, input.Changelog) if err != nil { @@ -3287,7 +3287,7 @@ func (r *mutationResolver) BulkPublishDocumentVersions(ctx context.Context, inpu prb := r.ProboService(ctx, input.DocumentIds[0].TenantID()) - identity := connect_v1.UserFromContext(ctx) + identity := connect_v1.IdentityFromContext(ctx) documentVersions, documents, err := prb.Documents.BulkPublishVersions( ctx, @@ -3343,7 +3343,7 @@ func (r *mutationResolver) BulkExportDocuments(ctx context.Context, input types. prb := r.ProboService(ctx, input.DocumentIds[0].TenantID()) - identity := connect_v1.UserFromContext(ctx) + identity := connect_v1.IdentityFromContext(ctx) options := probo.ExportPDFOptions{ WithWatermark: input.WithWatermark, @@ -3514,7 +3514,7 @@ func (r *mutationResolver) CancelSignatureRequest(ctx context.Context, input typ func (r *mutationResolver) SignDocument(ctx context.Context, input types.SignDocumentInput) (*types.SignDocumentPayload, error) { r.MustBeAuthorized(ctx, input.DocumentVersionID, iam.ActionSignDocument) - identity := connect_v1.UserFromContext(ctx) + identity := connect_v1.IdentityFromContext(ctx) prb := r.ProboService(ctx, input.DocumentVersionID.TenantID()) documentVersionSignature, err := prb.Documents.SignDocumentVersionByEmail(ctx, input.DocumentVersionID, identity.EmailAddress) @@ -3564,7 +3564,7 @@ func (r *mutationResolver) ExportSignableVersionDocumentPDF(ctx context.Context, panic(fmt.Errorf("cannot get document version: %w", err)) } - identity := connect_v1.UserFromContext(ctx) + identity := connect_v1.IdentityFromContext(ctx) documentFilter := coredata.NewDocumentFilter(nil).WithUserEmail(&identity.EmailAddress) _, err = prb.Documents.GetWithFilter(ctx, documentVersion.DocumentID, documentFilter) @@ -5919,7 +5919,8 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error // Viewer is the resolver for the viewer field. func (r *queryResolver) Viewer(ctx context.Context) (*types.Viewer, error) { - identity := connect_v1.UserFromContext(ctx) + identity := connect_v1.IdentityFromContext(ctx) + session := connect_v1.SessionFromContext(ctx) apiKey := connect_v1.APIKeyFromContext(ctx) diff --git a/pkg/server/api/mcp/v1/middleware.go b/pkg/server/api/mcp/v1/middleware.go index 1948b93f8..ea43ca73f 100644 --- a/pkg/server/api/mcp/v1/middleware.go +++ b/pkg/server/api/mcp/v1/middleware.go @@ -41,7 +41,7 @@ func RequireAPIKeyHandler( ) apiKey := connect_v1.APIKeyFromContext(ctx) - identity := connect_v1.UserFromContext(ctx) + identity := connect_v1.IdentityFromContext(ctx) if identity == nil { httpserver.RenderError(w, http.StatusUnauthorized, errors.New("authentication required")) return diff --git a/pkg/server/api/mcp/v1/resolver.go b/pkg/server/api/mcp/v1/resolver.go index 4a5841d76..c4f1929bf 100644 --- a/pkg/server/api/mcp/v1/resolver.go +++ b/pkg/server/api/mcp/v1/resolver.go @@ -19,7 +19,7 @@ type Resolver struct { } func (r *Resolver) MustBeAuthorized(ctx context.Context, entityID gid.GID, action iam.Action) { - user := connect_v1.UserFromContext(ctx) + user := connect_v1.IdentityFromContext(ctx) apiKey := connect_v1.APIKeyFromContext(ctx) if user == nil { panic(&iam.TenantAccessError{Message: "authentication required"}) diff --git a/pkg/server/api/mcp/v1/schema.resolvers.go b/pkg/server/api/mcp/v1/schema.resolvers.go index d31adcb7e..d9f6851db 100644 --- a/pkg/server/api/mcp/v1/schema.resolvers.go +++ b/pkg/server/api/mcp/v1/schema.resolvers.go @@ -21,7 +21,7 @@ import ( // ListOrganizationsTool handles the listOrganizations tool // List all organizations the user has access to func (r *Resolver) ListOrganizationsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListOrganizationsInput) (*mcp.CallToolResult, types.ListOrganizationsOutput, error) { - user := connect_v1.UserFromContext(ctx) + user := connect_v1.IdentityFromContext(ctx) organizations, err := r.iamSvc.AccountService.ListOrganizations(ctx, user.ID) if err != nil { @@ -1667,7 +1667,7 @@ func (r *Resolver) PublishDocumentVersionTool(ctx context.Context, req *mcp.Call svc := r.ProboService(ctx, input.DocumentID) - user := connect_v1.UserFromContext(ctx) + user := connect_v1.IdentityFromContext(ctx) document, documentVersion, err := svc.Documents.PublishVersion(ctx, input.DocumentID, user.ID, input.Changelog) if err != nil { diff --git a/pkg/server/api/trust/v1/trustauth/auth.go b/pkg/server/api/trust/v1/trustauth/auth.go index aad4a8a07..7cf382d2e 100644 --- a/pkg/server/api/trust/v1/trustauth/auth.go +++ b/pkg/server/api/trust/v1/trustauth/auth.go @@ -33,7 +33,7 @@ type TokenAccessData struct { } type ContextAccessor interface { - UserFromContext(ctx context.Context) *coredata.User + IdentityFromContext(ctx context.Context) *coredata.Identity TokenAccessFromContext(ctx context.Context) *TokenAccessData } @@ -46,8 +46,8 @@ func ValidateTenantAccess(ctx context.Context, accessor ContextAccessor, userTen return nil } - user := accessor.UserFromContext(ctx) - if user != nil { + identity := accessor.IdentityFromContext(ctx) + if identity != nil { userTenants, ok := ctx.Value(userTenantContextKey).(*[]gid.TenantID) if !ok || userTenants == nil { return fmt.Errorf("access denied: no tenant information available") @@ -65,10 +65,10 @@ func ValidateTenantAccess(ctx context.Context, accessor ContextAccessor, userTen } func GetCurrentUserRole(ctx context.Context, accessor ContextAccessor) types.Role { - user := accessor.UserFromContext(ctx) + identity := accessor.IdentityFromContext(ctx) tokenAccess := accessor.TokenAccessFromContext(ctx) - if user != nil || tokenAccess != nil { + if identity != nil || tokenAccess != nil { return types.RoleUser } return types.RoleNone diff --git a/pkg/server/api/trust/v1/v1_resolver.go b/pkg/server/api/trust/v1/v1_resolver.go index 7bfc1b8c8..c3f7b721a 100644 --- a/pkg/server/api/trust/v1/v1_resolver.go +++ b/pkg/server/api/trust/v1/v1_resolver.go @@ -74,7 +74,7 @@ func (r *documentResolver) IsUserAuthorized(ctx context.Context, obj *types.Docu // return false, nil // } - // userData := connect_v1.UserFromContext(ctx) + // userData := connect_v1.IdentityFromContext(ctx) // if userData != nil { // return true, nil // } @@ -99,7 +99,7 @@ func (r *documentResolver) HasUserRequestedAccess(ctx context.Context, obj *type // return false, nil // } - // userData := r.UserFromContext(ctx) + // userData := r.IdentityFromContext(ctx) // if userData != nil { // return false, nil // } @@ -137,7 +137,7 @@ func (r *frameworkResolver) DarkLogoURL(ctx context.Context, obj *types.Framewor func (r *mutationResolver) RequestAllAccesses(ctx context.Context, input types.RequestAllAccessesInput) (*types.RequestAccessesPayload, error) { // publicTrustService := r.PublicTrustService(ctx, input.TrustCenterID.TenantID()) - // userData := r.UserFromContext(ctx) + // userData := r.IdentityFromContext(ctx) // if userData != nil { // return nil, fmt.Errorf("session users cannot request trust center access") // } @@ -239,7 +239,7 @@ func (r *mutationResolver) ExportDocumentPDF(ctx context.Context, input types.Ex // } // } - // userData := UserFromContext(ctx) + // userData := IdentityFromContext(ctx) // var userEmail mail.Addr // if userData != nil { // userEmail = userData.EmailAddress @@ -319,7 +319,7 @@ func (r *mutationResolver) ExportReportPDF(ctx context.Context, input types.Expo // } // } - // userData := UserFromContext(ctx) + // userData := IdentityFromContext(ctx) // var userEmail mail.Addr // if userData != nil { // userEmail = userData.EmailAddress @@ -371,7 +371,7 @@ func (r *mutationResolver) RequestDocumentAccess(ctx context.Context, input type // fullname string // ) - // identity := connect_v1.UserFromContext(ctx) + // identity := connect_v1.IdentityFromContext(ctx) // if identity != nil { // email = identity.EmailAddress // fullname = identity.FullName @@ -422,7 +422,7 @@ func (r *mutationResolver) RequestReportAccess(ctx context.Context, input types. // return nil, fmt.Errorf("report is publicly available and does not require access request") // } - // userData := r.UserFromContext(ctx) + // userData := r.IdentityFromContext(ctx) // if userData != nil { // return nil, fmt.Errorf("session users cannot request trust center access") // } @@ -473,7 +473,7 @@ func (r *mutationResolver) RequestTrustCenterFileAccess(ctx context.Context, inp // return nil, fmt.Errorf("trust center file is publicly available and does not require access request") // } - // userData := r.UserFromContext(ctx) + // userData := r.IdentityFromContext(ctx) // if userData != nil { // return nil, fmt.Errorf("session users cannot request trust center access") // } @@ -571,7 +571,7 @@ func (r *mutationResolver) ExportTrustCenterFile(ctx context.Context, input type // } // } - // userData := UserFromContext(ctx) + // userData := IdentityFromContext(ctx) // var userEmail mail.Addr // if userData != nil { // userEmail = userData.EmailAddress @@ -753,7 +753,7 @@ func (r *reportResolver) IsUserAuthorized(ctx context.Context, obj *types.Report // return false, nil // } - // userData := r.UserFromContext(ctx) + // userData := r.IdentityFromContext(ctx) // if userData != nil { // return true, nil // } @@ -780,7 +780,7 @@ func (r *reportResolver) HasUserRequestedAccess(ctx context.Context, obj *types. // return false, nil // } - // userData := r.UserFromContext(ctx) + // userData := r.IdentityFromContext(ctx) // if userData != nil { // return false, nil // } @@ -836,7 +836,7 @@ func (r *trustCenterResolver) HasAcceptedNonDisclosureAgreement(ctx context.Cont // return false, nil // } - // userData := UserFromContext(ctx) + // userData := IdentityFromContext(ctx) // if userData != nil { // return true, nil // } @@ -963,7 +963,7 @@ func (r *trustCenterFileResolver) IsUserAuthorized(ctx context.Context, obj *typ // return false, nil // } - // userData := r.UserFromContext(ctx) + // userData := r.IdentityFromContext(ctx) // if userData != nil { // return true, nil // } @@ -990,7 +990,7 @@ func (r *trustCenterFileResolver) HasUserRequestedAccess(ctx context.Context, ob // return false, nil // } - // userData := r.UserFromContext(ctx) + // userData := r.IdentityFromContext(ctx) // if userData != nil { // return false, nil // }