refactor: cleanup / pr review
This commit is contained in:
parent
5e4f3fca62
commit
2d2183baaf
10 changed files with 38 additions and 2280 deletions
|
|
@ -27,7 +27,6 @@ import { Label } from "~/client/components/ui/label";
|
|||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/client/components/ui/select";
|
||||
import { Switch } from "~/client/components/ui/switch";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/client/components/ui/table";
|
||||
import { parseError } from "~/client/lib/errors";
|
||||
import { useOrganizationContext } from "~/client/hooks/use-org-context";
|
||||
import { formatDateWithMonth } from "~/client/lib/datetime";
|
||||
import { getOrigin } from "~/client/functions/get-origin";
|
||||
|
|
@ -56,9 +55,7 @@ export function SsoSettingsSection() {
|
|||
toast.success(v.body?.enabled ? "Automatic account linking enabled" : "Automatic account linking disabled");
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error("Failed to update provider", {
|
||||
description: parseError(error)?.message,
|
||||
});
|
||||
toast.error("Failed to update provider", { description: error.message });
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -68,7 +65,7 @@ export function SsoSettingsSection() {
|
|||
toast.success("SSO provider deleted");
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error("Failed to delete provider", { description: parseError(error)?.message });
|
||||
toast.error("Failed to delete provider", { description: error.message });
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -98,8 +95,8 @@ export function SsoSettingsSection() {
|
|||
setInviteEmail("");
|
||||
setInviteRole("member");
|
||||
},
|
||||
onError: (error: unknown) => {
|
||||
toast.error("Failed to create invitation", { description: parseError(error)?.message });
|
||||
onError: (error) => {
|
||||
toast.error("Failed to create invitation", { description: error.message });
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -113,8 +110,8 @@ export function SsoSettingsSection() {
|
|||
onSuccess: () => {
|
||||
toast.success("Invitation cancelled");
|
||||
},
|
||||
onError: (error: unknown) => {
|
||||
toast.error("Failed to cancel invitation", { description: parseError(error)?.message });
|
||||
onError: (error) => {
|
||||
toast.error("Failed to cancel invitation", { description: error.message });
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -124,7 +121,7 @@ export function SsoSettingsSection() {
|
|||
toast.success("Invitation deleted");
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error("Failed to delete invitation", { description: parseError(error)?.message });
|
||||
toast.error("Failed to delete invitation", { description: error.message });
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -90,8 +90,8 @@ export function CreateSsoProviderPage() {
|
|||
toast.success("SSO provider registered successfully");
|
||||
void navigate({ to: "/settings", search: { tab: "users" } });
|
||||
},
|
||||
onError: (error: unknown) => {
|
||||
toast.error("Failed to register provider", { description: parseError(error)?.message });
|
||||
onError: (error) => {
|
||||
toast.error("Failed to register provider", { description: error.message });
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,25 +0,0 @@
|
|||
ALTER TABLE `sso_provider` ADD `auto_link_matching_emails` integer DEFAULT true NOT NULL;--> statement-breakpoint
|
||||
PRAGMA foreign_keys=OFF;--> statement-breakpoint
|
||||
CREATE TABLE `__new_sso_provider` (
|
||||
`id` text PRIMARY KEY,
|
||||
`provider_id` text NOT NULL,
|
||||
`organization_id` text NOT NULL,
|
||||
`user_id` text,
|
||||
`issuer` text NOT NULL,
|
||||
`domain` text NOT NULL,
|
||||
`auto_link_matching_emails` integer DEFAULT true NOT NULL,
|
||||
`oidc_config` text,
|
||||
`saml_config` text,
|
||||
`created_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||
`updated_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||
CONSTRAINT `fk_sso_provider_organization_id_organization_id_fk` FOREIGN KEY (`organization_id`) REFERENCES `organization`(`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_sso_provider_user_id_users_table_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users_table`(`id`) ON DELETE SET NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
INSERT INTO `__new_sso_provider`(`id`, `provider_id`, `organization_id`, `user_id`, `issuer`, `domain`, `oidc_config`, `saml_config`, `created_at`, `updated_at`) SELECT `id`, `provider_id`, `organization_id`, `user_id`, `issuer`, `domain`, `oidc_config`, `saml_config`, `created_at`, `updated_at` FROM `sso_provider`;--> statement-breakpoint
|
||||
DROP TABLE `sso_provider`;--> statement-breakpoint
|
||||
ALTER TABLE `__new_sso_provider` RENAME TO `sso_provider`;--> statement-breakpoint
|
||||
PRAGMA foreign_keys=ON;--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `sso_provider_provider_id_uidx` ON `sso_provider` (`provider_id`);--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `sso_provider_organization_id_uidx` ON `sso_provider` (`organization_id`);--> statement-breakpoint
|
||||
CREATE INDEX `sso_provider_domain_idx` ON `sso_provider` (`domain`);
|
||||
|
|
@ -2,15 +2,16 @@ CREATE TABLE `sso_provider` (
|
|||
`id` text PRIMARY KEY,
|
||||
`provider_id` text NOT NULL,
|
||||
`organization_id` text NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`user_id` text,
|
||||
`issuer` text NOT NULL,
|
||||
`domain` text NOT NULL,
|
||||
`auto_link_matching_emails` integer DEFAULT false NOT NULL,
|
||||
`oidc_config` text,
|
||||
`saml_config` text,
|
||||
`created_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||
`updated_at` integer DEFAULT (unixepoch() * 1000) NOT NULL,
|
||||
CONSTRAINT `fk_sso_provider_organization_id_organization_id_fk` FOREIGN KEY (`organization_id`) REFERENCES `organization`(`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_sso_provider_user_id_users_table_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users_table`(`id`) ON DELETE CASCADE
|
||||
CONSTRAINT `fk_sso_provider_user_id_users_table_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users_table`(`id`) ON DELETE SET NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `sso_provider_provider_id_uidx` ON `sso_provider` (`provider_id`);--> statement-breakpoint
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"version": "7",
|
||||
"dialect": "sqlite",
|
||||
"id": "93504601-a3b2-4f09-92ca-16f132bcdd3a",
|
||||
"prevIds": ["ab028cd7-c0cb-44f4-843b-37e95ab3c667"],
|
||||
"id": "f1d1baed-4d81-40b5-86fb-56e9c173a27f",
|
||||
"prevIds": ["3a308c54-d950-464f-9490-fee06985fbeb"],
|
||||
"ddl": [
|
||||
{
|
||||
"name": "account",
|
||||
|
|
@ -1212,7 +1212,7 @@
|
|||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "true",
|
||||
"default": "false",
|
||||
"generated": null,
|
||||
"name": "auto_link_matching_emails",
|
||||
"entityType": "columns",
|
||||
|
|
@ -27,24 +27,12 @@ function buildOrgSlug(email: string) {
|
|||
return `${emailPrefix}-${Math.random().toString(36).slice(-4)}`;
|
||||
}
|
||||
|
||||
async function getProviderFromContext(ctx: GenericEndpointContext | null) {
|
||||
if (!ctx) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const providerId = extractProviderIdFromContext(ctx);
|
||||
if (!providerId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const provider = await db.query.ssoProvider.findFirst({ where: { providerId } });
|
||||
return provider;
|
||||
}
|
||||
|
||||
async function tryCreateInvitedMembership(userId: string, email: string, ctx: GenericEndpointContext | null) {
|
||||
logger.debug("Checking for pending invitations for user", userId, email);
|
||||
|
||||
const ssoProvider = await getProviderFromContext(ctx);
|
||||
const providerId = extractProviderIdFromContext(ctx);
|
||||
const ssoProvider = await db.query.ssoProvider.findFirst({ where: { providerId } });
|
||||
|
||||
if (!ssoProvider) {
|
||||
logger.debug("No SSO provider found in context, skipping invitation check");
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@ export function normalizeEmail(email: string): string {
|
|||
return email.trim().toLowerCase();
|
||||
}
|
||||
|
||||
export function extractProviderIdFromContext(ctx: GenericEndpointContext): string | null {
|
||||
export function extractProviderIdFromContext(ctx?: GenericEndpointContext | null) {
|
||||
if (!ctx) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ctx.params?.providerId) {
|
||||
return ctx.params.providerId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ export const publicSsoProvidersDto = type({
|
|||
providers: type({
|
||||
providerId: "string",
|
||||
organizationSlug: "string",
|
||||
}).array(),
|
||||
})
|
||||
.onUndeclaredKey("delete")
|
||||
.array(),
|
||||
});
|
||||
|
||||
export type PublicSsoProvidersDto = typeof publicSsoProvidersDto.infer;
|
||||
|
|
|
|||
|
|
@ -130,12 +130,10 @@ export class AuthService {
|
|||
* Update per-provider auto-linking setting
|
||||
*/
|
||||
async updateSsoProviderAutoLinking(providerId: string, enabled: boolean): Promise<boolean> {
|
||||
const existingProvider = await db
|
||||
.select({ id: ssoProvider.id })
|
||||
.from(ssoProvider)
|
||||
.where(eq(ssoProvider.providerId, providerId))
|
||||
.limit(1)
|
||||
.then((result) => result[0]);
|
||||
const existingProvider = await db.query.ssoProvider.findFirst({
|
||||
where: { providerId },
|
||||
columns: { id: true },
|
||||
});
|
||||
|
||||
if (!existingProvider) {
|
||||
return false;
|
||||
|
|
@ -159,10 +157,10 @@ export class AuthService {
|
|||
async getUserAccounts(userIds: string[]): Promise<Record<string, { id: string; providerId: string }[]>> {
|
||||
if (userIds.length === 0) return {};
|
||||
|
||||
const accounts = await db
|
||||
.select({ id: account.id, providerId: account.providerId, userId: account.userId })
|
||||
.from(account)
|
||||
.where(inArray(account.userId, userIds));
|
||||
const accounts = await db.query.account.findMany({
|
||||
where: { userId: { in: userIds } },
|
||||
columns: { id: true, providerId: true, userId: true },
|
||||
});
|
||||
|
||||
const grouped: Record<string, { id: string; providerId: string }[]> = {};
|
||||
for (const row of accounts) {
|
||||
|
|
@ -178,7 +176,10 @@ export class AuthService {
|
|||
* Delete a single account for a user, refusing if it is the last one
|
||||
*/
|
||||
async deleteUserAccount(userId: string, accountId: string): Promise<{ lastAccount: boolean }> {
|
||||
const userAccounts = await db.select({ id: account.id }).from(account).where(eq(account.userId, userId));
|
||||
const userAccounts = await db.query.account.findMany({
|
||||
where: { userId },
|
||||
columns: { id: true },
|
||||
});
|
||||
|
||||
if (userAccounts.length <= 1) {
|
||||
return { lastAccount: true };
|
||||
|
|
|
|||
Loading…
Reference in a new issue