chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
import type {
|
||||
DirectorySyncEffect,
|
||||
DirectorySyncStatus,
|
||||
OrgSsoStatus,
|
||||
SsoBeginError,
|
||||
SsoCompleteError,
|
||||
SsoController,
|
||||
SsoDecisionError,
|
||||
SsoFlow,
|
||||
SsoMutationError,
|
||||
SsoPortalError,
|
||||
SsoProfile,
|
||||
SsoResolutionDecision,
|
||||
SsoRouteDecision,
|
||||
SsoValidateError,
|
||||
SsoWebhookError,
|
||||
SsoWebhookEvent,
|
||||
} from "@trigger.dev/plugins";
|
||||
import { errAsync, okAsync, type ResultAsync } from "neverthrow";
|
||||
|
||||
// The default fallback used when no cloud SSO plugin is installed.
|
||||
// `decideRouteForEmail` returns no_sso so OSS deployments behave
|
||||
// identically to a deployment with no SSO feature at all. Mutation
|
||||
// methods return feature_disabled so callers can surface a clear
|
||||
// "not available" message in UI gated by `isUsingPlugin()`.
|
||||
//
|
||||
// The fallback never touches the database. It still accepts the loader's
|
||||
// Prisma input for signature parity with the real cloud plugin factory
|
||||
// (so the loader can swap implementations without changing its call),
|
||||
// but ignores it entirely.
|
||||
export class SsoFallback {
|
||||
constructor(_prisma?: unknown) {}
|
||||
|
||||
create(): SsoController {
|
||||
return new SsoFallbackController();
|
||||
}
|
||||
}
|
||||
|
||||
class SsoFallbackController implements SsoController {
|
||||
async isUsingPlugin(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
getStatus(_organizationId: string): ResultAsync<OrgSsoStatus, SsoDecisionError> {
|
||||
return okAsync({
|
||||
hasIdpOrg: false,
|
||||
enforced: false,
|
||||
jitProvisioningEnabled: false,
|
||||
jitDefaultRoleId: null,
|
||||
idpOrgId: null,
|
||||
primaryConnectionId: null,
|
||||
domains: [],
|
||||
connections: [],
|
||||
});
|
||||
}
|
||||
|
||||
generatePortalLink(_params: {
|
||||
organizationId: string;
|
||||
userId: string;
|
||||
intent: "sso" | "domain_verification" | "dsync";
|
||||
returnUrl: string;
|
||||
}): ResultAsync<{ url: string }, SsoPortalError> {
|
||||
return errAsync("idp_org_unavailable" as const);
|
||||
}
|
||||
|
||||
setEnforced(_params: {
|
||||
organizationId: string;
|
||||
enforced: boolean;
|
||||
}): ResultAsync<void, SsoMutationError> {
|
||||
return errAsync("feature_disabled" as const);
|
||||
}
|
||||
|
||||
setJitProvisioningEnabled(_params: {
|
||||
organizationId: string;
|
||||
enabled: boolean;
|
||||
}): ResultAsync<void, SsoMutationError> {
|
||||
return errAsync("feature_disabled" as const);
|
||||
}
|
||||
|
||||
setJitDefaultRole(_params: {
|
||||
organizationId: string;
|
||||
roleId: string | null;
|
||||
}): ResultAsync<void, SsoMutationError> {
|
||||
return errAsync("feature_disabled" as const);
|
||||
}
|
||||
|
||||
updateConfig(_params: {
|
||||
organizationId: string;
|
||||
enforced: boolean;
|
||||
jitProvisioningEnabled: boolean;
|
||||
jitDefaultRoleId: string | null;
|
||||
}): ResultAsync<void, SsoMutationError> {
|
||||
return errAsync("feature_disabled" as const);
|
||||
}
|
||||
|
||||
getDirectorySyncStatus(
|
||||
_organizationId: string
|
||||
): ResultAsync<DirectorySyncStatus, SsoDecisionError> {
|
||||
return okAsync({
|
||||
hasDirectory: false,
|
||||
hasActiveDirectory: false,
|
||||
allowExternalDomainSync: false,
|
||||
allowManualMembership: true,
|
||||
directoryDefaultRoleId: null,
|
||||
userCount: 0,
|
||||
directories: [],
|
||||
groups: [],
|
||||
});
|
||||
}
|
||||
|
||||
setDirectoryGroupRole(_params: {
|
||||
organizationId: string;
|
||||
groupId: string;
|
||||
roleId: string | null;
|
||||
}): ResultAsync<{ effects: DirectorySyncEffect[] }, SsoMutationError> {
|
||||
return errAsync("feature_disabled" as const);
|
||||
}
|
||||
|
||||
setDirectoryDefaultRole(_params: {
|
||||
organizationId: string;
|
||||
roleId: string | null;
|
||||
}): ResultAsync<void, SsoMutationError> {
|
||||
return errAsync("feature_disabled" as const);
|
||||
}
|
||||
|
||||
setAllowExternalDomainSync(_params: {
|
||||
organizationId: string;
|
||||
allowed: boolean;
|
||||
}): ResultAsync<void, SsoMutationError> {
|
||||
return errAsync("feature_disabled" as const);
|
||||
}
|
||||
|
||||
// OSS has no directory, so manual membership is always allowed.
|
||||
getMembershipPolicy(
|
||||
_organizationId: string
|
||||
): ResultAsync<{ manualMembershipAllowed: boolean }, SsoDecisionError> {
|
||||
return okAsync({ manualMembershipAllowed: true });
|
||||
}
|
||||
|
||||
setAllowManualMembership(_params: {
|
||||
organizationId: string;
|
||||
allowed: boolean;
|
||||
}): ResultAsync<void, SsoMutationError> {
|
||||
return errAsync("feature_disabled" as const);
|
||||
}
|
||||
|
||||
recordMembershipRemoval(_params: {
|
||||
organizationId: string;
|
||||
userId: string;
|
||||
reason: "manual_removal" | "self_leave";
|
||||
}): ResultAsync<void, SsoMutationError> {
|
||||
// No plugin → no JIT → nothing to guard against. No-op success.
|
||||
return okAsync(undefined as void);
|
||||
}
|
||||
|
||||
clearMembershipRemoval(_params: {
|
||||
organizationId: string;
|
||||
userId: string;
|
||||
}): ResultAsync<void, SsoMutationError> {
|
||||
return okAsync(undefined as void);
|
||||
}
|
||||
|
||||
decideRouteForEmail(_email: string): ResultAsync<SsoRouteDecision, SsoDecisionError> {
|
||||
return okAsync({ kind: "no_sso" as const });
|
||||
}
|
||||
|
||||
beginAuthorization(_params: {
|
||||
email: string;
|
||||
redirectTo: string;
|
||||
flow: SsoFlow;
|
||||
}): ResultAsync<{ url: string }, SsoBeginError> {
|
||||
return errAsync("feature_disabled" as const);
|
||||
}
|
||||
|
||||
completeAuthorization(_params: { code: string; state: string }): ResultAsync<
|
||||
{
|
||||
profile: SsoProfile;
|
||||
redirectTo: string;
|
||||
flow: SsoFlow;
|
||||
},
|
||||
SsoCompleteError
|
||||
> {
|
||||
return errAsync("connection_unknown" as const);
|
||||
}
|
||||
|
||||
completeIdpInitiatedAuthorization(_params: {
|
||||
code: string;
|
||||
}): ResultAsync<{ profile: SsoProfile; redirectTo: string }, SsoCompleteError> {
|
||||
return errAsync("connection_unknown" as const);
|
||||
}
|
||||
|
||||
// Fail-open: with no plugin there are no SSO sessions to invalidate,
|
||||
// and the host treats `valid: true` as "leave the session alone".
|
||||
validateSession(_params: {
|
||||
userId: string;
|
||||
idpOrgId: string;
|
||||
connectionId: string;
|
||||
}): ResultAsync<{ valid: boolean }, SsoValidateError> {
|
||||
return okAsync({ valid: true });
|
||||
}
|
||||
|
||||
resolveSsoIdentity(_params: {
|
||||
profile: SsoProfile;
|
||||
}): ResultAsync<SsoResolutionDecision, SsoMutationError> {
|
||||
return errAsync("feature_disabled" as const);
|
||||
}
|
||||
|
||||
attachSsoIdentity(_params: {
|
||||
userId: string;
|
||||
profile: SsoProfile;
|
||||
}): ResultAsync<void, SsoMutationError> {
|
||||
return errAsync("feature_disabled" as const);
|
||||
}
|
||||
|
||||
evaluateJit(_params: {
|
||||
userId: string;
|
||||
idpOrgId: string;
|
||||
}): ResultAsync<
|
||||
{ shouldProvision: boolean; organizationId: string; roleId: string | null },
|
||||
SsoMutationError
|
||||
> {
|
||||
return errAsync("feature_disabled" as const);
|
||||
}
|
||||
|
||||
verifyWebhook(_params: {
|
||||
rawBody: string;
|
||||
headers: Record<string, string>;
|
||||
}): ResultAsync<{ event: SsoWebhookEvent }, SsoWebhookError> {
|
||||
return errAsync("feature_disabled" as const);
|
||||
}
|
||||
|
||||
processWebhookEvent(
|
||||
_event: SsoWebhookEvent
|
||||
): ResultAsync<{ effects: DirectorySyncEffect[] }, SsoWebhookError> {
|
||||
// No plugin: nothing to verify or act on. The host's webhook proxy
|
||||
// already rejects unverified requests, so a call here just no-ops.
|
||||
return okAsync({ effects: [] });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user