fix(dashboard): use synthetic internal email for SSO owner row
client_users.email has a UNIQUE constraint. SSO users who already have a CaveauAI account share the same email address, causing provision to fail with "already belongs to another workspace". Fix: SSO-provisioned client_users rows use dbn-sso-{client_id}@dbn.tools.internal as the owner email so they never collide with real account rows. The real email stays on clients.contact_email for display purposes. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -145,14 +145,16 @@ final class CorpusProvision
|
||||
|
||||
private static function createOwnerUser(PDO $db, int $clientId, string $email, string $displayName): int
|
||||
{
|
||||
$stmt = $db->prepare('SELECT id, client_id FROM client_users WHERE email = ? LIMIT 1');
|
||||
$stmt->execute([$email]);
|
||||
$existing = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($existing && (int)$existing['client_id'] === $clientId) {
|
||||
return (int)$existing['id'];
|
||||
}
|
||||
if ($existing) {
|
||||
throw new RuntimeException("Email {$email} already belongs to another workspace.");
|
||||
// client_users.email has a UNIQUE constraint, so an SSO-provisioned workspace
|
||||
// uses a synthetic internal email to avoid conflicting with any real CaveauAI
|
||||
// account that may share the same address. The real email lives on clients.contact_email.
|
||||
$ssoEmail = self::ssoInternalEmail($db, $clientId);
|
||||
|
||||
$stmt = $db->prepare('SELECT id FROM client_users WHERE email = ? AND client_id = ? LIMIT 1');
|
||||
$stmt->execute([$ssoEmail, $clientId]);
|
||||
$existingId = (int)($stmt->fetchColumn() ?: 0);
|
||||
if ($existingId > 0) {
|
||||
return $existingId;
|
||||
}
|
||||
|
||||
$usernameBase = preg_replace('/[^a-z0-9._-]+/', '', strtolower(strstr($email, '@', true) ?: 'owner'));
|
||||
@@ -167,13 +169,20 @@ final class CorpusProvision
|
||||
$stmt->execute([
|
||||
$clientId,
|
||||
$username,
|
||||
$email,
|
||||
$ssoEmail,
|
||||
$displayName !== '' ? $displayName : null,
|
||||
password_hash(bin2hex(random_bytes(16)), PASSWORD_DEFAULT),
|
||||
]);
|
||||
return (int)$db->lastInsertId();
|
||||
}
|
||||
|
||||
private static function ssoInternalEmail(PDO $db, int $clientId): string
|
||||
{
|
||||
// Derive a stable internal-only email from the client_id so it is unique
|
||||
// even when the same real address already exists for another workspace.
|
||||
return 'dbn-sso-' . $clientId . '@dbn.tools.internal';
|
||||
}
|
||||
|
||||
private static function ensureDefaultCorpus(PDO $db, int $clientId, string $email): int
|
||||
{
|
||||
$stmt = $db->prepare(
|
||||
|
||||
Reference in New Issue
Block a user