Rename identity profile on membership profile
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -72,7 +72,7 @@ const (
|
|||||||
RightsRequestEntityType uint16 = 48
|
RightsRequestEntityType uint16 = 48
|
||||||
StateOfApplicabilityEntityType uint16 = 49
|
StateOfApplicabilityEntityType uint16 = 49
|
||||||
StateOfApplicabilityControlEntityType uint16 = 50
|
StateOfApplicabilityControlEntityType uint16 = 50
|
||||||
IdentityProfileEntityType uint16 = 51
|
MembershipProfileEntityType uint16 = 51
|
||||||
)
|
)
|
||||||
|
|
||||||
type EntityInfo struct {
|
type EntityInfo struct {
|
||||||
@@ -285,9 +285,9 @@ var entityRegistry = map[uint16]EntityInfo{
|
|||||||
Model: "StateOfApplicabilityControl",
|
Model: "StateOfApplicabilityControl",
|
||||||
Table: "states_of_applicability_controls",
|
Table: "states_of_applicability_controls",
|
||||||
},
|
},
|
||||||
IdentityProfileEntityType: {
|
MembershipProfileEntityType: {
|
||||||
Model: "IdentityProfile",
|
Model: "MembershipProfile",
|
||||||
Table: "iam_identity_profiles",
|
Table: "iam_membership_profiles",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ type (
|
|||||||
Identity struct {
|
Identity struct {
|
||||||
ID gid.GID `db:"id"`
|
ID gid.GID `db:"id"`
|
||||||
EmailAddress mail.Addr `db:"email_address"`
|
EmailAddress mail.Addr `db:"email_address"`
|
||||||
|
FullName string `db:"full_name"`
|
||||||
HashedPassword []byte `db:"hashed_password"`
|
HashedPassword []byte `db:"hashed_password"`
|
||||||
EmailAddressVerified bool `db:"email_address_verified"`
|
EmailAddressVerified bool `db:"email_address_verified"`
|
||||||
SAMLSubject *string `db:"saml_subject"`
|
SAMLSubject *string `db:"saml_subject"`
|
||||||
@@ -63,8 +64,10 @@ func (i *Identities) LoadByOrganizationID(
|
|||||||
SELECT
|
SELECT
|
||||||
id,
|
id,
|
||||||
email_address,
|
email_address,
|
||||||
|
full_name,
|
||||||
hashed_password,
|
hashed_password,
|
||||||
email_address_verified,
|
email_address_verified,
|
||||||
|
saml_subject,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
FROM
|
FROM
|
||||||
@@ -139,6 +142,7 @@ func (i *Identity) LoadByEmail(
|
|||||||
SELECT
|
SELECT
|
||||||
id,
|
id,
|
||||||
email_address,
|
email_address,
|
||||||
|
full_name,
|
||||||
hashed_password,
|
hashed_password,
|
||||||
email_address_verified,
|
email_address_verified,
|
||||||
saml_subject,
|
saml_subject,
|
||||||
@@ -182,6 +186,7 @@ func (i *Identity) LoadByID(
|
|||||||
SELECT
|
SELECT
|
||||||
id,
|
id,
|
||||||
email_address,
|
email_address,
|
||||||
|
full_name,
|
||||||
hashed_password,
|
hashed_password,
|
||||||
email_address_verified,
|
email_address_verified,
|
||||||
saml_subject,
|
saml_subject,
|
||||||
@@ -221,10 +226,11 @@ func (i *Identity) Insert(
|
|||||||
) error {
|
) error {
|
||||||
q := `
|
q := `
|
||||||
INSERT INTO
|
INSERT INTO
|
||||||
identities (id, email_address, hashed_password, email_address_verified, saml_subject, created_at, updated_at)
|
identities (id, email_address, full_name, hashed_password, email_address_verified, saml_subject, created_at, updated_at)
|
||||||
VALUES (
|
VALUES (
|
||||||
@identity_id,
|
@identity_id,
|
||||||
@email_address,
|
@email_address,
|
||||||
|
@full_name,
|
||||||
@hashed_password,
|
@hashed_password,
|
||||||
@email_address_verified,
|
@email_address_verified,
|
||||||
@saml_subject,
|
@saml_subject,
|
||||||
@@ -236,6 +242,7 @@ VALUES (
|
|||||||
args := pgx.StrictNamedArgs{
|
args := pgx.StrictNamedArgs{
|
||||||
"identity_id": i.ID,
|
"identity_id": i.ID,
|
||||||
"email_address": i.EmailAddress,
|
"email_address": i.EmailAddress,
|
||||||
|
"full_name": i.FullName,
|
||||||
"hashed_password": i.HashedPassword,
|
"hashed_password": i.HashedPassword,
|
||||||
"saml_subject": i.SAMLSubject,
|
"saml_subject": i.SAMLSubject,
|
||||||
"created_at": i.CreatedAt,
|
"created_at": i.CreatedAt,
|
||||||
@@ -265,6 +272,7 @@ UPDATE
|
|||||||
identities
|
identities
|
||||||
SET
|
SET
|
||||||
email_address = @email_address,
|
email_address = @email_address,
|
||||||
|
full_name = @full_name,
|
||||||
email_address_verified = @email_address_verified,
|
email_address_verified = @email_address_verified,
|
||||||
saml_subject = @saml_subject,
|
saml_subject = @saml_subject,
|
||||||
hashed_password = @hashed_password,
|
hashed_password = @hashed_password,
|
||||||
@@ -276,6 +284,7 @@ WHERE
|
|||||||
args := pgx.StrictNamedArgs{
|
args := pgx.StrictNamedArgs{
|
||||||
"identity_id": i.ID,
|
"identity_id": i.ID,
|
||||||
"email_address": i.EmailAddress,
|
"email_address": i.EmailAddress,
|
||||||
|
"full_name": i.FullName,
|
||||||
"email_address_verified": i.EmailAddressVerified,
|
"email_address_verified": i.EmailAddressVerified,
|
||||||
"saml_subject": i.SAMLSubject,
|
"saml_subject": i.SAMLSubject,
|
||||||
"updated_at": i.UpdatedAt,
|
"updated_at": i.UpdatedAt,
|
||||||
@@ -304,6 +313,7 @@ func (i *Identity) LoadBySAMLSubject(
|
|||||||
SELECT
|
SELECT
|
||||||
id,
|
id,
|
||||||
email_address,
|
email_address,
|
||||||
|
full_name,
|
||||||
hashed_password,
|
hashed_password,
|
||||||
email_address_verified,
|
email_address_verified,
|
||||||
saml_subject,
|
saml_subject,
|
||||||
|
|||||||
@@ -81,13 +81,14 @@ SELECT
|
|||||||
mbr.identity_id,
|
mbr.identity_id,
|
||||||
mbr.organization_id,
|
mbr.organization_id,
|
||||||
mbr.role,
|
mbr.role,
|
||||||
i.fullname AS full_name,
|
COALESCE(mp.full_name, i.full_name, '') as full_name,
|
||||||
i.email_address,
|
i.email_address,
|
||||||
mbr.created_at,
|
mbr.created_at,
|
||||||
mbr.updated_at
|
mbr.updated_at
|
||||||
FROM
|
FROM
|
||||||
mbr
|
mbr
|
||||||
JOIN identities i ON mbr.identity_id = i.id
|
JOIN identities i ON mbr.identity_id = i.id
|
||||||
|
LEFT JOIN iam_membership_profiles mp ON mp.membership_id = mbr.id
|
||||||
`
|
`
|
||||||
|
|
||||||
args := pgx.StrictNamedArgs{
|
args := pgx.StrictNamedArgs{
|
||||||
@@ -189,7 +190,7 @@ SELECT
|
|||||||
mbr.identity_id,
|
mbr.identity_id,
|
||||||
mbr.organization_id,
|
mbr.organization_id,
|
||||||
mbr.role,
|
mbr.role,
|
||||||
COALESCE(mp.full_name, dp.full_name, '') as full_name,
|
COALESCE(mp.full_name, i.full_name, '') as full_name,
|
||||||
i.email_address,
|
i.email_address,
|
||||||
mbr.created_at,
|
mbr.created_at,
|
||||||
mbr.updated_at
|
mbr.updated_at
|
||||||
@@ -198,9 +199,7 @@ FROM
|
|||||||
JOIN
|
JOIN
|
||||||
identities i ON mbr.identity_id = i.id
|
identities i ON mbr.identity_id = i.id
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
iam_identity_profiles mp ON mp.membership_id = mbr.id
|
iam_membership_profiles mp ON mp.membership_id = mbr.id
|
||||||
LEFT JOIN
|
|
||||||
iam_identity_profiles dp ON dp.identity_id = mbr.identity_id AND dp.membership_id IS NULL
|
|
||||||
`
|
`
|
||||||
|
|
||||||
query = fmt.Sprintf(query, scope.SQLFragment())
|
query = fmt.Sprintf(query, scope.SQLFragment())
|
||||||
@@ -333,7 +332,7 @@ SELECT
|
|||||||
mbr.identity_id,
|
mbr.identity_id,
|
||||||
mbr.organization_id,
|
mbr.organization_id,
|
||||||
mbr.role,
|
mbr.role,
|
||||||
COALESCE(mp.full_name, dp.full_name, '') as full_name,
|
COALESCE(mp.full_name, i.full_name, '') as full_name,
|
||||||
i.email_address,
|
i.email_address,
|
||||||
mbr.created_at,
|
mbr.created_at,
|
||||||
mbr.updated_at
|
mbr.updated_at
|
||||||
@@ -342,9 +341,7 @@ FROM
|
|||||||
JOIN
|
JOIN
|
||||||
identities i ON mbr.identity_id = i.id
|
identities i ON mbr.identity_id = i.id
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
iam_identity_profiles mp ON mp.membership_id = mbr.id
|
iam_membership_profiles mp ON mp.membership_id = mbr.id
|
||||||
LEFT JOIN
|
|
||||||
iam_identity_profiles dp ON dp.identity_id = mbr.identity_id AND dp.membership_id IS NULL
|
|
||||||
`
|
`
|
||||||
|
|
||||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||||
@@ -463,7 +460,7 @@ SELECT
|
|||||||
mbr.identity_id,
|
mbr.identity_id,
|
||||||
mbr.organization_id,
|
mbr.organization_id,
|
||||||
mbr.role,
|
mbr.role,
|
||||||
COALESCE(mp.full_name, dp.full_name, '') as full_name,
|
COALESCE(mp.full_name, i.full_name, '') as full_name,
|
||||||
i.email_address,
|
i.email_address,
|
||||||
mbr.created_at,
|
mbr.created_at,
|
||||||
mbr.updated_at
|
mbr.updated_at
|
||||||
@@ -472,19 +469,18 @@ FROM
|
|||||||
JOIN
|
JOIN
|
||||||
identities i ON mbr.identity_id = i.id
|
identities i ON mbr.identity_id = i.id
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
iam_identity_profiles mp ON mp.membership_id = mbr.id
|
iam_membership_profiles mp ON mp.membership_id = mbr.id
|
||||||
LEFT JOIN
|
WHERE
|
||||||
iam_identity_profiles dp ON dp.identity_id = mbr.identity_id AND dp.membership_id IS NULL
|
%s
|
||||||
ORDER BY
|
|
||||||
mbr.created_at DESC
|
|
||||||
`
|
`
|
||||||
|
|
||||||
query = fmt.Sprintf(query, scope.SQLFragment())
|
query = fmt.Sprintf(query, scope.SQLFragment(), cursor.SQLFragment())
|
||||||
|
|
||||||
args := pgx.StrictNamedArgs{
|
args := pgx.StrictNamedArgs{
|
||||||
"identity_id": identityID,
|
"identity_id": identityID,
|
||||||
}
|
}
|
||||||
maps.Copy(args, scope.SQLArguments())
|
maps.Copy(args, scope.SQLArguments())
|
||||||
|
maps.Copy(args, cursor.SQLArguments())
|
||||||
|
|
||||||
rows, err := conn.Query(ctx, query, args)
|
rows, err := conn.Query(ctx, query, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -508,18 +504,24 @@ func (m *Memberships) LoadByOrganizationID(
|
|||||||
cursor *page.Cursor[MembershipOrderField],
|
cursor *page.Cursor[MembershipOrderField],
|
||||||
) error {
|
) error {
|
||||||
query := `
|
query := `
|
||||||
WITH mbr AS (
|
WITH membership_with_profile AS (
|
||||||
SELECT
|
SELECT
|
||||||
id,
|
m.id,
|
||||||
identity_id,
|
m.identity_id,
|
||||||
organization_id,
|
m.organization_id,
|
||||||
role,
|
m.role,
|
||||||
created_at,
|
COALESCE(mp.full_name, i.full_name, '') AS full_name,
|
||||||
updated_at
|
i.email_address,
|
||||||
|
m.created_at,
|
||||||
|
m.updated_at
|
||||||
FROM
|
FROM
|
||||||
iam_memberships
|
iam_memberships m
|
||||||
|
JOIN
|
||||||
|
identities i ON m.identity_id = i.id
|
||||||
|
LEFT JOIN
|
||||||
|
iam_membership_profiles mp ON mp.membership_id = m.id
|
||||||
WHERE
|
WHERE
|
||||||
organization_id = @organization_id
|
m.organization_id = @organization_id
|
||||||
AND %s
|
AND %s
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
@@ -531,26 +533,10 @@ SELECT
|
|||||||
email_address,
|
email_address,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
FROM (
|
FROM
|
||||||
SELECT
|
membership_with_profile
|
||||||
mbr.id,
|
WHERE
|
||||||
mbr.identity_id,
|
%s
|
||||||
mbr.organization_id,
|
|
||||||
mbr.role,
|
|
||||||
COALESCE(mp.full_name, dp.full_name, '') as full_name,
|
|
||||||
i.email_address,
|
|
||||||
mbr.created_at,
|
|
||||||
mbr.updated_at
|
|
||||||
FROM
|
|
||||||
mbr
|
|
||||||
JOIN
|
|
||||||
identities i ON mbr.identity_id = i.id
|
|
||||||
LEFT JOIN
|
|
||||||
iam_identity_profiles mp ON mp.membership_id = mbr.id
|
|
||||||
LEFT JOIN
|
|
||||||
iam_identity_profiles dp ON dp.identity_id = mbr.identity_id AND dp.membership_id IS NULL
|
|
||||||
) AS membership_with_identity
|
|
||||||
WHERE %s
|
|
||||||
`
|
`
|
||||||
|
|
||||||
query = fmt.Sprintf(query, scope.SQLFragment(), cursor.SQLFragment())
|
query = fmt.Sprintf(query, scope.SQLFragment(), cursor.SQLFragment())
|
||||||
|
|||||||
@@ -27,68 +27,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
IdentityProfile struct {
|
MembershipProfile struct {
|
||||||
ID gid.GID `db:"id"`
|
ID gid.GID `db:"id"`
|
||||||
IdentityID gid.GID `db:"identity_id"`
|
MembershipID gid.GID `db:"membership_id"`
|
||||||
MembershipID *gid.GID `db:"membership_id"`
|
|
||||||
FullName string `db:"full_name"`
|
FullName string `db:"full_name"`
|
||||||
CreatedAt time.Time `db:"created_at"`
|
CreatedAt time.Time `db:"created_at"`
|
||||||
UpdatedAt time.Time `db:"updated_at"`
|
UpdatedAt time.Time `db:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
IdentityProfiles []*IdentityProfile
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *IdentityProfile) IsDefault() bool {
|
func (p *MembershipProfile) LoadByMembershipID(
|
||||||
return p.MembershipID == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadDefaultByIdentityID loads the default profile for an identity (where membership_id is NULL)
|
|
||||||
func (p *IdentityProfile) LoadDefaultByIdentityID(
|
|
||||||
ctx context.Context,
|
|
||||||
conn pg.Conn,
|
|
||||||
identityID gid.GID,
|
|
||||||
) error {
|
|
||||||
q := `
|
|
||||||
SELECT
|
|
||||||
id,
|
|
||||||
identity_id,
|
|
||||||
membership_id,
|
|
||||||
full_name,
|
|
||||||
created_at,
|
|
||||||
updated_at
|
|
||||||
FROM
|
|
||||||
iam_identity_profiles
|
|
||||||
WHERE
|
|
||||||
tenant_id IS NULL
|
|
||||||
AND identity_id = @identity_id
|
|
||||||
AND membership_id IS NULL
|
|
||||||
LIMIT 1;
|
|
||||||
`
|
|
||||||
|
|
||||||
args := pgx.StrictNamedArgs{"identity_id": identityID}
|
|
||||||
|
|
||||||
rows, err := conn.Query(ctx, q, args)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot query default identity profile: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
profile, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[IdentityProfile])
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
|
||||||
return ErrResourceNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("cannot collect default identity profile: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
*p = profile
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadByMembershipID loads the profile for a specific membership
|
|
||||||
func (p *IdentityProfile) LoadByMembershipID(
|
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Conn,
|
conn pg.Conn,
|
||||||
scope Scoper,
|
scope Scoper,
|
||||||
@@ -97,13 +45,12 @@ func (p *IdentityProfile) LoadByMembershipID(
|
|||||||
q := `
|
q := `
|
||||||
SELECT
|
SELECT
|
||||||
id,
|
id,
|
||||||
identity_id,
|
|
||||||
membership_id,
|
membership_id,
|
||||||
full_name,
|
full_name,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
FROM
|
FROM
|
||||||
iam_identity_profiles
|
iam_membership_profiles
|
||||||
WHERE
|
WHERE
|
||||||
%s
|
%s
|
||||||
AND membership_id = @membership_id
|
AND membership_id = @membership_id
|
||||||
@@ -120,7 +67,7 @@ LIMIT 1;
|
|||||||
return fmt.Errorf("cannot query identity profile: %w", err)
|
return fmt.Errorf("cannot query identity profile: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
profile, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[IdentityProfile])
|
profile, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[MembershipProfile])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
return ErrResourceNotFound
|
return ErrResourceNotFound
|
||||||
@@ -134,7 +81,7 @@ LIMIT 1;
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *IdentityProfile) LoadByID(
|
func (p *MembershipProfile) LoadByID(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Conn,
|
conn pg.Conn,
|
||||||
scope Scoper,
|
scope Scoper,
|
||||||
@@ -143,13 +90,12 @@ func (p *IdentityProfile) LoadByID(
|
|||||||
q := `
|
q := `
|
||||||
SELECT
|
SELECT
|
||||||
id,
|
id,
|
||||||
identity_id,
|
|
||||||
membership_id,
|
membership_id,
|
||||||
full_name,
|
full_name,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
FROM
|
FROM
|
||||||
iam_identity_profiles
|
iam_membership_profiles
|
||||||
WHERE
|
WHERE
|
||||||
%s
|
%s
|
||||||
AND id = @profile_id
|
AND id = @profile_id
|
||||||
@@ -166,7 +112,7 @@ LIMIT 1;
|
|||||||
return fmt.Errorf("cannot query identity profile: %w", err)
|
return fmt.Errorf("cannot query identity profile: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
profile, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[IdentityProfile])
|
profile, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[MembershipProfile])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
return ErrResourceNotFound
|
return ErrResourceNotFound
|
||||||
@@ -180,16 +126,15 @@ LIMIT 1;
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *IdentityProfile) Insert(
|
func (p *MembershipProfile) Insert(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Conn,
|
conn pg.Conn,
|
||||||
) error {
|
) error {
|
||||||
q := `
|
q := `
|
||||||
INSERT INTO
|
INSERT INTO
|
||||||
iam_identity_profiles (
|
iam_membership_profiles (
|
||||||
tenant_id,
|
tenant_id,
|
||||||
id,
|
id,
|
||||||
identity_id,
|
|
||||||
membership_id,
|
membership_id,
|
||||||
full_name,
|
full_name,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -198,7 +143,6 @@ INSERT INTO
|
|||||||
VALUES (
|
VALUES (
|
||||||
@tenant_id,
|
@tenant_id,
|
||||||
@id,
|
@id,
|
||||||
@identity_id,
|
|
||||||
@membership_id,
|
@membership_id,
|
||||||
@full_name,
|
@full_name,
|
||||||
@created_at,
|
@created_at,
|
||||||
@@ -209,7 +153,6 @@ VALUES (
|
|||||||
args := pgx.StrictNamedArgs{
|
args := pgx.StrictNamedArgs{
|
||||||
"tenant_id": p.ID.TenantID().String(),
|
"tenant_id": p.ID.TenantID().String(),
|
||||||
"id": p.ID,
|
"id": p.ID,
|
||||||
"identity_id": p.IdentityID,
|
|
||||||
"membership_id": p.MembershipID,
|
"membership_id": p.MembershipID,
|
||||||
"full_name": p.FullName,
|
"full_name": p.FullName,
|
||||||
"created_at": p.CreatedAt,
|
"created_at": p.CreatedAt,
|
||||||
@@ -224,14 +167,14 @@ VALUES (
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *IdentityProfile) Update(
|
func (p *MembershipProfile) Update(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Conn,
|
conn pg.Conn,
|
||||||
scope Scoper,
|
scope Scoper,
|
||||||
) error {
|
) error {
|
||||||
q := `
|
q := `
|
||||||
UPDATE
|
UPDATE
|
||||||
iam_identity_profiles
|
iam_membership_profiles
|
||||||
SET
|
SET
|
||||||
full_name = @full_name,
|
full_name = @full_name,
|
||||||
updated_at = @updated_at
|
updated_at = @updated_at
|
||||||
@@ -261,7 +204,7 @@ WHERE
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *IdentityProfile) Delete(
|
func (p *MembershipProfile) Delete(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Conn,
|
conn pg.Conn,
|
||||||
scope Scoper,
|
scope Scoper,
|
||||||
@@ -269,7 +212,7 @@ func (p *IdentityProfile) Delete(
|
|||||||
) error {
|
) error {
|
||||||
q := `
|
q := `
|
||||||
DELETE FROM
|
DELETE FROM
|
||||||
iam_identity_profiles
|
iam_membership_profiles
|
||||||
WHERE
|
WHERE
|
||||||
id = @profile_id
|
id = @profile_id
|
||||||
AND %s
|
AND %s
|
||||||
15
pkg/coredata/migrations/20251222T102632Z.sql
Normal file
15
pkg/coredata/migrations/20251222T102632Z.sql
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
ALTER TABLE identities ADD COLUMN full_name TEXT NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
UPDATE identities i
|
||||||
|
SET full_name = COALESCE(
|
||||||
|
(SELECT p.full_name FROM iam_identity_profiles p
|
||||||
|
WHERE p.identity_id = i.id AND p.membership_id IS NULL),
|
||||||
|
''
|
||||||
|
);
|
||||||
|
|
||||||
|
DELETE FROM iam_identity_profiles WHERE membership_id IS NULL;
|
||||||
|
DROP INDEX IF EXISTS idx_iam_identity_profiles_default;
|
||||||
|
|
||||||
|
ALTER TABLE iam_identity_profiles DROP COLUMN identity_id;
|
||||||
|
|
||||||
|
ALTER TABLE iam_identity_profiles RENAME TO iam_membership_profiles;
|
||||||
@@ -125,14 +125,9 @@ func (s AccountService) ChangeEmail(ctx context.Context, identityID gid.GID, req
|
|||||||
return fmt.Errorf("cannot update identity: %w", err)
|
return fmt.Errorf("cannot update identity: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
profile := &coredata.IdentityProfile{}
|
|
||||||
if err := profile.LoadDefaultByIdentityID(ctx, tx, identityID); err != nil {
|
|
||||||
return fmt.Errorf("cannot load default profile: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
subject, textBody, htmlBody, err := emails.RenderConfirmEmail(
|
subject, textBody, htmlBody, err := emails.RenderConfirmEmail(
|
||||||
s.baseURL,
|
s.baseURL,
|
||||||
profile.FullName,
|
identity.FullName,
|
||||||
confirmationUrl,
|
confirmationUrl,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -140,7 +135,7 @@ func (s AccountService) ChangeEmail(ctx context.Context, identityID gid.GID, req
|
|||||||
}
|
}
|
||||||
|
|
||||||
confirmationEmail := coredata.NewEmail(
|
confirmationEmail := coredata.NewEmail(
|
||||||
profile.FullName,
|
identity.FullName,
|
||||||
identity.EmailAddress,
|
identity.EmailAddress,
|
||||||
subject,
|
subject,
|
||||||
textBody,
|
textBody,
|
||||||
@@ -720,10 +715,10 @@ func (s AccountService) ListOrganizations(ctx context.Context, identityID gid.GI
|
|||||||
return organizations, nil
|
return organizations, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s AccountService) GetProfileForMembership(ctx context.Context, membershipID gid.GID) (*coredata.IdentityProfile, error) {
|
func (s AccountService) GetProfileForMembership(ctx context.Context, membershipID gid.GID) (*coredata.MembershipProfile, error) {
|
||||||
var (
|
var (
|
||||||
scope = coredata.NewScopeFromObjectID(membershipID)
|
scope = coredata.NewScopeFromObjectID(membershipID)
|
||||||
profile = &coredata.IdentityProfile{}
|
profile = &coredata.MembershipProfile{}
|
||||||
)
|
)
|
||||||
|
|
||||||
err := s.pg.WithConn(
|
err := s.pg.WithConn(
|
||||||
@@ -758,118 +753,3 @@ func (s AccountService) GetProfileForMembership(ctx context.Context, membershipI
|
|||||||
|
|
||||||
return profile, nil
|
return profile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s AccountService) GetDefaultProfile(ctx context.Context, identityID gid.GID) (*coredata.IdentityProfile, error) {
|
|
||||||
var profile = &coredata.IdentityProfile{}
|
|
||||||
|
|
||||||
err := s.pg.WithConn(
|
|
||||||
ctx,
|
|
||||||
func(conn pg.Conn) error {
|
|
||||||
err := profile.LoadDefaultByIdentityID(ctx, conn, identityID)
|
|
||||||
if err != nil {
|
|
||||||
if err == coredata.ErrResourceNotFound {
|
|
||||||
return NewProfileNotFoundError(identityID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("cannot load default profile: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return profile, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type UpdateIdentityProfileRequest struct {
|
|
||||||
MembershipID gid.GID
|
|
||||||
FullName *string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s AccountService) UpdateIdentityProfile(
|
|
||||||
ctx context.Context,
|
|
||||||
identityID gid.GID,
|
|
||||||
req *UpdateIdentityProfileRequest,
|
|
||||||
) (*coredata.IdentityProfile, error) {
|
|
||||||
// var (
|
|
||||||
// scope = coredata.NewScopeFromObjectID(req.MembershipID)
|
|
||||||
// profile = &coredata.IdentityProfile{}
|
|
||||||
// )
|
|
||||||
|
|
||||||
// err := s.pg.WithTx(
|
|
||||||
// ctx,
|
|
||||||
// func(tx pg.Conn) error {
|
|
||||||
// // First verify the membership belongs to the identity
|
|
||||||
// membership := &coredata.Membership{}
|
|
||||||
// err := membership.LoadByID(ctx, tx, scope, req.MembershipID)
|
|
||||||
// if err != nil {
|
|
||||||
// if err == coredata.ErrResourceNotFound {
|
|
||||||
// return NewMembershipNotFoundError(req.MembershipID)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return fmt.Errorf("cannot load membership: %w", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if membership.IdentityID != identityID {
|
|
||||||
// return NewMembershipNotFoundError(req.MembershipID)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Try to load existing membership profile
|
|
||||||
// err = profile.LoadByMembershipID(ctx, tx, scope, req.MembershipID)
|
|
||||||
// if err != nil && err != coredata.ErrResourceNotFound {
|
|
||||||
// return fmt.Errorf("cannot load identity profile: %w", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// now := time.Now()
|
|
||||||
|
|
||||||
// if err == coredata.ErrResourceNotFound {
|
|
||||||
// // Create new membership profile, optionally inheriting from default
|
|
||||||
// tenantID := req.MembershipID.TenantID()
|
|
||||||
// membershipID := req.MembershipID
|
|
||||||
|
|
||||||
// // Try to get default profile to inherit FullName
|
|
||||||
// defaultProfile := &coredata.IdentityProfile{}
|
|
||||||
// defaultFullName := ""
|
|
||||||
// if loadErr := defaultProfile.LoadDefaultByIdentityID(ctx, tx, identityID); loadErr == nil {
|
|
||||||
// defaultFullName = defaultProfile.FullName
|
|
||||||
// }
|
|
||||||
|
|
||||||
// tenantIDStr := tenantID.String()
|
|
||||||
// profile = &coredata.IdentityProfile{
|
|
||||||
// ID: gid.New(tenantID, coredata.IdentityProfileEntityType),
|
|
||||||
// TenantID: &tenantIDStr,
|
|
||||||
// IdentityID: identityID,
|
|
||||||
// MembershipID: &membershipID,
|
|
||||||
// FullName: defaultFullName,
|
|
||||||
// CreatedAt: now,
|
|
||||||
// UpdatedAt: now,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Apply updates
|
|
||||||
// if req.FullName != nil {
|
|
||||||
// profile.FullName = *req.FullName
|
|
||||||
// }
|
|
||||||
// profile.UpdatedAt = now
|
|
||||||
|
|
||||||
// // Upsert the membership profile
|
|
||||||
// err = profile.UpsertMembership(ctx, tx)
|
|
||||||
// if err != nil {
|
|
||||||
// return fmt.Errorf("cannot upsert identity profile: %w", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return nil
|
|
||||||
// },
|
|
||||||
// )
|
|
||||||
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return profile, nil
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ func (s *AuthService) CreateIdentityFromInvitation(
|
|||||||
identity = &coredata.Identity{
|
identity = &coredata.Identity{
|
||||||
ID: gid.New(gid.NilTenant, coredata.IdentityEntityType),
|
ID: gid.New(gid.NilTenant, coredata.IdentityEntityType),
|
||||||
EmailAddress: invitation.Email,
|
EmailAddress: invitation.Email,
|
||||||
|
FullName: invitation.FullName,
|
||||||
HashedPassword: hashedPassword,
|
HashedPassword: hashedPassword,
|
||||||
EmailAddressVerified: true,
|
EmailAddressVerified: true,
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
@@ -171,19 +172,6 @@ func (s *AuthService) CreateIdentityFromInvitation(
|
|||||||
return fmt.Errorf("cannot insert identity: %w", err)
|
return fmt.Errorf("cannot insert identity: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultProfile := &coredata.IdentityProfile{
|
|
||||||
ID: gid.New(gid.NilTenant, coredata.IdentityProfileEntityType),
|
|
||||||
IdentityID: identity.ID,
|
|
||||||
FullName: invitation.FullName,
|
|
||||||
CreatedAt: now,
|
|
||||||
UpdatedAt: now,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = defaultProfile.Insert(ctx, tx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot insert default profile: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
session = coredata.NewRootSession(identity.ID, coredata.AuthMethodPassword, s.sessionDuration)
|
session = coredata.NewRootSession(identity.ID, coredata.AuthMethodPassword, s.sessionDuration)
|
||||||
err = session.Insert(ctx, tx)
|
err = session.Insert(ctx, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -285,14 +273,9 @@ func (s AuthService) SendPasswordResetInstructionByEmail(
|
|||||||
return fmt.Errorf("cannot load identity: %w", err)
|
return fmt.Errorf("cannot load identity: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
profile := &coredata.IdentityProfile{}
|
|
||||||
if err := profile.LoadDefaultByIdentityID(ctx, tx, identity.ID); err != nil {
|
|
||||||
return fmt.Errorf("cannot load default profile: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
subject, textBody, htmlBody, err := emails.RenderPasswordReset(
|
subject, textBody, htmlBody, err := emails.RenderPasswordReset(
|
||||||
s.baseURL,
|
s.baseURL,
|
||||||
profile.FullName,
|
identity.FullName,
|
||||||
resetPasswordUrl,
|
resetPasswordUrl,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -300,7 +283,7 @@ func (s AuthService) SendPasswordResetInstructionByEmail(
|
|||||||
}
|
}
|
||||||
|
|
||||||
passwordResetEmail := coredata.NewEmail(
|
passwordResetEmail := coredata.NewEmail(
|
||||||
profile.FullName,
|
identity.FullName,
|
||||||
identity.EmailAddress,
|
identity.EmailAddress,
|
||||||
subject,
|
subject,
|
||||||
textBody,
|
textBody,
|
||||||
@@ -340,20 +323,13 @@ func (s AuthService) CreateIdentityWithPassword(
|
|||||||
identity = &coredata.Identity{
|
identity = &coredata.Identity{
|
||||||
ID: gid.New(gid.NilTenant, coredata.IdentityEntityType),
|
ID: gid.New(gid.NilTenant, coredata.IdentityEntityType),
|
||||||
EmailAddress: req.Email,
|
EmailAddress: req.Email,
|
||||||
|
FullName: req.FullName,
|
||||||
HashedPassword: hashedPassword,
|
HashedPassword: hashedPassword,
|
||||||
EmailAddressVerified: false,
|
EmailAddressVerified: false,
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultProfile = &coredata.IdentityProfile{
|
|
||||||
ID: gid.New(gid.NilTenant, coredata.IdentityProfileEntityType),
|
|
||||||
IdentityID: identity.ID,
|
|
||||||
FullName: req.FullName,
|
|
||||||
CreatedAt: now,
|
|
||||||
UpdatedAt: now,
|
|
||||||
}
|
|
||||||
|
|
||||||
session = coredata.NewRootSession(identity.ID, coredata.AuthMethodPassword, 24*time.Hour*7)
|
session = coredata.NewRootSession(identity.ID, coredata.AuthMethodPassword, 24*time.Hour*7)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -409,11 +385,6 @@ func (s AuthService) CreateIdentityWithPassword(
|
|||||||
return fmt.Errorf("cannot insert identity: %w", err)
|
return fmt.Errorf("cannot insert identity: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = defaultProfile.Insert(ctx, tx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot insert default profile: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := confirmationEmail.Insert(ctx, tx); err != nil {
|
if err := confirmationEmail.Insert(ctx, tx); err != nil {
|
||||||
return fmt.Errorf("cannot insert email: %w", err)
|
return fmt.Errorf("cannot insert email: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -258,6 +258,7 @@ func (s *Service) HandleAssertion(
|
|||||||
*identity = coredata.Identity{
|
*identity = coredata.Identity{
|
||||||
ID: gid.New(gid.NilTenant, coredata.IdentityEntityType),
|
ID: gid.New(gid.NilTenant, coredata.IdentityEntityType),
|
||||||
EmailAddress: email,
|
EmailAddress: email,
|
||||||
|
FullName: fullname,
|
||||||
HashedPassword: nil,
|
HashedPassword: nil,
|
||||||
EmailAddressVerified: true,
|
EmailAddressVerified: true,
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
@@ -268,24 +269,12 @@ func (s *Service) HandleAssertion(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot insert identity: %w", err)
|
return fmt.Errorf("cannot insert identity: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultProfile := &coredata.IdentityProfile{
|
|
||||||
ID: gid.New(gid.NilTenant, coredata.IdentityProfileEntityType),
|
|
||||||
IdentityID: identity.ID,
|
|
||||||
FullName: fullname,
|
|
||||||
CreatedAt: now,
|
|
||||||
UpdatedAt: now,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = defaultProfile.Insert(ctx, tx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot insert default profile: %w", err)
|
|
||||||
}
|
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return fmt.Errorf("cannot load identity: %w", err)
|
return fmt.Errorf("cannot load identity: %w", err)
|
||||||
} else {
|
} else {
|
||||||
identity.SAMLSubject = &assertion.Subject.NameID.Value
|
identity.SAMLSubject = &assertion.Subject.NameID.Value
|
||||||
identity.EmailAddress = email
|
identity.EmailAddress = email
|
||||||
|
identity.FullName = fullname
|
||||||
identity.EmailAddressVerified = true
|
identity.EmailAddressVerified = true
|
||||||
identity.UpdatedAt = now
|
identity.UpdatedAt = now
|
||||||
|
|
||||||
@@ -316,10 +305,9 @@ func (s *Service) HandleAssertion(
|
|||||||
return fmt.Errorf("cannot insert membership: %w", err)
|
return fmt.Errorf("cannot insert membership: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
membershipProfile := &coredata.IdentityProfile{
|
membershipProfile := &coredata.MembershipProfile{
|
||||||
ID: gid.New(membership.ID.TenantID(), coredata.IdentityProfileEntityType),
|
ID: gid.New(membership.ID.TenantID(), coredata.MembershipProfileEntityType),
|
||||||
IdentityID: identity.ID,
|
MembershipID: membership.ID,
|
||||||
MembershipID: &membership.ID,
|
|
||||||
FullName: fullname,
|
FullName: fullname,
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
@@ -341,7 +329,7 @@ func (s *Service) HandleAssertion(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memberProfile := &coredata.IdentityProfile{}
|
memberProfile := &coredata.MembershipProfile{}
|
||||||
err = memberProfile.LoadByMembershipID(ctx, tx, coredata.NewNoScope(), membership.ID)
|
err = memberProfile.LoadByMembershipID(ctx, tx, coredata.NewNoScope(), membership.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot load membership profile: %w", err)
|
return fmt.Errorf("cannot load membership profile: %w", err)
|
||||||
|
|||||||
@@ -79,10 +79,6 @@ type Mutation {
|
|||||||
input: AssumeOrganizationSessionInput!
|
input: AssumeOrganizationSessionInput!
|
||||||
): AssumeOrganizationSessionPayload @session(required: PRESENT)
|
): AssumeOrganizationSessionPayload @session(required: PRESENT)
|
||||||
|
|
||||||
updateIdentityProfile(
|
|
||||||
input: UpdateIdentityProfileInput!
|
|
||||||
): UpdateIdentityProfilePayload @session(required: PRESENT)
|
|
||||||
|
|
||||||
revokeSession(input: RevokeSessionInput!): RevokeSessionPayload!
|
revokeSession(input: RevokeSessionInput!): RevokeSessionPayload!
|
||||||
@session(required: PRESENT)
|
@session(required: PRESENT)
|
||||||
revokeAllSessions: RevokeAllSessionsPayload @session(required: PRESENT)
|
revokeAllSessions: RevokeAllSessionsPayload @session(required: PRESENT)
|
||||||
@@ -134,12 +130,11 @@ type Mutation {
|
|||||||
type Identity implements Node {
|
type Identity implements Node {
|
||||||
id: ID!
|
id: ID!
|
||||||
email: EmailAddr!
|
email: EmailAddr!
|
||||||
|
fullName: String!
|
||||||
emailVerified: Boolean!
|
emailVerified: Boolean!
|
||||||
createdAt: Datetime!
|
createdAt: Datetime!
|
||||||
updatedAt: Datetime!
|
updatedAt: Datetime!
|
||||||
|
|
||||||
defaultProfile: IdentityProfile @goField(forceResolver: true) @isViewer
|
|
||||||
|
|
||||||
memberships(
|
memberships(
|
||||||
first: Int
|
first: Int
|
||||||
after: CursorKey
|
after: CursorKey
|
||||||
@@ -172,10 +167,9 @@ type Identity implements Node {
|
|||||||
): PersonalAPIKeyConnection @goField(forceResolver: true) @isViewer
|
): PersonalAPIKeyConnection @goField(forceResolver: true) @isViewer
|
||||||
}
|
}
|
||||||
|
|
||||||
type IdentityProfile implements Node {
|
type MembershipProfile implements Node {
|
||||||
id: ID!
|
id: ID!
|
||||||
fullName: String!
|
fullName: String!
|
||||||
identity: Identity!
|
|
||||||
createdAt: Datetime!
|
createdAt: Datetime!
|
||||||
updatedAt: Datetime!
|
updatedAt: Datetime!
|
||||||
}
|
}
|
||||||
@@ -233,7 +227,7 @@ type Membership implements Node {
|
|||||||
id: ID!
|
id: ID!
|
||||||
createdAt: Datetime!
|
createdAt: Datetime!
|
||||||
identity: Identity @goField(forceResolver: true)
|
identity: Identity @goField(forceResolver: true)
|
||||||
profile: IdentityProfile @goField(forceResolver: true) @isViewer
|
profile: MembershipProfile @goField(forceResolver: true) @isViewer
|
||||||
organization: Organization @goField(forceResolver: true)
|
organization: Organization @goField(forceResolver: true)
|
||||||
role: MembershipRole!
|
role: MembershipRole!
|
||||||
permissions: [Permission!]!
|
permissions: [Permission!]!
|
||||||
@@ -558,11 +552,6 @@ input AssumeOrganizationSessionInput {
|
|||||||
organizationId: ID!
|
organizationId: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateIdentityProfileInput {
|
|
||||||
membershipId: ID!
|
|
||||||
fullName: String
|
|
||||||
}
|
|
||||||
|
|
||||||
input RevokeSessionInput {
|
input RevokeSessionInput {
|
||||||
sessionId: ID!
|
sessionId: ID!
|
||||||
}
|
}
|
||||||
@@ -725,10 +714,6 @@ type DeactivateAccountPayload {
|
|||||||
success: Boolean!
|
success: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateIdentityProfilePayload {
|
|
||||||
profile: IdentityProfile
|
|
||||||
}
|
|
||||||
|
|
||||||
type RevokeSessionPayload {
|
type RevokeSessionPayload {
|
||||||
success: Boolean!
|
success: Boolean!
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,7 @@ func NewIdentity(identity *coredata.Identity) *Identity {
|
|||||||
return &Identity{
|
return &Identity{
|
||||||
ID: identity.ID,
|
ID: identity.ID,
|
||||||
Email: identity.EmailAddress,
|
Email: identity.EmailAddress,
|
||||||
|
FullName: identity.FullName,
|
||||||
EmailVerified: identity.EmailAddressVerified,
|
EmailVerified: identity.EmailAddressVerified,
|
||||||
CreatedAt: identity.CreatedAt,
|
CreatedAt: identity.CreatedAt,
|
||||||
UpdatedAt: identity.UpdatedAt,
|
UpdatedAt: identity.UpdatedAt,
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ package types
|
|||||||
|
|
||||||
import "go.probo.inc/probo/pkg/coredata"
|
import "go.probo.inc/probo/pkg/coredata"
|
||||||
|
|
||||||
func NewIdentityProfile(profile *coredata.IdentityProfile) *IdentityProfile {
|
func NewMembershipProfile(profile *coredata.MembershipProfile) *MembershipProfile {
|
||||||
return &IdentityProfile{
|
return &MembershipProfile{
|
||||||
ID: profile.ID,
|
ID: profile.ID,
|
||||||
FullName: profile.FullName,
|
FullName: profile.FullName,
|
||||||
CreatedAt: profile.CreatedAt,
|
CreatedAt: profile.CreatedAt,
|
||||||
|
|||||||
@@ -151,10 +151,10 @@ type ForgotPasswordPayload struct {
|
|||||||
type Identity struct {
|
type Identity struct {
|
||||||
ID gid.GID `json:"id"`
|
ID gid.GID `json:"id"`
|
||||||
Email mail.Addr `json:"email"`
|
Email mail.Addr `json:"email"`
|
||||||
|
FullName string `json:"fullName"`
|
||||||
EmailVerified bool `json:"emailVerified"`
|
EmailVerified bool `json:"emailVerified"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
DefaultProfile *IdentityProfile `json:"defaultProfile,omitempty"`
|
|
||||||
Memberships *MembershipConnection `json:"memberships,omitempty"`
|
Memberships *MembershipConnection `json:"memberships,omitempty"`
|
||||||
PendingInvitations *InvitationConnection `json:"pendingInvitations,omitempty"`
|
PendingInvitations *InvitationConnection `json:"pendingInvitations,omitempty"`
|
||||||
Sessions *SessionConnection `json:"sessions,omitempty"`
|
Sessions *SessionConnection `json:"sessions,omitempty"`
|
||||||
@@ -164,17 +164,6 @@ type Identity struct {
|
|||||||
func (Identity) IsNode() {}
|
func (Identity) IsNode() {}
|
||||||
func (this Identity) GetID() gid.GID { return this.ID }
|
func (this Identity) GetID() gid.GID { return this.ID }
|
||||||
|
|
||||||
type IdentityProfile struct {
|
|
||||||
ID gid.GID `json:"id"`
|
|
||||||
FullName string `json:"fullName"`
|
|
||||||
Identity *Identity `json:"identity"`
|
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (IdentityProfile) IsNode() {}
|
|
||||||
func (this IdentityProfile) GetID() gid.GID { return this.ID }
|
|
||||||
|
|
||||||
type Invitation struct {
|
type Invitation struct {
|
||||||
ID gid.GID `json:"id"`
|
ID gid.GID `json:"id"`
|
||||||
Email mail.Addr `json:"email"`
|
Email mail.Addr `json:"email"`
|
||||||
@@ -208,7 +197,7 @@ type Membership struct {
|
|||||||
ID gid.GID `json:"id"`
|
ID gid.GID `json:"id"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
Identity *Identity `json:"identity,omitempty"`
|
Identity *Identity `json:"identity,omitempty"`
|
||||||
Profile *IdentityProfile `json:"profile,omitempty"`
|
Profile *MembershipProfile `json:"profile,omitempty"`
|
||||||
Organization *Organization `json:"organization,omitempty"`
|
Organization *Organization `json:"organization,omitempty"`
|
||||||
Role coredata.MembershipRole `json:"role"`
|
Role coredata.MembershipRole `json:"role"`
|
||||||
Permissions []*Permission `json:"permissions"`
|
Permissions []*Permission `json:"permissions"`
|
||||||
@@ -223,6 +212,16 @@ type MembershipEdge struct {
|
|||||||
Cursor page.CursorKey `json:"cursor"`
|
Cursor page.CursorKey `json:"cursor"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MembershipProfile struct {
|
||||||
|
ID gid.GID `json:"id"`
|
||||||
|
FullName string `json:"fullName"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (MembershipProfile) IsNode() {}
|
||||||
|
func (this MembershipProfile) GetID() gid.GID { return this.ID }
|
||||||
|
|
||||||
type Mutation struct {
|
type Mutation struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,15 +451,6 @@ type SignUpPayload struct {
|
|||||||
Identity *Identity `json:"identity,omitempty"`
|
Identity *Identity `json:"identity,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateIdentityProfileInput struct {
|
|
||||||
MembershipID gid.GID `json:"membershipId"`
|
|
||||||
FullName *string `json:"fullName,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UpdateIdentityProfilePayload struct {
|
|
||||||
Profile *IdentityProfile `json:"profile,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UpdateOrganizationInput struct {
|
type UpdateOrganizationInput struct {
|
||||||
OrganizationID gid.GID `json:"organizationId"`
|
OrganizationID gid.GID `json:"organizationId"`
|
||||||
Name *string `json:"name,omitempty"`
|
Name *string `json:"name,omitempty"`
|
||||||
|
|||||||
@@ -24,17 +24,6 @@ import (
|
|||||||
"go.probo.inc/probo/pkg/server/gqlutils/types/cursor"
|
"go.probo.inc/probo/pkg/server/gqlutils/types/cursor"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultProfile is the resolver for the defaultProfile field.
|
|
||||||
func (r *identityResolver) DefaultProfile(ctx context.Context, obj *types.Identity) (*types.IdentityProfile, error) {
|
|
||||||
profile, err := r.iam.AccountService.GetDefaultProfile(ctx, obj.ID)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.ErrorCtx(ctx, "cannot get default profile", log.Error(err))
|
|
||||||
return nil, gqlutils.InternalServerError(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.NewIdentityProfile(profile), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Memberships is the resolver for the memberships field.
|
// Memberships is the resolver for the memberships field.
|
||||||
func (r *identityResolver) Memberships(ctx context.Context, obj *types.Identity, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MembershipOrderBy) (*types.MembershipConnection, error) {
|
func (r *identityResolver) Memberships(ctx context.Context, obj *types.Identity, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MembershipOrderBy) (*types.MembershipConnection, error) {
|
||||||
pageOrderBy := page.OrderBy[coredata.MembershipOrderField]{
|
pageOrderBy := page.OrderBy[coredata.MembershipOrderField]{
|
||||||
@@ -166,7 +155,7 @@ func (r *membershipResolver) Identity(ctx context.Context, obj *types.Membership
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Profile is the resolver for the profile field.
|
// Profile is the resolver for the profile field.
|
||||||
func (r *membershipResolver) Profile(ctx context.Context, obj *types.Membership) (*types.IdentityProfile, error) {
|
func (r *membershipResolver) Profile(ctx context.Context, obj *types.Membership) (*types.MembershipProfile, error) {
|
||||||
profile, err := r.iam.AccountService.GetProfileForMembership(ctx, obj.ID)
|
profile, err := r.iam.AccountService.GetProfileForMembership(ctx, obj.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var errProfileNotFound *iam.ErrProfileNotFound
|
var errProfileNotFound *iam.ErrProfileNotFound
|
||||||
@@ -178,7 +167,7 @@ func (r *membershipResolver) Profile(ctx context.Context, obj *types.Membership)
|
|||||||
return nil, gqlutils.InternalServerError(ctx)
|
return nil, gqlutils.InternalServerError(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return types.NewIdentityProfile(profile), nil
|
return types.NewMembershipProfile(profile), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Organization is the resolver for the organization field.
|
// Organization is the resolver for the organization field.
|
||||||
@@ -551,33 +540,6 @@ func (r *mutationResolver) AssumeOrganizationSession(ctx context.Context, input
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateIdentityProfile is the resolver for the updateIdentityProfile field.
|
|
||||||
func (r *mutationResolver) UpdateIdentityProfile(ctx context.Context, input types.UpdateIdentityProfileInput) (*types.UpdateIdentityProfilePayload, error) {
|
|
||||||
identity := IdentityFromContext(ctx)
|
|
||||||
|
|
||||||
profile, err := r.iam.AccountService.UpdateIdentityProfile(
|
|
||||||
ctx,
|
|
||||||
identity.ID,
|
|
||||||
&iam.UpdateIdentityProfileRequest{
|
|
||||||
MembershipID: input.MembershipID,
|
|
||||||
FullName: input.FullName,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
var errMembershipNotFound *iam.ErrMembershipNotFound
|
|
||||||
if errors.As(err, &errMembershipNotFound) {
|
|
||||||
return nil, gqlutils.NotFound(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.logger.ErrorCtx(ctx, "cannot update identity profile", log.Error(err))
|
|
||||||
return nil, gqlutils.InternalServerError(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &types.UpdateIdentityProfilePayload{
|
|
||||||
Profile: types.NewIdentityProfile(profile),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RevokeSession is the resolver for the revokeSession field.
|
// RevokeSession is the resolver for the revokeSession field.
|
||||||
func (r *mutationResolver) RevokeSession(ctx context.Context, input types.RevokeSessionInput) (*types.RevokeSessionPayload, error) {
|
func (r *mutationResolver) RevokeSession(ctx context.Context, input types.RevokeSessionInput) (*types.RevokeSessionPayload, error) {
|
||||||
identity := IdentityFromContext(ctx)
|
identity := IdentityFromContext(ctx)
|
||||||
|
|||||||
@@ -2167,18 +2167,11 @@ func (r *mutationResolver) ExportFramework(ctx context.Context, input types.Expo
|
|||||||
prb := r.ProboService(ctx, input.FrameworkID.TenantID())
|
prb := r.ProboService(ctx, input.FrameworkID.TenantID())
|
||||||
identity := connect_v1.IdentityFromContext(ctx)
|
identity := connect_v1.IdentityFromContext(ctx)
|
||||||
|
|
||||||
// Load default profile to get the full name
|
|
||||||
recipientName := ""
|
|
||||||
profile, err := r.iam.AccountService.GetDefaultProfile(ctx, identity.ID)
|
|
||||||
if err == nil {
|
|
||||||
recipientName = profile.FullName
|
|
||||||
}
|
|
||||||
|
|
||||||
exportErr, exportJobID := prb.Frameworks.RequestExport(
|
exportErr, exportJobID := prb.Frameworks.RequestExport(
|
||||||
ctx,
|
ctx,
|
||||||
input.FrameworkID,
|
input.FrameworkID,
|
||||||
identity.EmailAddress,
|
identity.EmailAddress,
|
||||||
recipientName,
|
identity.FullName,
|
||||||
)
|
)
|
||||||
if exportErr != nil {
|
if exportErr != nil {
|
||||||
panic(fmt.Errorf("cannot export framework: %w", exportErr))
|
panic(fmt.Errorf("cannot export framework: %w", exportErr))
|
||||||
@@ -3352,20 +3345,13 @@ func (r *mutationResolver) BulkExportDocuments(ctx context.Context, input types.
|
|||||||
|
|
||||||
identity := connect_v1.IdentityFromContext(ctx)
|
identity := connect_v1.IdentityFromContext(ctx)
|
||||||
|
|
||||||
// Load default profile to get the full name
|
|
||||||
recipientName := ""
|
|
||||||
profile, err := r.iam.AccountService.GetDefaultProfile(ctx, identity.ID)
|
|
||||||
if err == nil {
|
|
||||||
recipientName = profile.FullName
|
|
||||||
}
|
|
||||||
|
|
||||||
options := probo.ExportPDFOptions{
|
options := probo.ExportPDFOptions{
|
||||||
WithWatermark: input.WithWatermark,
|
WithWatermark: input.WithWatermark,
|
||||||
WithSignatures: input.WithSignatures,
|
WithSignatures: input.WithSignatures,
|
||||||
WatermarkEmail: input.WatermarkEmail,
|
WatermarkEmail: input.WatermarkEmail,
|
||||||
}
|
}
|
||||||
|
|
||||||
documentExport, exportErr := prb.Documents.RequestExport(ctx, input.DocumentIds, identity.EmailAddress, recipientName, options)
|
documentExport, exportErr := prb.Documents.RequestExport(ctx, input.DocumentIds, identity.EmailAddress, identity.FullName, options)
|
||||||
if exportErr != nil {
|
if exportErr != nil {
|
||||||
panic(fmt.Errorf("cannot request document export: %w", exportErr))
|
panic(fmt.Errorf("cannot request document export: %w", exportErr))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user