This commit is contained in:
Elias Schneider
2024-07-10 18:39:53 +02:00
7 changed files with 109 additions and 65 deletions

View File

@@ -71,7 +71,6 @@ const configVariables: ConfigVariables = {
enableShareEmailRecipients: { enableShareEmailRecipients: {
type: "boolean", type: "boolean",
defaultValue: "false", defaultValue: "false",
secret: false, secret: false,
}, },
shareRecipientsSubject: { shareRecipientsSubject: {
@@ -148,6 +147,11 @@ const configVariables: ConfigVariables = {
type: "boolean", type: "boolean",
defaultValue: "true", defaultValue: "true",
}, },
"disablePassword": {
type: "boolean",
defaultValue: "false",
secret: false,
},
"github-enabled": { "github-enabled": {
type: "boolean", type: "boolean",
defaultValue: "false", defaultValue: "false",
@@ -229,7 +233,7 @@ const configVariables: ConfigVariables = {
defaultValue: "", defaultValue: "",
obscured: true, obscured: true,
}, },
} },
}; };
type ConfigVariables = { type ConfigVariables = {
@@ -281,12 +285,15 @@ async function seedConfigVariables() {
async function migrateConfigVariables() { async function migrateConfigVariables() {
const existingConfigVariables = await prisma.config.findMany(); const existingConfigVariables = await prisma.config.findMany();
const orderMap: { [category: string]: number } = {};
for (const existingConfigVariable of existingConfigVariables) { for (const existingConfigVariable of existingConfigVariables) {
const configVariable = const configVariable =
configVariables[existingConfigVariable.category]?.[ configVariables[existingConfigVariable.category]?.[
existingConfigVariable.name existingConfigVariable.name
]; ];
// Delete the config variable if it doesn't exist in the seed
if (!configVariable) { if (!configVariable) {
await prisma.config.delete({ await prisma.config.delete({
where: { where: {
@@ -297,15 +304,11 @@ async function migrateConfigVariables() {
}, },
}); });
// Update the config variable if the metadata changed // Update the config variable if it exists in the seed
} else if ( } else {
JSON.stringify({ const variableOrder = Object.keys(
...configVariable, configVariables[existingConfigVariable.category]
name: existingConfigVariable.name, ).indexOf(existingConfigVariable.name);
category: existingConfigVariable.category,
value: existingConfigVariable.value,
}) != JSON.stringify(existingConfigVariable)
) {
await prisma.config.update({ await prisma.config.update({
where: { where: {
name_category: { name_category: {
@@ -318,8 +321,10 @@ async function migrateConfigVariables() {
name: existingConfigVariable.name, name: existingConfigVariable.name,
category: existingConfigVariable.category, category: existingConfigVariable.category,
value: existingConfigVariable.value, value: existingConfigVariable.value,
order: variableOrder,
}, },
}); });
orderMap[existingConfigVariable.category] = variableOrder + 1;
} }
} }
} }

View File

@@ -64,6 +64,9 @@ export class AuthService {
if (!dto.email && !dto.username) if (!dto.email && !dto.username)
throw new BadRequestException("Email or username is required"); 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({ const user = await this.prisma.user.findFirst({
where: { where: {
OR: [{ email: dto.email }, { username: dto.username }], OR: [{ email: dto.email }, { username: dto.username }],
@@ -102,6 +105,9 @@ export class AuthService {
} }
async requestResetPassword(email: string) { 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({ const user = await this.prisma.user.findFirst({
where: { email }, where: { email },
include: { resetPasswordToken: true }, include: { resetPasswordToken: true },
@@ -127,6 +133,9 @@ export class AuthService {
} }
async resetPassword(token: string, newPassword: string) { 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({ const user = await this.prisma.user.findFirst({
where: { resetPasswordToken: { token } }, where: { resetPasswordToken: { token } },
}); });

View File

@@ -28,6 +28,19 @@ import toast from "../../utils/toast.util";
import { safeRedirectPath } from "../../utils/router.util"; import { safeRedirectPath } from "../../utils/router.util";
const useStyles = createStyles((theme) => ({ const useStyles = createStyles((theme) => ({
signInWith: {
fontWeight: 500,
"&:before": {
content: "''",
flex: 1,
display: "block",
},
"&:after": {
content: "''",
flex: 1,
display: "block",
},
},
or: { or: {
"&:before": { "&:before": {
content: "''", content: "''",
@@ -128,6 +141,7 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
</Text> </Text>
)} )}
<Paper withBorder shadow="md" p={30} mt={30} radius="md"> <Paper withBorder shadow="md" p={30} mt={30} radius="md">
{config.get("oauth.disablePassword") || (
<form <form
onSubmit={form.onSubmit((values) => { onSubmit={form.onSubmit((values) => {
signIn(values.emailOrUsername, values.password); signIn(values.emailOrUsername, values.password);
@@ -155,22 +169,30 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
<FormattedMessage id="signin.button.submit" /> <FormattedMessage id="signin.button.submit" />
</Button> </Button>
</form> </form>
)}
{oauth.length > 0 && ( {oauth.length > 0 && (
<Stack mt="xl"> <Stack mt={config.get("oauth.disablePassword") ? undefined : "xl"}>
{config.get("oauth.disablePassword") ? (
<Group align="center" className={classes.signInWith}>
<Text>{t("signIn.oauth.signInWith")}</Text>
</Group>
) : (
<Group align="center" className={classes.or}> <Group align="center" className={classes.or}>
<Text>{t("signIn.oauth.or")}</Text> <Text>{t("signIn.oauth.or")}</Text>
</Group> </Group>
)}
<Group position="center"> <Group position="center">
{oauth.map((provider) => ( {oauth.map((provider) => (
<Button <Button
key={provider} key={provider}
component="a" component="a"
target="_blank"
title={t(`signIn.oauth.${provider}`)} title={t(`signIn.oauth.${provider}`)}
href={getOAuthUrl(config.get("general.appUrl"), provider)} href={getOAuthUrl(config.get("general.appUrl"), provider)}
variant="light" variant="light"
fullWidth
> >
{getOAuthIcon(provider)} {getOAuthIcon(provider)}
{"\u2002" + t(`signIn.oauth.${provider}`)}
</Button> </Button>
))} ))}
</Group> </Group>

View File

@@ -34,6 +34,7 @@ export default {
"signIn.notify.totp-required.title": "Zwei-Faktor-Authentifizierung benötigt", "signIn.notify.totp-required.title": "Zwei-Faktor-Authentifizierung benötigt",
"signIn.notify.totp-required.description": "Bitte füge deinen Zwei-Faktor-Authentifizierungscode ein", "signIn.notify.totp-required.description": "Bitte füge deinen Zwei-Faktor-Authentifizierungscode ein",
"signIn.oauth.or": "ODER", "signIn.oauth.or": "ODER",
"signIn.oauth.signInWith": "Anmelden mit",
"signIn.oauth.github": "GitHub", "signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google", "signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft", "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.allow-registration.description": "Benutzern erlauben, sich über Soziale Netzwerke zu registrieren",
"admin.config.oauth.ignore-totp": "TOTP ignorieren", "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.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": "GitHub",
"admin.config.oauth.github-enabled.description": "GitHub Anmeldung erlaubt", "admin.config.oauth.github-enabled.description": "GitHub Anmeldung erlaubt",
"admin.config.oauth.github-client-id": "GitHub Client-ID", "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.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.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.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.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.msg.cannot_get_user_info": "Deine Benutzerinformationen können nicht von diesem Konto {0} abgerufen werden.",
"error.param.provider_github": "GitHub", "error.param.provider_github": "GitHub",

View File

@@ -44,6 +44,7 @@ export default {
"signIn.notify.totp-required.description": "signIn.notify.totp-required.description":
"Please enter your two-factor authentication code", "Please enter your two-factor authentication code",
"signIn.oauth.or": "OR", "signIn.oauth.or": "OR",
"signIn.oauth.signInWith": "Sign in with",
"signIn.oauth.github": "GitHub", "signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google", "signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft", "signIn.oauth.microsoft": "Microsoft",
@@ -479,6 +480,9 @@ export default {
"admin.config.oauth.ignore-totp": "Ignore TOTP", "admin.config.oauth.ignore-totp": "Ignore TOTP",
"admin.config.oauth.ignore-totp.description": "admin.config.oauth.ignore-totp.description":
"Whether to ignore TOTP when user using social login", "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": "GitHub",
"admin.config.oauth.github-enabled.description": "admin.config.oauth.github-enabled.description":
"Whether GitHub login is enabled", "Whether GitHub login is enabled",

View File

@@ -294,8 +294,8 @@ export default {
"admin.config.general.app-url.description": "A Pingvin Share megosztáskezelőre mutató hivatkozás", "admin.config.general.app-url.description": "A Pingvin Share megosztáskezelőre mutató hivatkozás",
"admin.config.general.show-home-page": "Kezdőlap mutatása", "admin.config.general.show-home-page": "Kezdőlap mutatása",
"admin.config.general.show-home-page.description": "A kezdőlap mutatásának ki- és bekapcsolása", "admin.config.general.show-home-page.description": "A kezdőlap mutatásának ki- és bekapcsolása",
"admin.config.general.session-duration": "Session Duration", "admin.config.general.session-duration": "Munkamenet időtartama",
"admin.config.general.session-duration.description": "Time in hours after which a user must log in again (default: 3 months).", "admin.config.general.session-duration.description": "Annak az időtartamnak a megadása, amit követően a felhasználónak ismét be kell jelentkeznie (alapérték: 3 hónap).",
"admin.config.general.logo": "Logó", "admin.config.general.logo": "Logó",
"admin.config.general.logo.description": "A logó személyessé tételéhez töltsön fel egy új képet. A formátum legyen PNG, az oldalarány 1:1.", "admin.config.general.logo.description": "A logó személyessé tételéhez töltsön fel egy új képet. A formátum legyen PNG, az oldalarány 1:1.",
"admin.config.general.logo.placeholder": "Kép kiválasztása", "admin.config.general.logo.placeholder": "Kép kiválasztása",

View File

@@ -44,10 +44,10 @@ export default {
"signup.title": "계정 만들기", "signup.title": "계정 만들기",
"signup.description": "이미 계정이 있으신가요?", "signup.description": "이미 계정이 있으신가요?",
"signup.button.signin": "로그인", "signup.button.signin": "로그인",
"signup.input.username": "사용자 이름", "signup.input.username": "사용자",
"signup.input.username.placeholder": "당신의 사용지 이름", "signup.input.username.placeholder": "귀하의 사용자명",
"signup.input.email": "이메일", "signup.input.email": "이메일",
"signup.input.email.placeholder": "당신의 이메일", "signup.input.email.placeholder": "귀하의 이메일",
"signup.button.submit": "시작하기", "signup.button.submit": "시작하기",
// END /auth/signup // END /auth/signup
// /auth/totp // /auth/totp
@@ -258,13 +258,13 @@ export default {
// /share/[id] // /share/[id]
"share.title": "공유 {shareId}", "share.title": "공유 {shareId}",
"share.description": "내가 당신과 공유한 것을 보세요!", "share.description": "내가 당신과 공유한 것을 보세요!",
"share.error.visitor-limit-exceeded.title": "방문자 한 초과", "share.error.visitor-limit-exceeded.title": "방문자 한 초과",
"share.error.visitor-limit-exceeded.description": "The visitor limit from this share has been exceeded.", "share.error.visitor-limit-exceeded.description": "이 공유의 방문자 한도를 초과했습니다.",
"share.error.removed.title": "공유가 삭제됨", "share.error.removed.title": "공유가 삭제됨",
"share.error.not-found.title": "공유를 찾을 수 없습니다.", "share.error.not-found.title": "공유를 찾을 수 없습니다.",
"share.error.not-found.description": "당신이 찾는 공유는 존재하지 않습니다.", "share.error.not-found.description": "당신이 찾는 공유는 존재하지 않습니다.",
"share.modal.password.title": "비밀번호 필요", "share.modal.password.title": "비밀번호 필요",
"share.modal.password.description": "이 공유에 액세스하려면 공유의 암호를 입력하십시오.", "share.modal.password.description": "이 공유에 접근하려면 공유의 암호를 입력하십시오.",
"share.modal.password": "비밀번호", "share.modal.password": "비밀번호",
"share.modal.error.invalid-password": "잘못된 비밀번호", "share.modal.error.invalid-password": "잘못된 비밀번호",
"share.button.download-all": "모두 다운로드", "share.button.download-all": "모두 다운로드",
@@ -289,13 +289,13 @@ export default {
"admin.config.category.smtp": "SMTP", "admin.config.category.smtp": "SMTP",
"admin.config.category.oauth": "소셜 로그인", "admin.config.category.oauth": "소셜 로그인",
"admin.config.general.app-name": "앱 이름", "admin.config.general.app-name": "앱 이름",
"admin.config.general.app-name.description": "Name of the application", "admin.config.general.app-name.description": "이 앱의 이름",
"admin.config.general.app-url": "앱 URL", "admin.config.general.app-url": "앱 URL",
"admin.config.general.app-url.description": "Pingvin Share를 사용할 수 있는 URL", "admin.config.general.app-url.description": "Pingvin Share를 사용할 수 있는 URL",
"admin.config.general.show-home-page": "홈 페이지 표시", "admin.config.general.show-home-page": "홈 페이지 표시",
"admin.config.general.show-home-page.description": "홈 페이지를 표시할지 여부를 점검하십시오.", "admin.config.general.show-home-page.description": "홈 페이지를 표시할지 여부",
"admin.config.general.session-duration": "Session Duration", "admin.config.general.session-duration": "세션 기간",
"admin.config.general.session-duration.description": "Time in hours after which a user must log in again (default: 3 months).", "admin.config.general.session-duration.description": "사용자가 다시 로그인해야 하는 시간 (기본값: 3개월)",
"admin.config.general.logo": "로고", "admin.config.general.logo": "로고",
"admin.config.general.logo.description": "새 이미지를 업로드하여 로고를 변경하십시오. 이미지는 PNG여야 하며 1:1 비율이어야 합니다.", "admin.config.general.logo.description": "새 이미지를 업로드하여 로고를 변경하십시오. 이미지는 PNG여야 하며 1:1 비율이어야 합니다.",
"admin.config.general.logo.placeholder": "이미지 선택", "admin.config.general.logo.placeholder": "이미지 선택",
@@ -305,9 +305,9 @@ export default {
"admin.config.email.share-recipients-subject.description": "공유 수신자에게 전송되는 이메일의 제목입니다.", "admin.config.email.share-recipients-subject.description": "공유 수신자에게 전송되는 이메일의 제목입니다.",
"admin.config.email.share-recipients-message": "수신자 메시지 공유", "admin.config.email.share-recipients-message": "수신자 메시지 공유",
"admin.config.email.share-recipients-message.description": "공유 수신자에게 보내는 메시지입니다. 사용 가능한 변수:\n {creator} - 공유 작성자의 사용자 이름\n {shareUrl} - 공유의 URL\n {desc} - 공유에 대한 설명\n {expires} - 공유 만료일\n 변수는 실제 값으로 대체됩니다.", "admin.config.email.share-recipients-message.description": "공유 수신자에게 보내는 메시지입니다. 사용 가능한 변수:\n {creator} - 공유 작성자의 사용자 이름\n {shareUrl} - 공유의 URL\n {desc} - 공유에 대한 설명\n {expires} - 공유 만료일\n 변수는 실제 값으로 대체됩니다.",
"admin.config.email.reverse-share-subject": "역공유 제목", "admin.config.email.reverse-share-subject": "역방향 공유 제목",
"admin.config.email.reverse-share-subject.description": "누군가 당신이 공유한 역방향 공유 링크를 사용하여 공유를 생성했을 때 전송되는 이메일의 제목입니다.", "admin.config.email.reverse-share-subject.description": "누군가 당신이 공유한 역방향 공유 링크를 사용하여 공유를 생성했을 때 전송되는 이메일의 제목입니다.",
"admin.config.email.reverse-share-message": "역공유 메시지", "admin.config.email.reverse-share-message": "역방향 공유 메시지",
"admin.config.email.reverse-share-message.description": "누군가 귀하의 역방향 공유 링크를 사용하여 공유를 생성하면 전송되는 메시지입니다.. {shareUrl} 은 작성자 이름 및 공유 URL로 대체됩니다.", "admin.config.email.reverse-share-message.description": "누군가 귀하의 역방향 공유 링크를 사용하여 공유를 생성하면 전송되는 메시지입니다.. {shareUrl} 은 작성자 이름 및 공유 URL로 대체됩니다.",
"admin.config.email.reset-password-subject": "비밀번호 재설정 제목", "admin.config.email.reset-password-subject": "비밀번호 재설정 제목",
"admin.config.email.reset-password-subject.description": "사용자가 암호 재설정을 요청할 때 전송되는 메일의 제목입니다.", "admin.config.email.reset-password-subject.description": "사용자가 암호 재설정을 요청할 때 전송되는 메일의 제목입니다.",
@@ -321,16 +321,16 @@ export default {
"admin.config.share.allow-registration.description": "등록 가능 여부", "admin.config.share.allow-registration.description": "등록 가능 여부",
"admin.config.share.allow-unauthenticated-shares": "인증되지 않은 공유 허용", "admin.config.share.allow-unauthenticated-shares": "인증되지 않은 공유 허용",
"admin.config.share.allow-unauthenticated-shares.description": "인증되지 않은 사용자가 공유를 생성할 수 있는지 여부", "admin.config.share.allow-unauthenticated-shares.description": "인증되지 않은 사용자가 공유를 생성할 수 있는지 여부",
"admin.config.share.max-expiration": "최대 만료", "admin.config.share.max-expiration": "최대 만료 시간",
"admin.config.share.max-expiration.description": "최대 공유 만료 시간입니다. 만료 기간을 설정하지 않는경우 0으로 설정합니다.", "admin.config.share.max-expiration.description": "공유의 최대 만료 시간. 무제한 만료를 허용하려면 0으로 설정하세요.",
"admin.config.share.max-size": "최대 크기", "admin.config.share.max-size": "최대 크기",
"admin.config.share.max-size.description": "공유 최대 크기 - 바이트", "admin.config.share.max-size.description": "공유 최대 크기 (바이트)",
"admin.config.share.zip-compression-level": "압축 레벨", "admin.config.share.zip-compression-level": "Zip 압축 레벨",
"admin.config.share.zip-compression-level.description": "파일 크기와 압축 속도 간의 균형을 맞추도록 레벨을 조정합니다. 유효한 값의 범위는 0에서 9까지이며, 0은 압축되지 않고 9는 최대 압축입니다. ", "admin.config.share.zip-compression-level.description": "파일 크기와 압축 속도 간의 균형을 맞추도록 레벨을 조정합니다. 유효한 값의 범위는 0에서 9까지이며, 0은 압축되지 않고 9는 최대 압축입니다. ",
"admin.config.share.chunk-size": "청크 크기", "admin.config.share.chunk-size": "청크 크기",
"admin.config.share.chunk-size.description": "업로드할 청크 크기(바이트 단위)를 조정하여 인터넷 연결에 따라 효율성과 신뢰성의 균형을 유지합니다. 더 작은 청크는 불안정한 연결에 대한 성공률을 향상시킬 수 있고, 더 큰 청크는 안정적인 연결에 대한 업로드 속도를 높일 수 있습니다.", "admin.config.share.chunk-size.description": "업로드할 청크 크기(바이트 단위)를 조정하여 인터넷 연결에 따라 효율성과 신뢰성의 균형을 유지합니다. 더 작은 청크는 불안정한 연결에 대한 성공률을 향상시킬 수 있고, 더 큰 청크는 안정적인 연결에 대한 업로드 속도를 높일 수 있습니다.",
"admin.config.share.auto-open-share-modal": "Auto open create share modal", "admin.config.share.auto-open-share-modal": "공유 생성 창 자동 열기",
"admin.config.share.auto-open-share-modal.description": "The share creation modal automatically appears when a user selects files, eliminating the need to manually click the button.", "admin.config.share.auto-open-share-modal.description": "사용자가 파일을 선택하면 공유 생성 창이 자동으로 나타나서 버튼을 수동으로 클릭할 필요가 없습니다.",
"admin.config.smtp.enabled": "활성화됨", "admin.config.smtp.enabled": "활성화됨",
"admin.config.smtp.enabled.description": "SMTP 사용 여부 SMTP 서버의 호스트, 포트, 전자 메일, 사용자 및 암호를 입력한 경우에만 true로 설정합니다.", "admin.config.smtp.enabled.description": "SMTP 사용 여부 SMTP 서버의 호스트, 포트, 전자 메일, 사용자 및 암호를 입력한 경우에만 true로 설정합니다.",
"admin.config.smtp.host": "호스트", "admin.config.smtp.host": "호스트",