Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / cmd/sync-tenant-identity/main.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | cmd/sync-tenant-identity/main.go |
| Original SHA-256 | d74991f4cac2fe0d78fccd54325b8206378f81ba84342144bc82a0564d014f09 |
| Git blob SHA | 895fd31a32278628b733dcbce78439601a2ccbf1 |
| Reading-copy SHA-256 | d74991f4cac2fe0d78fccd54325b8206378f81ba84342144bc82a0564d014f09 |
Recovered source transcript
Download complete source text ↓108 lines · 4.2 KB
// Command sync-tenant-identity copies an organization and its users from the control plane into that
// tenant's own database, so the tenant's business tables can reference its org and users. It is run at
// onboarding (after provision-tenant creates the database and the directory row is registered) and again
// whenever the org's users change (a new invite accepted). It is a migrator role operator tool and is
// idempotent.
//
// Usage:
//
// sync-tenant-identity -control-dsn postgres://missionmatch_migrator:...@host/controlplane -org <uuid>
package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/missionworkforce/missionmatch/internal/platform/database"
"github.com/missionworkforce/missionmatch/internal/platform/directory"
"github.com/missionworkforce/missionmatch/internal/platform/identity"
)
func main() {
controlDSN := flag.String("control-dsn", "", "migrator DSN to the control plane database, defaults to MM_MIGRATOR_DATABASE_URL")
orgStr := flag.String("org", "", "organization id to sync")
tenantDSN := flag.String("tenant-dsn", "", "migrator DSN for the TENANT database; required when the tenant does not share the control plane credential, defaults to MM_TENANT_MIGRATOR_DSN")
flag.Parse()
if *controlDSN == "" {
*controlDSN = os.Getenv("MM_MIGRATOR_DATABASE_URL")
}
if *tenantDSN == "" {
*tenantDSN = os.Getenv("MM_TENANT_MIGRATOR_DSN")
}
if *controlDSN == "" || *orgStr == "" {
log.Fatal("sync-tenant-identity: -control-dsn (or MM_MIGRATOR_DATABASE_URL) and -org are required")
}
org, err := uuid.Parse(*orgStr)
if err != nil {
log.Fatalf("sync-tenant-identity: -org is not a uuid: %v", err)
}
ctx := context.Background()
control, err := pgxpool.New(ctx, *controlDSN)
if err != nil {
log.Fatalf("sync-tenant-identity: control plane pool: %v", err)
}
defer control.Close()
if err := database.AssertMigratorRole(ctx, control); err != nil {
log.Fatalf("sync-tenant-identity: %v", err)
}
tc, err := directory.New(control).Lookup(ctx, org)
if err != nil {
log.Fatalf("sync-tenant-identity: %v", err)
}
// Host and port come from the DIRECTORY, not from the control plane DSN.
//
// This used to swap only the database NAME onto the control plane connection, which silently connected
// straight back to the control plane and "seeded" rows that were already there. It reported success and
// changed nothing. That was survivable only while every tenant shared one machine; the whole point of
// tenant_directory carrying db_host and db_port is that they no longer do, so ignoring them means the
// tool always writes to the wrong database the moment a tenant lives anywhere else.
// The credential is the other half of the same wrong assumption. Reusing the control plane password
// works only while every database shares one, which stopped being true when tenants moved onto their own
// machines: each tenant database has its own credential, resolved at runtime from the secrets store by
// the pool manager. This operator tool has no secrets resolver, so the credential is supplied
// explicitly and the derived form is a convenience for the co located case only.
dsn := tenantDSNFor(*controlDSN, tc)
if *tenantDSN != "" {
dsn = *tenantDSN
}
tenant, err := pgxpool.New(ctx, dsn)
if err != nil {
log.Fatalf("sync-tenant-identity: tenant pool: %v", err)
}
defer tenant.Close()
n, err := identity.SeedTenant(ctx, control, tenant, org)
if err != nil {
log.Fatalf("sync-tenant-identity: %v", err)
}
log.Printf("sync-tenant-identity: seeded org %s and %d user(s) into tenant database %q", org, n, tc.DBName)
}
// tenantDSNFor builds the tenant connection from the DIRECTORY row, reusing only the credential from the
// control plane DSN. Host, port and database name are the directory's, because the directory is the
// authority on where a tenant actually lives.
func tenantDSNFor(dsn string, tc directory.TenantConn) string {
cfg, err := pgx.ParseConfig(dsn)
if err != nil {
return dsn
}
out := fmt.Sprintf("host=%s port=%d user=%s dbname=%s", tc.Host, tc.Port, cfg.User, tc.DBName)
if cfg.Password != "" {
out += " password=" + cfg.Password
}
if cfg.TLSConfig == nil {
out += " sslmode=disable"
}
return out
}