Rename user into identity
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
8
pkg/coredata/migrations/20251220T104530Z.sql
Normal file
8
pkg/coredata/migrations/20251220T104530Z.sql
Normal file
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
// }
|
||||
|
||||
@@ -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
|
||||
// }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -413,7 +413,7 @@ var Permissions = map[uint16]map[Action][]Role{
|
||||
ActionGetTrustCenterFile: EditRoles,
|
||||
ActionDeleteTrustCenterFile: EditRoles,
|
||||
},
|
||||
coredata.UserEntityType: {
|
||||
coredata.IdentityEntityType: {
|
||||
ActionGet: NonEmployeeRoles,
|
||||
},
|
||||
coredata.MembershipEntityType: {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
},
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"})
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user