Files
llmgw/internal/store/directory.go
T
Ilker ManapandClaude Opus 5.5 f27d722111 People sign in with their Active Directory account
auth.ldap checks a username and password against the domain controllers
over LDAPS or StartTLS: found with a service account, checked by binding
as the person. Groups, nested ones included, give staff roles and places
in the tree. Accounts are tied to the objectGUID; the leader reads the
directory every 15 minutes, suspends whom it disabled or removed and
reinstates them after, and refuses a sync that would suspend most.

Entra ID sends no groups for someone in more than 200, only where to
fetch them; that was read as none and took an administrator's role away.
It is now left as it was, and logged. An account made by SCIM or OIDC
can be reinstated again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 10:59:50 +02:00

93 lines
3.0 KiB
Go

package store
import (
"context"
"database/sql"
"errors"
"time"
)
// DirectoryLink is an account kept by an outside directory: which one, the
// account's id there, and whether the directory's sync is what suspended it.
type DirectoryLink struct {
UserID string
Directory string // "ldap"
ExternalID string // an AD objectGUID
// SuspendedBySync is an account suspended because the directory
// disabled or lost it, and so reinstated when the directory has it
// enabled again. One an administrator suspended is not.
SuspendedBySync bool
SyncedAt time.Time
}
// DirectoryUser is the account a directory knows by that id.
func (s *DB) DirectoryUser(ctx context.Context, directory, externalID string) (DirectoryLink, error) {
var l DirectoryLink
var suspended int
var synced int64
err := s.db.QueryRowContext(ctx,
`SELECT user_id, directory, external_id, suspended_by_sync, synced_at FROM user_directory
WHERE directory = ? AND external_id = ?`, directory, externalID).
Scan(&l.UserID, &l.Directory, &l.ExternalID, &suspended, &synced)
if errors.Is(err, sql.ErrNoRows) {
return DirectoryLink{}, ErrNotFound
}
if err != nil {
return DirectoryLink{}, err
}
l.SuspendedBySync, l.SyncedAt = suspended != 0, directoryTime(synced)
return l, nil
}
// LinkDirectory records that a directory keeps an account, by its id there.
func (s *DB) LinkDirectory(ctx context.Context, userID, directory, externalID string, at time.Time) error {
_, err := s.db.ExecContext(ctx,
`INSERT INTO user_directory (user_id, directory, external_id, synced_at) VALUES (?, ?, ?, ?)
ON CONFLICT (user_id) DO UPDATE SET directory = excluded.directory, external_id = excluded.external_id,
synced_at = excluded.synced_at`,
userID, directory, externalID, at.UnixMilli())
return err
}
// DirectoryLinks are every account a directory keeps.
func (s *DB) DirectoryLinks(ctx context.Context, directory string) ([]DirectoryLink, error) {
rows, err := s.db.QueryContext(ctx,
`SELECT user_id, directory, external_id, suspended_by_sync, synced_at FROM user_directory WHERE directory = ?`,
directory)
if err != nil {
return nil, err
}
defer func() { _ = rows.Close() }()
var out []DirectoryLink
for rows.Next() {
var l DirectoryLink
var suspended int
var synced int64
if err := rows.Scan(&l.UserID, &l.Directory, &l.ExternalID, &suspended, &synced); err != nil {
return nil, err
}
l.SuspendedBySync, l.SyncedAt = suspended != 0, directoryTime(synced)
out = append(out, l)
}
return out, rows.Err()
}
// SetSyncSuspended says whether the directory's sync is what has an account
// suspended, and when it last looked.
func (s *DB) SetSyncSuspended(ctx context.Context, userID string, suspended bool, at time.Time) error {
v := 0
if suspended {
v = 1
}
_, err := s.db.ExecContext(ctx,
`UPDATE user_directory SET suspended_by_sync = ?, synced_at = ? WHERE user_id = ?`, v, at.UnixMilli(), userID)
return err
}
func directoryTime(ms int64) time.Time {
if ms == 0 {
return time.Time{}
}
return time.UnixMilli(ms)
}