Promote AgentCompany-New to the repository root and retire the old app
The legacy application - its src, tests, landing site, promo assets, docs, release tooling, CI workflows, and root configs - leaves the tree; it stays reachable in history. Everything under AgentCompany-New moves up unchanged: same package name, so the production userData/database path is untouched, and e2e/build scripts already resolved paths relative to the project. The developer cred.txt stays gitignored at its new location. Full verify:ci passes from the new root.
This commit is contained in:
1 parent
2350f744e3
commit
07d0b4587d
1904 files changed
+1133
-282075
No files matched your search
@@ -0,0 +1,180 @@
|
||||
import type { DatabaseSync } from "node:sqlite";
|
||||
import { createHash } from "node:crypto";
|
||||
import type { ProviderAuthType } from "../../shared/domain";
|
||||
import { createId } from "../../shared/ids";
|
||||
import { AuditService } from "./audit-service";
|
||||
import { SecretVault } from "./secret-vault";
|
||||
|
||||
export interface ProviderSecretVault {
|
||||
encryptSecret(secret: string): Buffer;
|
||||
decryptSecret(ciphertext: Buffer): string;
|
||||
}
|
||||
|
||||
export interface CreateProviderAuthProfileInput {
|
||||
orgId: string;
|
||||
providerId: string;
|
||||
providerKey: string;
|
||||
authType: ProviderAuthType;
|
||||
secret: string;
|
||||
profileKind: string;
|
||||
sourceType: string;
|
||||
sourceRef: string | null;
|
||||
capabilityTags: string[];
|
||||
}
|
||||
|
||||
export interface ProviderAuthProfileRef {
|
||||
authProfileId: string;
|
||||
credentialId: string;
|
||||
displayHint: string;
|
||||
}
|
||||
|
||||
export interface RevokeProviderAuthProfileInput {
|
||||
orgId: string;
|
||||
providerId: string;
|
||||
providerKey: string;
|
||||
}
|
||||
|
||||
export class AuthBrokerService {
|
||||
private readonly audit: AuditService;
|
||||
|
||||
constructor(
|
||||
private readonly db: DatabaseSync,
|
||||
private readonly vault: ProviderSecretVault = new SecretVault(),
|
||||
) {
|
||||
this.audit = new AuditService(db);
|
||||
}
|
||||
|
||||
createProviderProfile(input: CreateProviderAuthProfileInput): ProviderAuthProfileRef {
|
||||
const secret = input.secret.trim();
|
||||
if (!secret) throw new Error("Credential is required.");
|
||||
const credentialId = createId("cred");
|
||||
const authProfileId = createId("authp");
|
||||
const displayHint = credentialDisplayHint(secret);
|
||||
const now = new Date().toISOString();
|
||||
const capabilityJson = JSON.stringify({
|
||||
providerKey: input.providerKey,
|
||||
authType: input.authType,
|
||||
capabilityTags: input.capabilityTags,
|
||||
});
|
||||
|
||||
this.db.prepare(`
|
||||
insert into credentials(id, org_id, provider_key, kind, ciphertext, display_hint, created_at)
|
||||
values (?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(credentialId, input.orgId, input.providerKey, input.authType, this.vault.encryptSecret(secret), displayHint, now);
|
||||
this.db.prepare(`
|
||||
update provider_auth_profiles
|
||||
set active = 0
|
||||
where org_id = ? and provider_id = ? and active = 1
|
||||
`).run(input.orgId, input.providerId);
|
||||
this.db.prepare(`
|
||||
insert into provider_auth_profiles(
|
||||
id, org_id, provider_id, credential_id, provider_key, auth_type,
|
||||
profile_kind, display_hint, source_type, source_ref, capability_json,
|
||||
active, created_at
|
||||
) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)
|
||||
`).run(
|
||||
authProfileId,
|
||||
input.orgId,
|
||||
input.providerId,
|
||||
credentialId,
|
||||
input.providerKey,
|
||||
input.authType,
|
||||
input.profileKind,
|
||||
displayHint,
|
||||
input.sourceType,
|
||||
input.sourceRef,
|
||||
capabilityJson,
|
||||
now,
|
||||
);
|
||||
this.audit.record({
|
||||
orgId: input.orgId,
|
||||
entityType: "provider_auth_profile",
|
||||
entityId: authProfileId,
|
||||
actorType: "system",
|
||||
actorId: null,
|
||||
action: "created",
|
||||
payload: {
|
||||
providerId: input.providerId,
|
||||
providerKey: input.providerKey,
|
||||
authType: input.authType,
|
||||
profileKind: input.profileKind,
|
||||
},
|
||||
});
|
||||
return { authProfileId, credentialId, displayHint };
|
||||
}
|
||||
|
||||
loadProviderSecret(input: {
|
||||
orgId: string;
|
||||
providerId: string;
|
||||
providerKey: string;
|
||||
credentialId: string;
|
||||
}): string {
|
||||
const row = this.db.prepare(`
|
||||
select credentials.ciphertext
|
||||
from provider_auth_profiles
|
||||
join credentials on credentials.id = provider_auth_profiles.credential_id
|
||||
where provider_auth_profiles.org_id = ?
|
||||
and provider_auth_profiles.provider_id = ?
|
||||
and provider_auth_profiles.provider_key = ?
|
||||
and provider_auth_profiles.credential_id = ?
|
||||
and provider_auth_profiles.active = 1
|
||||
and credentials.org_id = provider_auth_profiles.org_id
|
||||
and credentials.provider_key = provider_auth_profiles.provider_key
|
||||
`).get(input.orgId, input.providerId, input.providerKey, input.credentialId) as { ciphertext: Uint8Array } | undefined;
|
||||
if (!row) throw new Error("Provider credential was not found.");
|
||||
return this.vault.decryptSecret(Buffer.from(row.ciphertext));
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-encrypts a refreshed OAuth credential into its existing credential row,
|
||||
* keeping the same credential id (and active auth profile) so request-time
|
||||
* token refresh persists without re-binding the provider.
|
||||
*/
|
||||
updateProviderSecret(input: { orgId: string; providerKey: string; credentialId: string; secret: string }): void {
|
||||
const secret = input.secret.trim();
|
||||
if (!secret) throw new Error("Credential is required.");
|
||||
const displayHint = credentialDisplayHint(secret);
|
||||
const result = this.db.prepare(`
|
||||
update credentials
|
||||
set ciphertext = ?, display_hint = ?
|
||||
where id = ? and org_id = ? and provider_key = ?
|
||||
`).run(this.vault.encryptSecret(secret), displayHint, input.credentialId, input.orgId, input.providerKey);
|
||||
if (result.changes === 0) throw new Error("Provider credential was not found.");
|
||||
this.db.prepare("update provider_auth_profiles set display_hint = ? where credential_id = ? and org_id = ?")
|
||||
.run(displayHint, input.credentialId, input.orgId);
|
||||
}
|
||||
|
||||
revokeProviderProfile(input: RevokeProviderAuthProfileInput): void {
|
||||
const row = this.db.prepare(`
|
||||
select id, credential_id
|
||||
from provider_auth_profiles
|
||||
where org_id = ?
|
||||
and provider_id = ?
|
||||
and provider_key = ?
|
||||
and active = 1
|
||||
`).get(input.orgId, input.providerId, input.providerKey) as { id: string; credential_id: string } | undefined;
|
||||
if (!row) throw new Error("Provider credential was not found.");
|
||||
this.db.prepare("update provider_auth_profiles set active = 0 where id = ? and org_id = ?")
|
||||
.run(row.id, input.orgId);
|
||||
this.audit.record({
|
||||
orgId: input.orgId,
|
||||
entityType: "provider_auth_profile",
|
||||
entityId: row.id,
|
||||
actorType: "system",
|
||||
actorId: null,
|
||||
action: "revoked",
|
||||
payload: {
|
||||
providerId: input.providerId,
|
||||
providerKey: input.providerKey,
|
||||
credentialId: row.credential_id,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function credentialDisplayHint(secret: string): string {
|
||||
const clean = secret.trim();
|
||||
if (clean.startsWith("{")) return `json:${createHash("sha256").update(clean).digest("hex").slice(0, 10)}`;
|
||||
if (clean.length <= 10) return `${clean.slice(0, 2)}...`;
|
||||
return `${clean.slice(0, 6)}...${clean.slice(-4)}`;
|
||||
}
|
||||
Reference in new issue
Block a user