Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
511ae933fa | ||
|
|
df2521b192 | ||
|
|
8f16d6b53e | ||
|
|
3310fe53b3 | ||
|
|
adc4af996d | ||
|
|
61edc4f4f6 |
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,3 +1,15 @@
|
||||
## [1.1.3](https://github.com/stonith404/pingvin-share/compare/v1.1.2...v1.1.3) (2024-09-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* improve the LDAP implementation ([#615](https://github.com/stonith404/pingvin-share/issues/615)) ([3310fe5](https://github.com/stonith404/pingvin-share/commit/3310fe53b3e4c89db78d57ede6c8d57d8137ecc1)), closes [#601](https://github.com/stonith404/pingvin-share/issues/601)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* omit invalid username characters in oidc registration ([adc4af9](https://github.com/stonith404/pingvin-share/commit/adc4af996d30b295b06e4ee517aa53be62c0f6c1))
|
||||
|
||||
## [1.1.2](https://github.com/stonith404/pingvin-share/compare/v1.1.1...v1.1.2) (2024-09-24)
|
||||
|
||||
|
||||
|
||||
93
backend/package-lock.json
generated
93
backend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "pingvin-share-backend",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pingvin-share-backend",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"dependencies": {
|
||||
"@nestjs/cache-manager": "^2.2.2",
|
||||
"@nestjs/common": "^10.4.3",
|
||||
@@ -32,6 +32,7 @@
|
||||
"cookie-parser": "^1.4.6",
|
||||
"jmespath": "^0.16.0",
|
||||
"ldapjs": "^3.0.7",
|
||||
"ldapts": "^7.2.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"moment": "^2.30.1",
|
||||
"nanoid": "^3.3.7",
|
||||
@@ -1780,6 +1781,14 @@
|
||||
"@types/readdir-glob": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/asn1": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/asn1/-/asn1-0.2.4.tgz",
|
||||
"integrity": "sha512-V91DSJ2l0h0gRhVP4oBfBzRBN9lAbPUkGDMCnwedqPKX2d84aAMc9CulOvxdw1f7DfEYx99afab+Rsm3e52jhA==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/body-parser": {
|
||||
"version": "1.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
|
||||
@@ -2758,7 +2767,6 @@
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
@@ -3699,12 +3707,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dev": true,
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
@@ -5555,6 +5562,53 @@
|
||||
"node": ">=0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ldapts": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ldapts/-/ldapts-7.2.0.tgz",
|
||||
"integrity": "sha512-jFo3JI46nveXgILcEhUxR7N9it9d6gIooGAaem5OdXbXFjb6kIGdtI6FE2Y6SnT+XRvZvHy3diM5sdWzMsMK5w==",
|
||||
"dependencies": {
|
||||
"@types/asn1": ">=0.2.4",
|
||||
"asn1": "~0.2.6",
|
||||
"debug": "~4.3.7",
|
||||
"strict-event-emitter-types": "~2.0.0",
|
||||
"uuid": "~10.0.0",
|
||||
"whatwg-url": "~14.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/ldapts/node_modules/tr46": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz",
|
||||
"integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==",
|
||||
"dependencies": {
|
||||
"punycode": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/ldapts/node_modules/webidl-conversions": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/ldapts/node_modules/whatwg-url": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz",
|
||||
"integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==",
|
||||
"dependencies": {
|
||||
"tr46": "^5.0.0",
|
||||
"webidl-conversions": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/levn": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
||||
@@ -5882,9 +5936,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/mute-stream": {
|
||||
"version": "0.0.8",
|
||||
@@ -6767,10 +6821,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||
"dev": true,
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -7248,11 +7301,6 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/serialised-error": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/serialised-error/-/serialised-error-1.1.3.tgz",
|
||||
@@ -7511,6 +7559,11 @@
|
||||
"bare-events": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strict-event-emitter-types": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz",
|
||||
"integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA=="
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pingvin-share-backend",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"scripts": {
|
||||
"build": "nest build",
|
||||
"dev": "cross-env NODE_ENV=development nest start --watch",
|
||||
@@ -25,7 +25,6 @@
|
||||
"@nestjs/throttler": "^6.2.1",
|
||||
"@prisma/client": "^5.19.1",
|
||||
"@types/jmespath": "^0.15.2",
|
||||
"@types/ldapjs": "^3.0.6",
|
||||
"archiver": "^7.0.1",
|
||||
"argon2": "^0.41.1",
|
||||
"body-parser": "^1.20.3",
|
||||
@@ -36,7 +35,7 @@
|
||||
"content-disposition": "^0.5.4",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"jmespath": "^0.16.0",
|
||||
"ldapjs": "^3.0.7",
|
||||
"ldapts": "^7.2.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"moment": "^2.30.1",
|
||||
"nanoid": "^3.3.7",
|
||||
|
||||
@@ -178,6 +178,15 @@ const configVariables: ConfigVariables = {
|
||||
adminGroups: {
|
||||
type: "string",
|
||||
defaultValue: ""
|
||||
},
|
||||
|
||||
fieldNameMemberOf: {
|
||||
type: "string",
|
||||
defaultValue: "memberOf",
|
||||
},
|
||||
fieldNameEmail: {
|
||||
type: "string",
|
||||
defaultValue: "userPrincipalName",
|
||||
}
|
||||
},
|
||||
oauth: {
|
||||
|
||||
@@ -66,8 +66,9 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async signIn(dto: AuthSignInDTO, ip: string) {
|
||||
if (!dto.email && !dto.username)
|
||||
if (!dto.email && !dto.username) {
|
||||
throw new BadRequestException("Email or username is required");
|
||||
}
|
||||
|
||||
if (!this.config.get("oauth.disablePassword")) {
|
||||
const user = await this.prisma.user.findFirst({
|
||||
@@ -85,18 +86,22 @@ export class AuthService {
|
||||
}
|
||||
|
||||
if (this.config.get("ldap.enabled")) {
|
||||
this.logger.debug(`Trying LDAP login for user ${dto.username}`);
|
||||
/*
|
||||
* E-mail-like user credentials are passed as the email property
|
||||
* instead of the username. Since the username format does not matter
|
||||
* when searching for users in LDAP, we simply use the username
|
||||
* in whatever format it is provided.
|
||||
*/
|
||||
const ldapUsername = dto.username || dto.email;
|
||||
this.logger.debug(`Trying LDAP login for user ${ldapUsername}`);
|
||||
const ldapUser = await this.ldapService.authenticateUser(
|
||||
dto.username,
|
||||
ldapUsername,
|
||||
dto.password,
|
||||
);
|
||||
if (ldapUser) {
|
||||
const user = await this.userService.findOrCreateFromLDAP(
|
||||
dto.username,
|
||||
ldapUser,
|
||||
);
|
||||
const user = await this.userService.findOrCreateFromLDAP(dto, ldapUser);
|
||||
this.logger.log(
|
||||
`Successful LDAP login for user ${user.email} from IP ${ip}`,
|
||||
`Successful LDAP login for user ${ldapUsername} (${user.id}) from IP ${ip}`,
|
||||
);
|
||||
return this.generateToken(user);
|
||||
}
|
||||
|
||||
@@ -1,101 +1,7 @@
|
||||
import { Inject, Injectable, Logger } from "@nestjs/common";
|
||||
import * as ldap from "ldapjs";
|
||||
import {
|
||||
AttributeJson,
|
||||
InvalidCredentialsError,
|
||||
SearchCallbackResponse,
|
||||
SearchOptions,
|
||||
} from "ldapjs";
|
||||
import { inspect } from "node:util";
|
||||
import { ConfigService } from "../config/config.service";
|
||||
|
||||
type LdapSearchEntry = {
|
||||
objectName: string;
|
||||
attributes: AttributeJson[];
|
||||
};
|
||||
|
||||
async function ldapExecuteSearch(
|
||||
client: ldap.Client,
|
||||
base: string,
|
||||
options: SearchOptions,
|
||||
): Promise<LdapSearchEntry[]> {
|
||||
const searchResponse = await new Promise<SearchCallbackResponse>(
|
||||
(resolve, reject) => {
|
||||
client.search(base, options, (err, res) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(res);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return await new Promise<any[]>((resolve, reject) => {
|
||||
const entries: LdapSearchEntry[] = [];
|
||||
searchResponse.on("searchEntry", (entry) =>
|
||||
entries.push({
|
||||
attributes: entry.pojo.attributes,
|
||||
objectName: entry.pojo.objectName,
|
||||
}),
|
||||
);
|
||||
searchResponse.once("error", reject);
|
||||
searchResponse.once("end", () => resolve(entries));
|
||||
});
|
||||
}
|
||||
|
||||
async function ldapBindUser(
|
||||
client: ldap.Client,
|
||||
dn: string,
|
||||
password: string,
|
||||
): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
client.bind(dn, password, (error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function ldapCreateConnection(
|
||||
logger: Logger,
|
||||
url: string,
|
||||
): Promise<ldap.Client> {
|
||||
const ldapClient = ldap.createClient({
|
||||
url: url.split(","),
|
||||
connectTimeout: 10_000,
|
||||
timeout: 10_000,
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
ldapClient.once("error", reject);
|
||||
ldapClient.on("setupError", reject);
|
||||
ldapClient.on("socketTimeout", reject);
|
||||
ldapClient.on("connectRefused", () =>
|
||||
reject(new Error("connection has been refused")),
|
||||
);
|
||||
ldapClient.on("connectTimeout", () =>
|
||||
reject(new Error("connect timed out")),
|
||||
);
|
||||
ldapClient.on("connectError", reject);
|
||||
|
||||
ldapClient.on("connect", resolve);
|
||||
}).catch((error) => {
|
||||
logger.error(`Connect error: ${inspect(error)}`);
|
||||
ldapClient.destroy();
|
||||
throw error;
|
||||
});
|
||||
|
||||
return ldapClient;
|
||||
}
|
||||
|
||||
export type LdapAuthenticateResult = {
|
||||
userDn: string;
|
||||
attributes: Record<string, string[]>;
|
||||
};
|
||||
import { Client, Entry, InvalidCredentialsError } from "ldapts";
|
||||
|
||||
@Injectable()
|
||||
export class LdapService {
|
||||
@@ -105,40 +11,42 @@ export class LdapService {
|
||||
private readonly serviceConfig: ConfigService,
|
||||
) {}
|
||||
|
||||
private async createLdapConnection(): Promise<ldap.Client> {
|
||||
private async createLdapConnection(): Promise<Client> {
|
||||
const ldapUrl = this.serviceConfig.get("ldap.url");
|
||||
if (!ldapUrl) {
|
||||
throw new Error("LDAP server URL is not defined");
|
||||
}
|
||||
|
||||
const ldapClient = await ldapCreateConnection(this.logger, ldapUrl);
|
||||
try {
|
||||
const bindDn = this.serviceConfig.get("ldap.bindDn") || null;
|
||||
if (bindDn) {
|
||||
try {
|
||||
await ldapBindUser(
|
||||
ldapClient,
|
||||
bindDn,
|
||||
this.serviceConfig.get("ldap.bindPassword"),
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.warn(`Failed to bind to default user: ${error}`);
|
||||
throw new Error("failed to bind to default user");
|
||||
}
|
||||
}
|
||||
const ldapClient = new Client({
|
||||
url: ldapUrl,
|
||||
timeout: 15_000,
|
||||
connectTimeout: 15_000,
|
||||
});
|
||||
|
||||
return ldapClient;
|
||||
} catch (error) {
|
||||
ldapClient.destroy();
|
||||
throw error;
|
||||
const bindDn = this.serviceConfig.get("ldap.bindDn") || null;
|
||||
if (bindDn) {
|
||||
try {
|
||||
await ldapClient.bind(
|
||||
bindDn,
|
||||
this.serviceConfig.get("ldap.bindPassword"),
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.warn(`Failed to bind to default user: ${error}`);
|
||||
throw new Error("failed to bind to default user");
|
||||
}
|
||||
}
|
||||
|
||||
return ldapClient;
|
||||
}
|
||||
|
||||
public async authenticateUser(
|
||||
username: string,
|
||||
password: string,
|
||||
): Promise<LdapAuthenticateResult | null> {
|
||||
if (!username.match(/^[a-zA-Z0-0]+$/)) {
|
||||
): Promise<Entry | null> {
|
||||
if (!username.match(/^[a-zA-Z0-9-_.@]+$/)) {
|
||||
this.logger.verbose(
|
||||
`Username ${username} does not match username pattern. Authentication failed.`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -149,45 +57,48 @@ export class LdapService {
|
||||
|
||||
const ldapClient = await this.createLdapConnection();
|
||||
try {
|
||||
const [result] = await ldapExecuteSearch(ldapClient, searchBase, {
|
||||
const { searchEntries } = await ldapClient.search(searchBase, {
|
||||
filter: searchQuery,
|
||||
scope: "sub",
|
||||
|
||||
attributes: ["*"],
|
||||
returnAttributeValues: true,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
if (searchEntries.length > 1) {
|
||||
/* too many users found */
|
||||
this.logger.verbose(
|
||||
`Authentication for username ${username} failed. Too many users found with query ${searchQuery}`,
|
||||
);
|
||||
return null;
|
||||
} else if (searchEntries.length == 0) {
|
||||
/* user not found */
|
||||
this.logger.verbose(
|
||||
`Authentication for username ${username} failed. No user found with query ${searchQuery}`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const targetEntity = searchEntries[0];
|
||||
this.logger.verbose(
|
||||
`Trying to authenticate ${username} against LDAP user ${targetEntity.dn}`,
|
||||
);
|
||||
try {
|
||||
await ldapBindUser(ldapClient, result.objectName, password);
|
||||
|
||||
/*
|
||||
* In theory we could query the user attributes now,
|
||||
* but as we must query the user attributes for validation anyways
|
||||
* we'll create a second ldap server connection.
|
||||
*/
|
||||
return {
|
||||
userDn: result.objectName,
|
||||
attributes: Object.fromEntries(
|
||||
result.attributes.map((attribute) => [
|
||||
attribute.type,
|
||||
attribute.values,
|
||||
]),
|
||||
),
|
||||
};
|
||||
await ldapClient.bind(targetEntity.dn, password);
|
||||
return targetEntity;
|
||||
} catch (error) {
|
||||
if (error instanceof InvalidCredentialsError) {
|
||||
this.logger.verbose(
|
||||
`Failed to authenticate ${username} against ${targetEntity.dn}. Invalid credentials.`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
this.logger.warn(`LDAP user bind failure: ${inspect(error)}`);
|
||||
this.logger.warn(`User bind failure: ${inspect(error)}`);
|
||||
return null;
|
||||
} finally {
|
||||
ldapClient.destroy();
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.warn(`LDAP connect error: ${inspect(error)}`);
|
||||
this.logger.warn(`Connect error: ${inspect(error)}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { LogLevel } from "@nestjs/common";
|
||||
|
||||
export const DATA_DIRECTORY = process.env.DATA_DIRECTORY || "./data";
|
||||
export const SHARE_DIRECTORY = `${DATA_DIRECTORY}/uploads/shares`;
|
||||
export const DATABASE_URL =
|
||||
@@ -7,3 +9,7 @@ export const CLAMAV_HOST =
|
||||
process.env.CLAMAV_HOST ||
|
||||
(process.env.NODE_ENV == "docker" ? "clamav" : "127.0.0.1");
|
||||
export const CLAMAV_PORT = parseInt(process.env.CLAMAV_PORT) || 3310;
|
||||
|
||||
export const LOG_LEVEL_AVAILABLE: LogLevel[] = ['verbose', 'debug', 'log', 'warn', 'error', 'fatal'];
|
||||
export const LOG_LEVEL_DEFAULT: LogLevel = process.env.NODE_ENV === 'development' ? "verbose" : "log";
|
||||
export const LOG_LEVEL_ENV = `${process.env.PV_LOG_LEVEL || ""}`;
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
ClassSerializerInterceptor,
|
||||
Logger,
|
||||
LogLevel,
|
||||
ValidationPipe,
|
||||
} from "@nestjs/common";
|
||||
import { NestFactory, Reflector } from "@nestjs/core";
|
||||
@@ -12,10 +13,35 @@ import { NextFunction, Request, Response } from "express";
|
||||
import * as fs from "fs";
|
||||
import { AppModule } from "./app.module";
|
||||
import { ConfigService } from "./config/config.service";
|
||||
import { DATA_DIRECTORY } from "./constants";
|
||||
import {
|
||||
DATA_DIRECTORY,
|
||||
LOG_LEVEL_AVAILABLE,
|
||||
LOG_LEVEL_DEFAULT,
|
||||
LOG_LEVEL_ENV,
|
||||
} from "./constants";
|
||||
|
||||
function generateNestJsLogLevels(): LogLevel[] {
|
||||
if (LOG_LEVEL_ENV) {
|
||||
const levelIndex = LOG_LEVEL_AVAILABLE.indexOf(LOG_LEVEL_ENV as any);
|
||||
if (levelIndex === -1) {
|
||||
throw new Error(`log level ${LOG_LEVEL_ENV} unknown`);
|
||||
}
|
||||
|
||||
return LOG_LEVEL_AVAILABLE.slice(levelIndex, LOG_LEVEL_AVAILABLE.length);
|
||||
} else {
|
||||
const levelIndex = LOG_LEVEL_AVAILABLE.indexOf(LOG_LEVEL_DEFAULT);
|
||||
return LOG_LEVEL_AVAILABLE.slice(levelIndex, LOG_LEVEL_AVAILABLE.length);
|
||||
}
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create<NestExpressApplication>(AppModule);
|
||||
const logLevels = generateNestJsLogLevels();
|
||||
Logger.log(`Showing ${logLevels.join(", ")} messages`);
|
||||
|
||||
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
|
||||
logger: logLevels,
|
||||
});
|
||||
|
||||
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
|
||||
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
|
||||
|
||||
|
||||
@@ -108,8 +108,10 @@ export class OAuthService {
|
||||
}
|
||||
|
||||
private async getAvailableUsername(preferredUsername: string) {
|
||||
// only remove + and - from preferred username for now (maybe not enough)
|
||||
let username = preferredUsername.replace(/[+-]/g, "").substring(0, 20);
|
||||
// Only keep letters, numbers, dots, and underscores. Truncate to 20 characters.
|
||||
let username = preferredUsername
|
||||
.replace(/[^a-zA-Z0-9._]/g, "")
|
||||
.substring(0, 20);
|
||||
while (true) {
|
||||
const user = await this.prisma.user.findFirst({
|
||||
where: {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { DATABASE_URL } from "../constants";
|
||||
|
||||
@Injectable()
|
||||
export class PrismaService extends PrismaClient {
|
||||
private readonly logger = new Logger(PrismaService.name);
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
datasources: {
|
||||
@@ -12,6 +14,6 @@ export class PrismaService extends PrismaClient {
|
||||
},
|
||||
},
|
||||
});
|
||||
super.$connect().then(() => console.info("Connected to the database"));
|
||||
super.$connect().then(() => this.logger.log("Connected to the database"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BadRequestException, Injectable } from "@nestjs/common";
|
||||
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
||||
import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
|
||||
import * as argon from "argon2";
|
||||
import * as crypto from "crypto";
|
||||
@@ -8,10 +8,14 @@ import { FileService } from "../file/file.service";
|
||||
import { CreateUserDTO } from "./dto/createUser.dto";
|
||||
import { UpdateUserDto } from "./dto/updateUser.dto";
|
||||
import { ConfigService } from "../config/config.service";
|
||||
import { LdapAuthenticateResult } from "../auth/ldap.service";
|
||||
import { Entry } from "ldapts";
|
||||
import { AuthSignInDTO } from "src/auth/dto/authSignIn.dto";
|
||||
import { inspect } from "util";
|
||||
|
||||
@Injectable()
|
||||
export class UserSevice {
|
||||
private readonly logger = new Logger(UserSevice.name);
|
||||
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private emailService: EmailService,
|
||||
@@ -92,33 +96,114 @@ export class UserSevice {
|
||||
return await this.prisma.user.delete({ where: { id } });
|
||||
}
|
||||
|
||||
async findOrCreateFromLDAP(username: string, ldap: LdapAuthenticateResult) {
|
||||
const passwordHash = await argon.hash(crypto.randomUUID());
|
||||
const userEmail =
|
||||
ldap.attributes["userPrincipalName"]?.at(0) ??
|
||||
`${crypto.randomUUID()}@ldap.local`;
|
||||
const adminGroup = this.configService.get("ldap.adminGroups");
|
||||
const isAdmin = ldap.attributes["memberOf"]?.includes(adminGroup) ?? false;
|
||||
async findOrCreateFromLDAP(
|
||||
providedCredentials: AuthSignInDTO,
|
||||
ldapEntry: Entry,
|
||||
) {
|
||||
const fieldNameMemberOf = this.configService.get("ldap.fieldNameMemberOf");
|
||||
const fieldNameEmail = this.configService.get("ldap.fieldNameEmail");
|
||||
|
||||
let isAdmin = false;
|
||||
if (fieldNameMemberOf in ldapEntry) {
|
||||
const adminGroup = this.configService.get("ldap.adminGroups");
|
||||
const entryGroups = Array.isArray(ldapEntry[fieldNameMemberOf])
|
||||
? ldapEntry[fieldNameMemberOf]
|
||||
: [ldapEntry[fieldNameMemberOf]];
|
||||
isAdmin = entryGroups.includes(adminGroup) ?? false;
|
||||
} else {
|
||||
this.logger.warn(
|
||||
`Trying to create/update a ldap user but the member field ${fieldNameMemberOf} is not present.`,
|
||||
);
|
||||
}
|
||||
|
||||
let userEmail: string | null = null;
|
||||
if (fieldNameEmail in ldapEntry) {
|
||||
const value = Array.isArray(ldapEntry[fieldNameEmail])
|
||||
? ldapEntry[fieldNameEmail][0]
|
||||
: ldapEntry[fieldNameEmail];
|
||||
if (value) {
|
||||
userEmail = value.toString();
|
||||
}
|
||||
} else {
|
||||
this.logger.warn(
|
||||
`Trying to create/update a ldap user but the email field ${fieldNameEmail} is not present.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (providedCredentials.email) {
|
||||
/* if LDAP does not provides an users email address, take the user provided email address instead */
|
||||
userEmail = providedCredentials.email;
|
||||
}
|
||||
|
||||
const randomId = crypto.randomUUID();
|
||||
const placeholderUsername = `ldap_user_${randomId}`;
|
||||
const placeholderEMail = `${randomId}@ldap.local`;
|
||||
|
||||
try {
|
||||
return await this.prisma.user.upsert({
|
||||
const user = await this.prisma.user.upsert({
|
||||
create: {
|
||||
username,
|
||||
email: userEmail,
|
||||
password: passwordHash,
|
||||
isAdmin,
|
||||
ldapDN: ldap.userDn,
|
||||
},
|
||||
update: {
|
||||
username,
|
||||
email: userEmail,
|
||||
username: providedCredentials.username ?? placeholderUsername,
|
||||
email: userEmail ?? placeholderEMail,
|
||||
password: await argon.hash(crypto.randomUUID()),
|
||||
|
||||
isAdmin,
|
||||
ldapDN: ldap.userDn,
|
||||
ldapDN: ldapEntry.dn,
|
||||
},
|
||||
update: {
|
||||
isAdmin,
|
||||
ldapDN: ldapEntry.dn,
|
||||
},
|
||||
where: {
|
||||
ldapDN: ldap.userDn,
|
||||
ldapDN: ldapEntry.dn,
|
||||
},
|
||||
});
|
||||
|
||||
if (user.username === placeholderUsername) {
|
||||
/* Give the user a human readable name if the user has been created with a placeholder username */
|
||||
await this.prisma.user
|
||||
.update({
|
||||
where: {
|
||||
id: user.id,
|
||||
},
|
||||
data: {
|
||||
username: `user_${user.id}`,
|
||||
},
|
||||
})
|
||||
.then((newUser) => {
|
||||
user.username = newUser.username;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.logger.warn(
|
||||
`Failed to update users ${user.id} placeholder username: ${inspect(error)}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (userEmail && userEmail !== user.email) {
|
||||
/* Sync users email if it has changed */
|
||||
await this.prisma.user
|
||||
.update({
|
||||
where: {
|
||||
id: user.id,
|
||||
},
|
||||
data: {
|
||||
email: userEmail,
|
||||
},
|
||||
})
|
||||
.then((newUser) => {
|
||||
this.logger.log(
|
||||
`Updated users ${user.id} email from ldap from ${user.email} to ${userEmail}.`,
|
||||
);
|
||||
user.email = newUser.email;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.logger.error(
|
||||
`Failed to update users ${user.id} email to ${userEmail}: ${inspect(error)}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return user;
|
||||
} catch (e) {
|
||||
if (e instanceof PrismaClientKnownRequestError) {
|
||||
if (e.code == "P2002") {
|
||||
|
||||
@@ -56,6 +56,11 @@ const sidebars: SidebarsConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
label: "Demo",
|
||||
href: "https://pingvin-share.dev.eliasschneider.com",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
label: "Discord",
|
||||
|
||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "pingvin-share-frontend",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pingvin-share-frontend",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/server": "^11.11.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pingvin-share-frontend",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
|
||||
@@ -80,9 +80,7 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
|
||||
useState(false);
|
||||
|
||||
const validationSchema = yup.object().shape({
|
||||
emailOrUsername: config.get("ldap.enabled")
|
||||
? yup.string().matches(/^[^@]+$/, t("signIn.error.invalid-username"))
|
||||
: yup.string().required(t("common.error.field-required")),
|
||||
emailOrUsername: yup.string().required(t("common.error.field-required")),
|
||||
password: yup
|
||||
.string()
|
||||
.min(8, t("common.error.too-short", { length: 8 }))
|
||||
@@ -174,16 +172,8 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
|
||||
})}
|
||||
>
|
||||
<TextInput
|
||||
label={
|
||||
config.get("ldap.enabled")
|
||||
? t("signup.input.username")
|
||||
: t("signin.input.email-or-username")
|
||||
}
|
||||
placeholder={
|
||||
config.get("ldap.enabled")
|
||||
? t("signup.input.username.placeholder")
|
||||
: t("signin.input.email-or-username.placeholder")
|
||||
}
|
||||
label={t("signin.input.email-or-username")}
|
||||
placeholder={t("signin.input.email-or-username.placeholder")}
|
||||
{...form.getInputProps("emailOrUsername")}
|
||||
/>
|
||||
<PasswordInput
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "أنشئ حسابًا",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "هذه الصفحة غير موجودة.",
|
||||
"404.button.home": "أعدني للصفحة الرئيسية",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Create an account",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Oops this page doesn't exist.",
|
||||
"404.button.home": "Bring me back home",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Vytvořit účet",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Skupina potřebná pro administrativní přístup.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Jejda, tato stránka neexistuje.",
|
||||
"404.button.home": "Bring me back home",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Opret en bruger",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Ups! Denne side findes ikke.",
|
||||
"404.button.home": "Gå tilbage",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Erstelle ein Konto",
|
||||
@@ -132,7 +131,7 @@ export default {
|
||||
// END /account/shares
|
||||
// /account/reverseShares
|
||||
"account.reverseShares.title": "Externe Freigaben",
|
||||
"account.reverseShares.description": "Eine externe Freigabe erlaubt dir eine einzigartige URL zu erstellen, die externen Benutzern erlaubt Dateien hochzuladen.",
|
||||
"account.reverseShares.description": "Eine externe Freigabe erlaubt dir eine einzigartige URL zu erstellen, die externen Benutzern erlaubt, Dateien hochzuladen.",
|
||||
"account.reverseShares.title.empty": "Es ist leer hier 👀",
|
||||
"account.reverseShares.description.empty": "Du hast keine externen Freigaben erstellt.",
|
||||
// showCreateReverseShareModal.tsx
|
||||
@@ -156,7 +155,7 @@ export default {
|
||||
"account.reverseShares.modal.simplified": "Einfacher Modus",
|
||||
"account.reverseShares.modal.simplified.description": "Mache es der Person einfach, die die Datei hochlädt, sie mit Dir zu teilen. Sie können nur den Namen und die Beschreibung der Freigabe ändern.",
|
||||
"account.reverseShares.modal.public-access": "Öffentlicher Zugriff",
|
||||
"account.reverseShares.modal.public-access.description": "Make the created shares with this reverse share public. If disabled, only you and the creator of the share can view it.",
|
||||
"account.reverseShares.modal.public-access.description": "Mache die erstellten Freigaben mit dieser Rückfreigabe öffentlich. Wenn deaktiviert, kannst nur du und der Ersteller der Freigaben diese anzeigen.",
|
||||
"account.reverseShares.modal.max-use.label": "Maximale Nutzungen",
|
||||
"account.reverseShares.modal.max-use.description": "Die maximale Anzahl von Verwendungen der URL, um Dateien hochzuladen.",
|
||||
"account.reverseShare.never-expires": "Diese externe Freigabe wird nicht ablaufen.",
|
||||
@@ -325,7 +324,7 @@ export default {
|
||||
"admin.config.email.invite-subject": "Betreff für Einladung",
|
||||
"admin.config.email.invite-subject.description": "Betreff der E-Mail, die gesendet wird, wenn ein Administrator einen Benutzer einlädt.",
|
||||
"admin.config.email.invite-message": "Nachricht für Einladung",
|
||||
"admin.config.email.invite-message.description": "Message which gets sent when an admin invites a user. {url} will be replaced with the invite URL, {email} with the email and {password} with the password of the user.",
|
||||
"admin.config.email.invite-message.description": "Nachricht welche gesendet wird, wenn der Administrator einen Nutzer einlädt. {url} wird ersetzt durch die Einladung URL, {email} mit der die E-Mail und {password} mit dem Passwort des Benutzers.",
|
||||
"admin.config.share.allow-registration": "Registrierung erlauben",
|
||||
"admin.config.share.allow-registration.description": "Gibt an, ob eine Registrierung erlaubt ist",
|
||||
"admin.config.share.allow-unauthenticated-shares": "Nicht authentifizierte Freigaben erlauben",
|
||||
@@ -417,9 +416,13 @@ export default {
|
||||
"admin.config.ldap.search-base": "User base",
|
||||
"admin.config.ldap.search-base.description": "Base location, where the user search will be performed",
|
||||
"admin.config.ldap.search-query": "User query",
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.search-query.description": "Die Benutzer Abfrage wird in der \"Benutzerdatenbank\" gesucht für den LDAP Benutzer. %username% kann als Platzhalter für den Benutzer eingegeben werden.",
|
||||
"admin.config.ldap.admin-groups": "Administratorengruppe",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.admin-groups.description": "Gruppe benötigt für den Administrations Zugang.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Ups, diese Seite existiert nicht.",
|
||||
"404.button.home": "Zurück zur Startseite",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Δημιουργία λογαριασμού",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Ουπς. Αυτή η σελίδα δεν υπάρχει.",
|
||||
"404.button.home": "Πήγαινέ με πίσω",
|
||||
|
||||
@@ -50,7 +50,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
|
||||
// END /auth/signin
|
||||
|
||||
@@ -586,6 +585,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
|
||||
// 404
|
||||
"404.description": "Oops this page doesn't exist.",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Crear una cuenta",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Oops esta página no existe.",
|
||||
"404.button.home": "Regrésame al inicio",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Rekisteröidy",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Hups tätä sivua ei ole olemassa.",
|
||||
"404.button.home": "Tuo minut takaisin kotiin",
|
||||
|
||||
@@ -24,10 +24,10 @@ export default {
|
||||
// END /
|
||||
// /auth/signin
|
||||
"signin.title": "Content de vous revoir",
|
||||
"signin.description": "Pas encore de compte ?",
|
||||
"signin.description": "Vous n'avez pas encore de compte ?",
|
||||
"signin.button.signup": "S’inscrire",
|
||||
"signin.input.email-or-username": "Courriel ou surnom",
|
||||
"signin.input.email-or-username.placeholder": "Votre courriel ou surnom",
|
||||
"signin.input.email-or-username": "Courriel ou nom d'utilisateur",
|
||||
"signin.input.email-or-username.placeholder": "Votre courriel ou nom d'utilisateur",
|
||||
"signin.input.password": "Mot de passe",
|
||||
"signin.input.password.placeholder": "Votre mot de passe",
|
||||
"signin.button.submit": "Se connecter",
|
||||
@@ -40,14 +40,13 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Créer un compte",
|
||||
"signup.description": "Vous avez déjà un compte ?",
|
||||
"signup.button.signin": "Se connecter",
|
||||
"signup.input.username": "Surnom",
|
||||
"signup.input.username.placeholder": "Votre surnom",
|
||||
"signup.input.username": "Nom d'utilisateur",
|
||||
"signup.input.username.placeholder": "Votre nom d'utilisateur",
|
||||
"signup.input.email": "Adresse email",
|
||||
"signup.input.email.placeholder": "Votre courriel",
|
||||
"signup.button.submit": "Commençons",
|
||||
@@ -325,7 +324,7 @@ export default {
|
||||
"admin.config.email.invite-subject": "Sujet d’une invitation",
|
||||
"admin.config.email.invite-subject.description": "Intitulé du courriel envoyé lorsqu’un administrateur invite un utilisateur.",
|
||||
"admin.config.email.invite-message": "Message d’une invitation",
|
||||
"admin.config.email.invite-message.description": "Message which gets sent when an admin invites a user. {url} will be replaced with the invite URL, {email} with the email and {password} with the password of the user.",
|
||||
"admin.config.email.invite-message.description": "Message qui est envoyé lorsqu'un administrateur invite un utilisateur. {url} sera remplacé avec l'URL d'invitation, {email} avec le courriel et {password} avec le mot de passe de l'utilisateur.",
|
||||
"admin.config.share.allow-registration": "Autoriser les inscriptions",
|
||||
"admin.config.share.allow-registration.description": "Permet aux visiteurs de créer un compte.",
|
||||
"admin.config.share.allow-unauthenticated-shares": "Autoriser les partages anonymes",
|
||||
@@ -419,7 +418,11 @@ export default {
|
||||
"admin.config.ldap.search-query": "Requête utilisateur",
|
||||
"admin.config.ldap.search-query.description": "La requête utilisateur sera utilisée pour rechercher dans la ‘base d'utilisateurs’ de l'utilisateur LDAP. %username% peut être utilisé comme espace réservé pour les entrées données par l'utilisateur.",
|
||||
"admin.config.ldap.admin-groups": "Groupe administrateur",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.admin-groups.description": "Un groupe est nécessaire pour un accès administratif.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Désolé, mais cette page n’existe pas.",
|
||||
"404.button.home": "Retour à l’accueil",
|
||||
@@ -457,7 +460,7 @@ export default {
|
||||
"common.text.link": "Lien",
|
||||
"common.text.navigate-to-link": "Accéder au lien",
|
||||
"common.text.or": "ou",
|
||||
"common.text.redirecting": "Redirecting...",
|
||||
"common.text.redirecting": "Redirection...",
|
||||
"common.button.go-back": "Précédent",
|
||||
"common.button.go-home": "Accueil",
|
||||
"common.notify.copied": "Votre lien a été copié dans le presse-papiers",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Fiók létrehozása",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "A felhasználó lekérdezés kísérli meg az LDAP felhasználó elérését a felhasználóbázisban. %username% helyettesítheti az adott felhasználónevet.",
|
||||
"admin.config.ldap.admin-groups": "Admin csoport",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Hoppá - ez az oldal nem létezik.",
|
||||
"404.button.home": "Vissza a Kezdőlapra",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Nome utente non valido",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Crea un account",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "La query utente verrà utilizzata per cercare la 'base utente' per l'utente LDAP. %username% può essere usato come segnaposto per l'input dato dall'utente.",
|
||||
"admin.config.ldap.admin-groups": "Gruppo di amministrazione",
|
||||
"admin.config.ldap.admin-groups.description": "Gruppo richiesto per l’accesso amministrativo.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Ops, questa pagina non esiste.",
|
||||
"404.button.home": "Riportami a casa",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "アカウントを作成",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "ユーザークエリはLDAPユーザーの「ユーザーベース」を検索するために使用されます。 %username% は、入力されたユーザーのプレースホルダとして使用できます。",
|
||||
"admin.config.ldap.admin-groups": "管理者グループ",
|
||||
"admin.config.ldap.admin-groups.description": "管理者アクセスに必要なグループです。",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "ページが見つかりません。",
|
||||
"404.button.home": "ホームに戻る",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "마이크로소프트",
|
||||
"signIn.oauth.discord": "디스코드",
|
||||
"signIn.oauth.oidc": "오픈ID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "계정 만들기",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "이런, 이 페이지는 존재하지 않습니다.",
|
||||
"404.button.home": "나를 집으로 데려다 줘",
|
||||
|
||||
@@ -34,13 +34,12 @@ export default {
|
||||
"signIn.notify.totp-required.title": "Tweestapsverificatie vereist",
|
||||
"signIn.notify.totp-required.description": "Voer uw tweestapsverificatiecode in",
|
||||
"signIn.oauth.or": "OF",
|
||||
"signIn.oauth.signInWith": "Sign in with",
|
||||
"signIn.oauth.signInWith": "Registreer met",
|
||||
"signIn.oauth.github": "GitHub",
|
||||
"signIn.oauth.google": "Google",
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Account aanmaken",
|
||||
@@ -151,7 +150,7 @@ export default {
|
||||
"account.reverseShares.modal.expiration.year-singular": "Jaar",
|
||||
"account.reverseShares.modal.expiration.year-plural": "Jaren",
|
||||
"account.reverseShares.modal.max-size.label": "Maximale share-grootte",
|
||||
"account.reverseShares.modal.send-email": "Stuur e-mail notificatie",
|
||||
"account.reverseShares.modal.send-email": "Stuur e-mailnotificatie",
|
||||
"account.reverseShares.modal.send-email.description": "Stuur een e-mail notificatie wanneer er bestanden zijn gedeeld via deze omgekeerde share link.",
|
||||
"account.reverseShares.modal.simplified": "Simple mode",
|
||||
"account.reverseShares.modal.simplified.description": "Maak het makkelijk voor de persoon die het bestand uploadt om het met u te delen. Ze kunnen alleen de naam en beschrijving van de share aanpassen.",
|
||||
@@ -352,7 +351,7 @@ export default {
|
||||
"admin.config.smtp.username.description": "Gebruikersnaam van de SMTP-server",
|
||||
"admin.config.smtp.password": "Wachtwoord",
|
||||
"admin.config.smtp.password.description": "Wachtwoord van de SMTP-server",
|
||||
"admin.config.smtp.button.test": "Test e-mail verzenden",
|
||||
"admin.config.smtp.button.test": "Teste-mail verzenden",
|
||||
"admin.config.smtp.allow-unauthorized-certificates": "Vertrouw ongeautoriseerde SMTP-servercertificaten",
|
||||
"admin.config.smtp.allow-unauthorized-certificates.description": "Zet dit alleen aan als je de self signed certificates vertrouwt.",
|
||||
"admin.config.oauth.allow-registration": "Sta registratie toe",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin groep",
|
||||
"admin.config.ldap.admin-groups.description": "Groep vereist voor administratieve toegang.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Oeps, deze pagina bestaat niet.",
|
||||
"404.button.home": "Breng me terug naar huis",
|
||||
@@ -470,4 +473,4 @@ export default {
|
||||
"common.error.exact-length": "Moet precies {length} tekens bevatten",
|
||||
"common.error.invalid-number": "Moet een getal zijn",
|
||||
"common.error.field-required": "Dit veld is verplicht"
|
||||
};
|
||||
};
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Utwórz konto",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Ups! Ta strona nie istnieje.",
|
||||
"404.button.home": "Wróć do strony domowej",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Usuário inválido",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Criar uma conta",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "A consulta do usuário será usada para pesquisar a 'base de usuários' para o usuário LDAP. %username% pode ser usado como espaço reservado para o usuário fornecido na entrada.",
|
||||
"admin.config.ldap.admin-groups": "Grupo de administração",
|
||||
"admin.config.ldap.admin-groups.description": "Grupo necessário para acesso administrativo.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Ops, esta página não existe.",
|
||||
"404.button.home": "Me traga de volta para casa",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Создать аккаунт",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Упс, этой страницы не существует.",
|
||||
"404.button.home": "Верните меня домой",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Ustvarite račun",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Ups! Ta stran ne obstaja.",
|
||||
"404.button.home": "Pelji me domov",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Неважеће корисничко име",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Направи налог",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Опа - Ова страна не постоји.",
|
||||
"404.button.home": "Врати ме на почетак",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Skapa ett konto",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Hoppsan den här sidan finns inte.",
|
||||
"404.button.home": "Ta mig tillbaka hem",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "สมัครบัญชี",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "ไม่พบหน้าที่คุณกำลังมองหา",
|
||||
"404.button.home": "หน้าแรก",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Bir hesap oluştur",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Haydaa, böyle bir sayfa yok.",
|
||||
"404.button.home": "Beni eve götür",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Створити акаунт",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Бляха, цієї строрінки не існує.",
|
||||
"404.button.home": "Поверни мене додому",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Tạo tài khoản",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "Oops this page doesn't exist.",
|
||||
"404.button.home": "Về trang chủ",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "创建账户",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "将用于在 'User base'中搜索 LDAP 用户。%username% 可以作为用户输入的占位符。",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "当前的页面走丢啦",
|
||||
"404.button.home": "返回主页",
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
"signIn.oauth.microsoft": "Microsoft",
|
||||
"signIn.oauth.discord": "Discord",
|
||||
"signIn.oauth.oidc": "OpenID",
|
||||
"signIn.error.invalid-username": "Invalid username",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "建立帳號",
|
||||
@@ -420,6 +419,10 @@ export default {
|
||||
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
|
||||
"admin.config.ldap.admin-groups": "Admin group",
|
||||
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
|
||||
"admin.config.ldap.field-name-member-of": "User groups attribute name",
|
||||
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
|
||||
"admin.config.ldap.field-name-email": "User email attribute name",
|
||||
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
|
||||
// 404
|
||||
"404.description": "查無此頁",
|
||||
"404.button.home": "返回主頁",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Center,
|
||||
Container,
|
||||
@@ -142,6 +143,9 @@ const Account = () => {
|
||||
<Paper withBorder p="xl">
|
||||
<Title order={5} mb="xs">
|
||||
<FormattedMessage id="account.card.info.title" />
|
||||
{user?.isLdap ? (
|
||||
<Badge style={{ marginLeft: "1em" }}>LDAP</Badge>
|
||||
) : null}
|
||||
</Title>
|
||||
<form
|
||||
onSubmit={accountForm.onSubmit((values) =>
|
||||
@@ -162,13 +166,16 @@ const Account = () => {
|
||||
/>
|
||||
<TextInput
|
||||
label={t("account.card.info.email")}
|
||||
disabled={user?.isLdap}
|
||||
{...accountForm.getInputProps("email")}
|
||||
/>
|
||||
<Group position="right">
|
||||
<Button type="submit">
|
||||
<FormattedMessage id="common.button.save" />
|
||||
</Button>
|
||||
</Group>
|
||||
{!user?.isLdap && (
|
||||
<Group position="right">
|
||||
<Button type="submit">
|
||||
<FormattedMessage id="common.button.save" />
|
||||
</Button>
|
||||
</Group>
|
||||
)}
|
||||
</Stack>
|
||||
</form>
|
||||
</Paper>
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "pingvin-share",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pingvin-share",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"devDependencies": {
|
||||
"conventional-changelog-cli": "^3.0.0"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pingvin-share",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"scripts": {
|
||||
"format": "cd frontend && npm run format && cd ../backend && npm run format",
|
||||
"lint": "cd frontend && npm run lint && cd ../backend && npm run lint",
|
||||
|
||||
Reference in New Issue
Block a user