Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/platform/identity/identity.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | internal/platform/identity/identity.go |
| Original SHA-256 | 34275742cddeacd9dfd668954a7e27095a22e5a154c0c53d720c892353f3544c |
| Git blob SHA | 23b0744af928b3f6cbc8a1702f81951f808a3ab3 |
| Reading-copy SHA-256 | 34275742cddeacd9dfd668954a7e27095a22e5a154c0c53d720c892353f3544c |
Every recorded occurrence
These 4 records contain identical original source bytes. Their paths and revisions remain part of the archive.
| Original record | Branch or archive | Revision / identifier | Original location |
|---|---|---|---|
| Janady13/missionmatch / internal/platform/identity/identity.go | feature/opportunity-to-outcome | 720d47fae476b0d38477ae4401697d372b1ea2bc | Open original |
| Janady13/missionmatch / internal/platform/identity/identity.go | per-tenant-routing | 9bde0f9a74b89d01980fa658e5253acda7528d0c | Open original |
| Janady13/missionmatch / internal/platform/identity/identity.go | deploy-and-ops | bdacd9d0b4d5998d25fc74bf4007f4c3ec538c7a | Open original |
| Janady13/missionmatch / internal/platform/identity/identity.go | feature/aioe-upgrade | 2178e7298d0e5d9af83afa12402fd587f0ba6ed8 | Open original |
Recovered source transcript
Download complete source text ↓74 lines · 3.5 KB
// Package identity seeds an organization and its users from the shared CONTROL PLANE into a tenant's own
// database under the database per tenant model. Identity (organizations, users, and the secret bearing
// auth tables) is authoritative in the control plane: that is where login resolves a human by global email
// before any tenant is chosen, and where sessions and MFA live. But each tenant database's BUSINESS tables
// foreign key to its own organizations and users rows (requirements.created_by, candidate_evaluations.
// evaluated_by, slate_members.decided_by, staffing_recommendations.created_by and decided_by, the eeo
// notice disclosed_by), so the acting org and users must also exist as LOCAL rows in the tenant database
// for those references to resolve.
//
// Those rows are written by the MIGRATOR, not the application: organizations and users are provisioned by
// the migrator (the app role has no INSERT on them, and organizations.name and slug are NOT NULL, so they
// cannot be reconstructed from a request), so identity is seeded at onboarding and re synced whenever the
// org's users change. The copy is idempotent (UPSERT), so a re run is safe.
package identity
import (
"context"
"fmt"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgxpool"
)
// SeedTenant copies the organization and all of its users from the control plane database into the tenant
// database, so the tenant's business tables can reference them. control and tenant are MIGRATOR pools
// (the migrator owns organizations and users); tenant points at the tenant's own database. It returns how
// many users were synced. Idempotent.
func SeedTenant(ctx context.Context, control, tenant *pgxpool.Pool, org uuid.UUID) (int, error) {
if control == nil || tenant == nil {
return 0, fmt.Errorf("identity: control and tenant pools are required")
}
var name, slug, status string
if err := control.QueryRow(ctx, "SELECT name, slug, status FROM organizations WHERE id = $1", org).Scan(&name, &slug, &status); err != nil {
return 0, fmt.Errorf("identity: read org %s from control plane: %w", org, err)
}
if _, err := tenant.Exec(ctx,
`INSERT INTO organizations (id, name, slug, status) VALUES ($1, $2, $3, $4)
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, slug = EXCLUDED.slug, status = EXCLUDED.status, updated_at = now()`,
org, name, slug, status); err != nil {
return 0, fmt.Errorf("identity: seed org into tenant database: %w", err)
}
rows, err := control.Query(ctx, "SELECT id, email, full_name, role, status FROM users WHERE organization_id = $1", org)
if err != nil {
return 0, fmt.Errorf("identity: read users from control plane: %w", err)
}
defer rows.Close()
type u struct {
id uuid.UUID
email, fullName, role, stat string
}
var users []u
for rows.Next() {
var x u
if err := rows.Scan(&x.id, &x.email, &x.fullName, &x.role, &x.stat); err != nil {
return 0, err
}
users = append(users, x)
}
if err := rows.Err(); err != nil {
return 0, err
}
for _, x := range users {
if _, err := tenant.Exec(ctx,
`INSERT INTO users (id, organization_id, email, full_name, role, status) VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (organization_id, id) DO UPDATE SET email = EXCLUDED.email, full_name = EXCLUDED.full_name, role = EXCLUDED.role, status = EXCLUDED.status, updated_at = now()`,
x.id, org, x.email, x.fullName, x.role, x.stat); err != nil {
return 0, fmt.Errorf("identity: seed user %s into tenant database: %w", x.id, err)
}
}
return len(users), nil
}