Accept/Expire invitations on SAML & SCIM operations

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-01-08 20:01:41 +01:00
committed by Bryan Frimin
parent cfcbda4519
commit b961069e1d
5 changed files with 168 additions and 102 deletions

View File

@@ -164,6 +164,72 @@ WHERE
return nil
}
func (i *Invitations) AcceptByEmailAndOrganization(
ctx context.Context,
conn pg.Conn,
scope Scoper,
email mail.Addr,
organizationID gid.GID,
filter *InvitationFilter,
) error {
q := `
UPDATE iam_invitations SET accepted_at = NOW()
WHERE
email = @email
AND organization_id = @organization_id
AND %s
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
args := pgx.StrictNamedArgs{
"email": email,
"organization_id": organizationID,
}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
if _, err := conn.Exec(ctx, q, args); err != nil {
return fmt.Errorf("cannot accept invitations: %w", err)
}
return nil
}
func (i *Invitations) ExpireByEmailAndOrganization(
ctx context.Context,
conn pg.Conn,
scope Scoper,
email mail.Addr,
organizationID gid.GID,
filter *InvitationFilter,
) error {
q := `
UPDATE iam_invitations SET expires_at = NOW()
WHERE
email = @email
AND organization_id = @organization_id
AND %s
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
args := pgx.StrictNamedArgs{
"email": email,
"organization_id": organizationID,
}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
if _, err := conn.Exec(ctx, q, args); err != nil {
return fmt.Errorf("cannot expire invitations: %w", err)
}
return nil
}
// AuthorizationAttributes loads the minimal authorization attributes for policy condition evaluation.
// It is intentionally lightweight and does not populate the Invitation struct.
func (i *Invitation) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
@@ -230,7 +296,7 @@ DELETE FROM
iam_invitations
WHERE
%s
AND id = @invitation_id
AND id = @invitation_id
`
query = fmt.Sprintf(query, scope.SQLFragment())
@@ -369,11 +435,11 @@ func (i *Invitations) CountByOrganizationID(
) (int, error) {
q := `
SELECT
COUNT(*)
COUNT(*)
FROM
iam_invitations
iam_invitations
WHERE
organization_id = @organization_id AND %s AND %s
organization_id = @organization_id AND %s AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
@@ -405,12 +471,12 @@ func (i *Invitations) CountByEmail(
) (int, error) {
q := `
SELECT
COUNT(*)
COUNT(*)
FROM
iam_invitations
iam_invitations
WHERE
email = @email
AND %s
email = @email
AND %s
`
q = fmt.Sprintf(q, filter.SQLFragment())

View File

@@ -339,78 +339,6 @@ LEFT JOIN
return nil
}
func (m *Membership) LoadByEmailAndOrganization(
ctx context.Context,
conn pg.Conn,
scope Scoper,
email mail.Addr,
organizationID gid.GID,
) error {
q := `
WITH mbr AS (
SELECT
am.id,
am.identity_id,
am.organization_id,
am.role,
am.source,
am.state,
am.created_at,
am.updated_at
FROM
iam_memberships am
JOIN
identities i ON am.identity_id = i.id
WHERE
i.email_address = @email
AND am.organization_id = @organization_id
AND %s
)
SELECT
mbr.id,
mbr.identity_id,
mbr.organization_id,
mbr.role,
mbr.source,
mbr.state,
COALESCE(mp.full_name, i.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_membership_profiles mp ON mp.membership_id = mbr.id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"email": email,
"organization_id": organizationID,
}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query membership by email: %w", err)
}
membership, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Membership])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrResourceNotFound
}
return fmt.Errorf("cannot collect membership: %w", err)
}
*m = membership
return nil
}
func (m *Membership) Update(ctx context.Context, conn pg.Conn, scope Scoper) error {
query := `
UPDATE