mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-07-01 03:55:22 +02:00
- Added support for editing existing SAML providers, allowing users to update issuer, domains, entry point, and certificate. - Introduced a new function to parse SAML configuration from JSON. - Updated the UI to reflect changes in the registration dialog based on whether the user is adding or editing a provider. - Improved user feedback with success messages tailored for registration and updates. - Added a new column `created_at` to the `sso_provider` table for better tracking of provider creation times.
134 lines
4.1 KiB
TypeScript
134 lines
4.1 KiB
TypeScript
import { relations } from "drizzle-orm";
|
|
import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
|
import { z } from "zod";
|
|
import { organization } from "./account";
|
|
import { user } from "./user";
|
|
|
|
export const ssoProvider = pgTable("sso_provider", {
|
|
id: text("id").primaryKey(),
|
|
issuer: text("issuer").notNull(),
|
|
oidcConfig: text("oidc_config"),
|
|
samlConfig: text("saml_config"),
|
|
providerId: text("provider_id").notNull().unique(),
|
|
userId: text("user_id").references(() => user.id, { onDelete: "cascade" }),
|
|
organizationId: text("organization_id").references(() => organization.id, {
|
|
onDelete: "cascade",
|
|
}),
|
|
domain: text("domain").notNull(),
|
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
});
|
|
|
|
export const ssoProviderRelations = relations(ssoProvider, ({ one }) => ({
|
|
organization: one(organization, {
|
|
fields: [ssoProvider.organizationId],
|
|
references: [organization.id],
|
|
}),
|
|
user: one(user, {
|
|
fields: [ssoProvider.userId],
|
|
references: [user.id],
|
|
}),
|
|
}));
|
|
const domainRegex = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/;
|
|
export const ssoProviderBodySchema = z.object({
|
|
providerId: z.string({}),
|
|
issuer: z.string({}),
|
|
domains: z
|
|
.string()
|
|
.array()
|
|
.transform((val) =>
|
|
Array.from(
|
|
new Set(val.map((d) => d.trim().toLowerCase()).filter(Boolean)),
|
|
),
|
|
)
|
|
.refine((val) => val.every((d) => domainRegex.test(d)), {
|
|
message: "Invalid domain",
|
|
path: ["domains"],
|
|
}),
|
|
oidcConfig: z
|
|
.object({
|
|
clientId: z.string({}),
|
|
clientSecret: z.string({}),
|
|
authorizationEndpoint: z.string({}).optional(),
|
|
tokenEndpoint: z.string({}).optional(),
|
|
userInfoEndpoint: z.string({}).optional(),
|
|
tokenEndpointAuthentication: z
|
|
.enum(["client_secret_post", "client_secret_basic"])
|
|
.optional(),
|
|
jwksEndpoint: z.string({}).optional(),
|
|
discoveryEndpoint: z.string().optional(),
|
|
skipDiscovery: z.boolean().optional(),
|
|
scopes: z.array(z.string()).optional(),
|
|
pkce: z.boolean().default(true).optional(),
|
|
mapping: z
|
|
.object({
|
|
id: z.string({}),
|
|
email: z.string({}),
|
|
emailVerified: z.string({}).optional(),
|
|
name: z.string({}),
|
|
image: z.string({}).optional(),
|
|
extraFields: z.record(z.string(), z.any()).optional(),
|
|
})
|
|
.optional(),
|
|
})
|
|
.optional(),
|
|
samlConfig: z
|
|
.object({
|
|
entryPoint: z.string({}),
|
|
cert: z.string({}),
|
|
callbackUrl: z.string({}),
|
|
audience: z.string().optional(),
|
|
idpMetadata: z
|
|
.object({
|
|
metadata: z.string().optional(),
|
|
entityID: z.string().optional(),
|
|
cert: z.string().optional(),
|
|
privateKey: z.string().optional(),
|
|
privateKeyPass: z.string().optional(),
|
|
isAssertionEncrypted: z.boolean().optional(),
|
|
encPrivateKey: z.string().optional(),
|
|
encPrivateKeyPass: z.string().optional(),
|
|
singleSignOnService: z
|
|
.array(
|
|
z.object({
|
|
Binding: z.string(),
|
|
Location: z.string(),
|
|
}),
|
|
)
|
|
.optional(),
|
|
})
|
|
.optional(),
|
|
spMetadata: z.object({
|
|
metadata: z.string().optional(),
|
|
entityID: z.string().optional(),
|
|
binding: z.string().optional(),
|
|
privateKey: z.string().optional(),
|
|
privateKeyPass: z.string().optional(),
|
|
isAssertionEncrypted: z.boolean().optional(),
|
|
encPrivateKey: z.string().optional(),
|
|
encPrivateKeyPass: z.string().optional(),
|
|
}),
|
|
wantAssertionsSigned: z.boolean().optional(),
|
|
authnRequestsSigned: z.boolean().optional(),
|
|
signatureAlgorithm: z.string().optional(),
|
|
digestAlgorithm: z.string().optional(),
|
|
identifierFormat: z.string().optional(),
|
|
privateKey: z.string().optional(),
|
|
decryptionPvk: z.string().optional(),
|
|
additionalParams: z.record(z.string(), z.any()).optional(),
|
|
mapping: z
|
|
.object({
|
|
id: z.string({}),
|
|
email: z.string({}),
|
|
emailVerified: z.string({}).optional(),
|
|
name: z.string({}),
|
|
firstName: z.string({}).optional(),
|
|
lastName: z.string({}).optional(),
|
|
extraFields: z.record(z.string(), z.any()).optional(),
|
|
})
|
|
.optional(),
|
|
})
|
|
.optional(),
|
|
organizationId: z.string({}).optional(),
|
|
overrideUserInfo: z.boolean({}).default(false).optional(),
|
|
});
|