package plugins import ( "context" "errors" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) // UserIdentity is the public-facing identity surfaced to plugins via the // X-Silo-User-Name / X-Silo-Profile-Name / X-Silo-Profile-Primary // headers. Plugins use it to render "user#profile" strings in app-pairing // UIs without reaching back into the browser's localStorage. type UserIdentity struct { Username string ProfileName string ProfileIsPrimary bool } // UserIdentityLookup resolves a user id + active profile id into a // presentable identity. Either field may be returned empty when the row is // missing — callers omit the corresponding header. type UserIdentityLookup interface { LookupIdentity(ctx context.Context, userID int, profileID string) (UserIdentity, error) } // PgUserIdentityLookup reads username from `users` and profile name + // is_primary from `user_profiles`. Hits the same tables silo's web // client uses for `/auth/me` and `/profiles`, so the plugin proxy returns // the same names operators see in the main UI. type PgUserIdentityLookup struct { pool *pgxpool.Pool } func NewPgUserIdentityLookup(pool *pgxpool.Pool) *PgUserIdentityLookup { return &PgUserIdentityLookup{pool: pool} } func (l *PgUserIdentityLookup) LookupIdentity(ctx context.Context, userID int, profileID string) (UserIdentity, error) { var out UserIdentity if l == nil || l.pool == nil || userID <= 0 { return out, nil } err := l.pool.QueryRow(ctx, "SELECT username FROM users WHERE id = $1", userID, ).Scan(&out.Username) if err != nil && !errors.Is(err, pgx.ErrNoRows) { return out, err } if profileID == "" { return out, nil } // Verify the profile belongs to this user before returning its name — // otherwise a client could stamp another user's profile_id and have the // plugin display someone else's profile name. err = l.pool.QueryRow(ctx, "SELECT name, is_primary FROM user_profiles WHERE id = $1 AND user_id = $2", profileID, userID, ).Scan(&out.ProfileName, &out.ProfileIsPrimary) if err != nil && !errors.Is(err, pgx.ErrNoRows) { return out, err } return out, nil }