Files
silo-server/internal/plugins/user_identity_lookup.go
T

68 lines
2.1 KiB
Go

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
}