Audit provider sign-in launches by employee

This commit is contained in:
ZacharyZhang-NY committed 2026-05-23 22:20:16 -04:00
1 parent 335d57b77f
commit de90d6e944
6 files changed
+61 -10

No files matched your search

@@ -121,7 +121,7 @@ export function registerIpcHandlers(deps: IpcDeps): void {
});
ipcMain.handle(IPC_CHANNELS.launchProviderSignIn, (_event, input) => {
try {
providers.launchSignInWindow(input.orgId, input.providerId);
providers.launchSignInWindow(input);
return ok(null);
} catch (error) {
return fail("provider_sign_in_failed", error instanceof Error ? error.message : String(error));
@@ -1,5 +1,5 @@
import type { DatabaseSync } from "node:sqlite";
import type { CancelProviderResponseInput, CreateProviderResponseInput, RevokeProviderCredentialInput, SaveProviderCredentialInput, StreamProviderResponseInput, UpdateProviderPolicyInput } from "../../shared/commands";
import type { CancelProviderResponseInput, CreateProviderResponseInput, LaunchProviderSignInInput, RevokeProviderCredentialInput, SaveProviderCredentialInput, StreamProviderResponseInput, UpdateProviderPolicyInput } from "../../shared/commands";
import { FEATURE_FLAG_KEY, PROVIDER_AUTH_TYPE, PROVIDER_DATA_POLICY, PROVIDER_EXECUTION_MODE, PROVIDER_REQUEST_STATE, type ProviderAuthType, type ProviderExecutionMode } from "../../shared/domain";
import type { ProviderStreamRendererEvent } from "../../shared/provider-request-domain";
import { PROVIDER_CATALOG } from "../../shared/provider-catalog";
@@ -14,7 +14,7 @@ import { ProviderRateLimitRegistry } from "./provider-rate-limit-registry";
import { ProviderResponseObserver } from "./provider-response-observer";
import { hashPrompt, readProviderResponseId, requireStreamText } from "./provider-response-utils";
import { ProviderRequestStore } from "./provider-request-store";
import { launchProviderSignInWindow } from "./provider-sign-in-window";
import { launchProviderSignInWindow, type LaunchProviderSignInWindowInput } from "./provider-sign-in-window";
import type { ProviderObservedRateLimitState } from "../../shared/provider-runtime-domain";
const AUTH_STATE_CONFIGURED = "configured";
@@ -41,6 +41,7 @@ export class ProviderService {
private readonly db: DatabaseSync,
private readonly adapters: ProviderAdapterRegistry = providerAdapters,
vault: ProviderSecretVault = new SecretVault(),
private readonly launchSignIn: (input: LaunchProviderSignInWindowInput) => void = launchProviderSignInWindow,
) {
this.audit = new AuditService(db);
this.authBroker = new AuthBrokerService(db, vault);
@@ -141,23 +142,24 @@ export class ProviderService {
}
}
launchSignInWindow(orgId: string, providerId: string): void {
launchSignInWindow(input: LaunchProviderSignInInput): void {
this.assertEmployee(input.orgId, input.employeeId);
const provider = this.db.prepare(`
select id, provider_key, label, auth_type
from providers
where id = ? and org_id = ?
`).get(providerId, orgId) as { id: string; provider_key: string; label: string; auth_type: string } | undefined;
`).get(input.providerId, input.orgId) as { id: string; provider_key: string; label: string; auth_type: string } | undefined;
if (!provider) throw new Error("Provider was not found.");
const catalogEntry = this.requireCatalogEntry(provider.provider_key);
if (!catalogEntry.signInUrl) throw new Error(`${provider.label} does not support browser sign-in.`);
launchProviderSignInWindow({ providerId: provider.id, label: provider.label, signInUrl: catalogEntry.signInUrl });
this.launchSignIn({ providerId: provider.id, label: provider.label, signInUrl: catalogEntry.signInUrl });
this.audit.record({
orgId,
orgId: input.orgId,
entityType: "provider",
entityId: provider.id,
actorType: "employee",
actorId: null,
actorId: input.employeeId,
action: "sign_in_window_opened",
payload: { providerKey: provider.provider_key, authType: provider.auth_type },
});
@@ -465,6 +467,12 @@ export class ProviderService {
return entry;
}
private assertEmployee(orgId: string, employeeId: string): void {
const employee = this.db.prepare("select id from employees where id = ? and org_id = ?")
.get(employeeId, orgId) as { id: string } | undefined;
if (!employee) throw new Error("Provider sign-in employee was not found.");
}
private recordProviderHealth(providerId: string, orgId: string, healthState: string, error: string | null): void {
this.db.prepare(`
update providers
@@ -131,6 +131,7 @@ export function SettingsPage({ snapshot, actions }: PageProps) {
{tab === "providers" && (
<ProvidersPanel
orgId={orgId}
employeeId={employee.id}
providers={providers}
catalog={snapshot.providerCatalog}
agents={agents}
@@ -15,6 +15,7 @@ import type { SnapshotActions } from "../../app/useSnapshot";
interface ProvidersPanelProps {
orgId: string;
employeeId: string;
providers: ProviderRecord[];
catalog: ProviderCatalogEntry[];
agents: AgentRecord[];
@@ -32,7 +33,7 @@ interface CloudFields {
const CLOUD_DEFAULTS: CloudFields = { baseUrl: "", region: "", projectId: "", deployment: "" };
export function ProvidersPanel({ orgId, providers, catalog, agents, providerRateLimits, providerRequests, actions }: ProvidersPanelProps) {
export function ProvidersPanel({ orgId, employeeId, providers, catalog, agents, providerRateLimits, providerRequests, actions }: ProvidersPanelProps) {
const [active, setActive] = useState<ProviderRecord | null>(null);
const [signInLaunched, setSignInLaunched] = useState(false);
const connected = providers.filter((provider) => provider.enabled);
@@ -60,7 +61,7 @@ export function ProvidersPanel({ orgId, providers, catalog, agents, providerRate
const launchSignIn = () => {
if (!active) return;
setSignInLaunched(true);
void actions.run(() => window.agentCompany.launchProviderSignIn({ orgId, providerId: active.id }));
void actions.run(() => window.agentCompany.launchProviderSignIn({ orgId, providerId: active.id, employeeId }));
};
return (
+1
View File
@@ -170,6 +170,7 @@ export interface RevokeProviderCredentialInput {
export interface LaunchProviderSignInInput {
orgId: string;
providerId: string;
employeeId: string;
}
export interface CheckProviderHealthInput {