diff --git a/backend/prisma/seed/config.seed.ts b/backend/prisma/seed/config.seed.ts index 99e1ea0..0c9e211 100644 --- a/backend/prisma/seed/config.seed.ts +++ b/backend/prisma/seed/config.seed.ts @@ -71,7 +71,6 @@ const configVariables: ConfigVariables = { enableShareEmailRecipients: { type: "boolean", defaultValue: "false", - secret: false, }, shareRecipientsSubject: { @@ -148,6 +147,11 @@ const configVariables: ConfigVariables = { type: "boolean", defaultValue: "true", }, + "disablePassword": { + type: "boolean", + defaultValue: "false", + secret: false, + }, "github-enabled": { type: "boolean", defaultValue: "false", @@ -229,7 +233,7 @@ const configVariables: ConfigVariables = { defaultValue: "", obscured: true, }, - } + }, }; type ConfigVariables = { @@ -281,12 +285,15 @@ async function seedConfigVariables() { async function migrateConfigVariables() { const existingConfigVariables = await prisma.config.findMany(); + const orderMap: { [category: string]: number } = {}; for (const existingConfigVariable of existingConfigVariables) { const configVariable = configVariables[existingConfigVariable.category]?.[ - existingConfigVariable.name + existingConfigVariable.name ]; + + // Delete the config variable if it doesn't exist in the seed if (!configVariable) { await prisma.config.delete({ where: { @@ -297,15 +304,11 @@ async function migrateConfigVariables() { }, }); - // Update the config variable if the metadata changed - } else if ( - JSON.stringify({ - ...configVariable, - name: existingConfigVariable.name, - category: existingConfigVariable.category, - value: existingConfigVariable.value, - }) != JSON.stringify(existingConfigVariable) - ) { + // Update the config variable if it exists in the seed + } else { + const variableOrder = Object.keys( + configVariables[existingConfigVariable.category] + ).indexOf(existingConfigVariable.name); await prisma.config.update({ where: { name_category: { @@ -318,8 +321,10 @@ async function migrateConfigVariables() { name: existingConfigVariable.name, category: existingConfigVariable.category, value: existingConfigVariable.value, + order: variableOrder, }, }); + orderMap[existingConfigVariable.category] = variableOrder + 1; } } } diff --git a/backend/src/auth/auth.service.ts b/backend/src/auth/auth.service.ts index f40b518..588720a 100644 --- a/backend/src/auth/auth.service.ts +++ b/backend/src/auth/auth.service.ts @@ -61,6 +61,9 @@ export class AuthService { if (!dto.email && !dto.username) throw new BadRequestException("Email or username is required"); + if (this.config.get("oauth.disablePassword")) + throw new ForbiddenException("Password sign in is disabled"); + const user = await this.prisma.user.findFirst({ where: { OR: [{ email: dto.email }, { username: dto.username }], @@ -94,6 +97,9 @@ export class AuthService { } async requestResetPassword(email: string) { + if (this.config.get("oauth.disablePassword")) + throw new ForbiddenException("Password sign in is disabled"); + const user = await this.prisma.user.findFirst({ where: { email }, include: { resetPasswordToken: true }, @@ -119,6 +125,9 @@ export class AuthService { } async resetPassword(token: string, newPassword: string) { + if (this.config.get("oauth.disablePassword")) + throw new ForbiddenException("Password sign in is disabled"); + const user = await this.prisma.user.findFirst({ where: { resetPasswordToken: { token } }, }); diff --git a/frontend/src/components/auth/SignInForm.tsx b/frontend/src/components/auth/SignInForm.tsx index 7696066..de6436d 100644 --- a/frontend/src/components/auth/SignInForm.tsx +++ b/frontend/src/components/auth/SignInForm.tsx @@ -28,6 +28,19 @@ import toast from "../../utils/toast.util"; import { safeRedirectPath } from "../../utils/router.util"; const useStyles = createStyles((theme) => ({ + signInWith: { + fontWeight: 500, + "&:before": { + content: "''", + flex: 1, + display: "block", + }, + "&:after": { + content: "''", + flex: 1, + display: "block", + }, + }, or: { "&:before": { content: "''", @@ -128,49 +141,58 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => { )} -
{ - signIn(values.emailOrUsername, values.password); - })} - > - - - {config.get("smtp.enabled") && ( - - - - - - )} - - + {config.get("oauth.disablePassword") || ( +
{ + signIn(values.emailOrUsername, values.password); + })} + > + + + {config.get("smtp.enabled") && ( + + + + + + )} + + + )} {oauth.length > 0 && ( - - - {t("signIn.oauth.or")} - + + {config.get("oauth.disablePassword") ? ( + + {t("signIn.oauth.signInWith")} + + ) : ( + + {t("signIn.oauth.or")} + + )} {oauth.map((provider) => ( ))} diff --git a/frontend/src/i18n/translations/de-DE.ts b/frontend/src/i18n/translations/de-DE.ts index d3aa42a..8d1bbb5 100644 --- a/frontend/src/i18n/translations/de-DE.ts +++ b/frontend/src/i18n/translations/de-DE.ts @@ -34,6 +34,7 @@ export default { "signIn.notify.totp-required.title": "Zwei-Faktor-Authentifizierung benötigt", "signIn.notify.totp-required.description": "Bitte füge deinen Zwei-Faktor-Authentifizierungscode ein", "signIn.oauth.or": "ODER", + "signIn.oauth.signInWith": "Anmelden mit", "signIn.oauth.github": "GitHub", "signIn.oauth.google": "Google", "signIn.oauth.microsoft": "Microsoft", @@ -348,6 +349,9 @@ export default { "admin.config.oauth.allow-registration.description": "Benutzern erlauben, sich über Soziale Netzwerke zu registrieren", "admin.config.oauth.ignore-totp": "TOTP ignorieren", "admin.config.oauth.ignore-totp.description": "Gibt an, ob TOTP ignoriert werden soll, wenn sich der Benutzer über Soziale Netzwerke anmeldet", + "admin.config.oauth.disable-password": "Anmelden mit Passwort deaktivieren", + "admin.config.oauth.disable-password.description": + "Deaktiviert das Anmelden mit Passwort\nStelle vor Aktivierung dieser Konfiguration sicher, dass ein OAuth-Provider korrekt konfiguriert ist, um nicht ausgesperrt zu werden.", "admin.config.oauth.github-enabled": "GitHub", "admin.config.oauth.github-enabled.description": "GitHub Anmeldung erlaubt", "admin.config.oauth.github-client-id": "GitHub Client-ID", @@ -401,7 +405,7 @@ export default { "error.msg.no_email": "Kann die E-Mail-Adresse von dem Konto {0} nicht abrufen.", "error.msg.already_linked": "Das Konto {0} ist bereits mit einem anderen Konto verknüpft.", "error.msg.not_linked": "Das Konto {0} wurde noch nicht mit einem Konto verknüpft.", - "error.msg.unverified_account": "Dieses Konto {0} wurde noch nicht verifiziert, bitte versuchen Sie es nach der Verifikation erneut.", + "error.msg.unverified_account": "Dieses Konto {0} wurde noch nicht verifiziert, bitte versuche es nach der Verifikation erneut.", "error.msg.discord_guild_permission_denied": "Du bist nicht berechtigt, Dich anzumelden.", "error.msg.cannot_get_user_info": "Deine Benutzerinformationen können nicht von diesem Konto {0} abgerufen werden.", "error.param.provider_github": "GitHub", diff --git a/frontend/src/i18n/translations/en-US.ts b/frontend/src/i18n/translations/en-US.ts index 0959695..0b6d201 100644 --- a/frontend/src/i18n/translations/en-US.ts +++ b/frontend/src/i18n/translations/en-US.ts @@ -44,6 +44,7 @@ export default { "signIn.notify.totp-required.description": "Please enter your two-factor authentication code", "signIn.oauth.or": "OR", + "signIn.oauth.signInWith": "Sign in with", "signIn.oauth.github": "GitHub", "signIn.oauth.google": "Google", "signIn.oauth.microsoft": "Microsoft", @@ -479,6 +480,9 @@ export default { "admin.config.oauth.ignore-totp": "Ignore TOTP", "admin.config.oauth.ignore-totp.description": "Whether to ignore TOTP when user using social login", + "admin.config.oauth.disable-password": "Disable password login", + "admin.config.oauth.disable-password.description": + "Whether to disable password login\nMake sure that an OAuth provider is properly configured before activating this configuration to avoid being locked out.", "admin.config.oauth.github-enabled": "GitHub", "admin.config.oauth.github-enabled.description": "Whether GitHub login is enabled",