mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: ci@photoprism.app\n"
|
||||
"POT-Creation-Date: 2024-03-21 13:35+0000\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-03-22 14:48+0000\n"
|
||||
"PO-Revision-Date: 2024-04-03 10:23+0000\n"
|
||||
"Last-Translator: Admin <hello@photoprism.app>\n"
|
||||
"Language-Team: German <https://translate.photoprism.app/projects/photoprism/"
|
||||
|
||||
@@ -379,10 +379,10 @@ export default class Session {
|
||||
return LoginPage === window.location.href.substring(window.location.href.lastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
login(username, password, passcode, token) {
|
||||
login(username, password, code, token) {
|
||||
this.reset();
|
||||
|
||||
return Api.post("session", { username, password, passcode, token }).then((resp) => {
|
||||
return Api.post("session", { username, password, code, token }).then((resp) => {
|
||||
const reload = this.config.getLanguage() !== resp.data?.config?.settings?.ui?.language;
|
||||
this.setResp(resp);
|
||||
this.onLogin();
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
<v-card-text class="py-0 px-2">
|
||||
<v-layout wrap align-top>
|
||||
<v-flex xs12 class="pa-2 body-1">
|
||||
<translate>Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:</translate>
|
||||
<translate>Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:</translate>
|
||||
</v-flex>
|
||||
<v-flex xs12 class="pa-2">
|
||||
<img :src="key.QRCode" class="width-100" alt="QR Code" />
|
||||
@@ -81,9 +81,9 @@
|
||||
</v-flex>
|
||||
<v-flex xs12 class="pa-2">
|
||||
<v-text-field
|
||||
v-model="passcode"
|
||||
v-model="code"
|
||||
:disabled="busy"
|
||||
name="passcode"
|
||||
name="one-time-code"
|
||||
type="text"
|
||||
:label="$gettext('Verification Code')"
|
||||
mask="### ###"
|
||||
@@ -97,7 +97,7 @@
|
||||
autocapitalize="none"
|
||||
autocomplete="one-time-code"
|
||||
browser-autocomplete="one-time-code"
|
||||
class="input-passcode"
|
||||
class="input-code"
|
||||
color="secondary-dark"
|
||||
prepend-inner-icon="verified_user"
|
||||
@keyup.enter.native="onConfirm"
|
||||
@@ -110,7 +110,7 @@
|
||||
<v-btn depressed color="secondary-light" class="action-cancel ml-0" @click.stop="close">
|
||||
<translate>Cancel</translate>
|
||||
</v-btn>
|
||||
<v-btn depressed color="primary-button" class="action-confirm white--text compact mr-0" :disabled="passcode.length !== 6" @click.stop="onConfirm">
|
||||
<v-btn depressed color="primary-button" class="action-confirm white--text compact mr-0" :disabled="code.length !== 6" @click.stop="onConfirm">
|
||||
<translate>Confirm</translate>
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
@@ -122,7 +122,7 @@
|
||||
<v-card-text class="py-0 px-2">
|
||||
<v-layout wrap align-top>
|
||||
<v-flex xs12 class="pa-2 body-2">
|
||||
<translate>Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:</translate>
|
||||
<translate>Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:</translate>
|
||||
</v-flex>
|
||||
<v-flex xs12 class="pa-2">
|
||||
<v-text-field
|
||||
@@ -257,7 +257,7 @@ export default {
|
||||
busy: false,
|
||||
isDemo: this.$config.get("demo"),
|
||||
isPublic: this.$config.get("public"),
|
||||
passcode: "",
|
||||
code: "",
|
||||
password: "",
|
||||
recoveryCodeCopied: false,
|
||||
showPassword: false,
|
||||
@@ -312,7 +312,7 @@ export default {
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
this.passcode = "";
|
||||
this.code = "";
|
||||
this.password = "";
|
||||
this.showPassword = false;
|
||||
this.recoveryCodeCopied = false;
|
||||
@@ -339,19 +339,19 @@ export default {
|
||||
});
|
||||
},
|
||||
onConfirm() {
|
||||
if (this.busy || this.passcode === "") {
|
||||
if (this.busy || this.code === "") {
|
||||
return;
|
||||
}
|
||||
this.busy = true;
|
||||
this.model
|
||||
.confirmPasscode(this.passcode)
|
||||
.confirmPasscode(this.code)
|
||||
.then((resp) => {
|
||||
this.key = resp;
|
||||
this.$notify.success(this.$gettext("Successfully verified"));
|
||||
})
|
||||
.finally(() => {
|
||||
this.busy = false;
|
||||
this.passcode = "";
|
||||
this.code = "";
|
||||
this.password = "";
|
||||
this.showPassword = false;
|
||||
this.recoveryCodeCopied = false;
|
||||
|
||||
@@ -524,7 +524,7 @@ msgstr "Verander privaat vlag"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -712,7 +712,7 @@ msgid "Debug Logs"
|
||||
msgstr "Ontfout logs"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2183,7 +2183,7 @@ msgid "Scan"
|
||||
msgstr "Skandeer"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2256,7 +2256,7 @@ msgstr "Diens-URL"
|
||||
msgid "Services"
|
||||
msgstr "Dienste"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sessie"
|
||||
|
||||
@@ -2611,7 +2611,8 @@ msgid "Unknown"
|
||||
msgstr "Onbekend"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Ongeregistreer"
|
||||
|
||||
@@ -2640,7 +2641,7 @@ msgstr "Dateer indeks op"
|
||||
msgid "Updating moments"
|
||||
msgstr "Dateer tans oomblikke op"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Dateer tans prent op …"
|
||||
|
||||
@@ -2721,7 +2722,7 @@ msgid "Use Presets"
|
||||
msgstr "Gebruik voorafinstellings"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "تغيير العلم الخاص"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "سجلات التصحيح"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "مسح"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL الخدمة"
|
||||
msgid "Services"
|
||||
msgstr "خدمات"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "حصة"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "مجهول"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "غير مسجل"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "تحديث الفهرس"
|
||||
msgid "Updating moments"
|
||||
msgstr "تحديث اللحظات"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "جارٍ تحديث الصورة ..."
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "استخدم الإعدادات المسبقة"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -524,7 +524,7 @@ msgstr "Змяніць прыватны тэг"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -712,7 +712,7 @@ msgid "Debug Logs"
|
||||
msgstr "Журналы адладкі"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2183,7 +2183,7 @@ msgid "Scan"
|
||||
msgstr "сканаваць"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2256,7 +2256,7 @@ msgstr "URL службы"
|
||||
msgid "Services"
|
||||
msgstr "Сэрвісы"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "сесія"
|
||||
|
||||
@@ -2611,7 +2611,8 @@ msgid "Unknown"
|
||||
msgstr "Невядомы"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Незарэгістраваны"
|
||||
|
||||
@@ -2640,7 +2641,7 @@ msgstr "Абнаўленне індэкса"
|
||||
msgid "Updating moments"
|
||||
msgstr "Абнаўленне момантаў"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Абнаўленне выявы…"
|
||||
|
||||
@@ -2721,7 +2722,7 @@ msgid "Use Presets"
|
||||
msgstr "Выкарыстоўвайце налады"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Промяна на частния флаг"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Протоколи за отработване"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Сканиране"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL адрес на услугата"
|
||||
msgid "Services"
|
||||
msgstr "URL адрес на услугата"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Сесия"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Неизвестно"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Нерегистриран"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Актуализиране на индекса"
|
||||
msgid "Updating moments"
|
||||
msgstr "Актуализиране на моменти"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Актуализиране на визуализациите"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Използване на предварителни настройки"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Canvia la bandera privada"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Registres de depuració"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Escaneig"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL del servei"
|
||||
msgid "Services"
|
||||
msgstr "Serveis"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sessió"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Desconegut"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "No registrat"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "S'està actualitzant l'índex"
|
||||
msgid "Updating moments"
|
||||
msgstr "Moments d'actualització"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "S'està actualitzant la imatge..."
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Utilitzeu presets"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Změnit soukromou vlaječku"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Protokoly ladění"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Sken"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL služby"
|
||||
msgid "Services"
|
||||
msgstr "Služby"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Relace"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Neznámé"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Neregistrovaný"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Aktualizace indexace"
|
||||
msgid "Updating moments"
|
||||
msgstr "Aktualizace okamžiků"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Aktualizace obrázku…"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Použít předvolby"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Ændre privat flag"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Fejlfindingslog"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Scan"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "Service-URL"
|
||||
msgid "Services"
|
||||
msgstr "Tjenester"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Session"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Ukendt"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Uregistreret"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Opdaterer indeks"
|
||||
msgid "Updating moments"
|
||||
msgstr "Opdaterer øjeblikke"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Opdatering af billede…"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Brug forudindstillinger"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Als privat markieren"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Debug Logs"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,8 +2186,8 @@ msgid "Scan"
|
||||
msgstr "Scan"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgstr "Scanne den QR-Code mit deiner Authenticator-App oder verwende den unten gezeigten Schlüssel für die Einrichtung und gib dann den generierten Verifizierungscode ein:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
msgid "Scans"
|
||||
@@ -2259,7 +2259,7 @@ msgstr "Dienst-URL"
|
||||
msgid "Services"
|
||||
msgstr "Dienste"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Session"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Unbekannt"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Unregistriert"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Aktualisiere Index"
|
||||
msgid "Updating moments"
|
||||
msgstr "Aktualisiere Erlebnisse"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Bild wird aktualisiert…"
|
||||
|
||||
@@ -2724,8 +2725,8 @@ msgid "Use Presets"
|
||||
msgstr "Presets anwenden"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgstr "Verwende den folgenden Wiederherstellungscode, um auf dein Konto zuzugreifen, wenn du mit deiner Authenticator-App bzw. deinem Gerät keinen gültigen Verifizierungscode generieren kannst:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
#: src/page/admin/sessions.vue:2 src/page/admin/sessions.vue:26
|
||||
@@ -2910,6 +2911,12 @@ msgstr "Deine Bilder werden kontinuierlich analysiert, um automatisch Alben von
|
||||
msgid "Zoom in/out"
|
||||
msgstr "Herein/Herauszoomen"
|
||||
|
||||
#~ msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
#~ msgstr "Scanne den QR-Code mit deiner Authenticator-App oder verwende den unten gezeigten Schlüssel für die Einrichtung und gib dann den generierten Verifizierungscode ein:"
|
||||
|
||||
#~ msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
#~ msgstr "Verwende den folgenden Wiederherstellungscode, um auf dein Konto zuzugreifen, wenn du mit deiner Authenticator-App bzw. deinem Gerät keinen gültigen Verifizierungscode generieren kannst:"
|
||||
|
||||
#~ msgid "providerName"
|
||||
#~ msgstr "providerName"
|
||||
|
||||
|
||||
@@ -524,7 +524,7 @@ msgstr "Αλλαγή ιδιωτικής κατάστασης"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -712,7 +712,7 @@ msgid "Debug Logs"
|
||||
msgstr "Αρχεία καταγραφής σφαλμάτων"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2183,7 +2183,7 @@ msgid "Scan"
|
||||
msgstr "Σάρωση"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2256,7 +2256,7 @@ msgstr "URL υπηρεσίας"
|
||||
msgid "Services"
|
||||
msgstr "URL υπηρεσίας"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Σύνοδος"
|
||||
|
||||
@@ -2611,7 +2611,8 @@ msgid "Unknown"
|
||||
msgstr "Άγνωστος"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Μη εγγεγραμμένο"
|
||||
|
||||
@@ -2640,7 +2641,7 @@ msgstr "Ενημέρωση ευρετηρίου"
|
||||
msgid "Updating moments"
|
||||
msgstr "Ενημέρωση στιγμών"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Ενημέρωση εικόνας…"
|
||||
|
||||
@@ -2721,7 +2722,7 @@ msgid "Use Presets"
|
||||
msgstr "Χρήση Προεπιλογών"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -526,7 +526,7 @@ msgstr ""
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -714,7 +714,7 @@ msgid "Debug Logs"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2185,7 +2185,7 @@ msgid "Scan"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2258,7 +2258,7 @@ msgstr ""
|
||||
msgid "Services"
|
||||
msgstr ""
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr ""
|
||||
|
||||
@@ -2613,7 +2613,8 @@ msgid "Unknown"
|
||||
msgstr ""
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr ""
|
||||
|
||||
@@ -2642,7 +2643,7 @@ msgstr ""
|
||||
msgid "Updating moments"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr ""
|
||||
|
||||
@@ -2723,7 +2724,7 @@ msgid "Use Presets"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Cambiar indicador de privado"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Registros de depuración"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2187,7 +2187,7 @@ msgid "Scan"
|
||||
msgstr "Escanear"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2260,7 +2260,7 @@ msgstr "URL del servicio"
|
||||
msgid "Services"
|
||||
msgstr "Servicios"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sesión"
|
||||
|
||||
@@ -2615,7 +2615,8 @@ msgid "Unknown"
|
||||
msgstr "Desconocido"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "No registrado"
|
||||
|
||||
@@ -2644,7 +2645,7 @@ msgstr "Actualizando índice"
|
||||
msgid "Updating moments"
|
||||
msgstr "Actualizando Momentos"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Actualizando la imagen…"
|
||||
|
||||
@@ -2725,7 +2726,7 @@ msgid "Use Presets"
|
||||
msgstr "Usar preselecciones"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -524,7 +524,7 @@ msgstr "Muuda isiklikkust"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -712,7 +712,7 @@ msgid "Debug Logs"
|
||||
msgstr "Tõrkeotsingu logid"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2183,7 +2183,7 @@ msgid "Scan"
|
||||
msgstr "Skannitud"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2256,7 +2256,7 @@ msgstr "Teenuse URL"
|
||||
msgid "Services"
|
||||
msgstr "Teenused"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sessioon"
|
||||
|
||||
@@ -2611,7 +2611,8 @@ msgid "Unknown"
|
||||
msgstr "Teadmata"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Mitteregistreeritud"
|
||||
|
||||
@@ -2640,7 +2641,7 @@ msgstr "Indeksi uuendamine"
|
||||
msgid "Updating moments"
|
||||
msgstr "Hetkede uuendamine"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Pildi uuendamine…"
|
||||
|
||||
@@ -2721,7 +2722,7 @@ msgid "Use Presets"
|
||||
msgstr "Kasuta eelseadistusi"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -524,7 +524,7 @@ msgstr "Aldatu bandera pribatua"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -712,7 +712,7 @@ msgid "Debug Logs"
|
||||
msgstr "Arazte-erregistroak"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2183,7 +2183,7 @@ msgid "Scan"
|
||||
msgstr "Eskaneatu"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2256,7 +2256,7 @@ msgstr "Zerbitzuaren URLa"
|
||||
msgid "Services"
|
||||
msgstr "Zerbitzuak"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Saioa"
|
||||
|
||||
@@ -2611,7 +2611,8 @@ msgid "Unknown"
|
||||
msgstr "Ezezaguna"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Erregistratu gabe"
|
||||
|
||||
@@ -2640,7 +2641,7 @@ msgstr "Indizea eguneratzen"
|
||||
msgid "Updating moments"
|
||||
msgstr "Eguneratzea uneak"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Irudia eguneratzen…"
|
||||
|
||||
@@ -2721,7 +2722,7 @@ msgid "Use Presets"
|
||||
msgstr "Erabili aurrezarpenak"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "تغییر پرچم خصوصی"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "گزارشهای اشکال زدایی"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "اسکن"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL سرویس"
|
||||
msgid "Services"
|
||||
msgstr "URL سرویس"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "جلسه"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "ناشناس"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "ثبت نشده"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "به روزرسانی نمایه ها"
|
||||
msgid "Updating moments"
|
||||
msgstr "به روزرسانی لحظه ها"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "به روزرسانی پیش نمایش ها"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "از Presets استفاده کنید"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -524,7 +524,7 @@ msgstr "Muuta yksityisyyden tilaa"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -712,7 +712,7 @@ msgid "Debug Logs"
|
||||
msgstr "Vianmäärityslokit"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2183,7 +2183,7 @@ msgid "Scan"
|
||||
msgstr "Skannaa"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2256,7 +2256,7 @@ msgstr "Palvelun URL-osoite"
|
||||
msgid "Services"
|
||||
msgstr "Palvelun URL-osoite"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Istunto"
|
||||
|
||||
@@ -2611,7 +2611,8 @@ msgid "Unknown"
|
||||
msgstr "Tuntematon"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Rekisteröimätön"
|
||||
|
||||
@@ -2640,7 +2641,7 @@ msgstr "Päivitetään indeksiä"
|
||||
msgid "Updating moments"
|
||||
msgstr "Päivitetään hetkiä"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Päivitetään esikatseluita"
|
||||
|
||||
@@ -2721,7 +2722,7 @@ msgid "Use Presets"
|
||||
msgstr "Käytä esiasetuksia"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Modifier le statut privé"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Journaux de débogage"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,8 +2186,8 @@ msgid "Scan"
|
||||
msgstr "Numérisée"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgstr "Scannez le code QR avec votre application d'authentification ou utilisez la clé d'installation indiquée ci-dessous, puis entrez le code généré pour la vérification :"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
msgid "Scans"
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL du service"
|
||||
msgid "Services"
|
||||
msgstr "Services"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Session"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Inconnu"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Non enregistré"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Mise à jour de l'index"
|
||||
msgid "Updating moments"
|
||||
msgstr "Mise à jour des moments"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Mise à jour de l'image…"
|
||||
|
||||
@@ -2724,8 +2725,8 @@ msgid "Use Presets"
|
||||
msgstr "Utiliser les préréglages"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgstr "Utilisez le code de récupération suivant pour accéder à votre compte si vous ne parvenez pas à générer un mot de passe valide avec votre application ou appareil d'authentification :"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
#: src/page/admin/sessions.vue:2 src/page/admin/sessions.vue:26
|
||||
@@ -2910,6 +2911,12 @@ msgstr "Votre bibliothèque est analysée en permanence pour créer automatiquem
|
||||
msgid "Zoom in/out"
|
||||
msgstr "Agrandir/Dézoomer"
|
||||
|
||||
#~ msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
#~ msgstr "Scannez le code QR avec votre application d'authentification ou utilisez la clé d'installation indiquée ci-dessous, puis entrez le code généré pour la vérification :"
|
||||
|
||||
#~ msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
#~ msgstr "Utilisez le code de récupération suivant pour accéder à votre compte si vous ne parvenez pas à générer un mot de passe valide avec votre application ou appareil d'authentification :"
|
||||
|
||||
#~ msgid "App Name"
|
||||
#~ msgstr "Nom de l'application"
|
||||
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "שינוי דגל הפרטיות"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Debug Logs"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "סרוק"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "נתיב השרות"
|
||||
msgid "Services"
|
||||
msgstr "שירותים"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "מוֹשָׁב"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "לא ידוע"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "משתמש לא רשום"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "מעדכן אינדקס"
|
||||
msgid "Updating moments"
|
||||
msgstr "מעדכן רגעים"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "מעדכן תמונה..."
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "השתמש בהגדרות קבועות מראש"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "निजी ध्वज बदलें"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "दोषमार्जन लॉग"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "स्कैन"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "सेवा URL"
|
||||
msgid "Services"
|
||||
msgstr "सेवाएं"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "सत्र"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "अनजान"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "अपंजीकृत"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "इंडेक्स अपडेट कर रहा है"
|
||||
msgid "Updating moments"
|
||||
msgstr "पल-पल का अपडेट"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "चित्र अपडेट किया जा रहा है…"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "प्रीसेट का उपयोग करें"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Promijenite privatnu zastavu"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Zapisnici otklanjanja pogrešaka"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Skeniraj"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL usluge"
|
||||
msgid "Services"
|
||||
msgstr "URL usluge"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sjednica"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Nepoznato"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Neregistriran"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Ažuriranje indeksa"
|
||||
msgid "Updating moments"
|
||||
msgstr "Trenuci ažuriranja"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Ažuriranje pregleda"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Koristite unaprijed postavljene postavke"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -526,7 +526,7 @@ msgstr "Privát fotóként jelölés"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -714,7 +714,7 @@ msgid "Debug Logs"
|
||||
msgstr "Hibakeresési naplók"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2185,7 +2185,7 @@ msgid "Scan"
|
||||
msgstr "Felderítés"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2258,7 +2258,7 @@ msgstr "Szolgáltatás URL-je"
|
||||
msgid "Services"
|
||||
msgstr "Szolgáltatások"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Ülés"
|
||||
|
||||
@@ -2613,7 +2613,8 @@ msgid "Unknown"
|
||||
msgstr "Ismeretlen"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Nem regisztrált"
|
||||
|
||||
@@ -2642,7 +2643,7 @@ msgstr "Index frissítése"
|
||||
msgid "Updating moments"
|
||||
msgstr "Frissítő pillanatok"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Kép frissítése…"
|
||||
|
||||
@@ -2723,7 +2724,7 @@ msgid "Use Presets"
|
||||
msgstr "Előbeállítások használata"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Ubah bendera pribadi"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Log Debug"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Pindai"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL Layanan"
|
||||
msgid "Services"
|
||||
msgstr "URL Layanan"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sesi"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Tidak diketahui"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Tidak terdaftar"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Memperbarui indeks"
|
||||
msgid "Updating moments"
|
||||
msgstr "Memperbarui momen"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Memperbarui pratinjau"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Gunakan Preset"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Cambiare la bandiera privata"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Registri di debug"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,8 +2186,8 @@ msgid "Scan"
|
||||
msgstr "Scansione"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgstr "Scannerizza il codice QR con la tua app di autenticazione, oppure utilizza la chiave di configurazione qui sotto. Successivamente, inserisci il codice generato per verificare:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
msgid "Scans"
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL Servizio"
|
||||
msgid "Services"
|
||||
msgstr "Servizi"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sessione"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Sconosciuto"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Non registrato"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Aggiornamento indici in corso"
|
||||
msgid "Updating moments"
|
||||
msgstr "Aggiornamento momenti in corso"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Aggiornamento dell'immagine…"
|
||||
|
||||
@@ -2724,8 +2725,8 @@ msgid "Use Presets"
|
||||
msgstr "Utilizzare le preimpostazioni"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgstr "Usa il codice di recupero seguente per accedere al tuo account quando ti è impossibile generare un codice con la tua app o il tuo dispositivo di autenticazione:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
#: src/page/admin/sessions.vue:2 src/page/admin/sessions.vue:26
|
||||
@@ -2910,6 +2911,12 @@ msgstr "La tua libreria viene continuamente analizzata per creare automaticament
|
||||
msgid "Zoom in/out"
|
||||
msgstr "Zoom avanti/indietro"
|
||||
|
||||
#~ msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
#~ msgstr "Scannerizza il codice QR con la tua app di autenticazione, oppure utilizza la chiave di configurazione qui sotto. Successivamente, inserisci il codice generato per verificare:"
|
||||
|
||||
#~ msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
#~ msgstr "Usa il codice di recupero seguente per accedere al tuo account quando ti è impossibile generare un codice con la tua app o il tuo dispositivo di autenticazione:"
|
||||
|
||||
#~ msgid "providerName"
|
||||
#~ msgstr "nome del fornitore"
|
||||
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "プライベートであるかどうかを変更"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "デバッグログ"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "スキャン"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "サービス URL"
|
||||
msgid "Services"
|
||||
msgstr "サービス"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "セッション"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "不明"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "未登録"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "インデックスを更新しています"
|
||||
msgid "Updating moments"
|
||||
msgstr "モーメントを更新しています"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "画像更新中…"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "プリセットを使用"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "비공개 플래그 변경하기"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "디버그 로그"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "스켄"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "서비스 URL"
|
||||
msgid "Services"
|
||||
msgstr "서비스"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "세션"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "알 수 없는"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "미등록"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "색인을 업데이트 중"
|
||||
msgid "Updating moments"
|
||||
msgstr "\"나의 순간\" 업데이트 중"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "사진 업데이트 중…"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "사전 설정 사용"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "گۆڕینی نیشانەنوێنی تایبەت"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "تۆماری هەڵەکان"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "پشکنین"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "بەستەری خزمەتگوزاری"
|
||||
msgid "Services"
|
||||
msgstr "بەستەری خزمەتگوزاری"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Rûniştinî"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "نەزانراو"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Neqeydkirî"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "نوێکردنەوەی نیشانە"
|
||||
msgid "Updating moments"
|
||||
msgstr "نوێکردنەوەی ساتەکان"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Rojanekirina pêşdîtinan"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "بەکارهێنانی پێش ڕێکخستنەکان"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Keisti privačią vėliavą"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Derinimo žurnalai"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Skenuoti"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "Paslaugos URL"
|
||||
msgid "Services"
|
||||
msgstr "Paslaugos URL"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sesija"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Nežinomas"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Neregistruotas"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Indekso atnaujinimas"
|
||||
msgid "Updating moments"
|
||||
msgstr "Akimirkų atnaujinimas"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Peržiūrų atnaujinimas"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Naudokite išankstinius nustatymus"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Tukar petanda peribadi"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Log Nyahpepijat"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Imbas"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL perkhidmatan"
|
||||
msgid "Services"
|
||||
msgstr "URL perkhidmatan"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sesi"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Tidak Diketahui"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Tidak berdaftar"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Mengemas kini indeks"
|
||||
msgid "Updating moments"
|
||||
msgstr "Mengemas kini detik"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Mengemas kini pratonton"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Gunakan Pratetap"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Endre private flagg"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Feilsøkingslogger"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Skann"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "Tjeneste-URL"
|
||||
msgid "Services"
|
||||
msgstr "Tjenester"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sesjon"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Ukjent"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Uregistrert"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Oppdaterer indeks"
|
||||
msgid "Updating moments"
|
||||
msgstr "Oppdaterer øyeblikk"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Oppdaterer forhåndsvisninger"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Bruk Forhåndsinnstillinger"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Privé vlag geschakeld"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Debug-logs"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Scan"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "Service URL"
|
||||
msgid "Services"
|
||||
msgstr "Diensten"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sessie"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Onbekend"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Ongeregistreerd"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Bijwerken van de index"
|
||||
msgid "Updating moments"
|
||||
msgstr "Momenten van actualisering"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Foto bijwerken…"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Gebruik Voorinstellingen"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Zmień prywatność"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Logi debugowania"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Zeskanowany dokument"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "Adres URL do usługi"
|
||||
msgid "Services"
|
||||
msgstr "Usługi"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sesja"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Nieznany"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Niezarejestrowany"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Aktualizowanie indeksu"
|
||||
msgid "Updating moments"
|
||||
msgstr "Aktualizowanie chwil"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Aktualizacja zdjęć…"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Użyj ustawień wstępnych"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Mudar marcação como privado"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Registros de depuração"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Pesquisar"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL do serviço"
|
||||
msgid "Services"
|
||||
msgstr "Serviços"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sessão"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Desconhecido"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Não registrado"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Atualizando o índice"
|
||||
msgid "Updating moments"
|
||||
msgstr "Atualizando momentos"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Atualizando a imagem.."
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Usar pré-definições"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Mudar marcação como privado"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Registros de depuração"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Buscar"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL do serviço"
|
||||
msgid "Services"
|
||||
msgstr "Serviços"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sessão"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Desconhecido"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Não registrado"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Atualizando índice"
|
||||
msgid "Updating moments"
|
||||
msgstr "Atualizando momentos"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Atualizando a imagem.."
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Usar pré-definições"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Schimbarea steagului privat"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Jurnalele de depanare"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Scanare"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL de serviciu"
|
||||
msgid "Services"
|
||||
msgstr "Servicii"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sesiunea"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Necunoscut"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Neînregistrat"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Actualizarea index-ului"
|
||||
msgid "Updating moments"
|
||||
msgstr "Actualizarea momentelor"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Actualizarea imaginii…"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Utilizați presetări"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Изменить флаг приватности"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Отладочные Логи"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Сканировать"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL сервиса"
|
||||
msgid "Services"
|
||||
msgstr "Сервисы"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Сессия"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Неизвестно"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Незарегистрированный"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Обновление индекса"
|
||||
msgid "Updating moments"
|
||||
msgstr "Обновление моментов"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Обновление изображения…"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Использовать предустановки"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Zmeniť privátne označenie"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Denníky ladenia"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Sken"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL Služby"
|
||||
msgid "Services"
|
||||
msgstr "Služby"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Zasadnutie"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Neznámy"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Neregistrovaný"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Aktualizuje sa index"
|
||||
msgid "Updating moments"
|
||||
msgstr "Aktualizujú sa momenty"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Aktualizácia obrázku.."
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Použiť predvoľbu"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -524,7 +524,7 @@ msgstr "Sprememba zasebne zastave"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -712,7 +712,7 @@ msgid "Debug Logs"
|
||||
msgstr "Dnevniki za odpravljanje napak"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2183,7 +2183,7 @@ msgid "Scan"
|
||||
msgstr "Skeniranje"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2256,7 +2256,7 @@ msgstr "URL storitve"
|
||||
msgid "Services"
|
||||
msgstr "URL storitve"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Seja"
|
||||
|
||||
@@ -2611,7 +2611,8 @@ msgid "Unknown"
|
||||
msgstr "Neznano"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Neregistrirani"
|
||||
|
||||
@@ -2640,7 +2641,7 @@ msgstr "Posodabljanje indeksa"
|
||||
msgid "Updating moments"
|
||||
msgstr "Posodabljanje trenutkov"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Posodabljanje predogledov"
|
||||
|
||||
@@ -2721,7 +2722,7 @@ msgid "Use Presets"
|
||||
msgstr "Uporaba prednastavitev"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Ändra den privata flaggan"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Felsökningsloggar"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Skanna"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "Tjänstens URL"
|
||||
msgid "Services"
|
||||
msgstr "Tjänstens URL"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Sammanträde"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Okänd"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Oregistrerad"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Uppdatering av index"
|
||||
msgid "Updating moments"
|
||||
msgstr "Uppdatering av ögonblick"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Uppdatering av förhandsgranskningar…"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Använd förinställningar"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "เปลี่ยนแฟล็กส่วนตัว"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "บันทึกการดีบัก"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "สแกน"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL บริการ"
|
||||
msgid "Services"
|
||||
msgstr "URL บริการ"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "การประชุม"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "ไม่รู้จัก"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "ไม่ได้ลงทะเบียน"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "กำลังอัปเดตดัชนี"
|
||||
msgid "Updating moments"
|
||||
msgstr "กำลังอัปเดตช่วงเวลา"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "กำลังอัปเดตตัวอย่าง"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "ใช้พรีเซ็ต"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Özel bayrağı değiştir"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Hata Kayıtları"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Tara"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "Hizmet URL'si"
|
||||
msgid "Services"
|
||||
msgstr "Hizmetler"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Oturum"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Bilinmeyen"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Kaydedilmemiş"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Dizin güncelleniyor"
|
||||
msgid "Updating moments"
|
||||
msgstr "Anların güncellenmesi"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Resim güncelleniyor.."
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Ön Ayarları Kullan"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -616,8 +616,8 @@ msgstr ""
|
||||
#: src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115
|
||||
#: src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129
|
||||
#: src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128
|
||||
#: src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53
|
||||
#: src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
@@ -842,7 +842,7 @@ msgstr ""
|
||||
#: src/options/admin.js:31
|
||||
#: src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100
|
||||
#: src/model/session.js:63
|
||||
#: src/model/session.js:64
|
||||
#: src/model/user.js:234
|
||||
#: src/options/auth.js:20
|
||||
#: src/options/auth.js:21
|
||||
@@ -2606,7 +2606,7 @@ msgstr ""
|
||||
|
||||
#: src/dialog/account/passcode.vue:51
|
||||
#: src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2690,7 +2690,7 @@ msgstr ""
|
||||
msgid "Services"
|
||||
msgstr ""
|
||||
|
||||
#: src/model/session.js:100
|
||||
#: src/model/session.js:101
|
||||
#: src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr ""
|
||||
@@ -3108,8 +3108,8 @@ msgstr ""
|
||||
#: src/page/settings/account.vue:40
|
||||
#: src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/page/settings/account.vue:40
|
||||
#: src/page/settings/account.vue:48
|
||||
#: src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr ""
|
||||
|
||||
@@ -3142,7 +3142,7 @@ msgid "Updating moments"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157
|
||||
#: src/page/settings/account.vue:140
|
||||
#: src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr ""
|
||||
|
||||
@@ -3237,7 +3237,7 @@ msgstr ""
|
||||
|
||||
#: src/dialog/account/passcode.vue:82
|
||||
#: src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "Змінити означку приватності"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "Журнали налагодження"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "Сканувати"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "URL служби"
|
||||
msgid "Services"
|
||||
msgstr "Послуги"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Сесія"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "Невідомий"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Незареєстрований"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "Оновлення індексу"
|
||||
msgid "Updating moments"
|
||||
msgstr "Оновлення моментів"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Оновлення зображення…"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "Використовуйте попередні налаштування"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -524,7 +524,7 @@ msgstr "Thay đổi cờ riêng tư"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -712,7 +712,7 @@ msgid "Debug Logs"
|
||||
msgstr "Nhật ký gỡ lỗi"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2183,7 +2183,7 @@ msgid "Scan"
|
||||
msgstr "Quét"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2256,7 +2256,7 @@ msgstr "URL dịch vụ"
|
||||
msgid "Services"
|
||||
msgstr "Dịch vụ"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "Phiên họp"
|
||||
|
||||
@@ -2611,7 +2611,8 @@ msgid "Unknown"
|
||||
msgstr "không xác định"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "Chưa đăng ký"
|
||||
|
||||
@@ -2640,7 +2641,7 @@ msgstr "Đang cập nhật chỉ mục"
|
||||
msgid "Updating moments"
|
||||
msgstr "Cập nhật khoảnh khắc"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "Đang cập nhật hình ảnh…"
|
||||
|
||||
@@ -2721,7 +2722,7 @@ msgid "Use Presets"
|
||||
msgstr "Sử dụng cài đặt trước"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "更改私有标记"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "调试日志"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2188,7 +2188,7 @@ msgid "Scan"
|
||||
msgstr "扫描"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2261,7 +2261,7 @@ msgstr "服务 URL"
|
||||
msgid "Services"
|
||||
msgstr "服务"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "会议"
|
||||
|
||||
@@ -2616,7 +2616,8 @@ msgid "Unknown"
|
||||
msgstr "未知"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "未注册的"
|
||||
|
||||
@@ -2645,7 +2646,7 @@ msgstr "更新索引"
|
||||
msgid "Updating moments"
|
||||
msgstr "更新时刻"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "更新图片…"
|
||||
|
||||
@@ -2726,7 +2727,7 @@ msgid "Use Presets"
|
||||
msgstr "使用预设"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -527,7 +527,7 @@ msgstr "更改私人選項"
|
||||
#: src/dialog/album/edit.vue:56 src/dialog/photo/edit/files.vue:147
|
||||
#: src/dialog/service/edit.vue:99 src/dialog/share.vue:96
|
||||
#: src/dialog/share.vue:115 src/page/albums.vue:563
|
||||
#: src/page/settings/account.vue:129 src/page/settings/account.vue:147
|
||||
#: src/page/settings/account.vue:128 src/page/settings/account.vue:146
|
||||
#: src/page/settings/advanced.vue:53 src/page/settings/general.vue:100
|
||||
#: src/page/settings/library.vue:52
|
||||
msgid "Changes successfully saved"
|
||||
@@ -715,7 +715,7 @@ msgid "Debug Logs"
|
||||
msgstr "除錯紀錄"
|
||||
|
||||
#: src/options/admin.js:31 src/page/admin/sessions.vue:60
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:63 src/model/user.js:234
|
||||
#: src/page/admin/users.vue:100 src/model/session.js:64 src/model/user.js:234
|
||||
#: src/options/auth.js:20 src/options/auth.js:21 src/options/auth.js:38
|
||||
#: src/options/auth.js:39 src/options/options.js:309 src/options/options.js:373
|
||||
#: src/options/themes.js:492 src/page/places.vue:141
|
||||
@@ -2186,7 +2186,7 @@ msgid "Scan"
|
||||
msgstr "掃描"
|
||||
|
||||
#: src/dialog/account/passcode.vue:51 src/dialog/account/passcode.vue:4
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated passcode for verification:"
|
||||
msgid "Scan the QR code with your authenticator app or use the setup key shown below and then enter the generated verification code:"
|
||||
msgstr ""
|
||||
|
||||
#: src/component/navigation.vue:125
|
||||
@@ -2259,7 +2259,7 @@ msgstr "服務 URL"
|
||||
msgid "Services"
|
||||
msgstr "服務"
|
||||
|
||||
#: src/model/session.js:100 src/options/auth.js:40
|
||||
#: src/model/session.js:101 src/options/auth.js:40
|
||||
msgid "Session"
|
||||
msgstr "工作階段"
|
||||
|
||||
@@ -2614,7 +2614,8 @@ msgid "Unknown"
|
||||
msgstr "未知"
|
||||
|
||||
#: src/page/settings/account.vue:40 src/page/settings/account.vue:48
|
||||
#: src/component/navigation.vue:93
|
||||
#: src/component/navigation.vue:93 src/page/settings/account.vue:39
|
||||
#: src/page/settings/account.vue:47
|
||||
msgid "Unregistered"
|
||||
msgstr "未註冊"
|
||||
|
||||
@@ -2643,7 +2644,7 @@ msgstr "更新索引"
|
||||
msgid "Updating moments"
|
||||
msgstr "更新時刻"
|
||||
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:140
|
||||
#: src/dialog/admin/users/edit.vue:157 src/page/settings/account.vue:139
|
||||
msgid "Updating picture…"
|
||||
msgstr "正在更新圖片…"
|
||||
|
||||
@@ -2724,7 +2725,7 @@ msgid "Use Presets"
|
||||
msgstr "使用預設"
|
||||
|
||||
#: src/dialog/account/passcode.vue:82 src/dialog/account/passcode.vue:4
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a valid passcode with your authenticator app or device:"
|
||||
msgid "Use the following recovery code to access your account when you are unable to generate a verification code with your authenticator app:"
|
||||
msgstr ""
|
||||
|
||||
#: src/options/admin.js:10 src/page/admin/sessions.vue:59
|
||||
|
||||
@@ -41,11 +41,12 @@ export class Session extends RestModel {
|
||||
UserAgent: "",
|
||||
ClientUID: "",
|
||||
ClientName: "",
|
||||
AuthID: "",
|
||||
AuthProvider: "",
|
||||
AuthMethod: "",
|
||||
AuthDomain: "",
|
||||
AuthScope: "",
|
||||
AuthID: "",
|
||||
GrantType: "",
|
||||
LastActive: 0,
|
||||
Expires: 0,
|
||||
Timeout: 0,
|
||||
|
||||
@@ -269,10 +269,10 @@ export class User extends RestModel {
|
||||
}).then((response) => Promise.resolve(response.data));
|
||||
}
|
||||
|
||||
confirmPasscode(passcode) {
|
||||
confirmPasscode(code) {
|
||||
return Api.post(this.getEntityResource() + "/passcode/confirm", {
|
||||
type: "totp",
|
||||
passcode: passcode,
|
||||
code: code,
|
||||
}).then((response) => Promise.resolve(response.data));
|
||||
}
|
||||
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
<p-auth-header></p-auth-header>
|
||||
<v-spacer></v-spacer>
|
||||
<v-layout wrap align-top>
|
||||
<template v-if="enterPasscode">
|
||||
<template v-if="enterCode">
|
||||
<v-flex xs12 class="pa-2 body-2">
|
||||
<translate>Please enter a valid verification code to access your account:</translate>
|
||||
</v-flex>
|
||||
<v-flex xs12 class="pa-2">
|
||||
<v-text-field
|
||||
id="auth-passcode"
|
||||
v-model="passcode"
|
||||
id="auth-code"
|
||||
v-model="code"
|
||||
:disabled="loading"
|
||||
name="passcode"
|
||||
name="code"
|
||||
type="text"
|
||||
:label="$gettext('Verification Code')"
|
||||
mask="nnn nnn nnn nnn"
|
||||
@@ -32,7 +32,7 @@
|
||||
autocomplete="one-time-code"
|
||||
browser-autocomplete="one-time-code"
|
||||
background-color="grey lighten-5"
|
||||
class="input-passcode text-selectable"
|
||||
class="input-code text-selectable"
|
||||
prepend-inner-icon="verified_user"
|
||||
color="primary"
|
||||
@keyup.enter.native="onLogin"
|
||||
@@ -47,7 +47,7 @@
|
||||
<v-text-field
|
||||
id="auth-username"
|
||||
v-model="username"
|
||||
:disabled="loading || enterPasscode"
|
||||
:disabled="loading || enterCode"
|
||||
name="username"
|
||||
type="text"
|
||||
:label="$gettext('Name')"
|
||||
@@ -97,7 +97,7 @@
|
||||
</template>
|
||||
<v-flex xs12 class="px-2 py-1 auth-actions">
|
||||
<div class="action-buttons auth-buttons text-xs-center">
|
||||
<v-btn v-if="enterPasscode" :color="colors.secondary" outline :block="$vuetify.breakpoint.xsOnly" :style="`color: ${colors.link}!important`" class="action-cancel ra-6 px-3 py-2 opacity-80" @click.stop.prevent="onCancel">
|
||||
<v-btn v-if="enterCode" :color="colors.secondary" outline :block="$vuetify.breakpoint.xsOnly" :style="`color: ${colors.link}!important`" class="action-cancel ra-6 px-3 py-2 opacity-80" @click.stop.prevent="onCancel">
|
||||
<translate>Cancel</translate>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="registerUri" :color="colors.secondary" outline :block="$vuetify.breakpoint.xsOnly" :style="`color: ${colors.link}!important`" class="action-register ra-6 px-3 py-2 opacity-80" @click.stop.prevent="onRegister">
|
||||
@@ -140,8 +140,8 @@ export default {
|
||||
username: "",
|
||||
password: "",
|
||||
showPassword: false,
|
||||
passcode: "",
|
||||
enterPasscode: false,
|
||||
code: "",
|
||||
enterCode: false,
|
||||
sponsor: this.$config.isSponsor(),
|
||||
config: this.$config.values,
|
||||
siteDescription: this.$config.getSiteDescription(),
|
||||
@@ -186,8 +186,8 @@ export default {
|
||||
this.username = "";
|
||||
this.password = "";
|
||||
this.showPassword = false;
|
||||
this.passcode = "";
|
||||
this.enterPasscode = false;
|
||||
this.code = "";
|
||||
this.enterCode = false;
|
||||
},
|
||||
onCancel() {
|
||||
if (this.loading) {
|
||||
@@ -201,7 +201,7 @@ export default {
|
||||
onLogin() {
|
||||
const username = this.username.trim();
|
||||
const password = this.password.trim();
|
||||
const passcode = this.passcode.trim();
|
||||
const code = this.code.trim();
|
||||
|
||||
if (username === "" || password === "") {
|
||||
return;
|
||||
@@ -209,13 +209,13 @@ export default {
|
||||
|
||||
this.loading = true;
|
||||
this.$session
|
||||
.login(username, password, passcode)
|
||||
.login(username, password, code)
|
||||
.then(() => {
|
||||
this.load();
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response?.data?.code === 32) {
|
||||
this.enterPasscode = true;
|
||||
this.enterCode = true;
|
||||
}
|
||||
this.loading = false;
|
||||
});
|
||||
|
||||
@@ -352,7 +352,6 @@
|
||||
import PAccountPasswordDialog from "dialog/account/password.vue";
|
||||
import countries from "options/countries.json";
|
||||
import Notify from "common/notify";
|
||||
import User from "model/user";
|
||||
import * as options from "options/options";
|
||||
|
||||
export default {
|
||||
|
||||
8
go.mod
8
go.mod
@@ -43,8 +43,8 @@ require (
|
||||
github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6
|
||||
github.com/urfave/cli v1.22.14
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/crypto v0.21.0
|
||||
golang.org/x/net v0.23.0
|
||||
golang.org/x/crypto v0.22.0
|
||||
golang.org/x/net v0.24.0
|
||||
gonum.org/v1/gonum v0.15.0
|
||||
gopkg.in/photoprism/go-tz.v2 v2.1.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
@@ -67,7 +67,7 @@ require (
|
||||
require github.com/gabriel-vasile/mimetype v1.4.3
|
||||
|
||||
require (
|
||||
golang.org/x/sync v0.6.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/time v0.5.0
|
||||
)
|
||||
|
||||
@@ -124,7 +124,7 @@ require (
|
||||
github.com/zitadel/logging v0.5.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
|
||||
golang.org/x/oauth2 v0.18.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
|
||||
16
go.sum
16
go.sum
@@ -385,8 +385,8 @@ golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -449,8 +449,8 @@ golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfS
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -466,8 +466,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -499,8 +499,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20191110171634-ad39bd3f0407/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
||||
@@ -103,11 +103,11 @@ func AuthenticateAdmin(app *gin.Engine, router *gin.RouterGroup) (authToken stri
|
||||
|
||||
// AuthenticateUser Register session routes and returns valid SessionId.
|
||||
// Call this func after registering other routes and before performing other requests.
|
||||
func AuthenticateUser(app *gin.Engine, router *gin.RouterGroup, name string, password string) (authToken string) {
|
||||
func AuthenticateUser(app *gin.Engine, router *gin.RouterGroup, username string, password string) (authToken string) {
|
||||
CreateSession(router)
|
||||
|
||||
r := PerformRequestWithBody(app, http.MethodPost, "/api/v1/session", form.AsJson(form.Login{
|
||||
UserName: name,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}))
|
||||
|
||||
|
||||
148
internal/api/oauth_create_token.go
Normal file
148
internal/api/oauth_create_token.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/dustin/go-humanize/english"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/get"
|
||||
"github.com/photoprism/photoprism/internal/server/limiter"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/header"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
)
|
||||
|
||||
// CreateOAuthToken creates a new access token for clients that
|
||||
// authenticate with valid OAuth2 client credentials.
|
||||
//
|
||||
// POST /api/v1/oauth/token
|
||||
func CreateOAuthToken(router *gin.RouterGroup) {
|
||||
router.POST("/oauth/token", func(c *gin.Context) {
|
||||
// Prevent CDNs from caching this endpoint.
|
||||
if header.IsCdn(c.Request) {
|
||||
AbortNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Get client IP address for logs and rate limiting checks.
|
||||
clientIp := ClientIP(c)
|
||||
|
||||
// Abort if running in public mode.
|
||||
if get.Config().Public() {
|
||||
event.AuditErr([]string{clientIp, "client", "create session", "oauth2", authn.ErrDisabledInPublicMode.Error()})
|
||||
AbortForbidden(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Disable caching of responses.
|
||||
c.Header(header.CacheControl, header.CacheControlNoStore)
|
||||
|
||||
// Token create request form.
|
||||
var f form.OAuthCreateToken
|
||||
var sess *entity.Session
|
||||
var client *entity.Client
|
||||
var err error
|
||||
|
||||
// Allow authentication with basic auth and form values.
|
||||
if clientId, clientSecret, _ := header.BasicAuth(c); clientId != "" && clientSecret != "" {
|
||||
f.GrantType = authn.GrantClientCredentials
|
||||
f.ClientID = clientId
|
||||
f.ClientSecret = clientSecret
|
||||
} else if err = c.ShouldBind(&f); err != nil {
|
||||
event.AuditWarn([]string{clientIp, "client", "create session", "oauth2", "%s"}, err)
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Check the credentials for completeness and the correct format.
|
||||
if err = f.Validate(); err != nil {
|
||||
event.AuditWarn([]string{clientIp, "client", "create session", "oauth2", "%s"}, err)
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
}
|
||||
|
||||
// Check request rate limit.
|
||||
r := limiter.Login.Request(clientIp)
|
||||
|
||||
// Abort if request rate limit is exceeded.
|
||||
if r.Reject() || limiter.Auth.Reject(clientIp) {
|
||||
limiter.AbortJSON(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Create a new session (access token) based on the grant type specified in the request.
|
||||
switch f.GrantType {
|
||||
case authn.GrantClientCredentials, authn.GrantUndefined:
|
||||
// Find client with the specified ID.
|
||||
client = entity.FindClientByUID(f.ClientID)
|
||||
|
||||
// Check if a client has been found, it is enabled, and the credentials are valid.
|
||||
if client == nil {
|
||||
event.AuditWarn([]string{clientIp, "client %s", "create session", "oauth2", authn.ErrInvalidClientID.Error()}, f.ClientID)
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
} else if !client.AuthEnabled {
|
||||
event.AuditWarn([]string{clientIp, "client %s", "create session", "oauth2", authn.ErrAuthenticationDisabled.Error()}, f.ClientID)
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
} else if method := client.Method(); !method.IsDefault() && method != authn.MethodOAuth2 {
|
||||
event.AuditWarn([]string{clientIp, "client %s", "create session", "oauth2", "method %s not supported"}, f.ClientID, clean.LogQuote(method.String()))
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
} else if client.InvalidSecret(f.ClientSecret) {
|
||||
event.AuditWarn([]string{clientIp, "client %s", "create session", "oauth2", authn.ErrInvalidClientSecret.Error()}, f.ClientID)
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
}
|
||||
|
||||
// Cancel failure rate limit reservation.
|
||||
r.Success()
|
||||
|
||||
// Create new client session.
|
||||
sess = client.NewSession(c, authn.GrantClientCredentials)
|
||||
case authn.GrantPassword:
|
||||
// Generate an app password for a user account and accept the password for confirmation.
|
||||
event.AuditWarn([]string{clientIp, "client %s", "create session", "oauth2", "password grant type is not implemented yet"}, f.ClientID)
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
default:
|
||||
event.AuditErr([]string{clientIp, "client %s", "create session", "oauth2", authn.ErrInvalidGrantType.Error()}, f.ClientID)
|
||||
c.AbortWithStatusJSON(sess.HttpStatus(), gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
}
|
||||
|
||||
// Save new session.
|
||||
if sess, err = get.Session().Save(sess); err != nil {
|
||||
event.AuditErr([]string{clientIp, "client %s", "create session", "oauth2", err.Error()}, f.ClientID)
|
||||
c.AbortWithStatusJSON(sess.HttpStatus(), gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
} else if sess == nil {
|
||||
event.AuditErr([]string{clientIp, "client %s", "create session", "oauth2", StatusFailed.String()}, f.ClientID)
|
||||
c.AbortWithStatusJSON(sess.HttpStatus(), gin.H{"error": i18n.Msg(i18n.ErrUnexpected)})
|
||||
return
|
||||
} else {
|
||||
event.AuditInfo([]string{clientIp, "client %s", "session %s", "oauth2", "created"}, f.ClientID, sess.RefID)
|
||||
}
|
||||
|
||||
// Delete any existing client sessions above the configured limit.
|
||||
if client == nil {
|
||||
// Skip deletion if not created by a client.
|
||||
} else if deleted := client.EnforceAuthTokenLimit(); deleted > 0 {
|
||||
event.AuditInfo([]string{clientIp, "client %s", "session %s", "oauth2", "deleted %s"}, f.ClientID, sess.RefID, english.Plural(deleted, "previously created client session", "previously created client sessions"))
|
||||
}
|
||||
|
||||
// Send response with access token, token type, and token lifetime.
|
||||
response := gin.H{
|
||||
"access_token": sess.AuthToken(),
|
||||
"token_type": sess.AuthTokenType(),
|
||||
"expires_in": sess.ExpiresIn(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
})
|
||||
}
|
||||
@@ -8,11 +8,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/header"
|
||||
)
|
||||
|
||||
@@ -28,7 +26,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
||||
var path = "/api/v1/oauth/token"
|
||||
|
||||
data := url.Values{
|
||||
"grant_type": {"client_credentials"},
|
||||
"grant_type": {authn.GrantClientCredentials.String()},
|
||||
"client_id": {"cs5cpu17n6gj2qo5"},
|
||||
"client_secret": {"xcCbOrw6I0vcoXzhnOmXhjpVSyFq0l0e"},
|
||||
"scope": {"metrics"},
|
||||
@@ -53,7 +51,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
||||
var path = "/api/v1/oauth/token"
|
||||
|
||||
data := url.Values{
|
||||
"grant_type": {"client_credentials"},
|
||||
"grant_type": {authn.GrantClientCredentials.String()},
|
||||
"client_id": {"cs5cpu17n6gj2qo5"},
|
||||
"client_secret": {"xcCbOrw6I0vcoXzhnOmXhjpVSyFq0l0e"},
|
||||
"scope": {"metrics"},
|
||||
@@ -80,7 +78,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
||||
var path = "/api/v1/oauth/token"
|
||||
|
||||
data := url.Values{
|
||||
"grant_type": {"client_credentials"},
|
||||
"grant_type": {authn.GrantClientCredentials.String()},
|
||||
"client_id": {"123"},
|
||||
"client_secret": {"xcCbOrw6I0vcoXzhnOmXhjpVSyFq0l0e"},
|
||||
"scope": {"metrics"},
|
||||
@@ -107,7 +105,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
||||
var path = "/api/v1/oauth/token"
|
||||
|
||||
data := url.Values{
|
||||
"grant_type": {"client_credentials"},
|
||||
"grant_type": {authn.GrantClientCredentials.String()},
|
||||
"client_id": {"cs5cpu17n6gj2yy6"},
|
||||
"client_secret": {"xcCbOrw6I0vcoXzhnOmXhjpVSyFq0l0e"},
|
||||
"scope": {"metrics"},
|
||||
@@ -134,7 +132,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
||||
var path = "/api/v1/oauth/token"
|
||||
|
||||
data := url.Values{
|
||||
"grant_type": {"client_credentials"},
|
||||
"grant_type": {authn.GrantClientCredentials.String()},
|
||||
"client_id": {"cs5cpu17n6gj2qo5"},
|
||||
"client_secret": {"xcCbOrw6I0vcoXzhnOmXhjpVSyFq0l0f"},
|
||||
"scope": {"metrics"},
|
||||
@@ -161,7 +159,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
||||
var path = "/api/v1/oauth/token"
|
||||
|
||||
data := url.Values{
|
||||
"grant_type": {"client_credentials"},
|
||||
"grant_type": {authn.GrantClientCredentials.String()},
|
||||
"client_id": {"cs5gfsvbd7ejzn8m"},
|
||||
"client_secret": {"aaCbOrw6I0vcoXzhnOmXhjpVSyFq0l0e"},
|
||||
"scope": {"metrics"},
|
||||
@@ -188,7 +186,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
||||
var path = "/api/v1/oauth/token"
|
||||
|
||||
data := url.Values{
|
||||
"grant_type": {"client_credentials"},
|
||||
"grant_type": {authn.GrantClientCredentials.String()},
|
||||
"client_id": {"cs5cpu17n6gj2jh6"},
|
||||
"client_secret": {"aaCbOrw6I0vcoXzhnOmXhjpVSyFq0l0e"},
|
||||
"scope": {"*"},
|
||||
@@ -205,103 +203,3 @@ func TestCreateOAuthToken(t *testing.T) {
|
||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRevokeOAuthToken(t *testing.T) {
|
||||
const tokenPath = "/api/v1/oauth/token"
|
||||
const revokePath = "/api/v1/oauth/revoke"
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
conf.SetAuthMode(config.AuthModePasswd)
|
||||
defer conf.SetAuthMode(config.AuthModePublic)
|
||||
|
||||
CreateOAuthToken(router)
|
||||
RevokeOAuthToken(router)
|
||||
|
||||
data := url.Values{
|
||||
"grant_type": {"client_credentials"},
|
||||
"client_id": {"cs5cpu17n6gj2qo5"},
|
||||
"client_secret": {"xcCbOrw6I0vcoXzhnOmXhjpVSyFq0l0e"},
|
||||
"scope": {"metrics"},
|
||||
}
|
||||
|
||||
createToken, _ := http.NewRequest("POST", tokenPath, strings.NewReader(data.Encode()))
|
||||
createToken.Header.Add(header.ContentType, header.ContentTypeForm)
|
||||
|
||||
createResp := httptest.NewRecorder()
|
||||
app.ServeHTTP(createResp, createToken)
|
||||
|
||||
t.Logf("Header: %s", createResp.Header())
|
||||
t.Logf("BODY: %s", createResp.Body.String())
|
||||
assert.Equal(t, http.StatusOK, createResp.Code)
|
||||
authToken := gjson.Get(createResp.Body.String(), "access_token").String()
|
||||
|
||||
revokeToken, _ := http.NewRequest("POST", revokePath, nil)
|
||||
revokeToken.Header.Add(header.XAuthToken, authToken)
|
||||
|
||||
revokeResp := httptest.NewRecorder()
|
||||
app.ServeHTTP(revokeResp, revokeToken)
|
||||
|
||||
t.Logf("Header: %s", revokeResp.Header())
|
||||
t.Logf("BODY: %s", revokeResp.Body.String())
|
||||
assert.Equal(t, http.StatusOK, revokeResp.Code)
|
||||
})
|
||||
t.Run("FormData", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
conf.SetAuthMode(config.AuthModePasswd)
|
||||
defer conf.SetAuthMode(config.AuthModePublic)
|
||||
|
||||
CreateOAuthToken(router)
|
||||
RevokeOAuthToken(router)
|
||||
|
||||
createData := url.Values{
|
||||
"grant_type": {"client_credentials"},
|
||||
"client_id": {"cs5cpu17n6gj2qo5"},
|
||||
"client_secret": {"xcCbOrw6I0vcoXzhnOmXhjpVSyFq0l0e"},
|
||||
"scope": {"metrics"},
|
||||
}
|
||||
|
||||
createToken, _ := http.NewRequest("POST", tokenPath, strings.NewReader(createData.Encode()))
|
||||
createToken.Header.Add(header.ContentType, header.ContentTypeForm)
|
||||
|
||||
createResp := httptest.NewRecorder()
|
||||
app.ServeHTTP(createResp, createToken)
|
||||
|
||||
t.Logf("Header: %s", createResp.Header())
|
||||
t.Logf("BODY: %s", createResp.Body.String())
|
||||
assert.Equal(t, http.StatusOK, createResp.Code)
|
||||
authToken := gjson.Get(createResp.Body.String(), "access_token").String()
|
||||
|
||||
revokeData := url.Values{
|
||||
"token": {authToken},
|
||||
"token_type_hint": {form.ClientAccessToken},
|
||||
}
|
||||
|
||||
revokeToken, _ := http.NewRequest("POST", revokePath, strings.NewReader(revokeData.Encode()))
|
||||
revokeToken.Header.Add(header.ContentType, header.ContentTypeForm)
|
||||
|
||||
revokeResp := httptest.NewRecorder()
|
||||
app.ServeHTTP(revokeResp, revokeToken)
|
||||
|
||||
t.Logf("Header: %s", revokeResp.Header())
|
||||
t.Logf("BODY: %s", revokeResp.Body.String())
|
||||
assert.Equal(t, http.StatusOK, revokeResp.Code)
|
||||
})
|
||||
t.Run("PublicMode", func(t *testing.T) {
|
||||
app, router, _ := NewApiTest()
|
||||
|
||||
RevokeOAuthToken(router)
|
||||
|
||||
sess := entity.SessionFixtures.Get("alice_token")
|
||||
|
||||
revokeToken, _ := http.NewRequest("POST", revokePath, nil)
|
||||
revokeToken.Header.Add(header.XAuthToken, sess.AuthToken())
|
||||
|
||||
revokeResp := httptest.NewRecorder()
|
||||
app.ServeHTTP(revokeResp, revokeToken)
|
||||
|
||||
t.Logf("Header: %s", revokeResp.Header())
|
||||
t.Logf("BODY: %s", revokeResp.Body.String())
|
||||
assert.Equal(t, http.StatusForbidden, revokeResp.Code)
|
||||
})
|
||||
}
|
||||
106
internal/api/oauth_revoke_token.go
Normal file
106
internal/api/oauth_revoke_token.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/get"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/header"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
)
|
||||
|
||||
// RevokeOAuthToken takes an access token and deletes it. A client may only delete its own tokens.
|
||||
//
|
||||
// POST /api/v1/oauth/revoke
|
||||
func RevokeOAuthToken(router *gin.RouterGroup) {
|
||||
router.POST("/oauth/revoke", func(c *gin.Context) {
|
||||
// Prevent CDNs from caching this endpoint.
|
||||
if header.IsCdn(c.Request) {
|
||||
AbortNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Get client IP address for logs and rate limiting checks.
|
||||
clientIp := ClientIP(c)
|
||||
|
||||
// Abort if running in public mode.
|
||||
if get.Config().Public() {
|
||||
event.AuditErr([]string{clientIp, "client", "delete session", "oauth2", authn.ErrDisabledInPublicMode.Error()})
|
||||
Abort(c, http.StatusForbidden, i18n.ErrForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// Token revokation request form.
|
||||
var f form.OAuthRevokeToken
|
||||
|
||||
// Get token from request header.
|
||||
authToken := AuthToken(c)
|
||||
|
||||
// Get the auth token to be revoked from the submitted form values or the request header.
|
||||
if err = c.ShouldBind(&f); err != nil && authToken == "" {
|
||||
event.AuditWarn([]string{clientIp, "client", "delete session", "oauth2", "%s"}, err)
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
} else if f.Empty() {
|
||||
f.AuthToken = authToken
|
||||
f.TypeHint = form.ClientAccessToken
|
||||
}
|
||||
|
||||
// Check the token form values.
|
||||
if err = f.Validate(); err != nil {
|
||||
event.AuditWarn([]string{clientIp, "client", "delete session", "oauth2", "%s"}, err)
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Disable caching of responses.
|
||||
c.Header(header.CacheControl, header.CacheControlNoStore)
|
||||
|
||||
// Find session based on auth token.
|
||||
sess, err := entity.FindSession(rnd.SessionID(f.AuthToken))
|
||||
|
||||
if err != nil {
|
||||
event.AuditErr([]string{clientIp, "client %s", "session %s", "delete session as %s", "oauth2", "%s"}, clean.Log(sess.ClientInfo()), clean.Log(sess.RefID), sess.ClientRole().String(), err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, i18n.NewResponse(http.StatusUnauthorized, i18n.ErrUnauthorized))
|
||||
return
|
||||
} else if sess == nil {
|
||||
event.AuditErr([]string{clientIp, "client %s", "session %s", "delete session as %s", "oauth2", authn.Denied}, clean.Log(sess.ClientInfo()), clean.Log(sess.RefID), sess.ClientRole().String())
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, i18n.NewResponse(http.StatusUnauthorized, i18n.ErrUnauthorized))
|
||||
return
|
||||
} else if sess.Abort(c) {
|
||||
event.AuditErr([]string{clientIp, "client %s", "session %s", "delete session as %s", "oauth2", authn.Denied}, clean.Log(sess.ClientInfo()), clean.Log(sess.RefID), sess.ClientRole().String())
|
||||
return
|
||||
} else if !sess.IsClient() {
|
||||
event.AuditErr([]string{clientIp, "client %s", "session %s", "delete session as %s", "oauth2", authn.Denied}, clean.Log(sess.ClientInfo()), clean.Log(sess.RefID), sess.ClientRole().String())
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, i18n.NewResponse(http.StatusForbidden, i18n.ErrForbidden))
|
||||
return
|
||||
} else {
|
||||
event.AuditInfo([]string{clientIp, "client %s", "session %s", "delete session as %s", "oauth2", authn.Granted}, clean.Log(sess.ClientInfo()), clean.Log(sess.RefID), sess.ClientRole().String())
|
||||
}
|
||||
|
||||
// Delete session cache and database record.
|
||||
if err = sess.Delete(); err != nil {
|
||||
// Log error.
|
||||
event.AuditErr([]string{clientIp, "client %s", "session %s", "delete session as %s", "oauth2", "%s"}, clean.Log(sess.ClientInfo()), clean.Log(sess.RefID), sess.ClientRole().String(), err)
|
||||
|
||||
// Return JSON error.
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, i18n.NewResponse(http.StatusNotFound, i18n.ErrNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
// Log event.
|
||||
event.AuditInfo([]string{clientIp, "client %s", "session %s", "oauth2", "deleted"}, clean.Log(sess.ClientInfo()), clean.Log(sess.RefID))
|
||||
|
||||
// Send response.
|
||||
c.JSON(http.StatusOK, DeleteSessionResponse(sess.ID))
|
||||
})
|
||||
}
|
||||
117
internal/api/oauth_revoke_token_test.go
Normal file
117
internal/api/oauth_revoke_token_test.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/pkg/header"
|
||||
)
|
||||
|
||||
func TestRevokeOAuthToken(t *testing.T) {
|
||||
const tokenPath = "/api/v1/oauth/token"
|
||||
const revokePath = "/api/v1/oauth/revoke"
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
conf.SetAuthMode(config.AuthModePasswd)
|
||||
defer conf.SetAuthMode(config.AuthModePublic)
|
||||
|
||||
CreateOAuthToken(router)
|
||||
RevokeOAuthToken(router)
|
||||
|
||||
data := url.Values{
|
||||
"grant_type": {"client_credentials"},
|
||||
"client_id": {"cs5cpu17n6gj2qo5"},
|
||||
"client_secret": {"xcCbOrw6I0vcoXzhnOmXhjpVSyFq0l0e"},
|
||||
"scope": {"metrics"},
|
||||
}
|
||||
|
||||
createToken, _ := http.NewRequest("POST", tokenPath, strings.NewReader(data.Encode()))
|
||||
createToken.Header.Add(header.ContentType, header.ContentTypeForm)
|
||||
|
||||
createResp := httptest.NewRecorder()
|
||||
app.ServeHTTP(createResp, createToken)
|
||||
|
||||
t.Logf("Header: %s", createResp.Header())
|
||||
t.Logf("BODY: %s", createResp.Body.String())
|
||||
assert.Equal(t, http.StatusOK, createResp.Code)
|
||||
authToken := gjson.Get(createResp.Body.String(), "access_token").String()
|
||||
|
||||
revokeToken, _ := http.NewRequest("POST", revokePath, nil)
|
||||
revokeToken.Header.Add(header.XAuthToken, authToken)
|
||||
|
||||
revokeResp := httptest.NewRecorder()
|
||||
app.ServeHTTP(revokeResp, revokeToken)
|
||||
|
||||
t.Logf("Header: %s", revokeResp.Header())
|
||||
t.Logf("BODY: %s", revokeResp.Body.String())
|
||||
assert.Equal(t, http.StatusOK, revokeResp.Code)
|
||||
})
|
||||
t.Run("FormData", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
conf.SetAuthMode(config.AuthModePasswd)
|
||||
defer conf.SetAuthMode(config.AuthModePublic)
|
||||
|
||||
CreateOAuthToken(router)
|
||||
RevokeOAuthToken(router)
|
||||
|
||||
createData := url.Values{
|
||||
"grant_type": {"client_credentials"},
|
||||
"client_id": {"cs5cpu17n6gj2qo5"},
|
||||
"client_secret": {"xcCbOrw6I0vcoXzhnOmXhjpVSyFq0l0e"},
|
||||
"scope": {"metrics"},
|
||||
}
|
||||
|
||||
createToken, _ := http.NewRequest("POST", tokenPath, strings.NewReader(createData.Encode()))
|
||||
createToken.Header.Add(header.ContentType, header.ContentTypeForm)
|
||||
|
||||
createResp := httptest.NewRecorder()
|
||||
app.ServeHTTP(createResp, createToken)
|
||||
|
||||
t.Logf("Header: %s", createResp.Header())
|
||||
t.Logf("BODY: %s", createResp.Body.String())
|
||||
assert.Equal(t, http.StatusOK, createResp.Code)
|
||||
authToken := gjson.Get(createResp.Body.String(), "access_token").String()
|
||||
|
||||
revokeData := url.Values{
|
||||
"token": {authToken},
|
||||
"token_type_hint": {form.ClientAccessToken},
|
||||
}
|
||||
|
||||
revokeToken, _ := http.NewRequest("POST", revokePath, strings.NewReader(revokeData.Encode()))
|
||||
revokeToken.Header.Add(header.ContentType, header.ContentTypeForm)
|
||||
|
||||
revokeResp := httptest.NewRecorder()
|
||||
app.ServeHTTP(revokeResp, revokeToken)
|
||||
|
||||
t.Logf("Header: %s", revokeResp.Header())
|
||||
t.Logf("BODY: %s", revokeResp.Body.String())
|
||||
assert.Equal(t, http.StatusOK, revokeResp.Code)
|
||||
})
|
||||
t.Run("PublicMode", func(t *testing.T) {
|
||||
app, router, _ := NewApiTest()
|
||||
|
||||
RevokeOAuthToken(router)
|
||||
|
||||
sess := entity.SessionFixtures.Get("alice_token")
|
||||
|
||||
revokeToken, _ := http.NewRequest("POST", revokePath, nil)
|
||||
revokeToken.Header.Add(header.XAuthToken, sess.AuthToken())
|
||||
|
||||
revokeResp := httptest.NewRecorder()
|
||||
app.ServeHTTP(revokeResp, revokeToken)
|
||||
|
||||
t.Logf("Header: %s", revokeResp.Header())
|
||||
t.Logf("BODY: %s", revokeResp.Body.String())
|
||||
assert.Equal(t, http.StatusForbidden, revokeResp.Code)
|
||||
})
|
||||
}
|
||||
@@ -58,10 +58,10 @@ func CreateSession(router *gin.RouterGroup) {
|
||||
|
||||
// Check request rate limit.
|
||||
var r *limiter.Request
|
||||
if f.Passcode == "" {
|
||||
r = limiter.Login.Request(clientIp)
|
||||
} else {
|
||||
if f.HasPasscode() {
|
||||
r = limiter.Login.RequestN(clientIp, 3)
|
||||
} else {
|
||||
r = limiter.Login.Request(clientIp)
|
||||
}
|
||||
|
||||
// Abort if failure rate limit is exceeded.
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/dustin/go-humanize/english"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/get"
|
||||
"github.com/photoprism/photoprism/internal/server/limiter"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/header"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
)
|
||||
|
||||
// CreateOAuthToken creates a new access token for clients that
|
||||
// authenticate with valid OAuth2 client credentials.
|
||||
//
|
||||
// POST /api/v1/oauth/token
|
||||
func CreateOAuthToken(router *gin.RouterGroup) {
|
||||
router.POST("/oauth/token", func(c *gin.Context) {
|
||||
// Prevent CDNs from caching this endpoint.
|
||||
if header.IsCdn(c.Request) {
|
||||
AbortNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Get client IP address for logs and rate limiting checks.
|
||||
clientIp := ClientIP(c)
|
||||
|
||||
if get.Config().Public() {
|
||||
// Abort if running in public mode.
|
||||
event.AuditErr([]string{clientIp, "client", "create session", "oauth2", authn.ErrDisabledInPublicMode.Error()})
|
||||
AbortForbidden(c)
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// Client authentication request credentials.
|
||||
var f form.ClientCredentials
|
||||
|
||||
// Allow authentication with basic auth and form values.
|
||||
if clientId, clientSecret, _ := header.BasicAuth(c); clientId != "" && clientSecret != "" {
|
||||
f.ClientID = clientId
|
||||
f.ClientSecret = clientSecret
|
||||
} else if err = c.ShouldBind(&f); err != nil {
|
||||
event.AuditWarn([]string{clientIp, "client", "create session", "oauth2", "%s"}, err)
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Check the credentials for completeness and the correct format.
|
||||
if err = f.Validate(); err != nil {
|
||||
event.AuditWarn([]string{clientIp, "client", "create session", "oauth2", "%s"}, err)
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
}
|
||||
|
||||
// Disable caching of responses.
|
||||
c.Header(header.CacheControl, header.CacheControlNoStore)
|
||||
|
||||
// Check request rate limit.
|
||||
r := limiter.Login.Request(clientIp)
|
||||
|
||||
// Abort if request rate limit is exceeded.
|
||||
if r.Reject() || limiter.Auth.Reject(clientIp) {
|
||||
limiter.AbortJSON(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Find the client that has the ID specified in the authentication request.
|
||||
client := entity.FindClientByUID(f.ClientID)
|
||||
|
||||
// Abort if the client ID or secret are invalid.
|
||||
if client == nil {
|
||||
event.AuditWarn([]string{clientIp, "client %s", "create session", "oauth2", authn.ErrInvalidClientID.Error()}, f.ClientID)
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
} else if !client.AuthEnabled {
|
||||
event.AuditWarn([]string{clientIp, "client %s", "create session", "oauth2", authn.ErrAuthenticationDisabled.Error()}, f.ClientID)
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
} else if method := client.Method(); !method.IsDefault() && method != authn.MethodOAuth2 {
|
||||
event.AuditWarn([]string{clientIp, "client %s", "create session", "oauth2", "method %s not supported"}, f.ClientID, clean.LogQuote(method.String()))
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
} else if client.InvalidSecret(f.ClientSecret) {
|
||||
event.AuditWarn([]string{clientIp, "client %s", "create session", "oauth2", authn.ErrInvalidClientSecret.Error()}, f.ClientID)
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
}
|
||||
|
||||
// Return the reserved request rate limit tokens after successful authentication.
|
||||
r.Success()
|
||||
|
||||
// Create new client session.
|
||||
sess := client.NewSession(c)
|
||||
|
||||
// Save new client session.
|
||||
if sess, err = get.Session().Save(sess); err != nil {
|
||||
event.AuditErr([]string{clientIp, "client %s", "create session", "oauth2", "%s"}, f.ClientID, err)
|
||||
c.AbortWithStatusJSON(sess.HttpStatus(), gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
} else if sess == nil {
|
||||
event.AuditErr([]string{clientIp, "client %s", "create session", "oauth2", StatusFailed.String()}, f.ClientID)
|
||||
c.AbortWithStatusJSON(sess.HttpStatus(), gin.H{"error": i18n.Msg(i18n.ErrUnexpected)})
|
||||
return
|
||||
} else {
|
||||
event.AuditInfo([]string{clientIp, "client %s", "session %s", "oauth2", "created"}, f.ClientID, sess.RefID)
|
||||
}
|
||||
|
||||
// Deletes old client sessions above the configured limit.
|
||||
if deleted := client.EnforceAuthTokenLimit(); deleted > 0 {
|
||||
event.AuditInfo([]string{clientIp, "client %s", "session %s", "oauth2", "deleted %s"}, f.ClientID, sess.RefID, english.Plural(deleted, "previously created client session", "previously created client sessions"))
|
||||
}
|
||||
|
||||
// Response includes access token, token type, and token lifetime.
|
||||
data := gin.H{
|
||||
"access_token": sess.AuthToken(),
|
||||
"token_type": sess.AuthTokenType(),
|
||||
"expires_in": sess.ExpiresIn(),
|
||||
}
|
||||
|
||||
// Return JSON response.
|
||||
c.JSON(http.StatusOK, data)
|
||||
})
|
||||
}
|
||||
|
||||
// RevokeOAuthToken takes an access token and deletes it. A client may only delete its own tokens.
|
||||
//
|
||||
// POST /api/v1/oauth/revoke
|
||||
func RevokeOAuthToken(router *gin.RouterGroup) {
|
||||
router.POST("/oauth/revoke", func(c *gin.Context) {
|
||||
// Prevent CDNs from caching this endpoint.
|
||||
if header.IsCdn(c.Request) {
|
||||
AbortNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Get client IP address for logs and rate limiting checks.
|
||||
clientIp := ClientIP(c)
|
||||
|
||||
// Abort if running in public mode.
|
||||
if get.Config().Public() {
|
||||
event.AuditErr([]string{clientIp, "client", "delete session", "oauth2", authn.ErrDisabledInPublicMode.Error()})
|
||||
Abort(c, http.StatusForbidden, i18n.ErrForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// Token revocation request data.
|
||||
var f form.ClientToken
|
||||
|
||||
authToken := AuthToken(c)
|
||||
|
||||
// Get the auth token to be revoked from the submitted form values or the request header.
|
||||
if err = c.ShouldBind(&f); err != nil && authToken == "" {
|
||||
event.AuditWarn([]string{clientIp, "client", "delete session", "oauth2", "%s"}, err)
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
} else if f.Empty() {
|
||||
f.AuthToken = authToken
|
||||
f.TypeHint = form.ClientAccessToken
|
||||
}
|
||||
|
||||
// Check the token form values.
|
||||
if err = f.Validate(); err != nil {
|
||||
event.AuditWarn([]string{clientIp, "client", "delete session", "oauth2", "%s"}, err)
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Disable caching of responses.
|
||||
c.Header(header.CacheControl, header.CacheControlNoStore)
|
||||
|
||||
// Find session based on auth token.
|
||||
sess, err := entity.FindSession(rnd.SessionID(f.AuthToken))
|
||||
|
||||
if err != nil {
|
||||
event.AuditErr([]string{clientIp, "client %s", "session %s", "delete session as %s", "oauth2", "%s"}, clean.Log(sess.ClientInfo()), clean.Log(sess.RefID), sess.ClientRole().String(), err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, i18n.NewResponse(http.StatusUnauthorized, i18n.ErrUnauthorized))
|
||||
return
|
||||
} else if sess == nil {
|
||||
event.AuditErr([]string{clientIp, "client %s", "session %s", "delete session as %s", "oauth2", authn.Denied}, clean.Log(sess.ClientInfo()), clean.Log(sess.RefID), sess.ClientRole().String())
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, i18n.NewResponse(http.StatusUnauthorized, i18n.ErrUnauthorized))
|
||||
return
|
||||
} else if sess.Abort(c) {
|
||||
event.AuditErr([]string{clientIp, "client %s", "session %s", "delete session as %s", "oauth2", authn.Denied}, clean.Log(sess.ClientInfo()), clean.Log(sess.RefID), sess.ClientRole().String())
|
||||
return
|
||||
} else if !sess.IsClient() {
|
||||
event.AuditErr([]string{clientIp, "client %s", "session %s", "delete session as %s", "oauth2", authn.Denied}, clean.Log(sess.ClientInfo()), clean.Log(sess.RefID), sess.ClientRole().String())
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, i18n.NewResponse(http.StatusForbidden, i18n.ErrForbidden))
|
||||
return
|
||||
} else {
|
||||
event.AuditInfo([]string{clientIp, "client %s", "session %s", "delete session as %s", "oauth2", authn.Granted}, clean.Log(sess.ClientInfo()), clean.Log(sess.RefID), sess.ClientRole().String())
|
||||
}
|
||||
|
||||
// Delete session cache and database record.
|
||||
if err = sess.Delete(); err != nil {
|
||||
// Log error.
|
||||
event.AuditErr([]string{clientIp, "client %s", "session %s", "delete session as %s", "oauth2", "%s"}, clean.Log(sess.ClientInfo()), clean.Log(sess.RefID), sess.ClientRole().String(), err)
|
||||
|
||||
// Return JSON error.
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, i18n.NewResponse(http.StatusNotFound, i18n.ErrNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
// Log event.
|
||||
event.AuditInfo([]string{clientIp, "client %s", "session %s", "oauth2", "deleted"}, clean.Log(sess.ClientInfo()), clean.Log(sess.RefID))
|
||||
|
||||
// Return JSON response for confirmation.
|
||||
c.JSON(http.StatusOK, DeleteSessionResponse(sess.ID))
|
||||
})
|
||||
}
|
||||
@@ -143,7 +143,7 @@ func TestCreateSession(t *testing.T) {
|
||||
CreateSession(router)
|
||||
|
||||
r := PerformRequestWithBody(app, http.MethodPost, "/api/v1/session", form.AsJson(form.Login{
|
||||
UserName: "admin",
|
||||
Username: "admin",
|
||||
Password: "xxx",
|
||||
}))
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ func ConfirmUserPasscode(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
// Verify passcode.
|
||||
valid, passcode, err := user.VerifyPasscode(frm.Passcode)
|
||||
valid, passcode, err := user.VerifyPasscode(frm.Passcode())
|
||||
|
||||
if err != nil {
|
||||
event.AuditErr([]string{ClientIP(c), "session %s", "users", user.UserName, "failed to verify passcode", clean.Error(err)}, s.RefID)
|
||||
@@ -201,7 +201,7 @@ func DeactivateUserPasscode(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
// checkUserPasscodeAuth checks authentication and authorization for the passcode endpoints.
|
||||
func checkUserPasscodeAuth(c *gin.Context, action acl.Permission) (*entity.Session, *entity.User, *form.UserPasscode, error) {
|
||||
func checkUserPasscodeAuth(c *gin.Context, action acl.Permission) (*entity.Session, *entity.User, *form.Passcode, error) {
|
||||
conf := get.Config()
|
||||
|
||||
// Prevent caching of API response.
|
||||
@@ -244,7 +244,7 @@ func checkUserPasscodeAuth(c *gin.Context, action acl.Permission) (*entity.Sessi
|
||||
return s, nil, nil, errors.New("unsupported")
|
||||
}
|
||||
|
||||
frm := &form.UserPasscode{}
|
||||
frm := &form.Passcode{}
|
||||
|
||||
// Validate request parameters.
|
||||
if err := c.BindJSON(frm); err != nil {
|
||||
|
||||
@@ -6,12 +6,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
)
|
||||
|
||||
func TestCreateUserPasscode(t *testing.T) {
|
||||
@@ -37,10 +37,10 @@ func TestCreateUserPasscode(t *testing.T) {
|
||||
CreateUserPasscode(router)
|
||||
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||
|
||||
f := form.UserPasscode{
|
||||
Passcode: "",
|
||||
Password: "Alice123!",
|
||||
f := form.Passcode{
|
||||
Type: "totp",
|
||||
Password: "Alice123!",
|
||||
Code: "",
|
||||
}
|
||||
if pcStr, err := json.Marshal(f); err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -56,10 +56,10 @@ func TestCreateUserPasscode(t *testing.T) {
|
||||
CreateUserPasscode(router)
|
||||
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||
|
||||
f := form.UserPasscode{
|
||||
Passcode: "",
|
||||
Password: "abcdef",
|
||||
f := form.Passcode{
|
||||
Type: "xxx",
|
||||
Password: "abcdef",
|
||||
Code: "",
|
||||
}
|
||||
if pcStr, err := json.Marshal(f); err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -75,10 +75,10 @@ func TestCreateUserPasscode(t *testing.T) {
|
||||
CreateUserPasscode(router)
|
||||
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||
|
||||
f := form.UserPasscode{
|
||||
Passcode: "",
|
||||
Password: "wrong",
|
||||
f := form.Passcode{
|
||||
Type: "totp",
|
||||
Password: "wrong",
|
||||
Code: "",
|
||||
}
|
||||
if pcStr, err := json.Marshal(f); err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -94,10 +94,10 @@ func TestCreateUserPasscode(t *testing.T) {
|
||||
CreateUserPasscode(router)
|
||||
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||
|
||||
f := form.UserPasscode{
|
||||
Passcode: "",
|
||||
Password: "Alice123!",
|
||||
f := form.Passcode{
|
||||
Type: "totp",
|
||||
Password: "Alice123!",
|
||||
Code: "",
|
||||
}
|
||||
if pcStr, err := json.Marshal(f); err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -122,10 +122,10 @@ func TestConfirmUserPasscode(t *testing.T) {
|
||||
ConfirmUserPasscode(router)
|
||||
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||
|
||||
f := form.UserPasscode{
|
||||
Passcode: "123",
|
||||
Password: "Alice123!",
|
||||
f := form.Passcode{
|
||||
Type: "totp",
|
||||
Password: "Alice123!",
|
||||
Code: "123",
|
||||
}
|
||||
if pcStr, err := json.Marshal(f); err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -141,10 +141,10 @@ func TestConfirmUserPasscode(t *testing.T) {
|
||||
ConfirmUserPasscode(router)
|
||||
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||
|
||||
f := form.UserPasscode{
|
||||
Passcode: "123456",
|
||||
Password: "Alice123!",
|
||||
f := form.Passcode{
|
||||
Type: "totp",
|
||||
Password: "Alice123!",
|
||||
Code: "123456",
|
||||
}
|
||||
if pcStr, err := json.Marshal(f); err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -163,10 +163,10 @@ func TestActivateUserPasscode(t *testing.T) {
|
||||
ActivateUserPasscode(router)
|
||||
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||
|
||||
f := form.UserPasscode{
|
||||
Passcode: "",
|
||||
Password: "Alice123!",
|
||||
f := form.Passcode{
|
||||
Type: "totp",
|
||||
Password: "Alice123!",
|
||||
Code: "",
|
||||
}
|
||||
if pcStr, err := json.Marshal(f); err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -182,10 +182,10 @@ func TestActivateUserPasscode(t *testing.T) {
|
||||
ActivateUserPasscode(router)
|
||||
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||
|
||||
f := form.UserPasscode{
|
||||
Passcode: "",
|
||||
Password: "Alice123!",
|
||||
f := form.Passcode{
|
||||
Type: "totp",
|
||||
Password: "Alice123!",
|
||||
Code: "",
|
||||
}
|
||||
if pcStr, err := json.Marshal(f); err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -213,10 +213,10 @@ func TestDeactivateUserPasscode(t *testing.T) {
|
||||
DeactivateUserPasscode(router)
|
||||
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||
|
||||
f := form.UserPasscode{
|
||||
Passcode: "",
|
||||
Password: "wrong",
|
||||
f := form.Passcode{
|
||||
Type: "totp",
|
||||
Password: "wrong",
|
||||
Code: "",
|
||||
}
|
||||
if pcStr, err := json.Marshal(f); err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -235,10 +235,10 @@ func TestUserPasscode(t *testing.T) {
|
||||
CreateUserPasscode(router)
|
||||
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||
|
||||
f0 := form.UserPasscode{
|
||||
Passcode: "",
|
||||
Password: "Alice123!",
|
||||
f0 := form.Passcode{
|
||||
Type: "totp",
|
||||
Password: "Alice123!",
|
||||
Code: "",
|
||||
}
|
||||
|
||||
pcStr, err := json.Marshal(f0)
|
||||
@@ -266,11 +266,12 @@ func TestUserPasscode(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
f := form.UserPasscode{
|
||||
Passcode: code,
|
||||
Password: "Alice123!",
|
||||
f := form.Passcode{
|
||||
Type: "totp",
|
||||
Password: "Alice123!",
|
||||
Code: code,
|
||||
}
|
||||
|
||||
pcStr, err = json.Marshal(f)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -443,9 +443,9 @@ func (m *Client) UpdateLastActive() *Client {
|
||||
}
|
||||
|
||||
// NewSession creates a new client session.
|
||||
func (m *Client) NewSession(c *gin.Context) *Session {
|
||||
func (m *Client) NewSession(c *gin.Context, t authn.GrantType) *Session {
|
||||
// Create, initialize, and return new session.
|
||||
return NewSession(m.AuthExpires, 0).SetContext(c).SetClient(m)
|
||||
return NewSession(m.AuthExpires, 0).SetContext(c).SetClient(m).SetGrantType(t)
|
||||
}
|
||||
|
||||
// EnforceAuthTokenLimit deletes client sessions above the configured limit and returns the number of deleted sessions.
|
||||
|
||||
@@ -51,6 +51,7 @@ type Session struct {
|
||||
AuthDomain string `gorm:"type:VARBINARY(255);default:'';" json:"AuthDomain" yaml:"AuthDomain,omitempty"`
|
||||
AuthID string `gorm:"type:VARBINARY(255);index;default:'';" json:"AuthID" yaml:"AuthID,omitempty"`
|
||||
AuthScope string `gorm:"size:1024;default:'';" json:"AuthScope" yaml:"AuthScope,omitempty"`
|
||||
GrantType string `gorm:"type:VARBINARY(64);default:'';" json:"GrantType" yaml:"GrantType,omitempty"`
|
||||
LastActive int64 `json:"LastActive" yaml:"LastActive,omitempty"`
|
||||
SessExpires int64 `gorm:"index" json:"Expires" yaml:"Expires,omitempty"`
|
||||
SessTimeout int64 `json:"Timeout" yaml:"Timeout,omitempty"`
|
||||
@@ -514,6 +515,38 @@ func (m *Session) SetAuthID(id string) *Session {
|
||||
return m
|
||||
}
|
||||
|
||||
// Provider returns the authentication provider.
|
||||
func (m *Session) Provider() authn.ProviderType {
|
||||
return authn.Provider(m.AuthProvider)
|
||||
}
|
||||
|
||||
// SetProvider updates the session's authentication provider.
|
||||
func (m *Session) SetProvider(provider authn.ProviderType) *Session {
|
||||
if provider == "" {
|
||||
return m
|
||||
}
|
||||
|
||||
m.AuthProvider = provider.String()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// Method returns the authentication method.
|
||||
func (m *Session) Method() authn.MethodType {
|
||||
return authn.Method(m.AuthMethod)
|
||||
}
|
||||
|
||||
// SetMethod sets a custom authentication method.
|
||||
func (m *Session) SetMethod(method authn.MethodType) *Session {
|
||||
if method == "" {
|
||||
return m
|
||||
}
|
||||
|
||||
m.AuthMethod = method.String()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// Scope returns the authorization scope as a sanitized string.
|
||||
func (m *Session) Scope() string {
|
||||
return clean.Scope(m.AuthScope)
|
||||
@@ -573,34 +606,18 @@ func (m *Session) SetScope(scope string) *Session {
|
||||
return m
|
||||
}
|
||||
|
||||
// Provider returns the authentication provider.
|
||||
func (m *Session) Provider() authn.ProviderType {
|
||||
return authn.Provider(m.AuthProvider)
|
||||
// AuthGrantType returns the session's grant type as authn.GrantType.
|
||||
func (m *Session) AuthGrantType() authn.GrantType {
|
||||
return authn.Grant(m.GrantType)
|
||||
}
|
||||
|
||||
// SetProvider updates the session's authentication provider.
|
||||
func (m *Session) SetProvider(provider authn.ProviderType) *Session {
|
||||
if provider == "" {
|
||||
// SetGrantType sets the session's grant type if no type has been set yet.
|
||||
func (m *Session) SetGrantType(t authn.GrantType) *Session {
|
||||
if t.IsUndefined() || m.GrantType != "" {
|
||||
return m
|
||||
}
|
||||
|
||||
m.AuthProvider = provider.String()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// Method returns the authentication method.
|
||||
func (m *Session) Method() authn.MethodType {
|
||||
return authn.Method(m.AuthMethod)
|
||||
}
|
||||
|
||||
// SetMethod sets a custom authentication method.
|
||||
func (m *Session) SetMethod(method authn.MethodType) *Session {
|
||||
if method == "" {
|
||||
return m
|
||||
}
|
||||
|
||||
m.AuthMethod = method.String()
|
||||
m.GrantType = t.String()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
|
||||
// Auth checks if the credentials are valid and returns the user and authentication provider.
|
||||
var Auth = func(f form.Login, m *Session, c *gin.Context) (user *User, provider authn.ProviderType, method authn.MethodType, err error) {
|
||||
// Get username from login form.
|
||||
nameName := f.Username()
|
||||
// Get sanitized username from login form.
|
||||
nameName := f.CleanUsername()
|
||||
|
||||
// Find registered user account.
|
||||
user = FindUserByName(nameName)
|
||||
@@ -77,16 +77,16 @@ func AuthLocal(user *User, f form.Login, m *Session, c *gin.Context) (provider a
|
||||
// Get client IP from request context.
|
||||
clientIp := header.ClientIP(c)
|
||||
|
||||
// Get username from login form.
|
||||
userName := f.Username()
|
||||
// Get sanitized username from login form.
|
||||
username := f.CleanUsername()
|
||||
|
||||
// Check if user account exists.
|
||||
if user == nil {
|
||||
message := authn.ErrAccountNotFound.Error()
|
||||
|
||||
if m != nil {
|
||||
event.AuditWarn([]string{clientIp, "session %s", "login as %s", message}, m.RefID, clean.LogQuote(userName))
|
||||
event.LoginError(clientIp, "api", userName, m.UserAgent, message)
|
||||
event.AuditWarn([]string{clientIp, "session %s", "login as %s", message}, m.RefID, clean.LogQuote(username))
|
||||
event.LoginError(clientIp, "api", username, m.UserAgent, message)
|
||||
m.Status = http.StatusUnauthorized
|
||||
}
|
||||
|
||||
@@ -98,8 +98,8 @@ func AuthLocal(user *User, f form.Login, m *Session, c *gin.Context) (provider a
|
||||
message := fmt.Sprintf("%s authentication disabled", authn.ProviderLocal.String())
|
||||
|
||||
if m != nil {
|
||||
event.AuditWarn([]string{clientIp, "session %s", "login as %s", message}, m.RefID, clean.LogQuote(userName))
|
||||
event.LoginError(clientIp, "api", userName, m.UserAgent, message)
|
||||
event.AuditWarn([]string{clientIp, "session %s", "login as %s", message}, m.RefID, clean.LogQuote(username))
|
||||
event.LoginError(clientIp, "api", username, m.UserAgent, message)
|
||||
m.Status = http.StatusUnauthorized
|
||||
}
|
||||
|
||||
@@ -108,8 +108,8 @@ func AuthLocal(user *User, f form.Login, m *Session, c *gin.Context) (provider a
|
||||
message := authn.ErrAccountDisabled.Error()
|
||||
|
||||
if m != nil {
|
||||
event.AuditWarn([]string{clientIp, "session %s", "login as %s", message}, m.RefID, clean.LogQuote(userName))
|
||||
event.LoginError(clientIp, "api", userName, m.UserAgent, message)
|
||||
event.AuditWarn([]string{clientIp, "session %s", "login as %s", message}, m.RefID, clean.LogQuote(username))
|
||||
event.LoginError(clientIp, "api", username, m.UserAgent, message)
|
||||
m.Status = http.StatusUnauthorized
|
||||
}
|
||||
|
||||
@@ -120,8 +120,8 @@ func AuthLocal(user *User, f form.Login, m *Session, c *gin.Context) (provider a
|
||||
if authSess, authUser, authErr := AuthSession(f, c); authSess != nil && authUser != nil && authErr == nil {
|
||||
if !authUser.IsRegistered() || authUser.UserUID != user.UserUID {
|
||||
message := authn.ErrInvalidUsername.Error()
|
||||
event.AuditErr([]string{clientIp, "session %s", "login as %s with app password", message}, m.RefID, clean.LogQuote(userName))
|
||||
event.LoginError(clientIp, "api", userName, m.UserAgent, message)
|
||||
event.AuditErr([]string{clientIp, "session %s", "login as %s with app password", message}, m.RefID, clean.LogQuote(username))
|
||||
event.LoginError(clientIp, "api", username, m.UserAgent, message)
|
||||
m.Status = http.StatusUnauthorized
|
||||
return provider, method, i18n.Error(i18n.ErrInvalidCredentials)
|
||||
} else if insufficientScope := authSess.InsufficientScope(acl.ResourceSessions, acl.Permissions{acl.ActionCreate}); insufficientScope || !authSess.IsClient() {
|
||||
@@ -131,8 +131,8 @@ func AuthLocal(user *User, f form.Login, m *Session, c *gin.Context) (provider a
|
||||
} else {
|
||||
message = authn.ErrUnauthorized.Error()
|
||||
}
|
||||
event.AuditErr([]string{clientIp, "session %s", "login as %s with app password", message}, m.RefID, clean.LogQuote(userName))
|
||||
event.LoginError(clientIp, "api", userName, m.UserAgent, message)
|
||||
event.AuditErr([]string{clientIp, "session %s", "login as %s with app password", message}, m.RefID, clean.LogQuote(username))
|
||||
event.LoginError(clientIp, "api", username, m.UserAgent, message)
|
||||
m.Status = http.StatusUnauthorized
|
||||
return provider, method, i18n.Error(i18n.ErrInvalidCredentials)
|
||||
} else {
|
||||
@@ -142,8 +142,8 @@ func AuthLocal(user *User, f form.Login, m *Session, c *gin.Context) (provider a
|
||||
m.ClientName = authSess.ClientName
|
||||
m.SetScope(authSess.Scope())
|
||||
m.SetMethod(authn.MethodSession)
|
||||
event.AuditInfo([]string{clientIp, "session %s", "login as %s with app password", authn.Succeeded}, m.RefID, clean.LogQuote(userName))
|
||||
event.LoginInfo(clientIp, "api", userName, m.UserAgent)
|
||||
event.AuditInfo([]string{clientIp, "session %s", "login as %s with app password", authn.Succeeded}, m.RefID, clean.LogQuote(username))
|
||||
event.LoginInfo(clientIp, "api", username, m.UserAgent)
|
||||
return provider, method, authErr
|
||||
}
|
||||
}
|
||||
@@ -153,8 +153,8 @@ func AuthLocal(user *User, f form.Login, m *Session, c *gin.Context) (provider a
|
||||
message := authn.ErrInvalidPassword.Error()
|
||||
|
||||
if m != nil {
|
||||
event.AuditErr([]string{clientIp, "session %s", "login as %s", message}, m.RefID, clean.LogQuote(userName))
|
||||
event.LoginError(clientIp, "api", userName, m.UserAgent, message)
|
||||
event.AuditErr([]string{clientIp, "session %s", "login as %s", message}, m.RefID, clean.LogQuote(username))
|
||||
event.LoginError(clientIp, "api", username, m.UserAgent, message)
|
||||
m.Status = http.StatusUnauthorized
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ func AuthLocal(user *User, f form.Login, m *Session, c *gin.Context) (provider a
|
||||
|
||||
// Check two-factor authentication, if enabled.
|
||||
if method = user.Method(); method.Is(authn.Method2FA) {
|
||||
if valid, _, passcodeErr := user.VerifyPasscode(f.Passcode); passcodeErr != nil {
|
||||
if valid, _, passcodeErr := user.VerifyPasscode(f.Passcode()); passcodeErr != nil {
|
||||
return provider, method, passcodeErr
|
||||
} else if !valid {
|
||||
return provider, method, authn.ErrInvalidPasscode
|
||||
@@ -175,8 +175,8 @@ func AuthLocal(user *User, f form.Login, m *Session, c *gin.Context) (provider a
|
||||
}
|
||||
|
||||
if m != nil {
|
||||
event.AuditInfo([]string{clientIp, "session %s", "login as %s", authn.Succeeded}, m.RefID, clean.LogQuote(userName))
|
||||
event.LoginInfo(clientIp, "api", userName, m.UserAgent)
|
||||
event.AuditInfo([]string{clientIp, "session %s", "login as %s", authn.Succeeded}, m.RefID, clean.LogQuote(username))
|
||||
event.LoginInfo(clientIp, "api", username, m.UserAgent)
|
||||
}
|
||||
|
||||
return provider, method, nil
|
||||
@@ -211,6 +211,7 @@ func (m *Session) LogIn(f form.Login, c *gin.Context) (err error) {
|
||||
}
|
||||
|
||||
m.SetUser(user)
|
||||
m.SetGrantType(authn.GrantPassword)
|
||||
}
|
||||
|
||||
// Try to redeem link share token, if provided.
|
||||
@@ -219,18 +220,18 @@ func (m *Session) LogIn(f form.Login, c *gin.Context) (err error) {
|
||||
|
||||
// Redeem token.
|
||||
if user.IsRegistered() {
|
||||
if shares := user.RedeemToken(f.ShareToken); shares == 0 {
|
||||
if shares := user.RedeemToken(f.Token); shares == 0 {
|
||||
message := authn.ErrInvalidShareToken.Error()
|
||||
event.AuditWarn([]string{m.IP(), "session %s", message}, m.RefID)
|
||||
m.Status = http.StatusNotFound
|
||||
return i18n.Error(i18n.ErrInvalidLink)
|
||||
} else {
|
||||
event.AuditInfo([]string{m.IP(), "session %s", "token redeemed for %d shares"}, m.RefID, user.RedeemToken(f.ShareToken))
|
||||
event.AuditInfo([]string{m.IP(), "session %s", "token redeemed for %d shares"}, m.RefID, user.RedeemToken(f.Token))
|
||||
}
|
||||
} else if data := m.Data(); data == nil {
|
||||
m.Status = http.StatusInternalServerError
|
||||
return i18n.Error(i18n.ErrUnexpected)
|
||||
} else if shares := data.RedeemToken(f.ShareToken); shares == 0 {
|
||||
} else if shares := data.RedeemToken(f.Token); shares == 0 {
|
||||
message := authn.ErrInvalidShareToken.Error()
|
||||
event.AuditWarn([]string{m.IP(), "session %s", message}, m.RefID)
|
||||
event.LoginError(m.IP(), "api", "", m.UserAgent, message)
|
||||
@@ -239,6 +240,7 @@ func (m *Session) LogIn(f form.Login, c *gin.Context) (err error) {
|
||||
} else {
|
||||
m.SetData(data)
|
||||
m.SetProvider(authn.ProviderLink)
|
||||
m.SetGrantType(authn.GrantShareToken)
|
||||
event.AuditInfo([]string{m.IP(), "session %s", "token redeemed for %d shares"}, m.RefID, shares, data)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestAuthSession(t *testing.T) {
|
||||
t.Run("RandomAppPassword", func(t *testing.T) {
|
||||
// Create test request form.
|
||||
f := form.Login{
|
||||
UserName: "alice",
|
||||
Username: "alice",
|
||||
Password: rnd.AppPassword(),
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ func TestAuthSession(t *testing.T) {
|
||||
t.Run("RandomAuthToken", func(t *testing.T) {
|
||||
// Create test request form.
|
||||
f := form.Login{
|
||||
UserName: "alice",
|
||||
Username: "alice",
|
||||
Password: rnd.AuthToken(),
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func TestAuthSession(t *testing.T) {
|
||||
|
||||
// Create test request form.
|
||||
f := form.Login{
|
||||
UserName: "alice",
|
||||
Username: "alice",
|
||||
Password: s.AuthToken(),
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ func TestAuthSession(t *testing.T) {
|
||||
|
||||
// Create test request form.
|
||||
f := form.Login{
|
||||
UserName: "alice",
|
||||
Username: "alice",
|
||||
Password: s.AuthToken(),
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ func TestAuthSession(t *testing.T) {
|
||||
|
||||
// Create test request form.
|
||||
f := form.Login{
|
||||
UserName: "alice",
|
||||
Username: "alice",
|
||||
Password: s.AuthToken(),
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ func TestAuthSession(t *testing.T) {
|
||||
t.Run("EmptyPassword", func(t *testing.T) {
|
||||
// Create test request form.
|
||||
f := form.Login{
|
||||
UserName: "alice",
|
||||
Username: "alice",
|
||||
Password: "",
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ func TestAuthLocal(t *testing.T) {
|
||||
|
||||
// Create test request form.
|
||||
frm := form.Login{
|
||||
UserName: "alice",
|
||||
Username: "alice",
|
||||
Password: "Alice123!",
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ func TestAuthLocal(t *testing.T) {
|
||||
|
||||
// Create test request form.
|
||||
frm := form.Login{
|
||||
UserName: "alice",
|
||||
Username: "alice",
|
||||
Password: "photoprism",
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ func TestAuthLocal(t *testing.T) {
|
||||
|
||||
// Create test request form.
|
||||
frm := form.Login{
|
||||
UserName: "friend",
|
||||
Username: "friend",
|
||||
Password: "!Friend321",
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ func TestAuthLocal(t *testing.T) {
|
||||
|
||||
// Create test request form.
|
||||
frm := form.Login{
|
||||
UserName: "friend",
|
||||
Username: "friend",
|
||||
Password: "!Friend321",
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ func TestSessionLogIn(t *testing.T) {
|
||||
|
||||
// Create login form.
|
||||
frm := form.Login{
|
||||
UserName: "admin",
|
||||
Username: "admin",
|
||||
Password: "photoprism",
|
||||
}
|
||||
|
||||
@@ -310,9 +310,9 @@ func TestSessionLogIn(t *testing.T) {
|
||||
|
||||
// Create login form.
|
||||
frm := form.Login{
|
||||
UserName: "jane",
|
||||
Passcode: passcode,
|
||||
Username: "jane",
|
||||
Password: "Jane123!",
|
||||
Code: passcode,
|
||||
}
|
||||
|
||||
// Create test request context.
|
||||
@@ -331,9 +331,9 @@ func TestSessionLogIn(t *testing.T) {
|
||||
|
||||
// Create login form.
|
||||
frm := form.Login{
|
||||
UserName: "jane",
|
||||
Passcode: "xxxxxx",
|
||||
Username: "jane",
|
||||
Password: "Jane123!",
|
||||
Code: "xxxxxx",
|
||||
}
|
||||
|
||||
// Create test request context.
|
||||
@@ -352,7 +352,7 @@ func TestSessionLogIn(t *testing.T) {
|
||||
|
||||
// Create login form.
|
||||
frm := form.Login{
|
||||
UserName: "jane",
|
||||
Username: "jane",
|
||||
Password: "Jane123!",
|
||||
}
|
||||
|
||||
@@ -372,7 +372,7 @@ func TestSessionLogIn(t *testing.T) {
|
||||
|
||||
// Create login form.
|
||||
frm := form.Login{
|
||||
UserName: "admin",
|
||||
Username: "admin",
|
||||
Password: "wrong",
|
||||
}
|
||||
|
||||
@@ -392,7 +392,7 @@ func TestSessionLogIn(t *testing.T) {
|
||||
|
||||
// Create login form.
|
||||
frm := form.Login{
|
||||
UserName: "foo",
|
||||
Username: "foo",
|
||||
Password: "password",
|
||||
}
|
||||
|
||||
@@ -412,7 +412,7 @@ func TestSessionLogIn(t *testing.T) {
|
||||
|
||||
// Create login form.
|
||||
frm := form.Login{
|
||||
ShareToken: "1jxf3jfn2k",
|
||||
Token: "1jxf3jfn2k",
|
||||
}
|
||||
|
||||
// Create test request context.
|
||||
@@ -432,7 +432,7 @@ func TestSessionLogIn(t *testing.T) {
|
||||
|
||||
// Create login form.
|
||||
frm := form.Login{
|
||||
ShareToken: "1jxf3jfxxx",
|
||||
Token: "1jxf3jfxxx",
|
||||
}
|
||||
|
||||
// Create test request context.
|
||||
@@ -470,7 +470,7 @@ func TestSessionLogIn(t *testing.T) {
|
||||
|
||||
// Create login form.
|
||||
frm := form.Login{
|
||||
ShareToken: "1jxf3jfn2k",
|
||||
Token: "1jxf3jfn2k",
|
||||
}
|
||||
|
||||
// Create test request context.
|
||||
@@ -490,7 +490,7 @@ func TestSessionLogIn(t *testing.T) {
|
||||
|
||||
// Create login form.
|
||||
frm := form.Login{
|
||||
ShareToken: "1jxf3jfxxx",
|
||||
Token: "1jxf3jfxxx",
|
||||
}
|
||||
|
||||
// Create test request context.
|
||||
@@ -514,9 +514,9 @@ func TestSessionLogIn(t *testing.T) {
|
||||
|
||||
// Create login form.
|
||||
frm := form.Login{
|
||||
UserName: "jane",
|
||||
Passcode: passcode,
|
||||
Username: "jane",
|
||||
Password: "Jane123!",
|
||||
Code: passcode,
|
||||
}
|
||||
|
||||
// Create test request context.
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/report"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/header"
|
||||
"github.com/photoprism/photoprism/pkg/report"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/unix"
|
||||
)
|
||||
@@ -544,6 +544,80 @@ func TestSession_SetAuthID(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSession_SetMethod(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
s := &Session{
|
||||
UserName: "test",
|
||||
RefID: "sessxkkcxxxz",
|
||||
AuthProvider: authn.ProviderAccessToken.String(),
|
||||
AuthMethod: authn.MethodPersonal.String(),
|
||||
}
|
||||
|
||||
m := s.SetMethod("")
|
||||
|
||||
assert.Equal(t, authn.ProviderAccessToken, m.Provider())
|
||||
assert.Equal(t, authn.MethodPersonal, m.Method())
|
||||
})
|
||||
t.Run("Test", func(t *testing.T) {
|
||||
s := &Session{
|
||||
UserName: "test",
|
||||
RefID: "sessxkkcxxxz",
|
||||
AuthProvider: authn.ProviderAccessToken.String(),
|
||||
AuthMethod: authn.MethodPersonal.String(),
|
||||
}
|
||||
|
||||
m := s.SetMethod("Test")
|
||||
|
||||
assert.Equal(t, authn.ProviderAccessToken, m.Provider())
|
||||
assert.Equal(t, authn.Method("Test"), m.Method())
|
||||
})
|
||||
t.Run("Test", func(t *testing.T) {
|
||||
s := &Session{
|
||||
UserName: "test",
|
||||
RefID: "sessxkkcxxxz",
|
||||
AuthProvider: authn.ProviderAccessToken.String(),
|
||||
AuthMethod: authn.MethodPersonal.String(),
|
||||
}
|
||||
|
||||
m := s.SetMethod(authn.MethodSession)
|
||||
|
||||
assert.Equal(t, authn.ProviderAccessToken, m.Provider())
|
||||
assert.Equal(t, authn.MethodSession, m.Method())
|
||||
})
|
||||
}
|
||||
|
||||
func TestSession_SetProvider(t *testing.T) {
|
||||
m := FindSessionByRefID("sessxkkcabce")
|
||||
assert.Equal(t, authn.ProviderDefault, m.Provider())
|
||||
m.SetProvider("")
|
||||
assert.Equal(t, authn.ProviderDefault, m.Provider())
|
||||
m.SetProvider(authn.ProviderLink)
|
||||
assert.Equal(t, authn.ProviderLink, m.Provider())
|
||||
m.SetProvider(authn.ProviderDefault)
|
||||
assert.Equal(t, authn.ProviderDefault, m.Provider())
|
||||
}
|
||||
|
||||
func TestSession_ChangePassword(t *testing.T) {
|
||||
m := FindSessionByRefID("sessxkkcabce")
|
||||
assert.Empty(t, m.PreviewToken)
|
||||
assert.Empty(t, m.DownloadToken)
|
||||
|
||||
err := m.ChangePassword("photoprism123")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.NotEmpty(t, m.PreviewToken)
|
||||
assert.NotEmpty(t, m.DownloadToken)
|
||||
|
||||
err2 := m.ChangePassword("Bobbob123!")
|
||||
|
||||
if err2 != nil {
|
||||
t.Fatal(err2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSession_ValidateScope(t *testing.T) {
|
||||
t.Run("AnyScope", func(t *testing.T) {
|
||||
s := &Session{
|
||||
@@ -659,78 +733,37 @@ func TestSession_SetScope(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSession_SetMethod(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
s := &Session{
|
||||
UserName: "test",
|
||||
RefID: "sessxkkcxxxz",
|
||||
AuthProvider: authn.ProviderAccessToken.String(),
|
||||
AuthMethod: authn.MethodPersonal.String(),
|
||||
func TestSession_SetGrantType(t *testing.T) {
|
||||
t.Run("Password", func(t *testing.T) {
|
||||
m := &Session{
|
||||
UserName: "test",
|
||||
RefID: "sessxkkcxxxz",
|
||||
AuthScope: "*",
|
||||
}
|
||||
|
||||
m := s.SetMethod("")
|
||||
expected := "password"
|
||||
|
||||
assert.Equal(t, authn.ProviderAccessToken, m.Provider())
|
||||
assert.Equal(t, authn.MethodPersonal, m.Method())
|
||||
m.SetGrantType(authn.GrantPassword)
|
||||
assert.Equal(t, expected, m.GrantType)
|
||||
m.SetGrantType(authn.GrantClientCredentials)
|
||||
assert.Equal(t, expected, m.GrantType)
|
||||
m.SetGrantType(authn.GrantUndefined)
|
||||
assert.Equal(t, expected, m.GrantType)
|
||||
assert.Equal(t, authn.GrantPassword, m.AuthGrantType())
|
||||
})
|
||||
t.Run("Test", func(t *testing.T) {
|
||||
s := &Session{
|
||||
UserName: "test",
|
||||
RefID: "sessxkkcxxxz",
|
||||
AuthProvider: authn.ProviderAccessToken.String(),
|
||||
AuthMethod: authn.MethodPersonal.String(),
|
||||
}
|
||||
t.Run("ClientCredentials", func(t *testing.T) {
|
||||
client := ClientFixtures.Pointer("alice")
|
||||
m := client.NewSession(&gin.Context{}, authn.GrantClientCredentials)
|
||||
|
||||
m := s.SetMethod("Test")
|
||||
expected := "client_credentials"
|
||||
|
||||
assert.Equal(t, authn.ProviderAccessToken, m.Provider())
|
||||
assert.Equal(t, authn.Method("Test"), m.Method())
|
||||
assert.Equal(t, expected, m.GrantType)
|
||||
m.SetGrantType(authn.GrantPassword)
|
||||
assert.Equal(t, expected, m.GrantType)
|
||||
m.SetGrantType(authn.GrantUndefined)
|
||||
assert.Equal(t, expected, m.GrantType)
|
||||
assert.Equal(t, authn.GrantClientCredentials, m.AuthGrantType())
|
||||
})
|
||||
t.Run("Test", func(t *testing.T) {
|
||||
s := &Session{
|
||||
UserName: "test",
|
||||
RefID: "sessxkkcxxxz",
|
||||
AuthProvider: authn.ProviderAccessToken.String(),
|
||||
AuthMethod: authn.MethodPersonal.String(),
|
||||
}
|
||||
|
||||
m := s.SetMethod(authn.MethodSession)
|
||||
|
||||
assert.Equal(t, authn.ProviderAccessToken, m.Provider())
|
||||
assert.Equal(t, authn.MethodSession, m.Method())
|
||||
})
|
||||
}
|
||||
|
||||
func TestSession_SetProvider(t *testing.T) {
|
||||
m := FindSessionByRefID("sessxkkcabce")
|
||||
assert.Equal(t, authn.ProviderDefault, m.Provider())
|
||||
m.SetProvider("")
|
||||
assert.Equal(t, authn.ProviderDefault, m.Provider())
|
||||
m.SetProvider(authn.ProviderLink)
|
||||
assert.Equal(t, authn.ProviderLink, m.Provider())
|
||||
m.SetProvider(authn.ProviderDefault)
|
||||
assert.Equal(t, authn.ProviderDefault, m.Provider())
|
||||
}
|
||||
|
||||
func TestSession_ChangePassword(t *testing.T) {
|
||||
m := FindSessionByRefID("sessxkkcabce")
|
||||
assert.Empty(t, m.PreviewToken)
|
||||
assert.Empty(t, m.DownloadToken)
|
||||
|
||||
err := m.ChangePassword("photoprism123")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.NotEmpty(t, m.PreviewToken)
|
||||
assert.NotEmpty(t, m.DownloadToken)
|
||||
|
||||
err2 := m.ChangePassword("Bobbob123!")
|
||||
|
||||
if err2 != nil {
|
||||
t.Fatal(err2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSession_SetPreviewToken(t *testing.T) {
|
||||
|
||||
@@ -13,7 +13,7 @@ func LoginData(level logrus.Level, ip, realm, name, browser, message string) Dat
|
||||
"level": level.String(),
|
||||
"ip": txt.Clip(ip, txt.ClipIP),
|
||||
"realm": txt.Clip(realm, txt.ClipRealm),
|
||||
"name": txt.Clip(name, txt.ClipUserName),
|
||||
"name": txt.Clip(name, txt.ClipUsername),
|
||||
"browser": txt.Clip(browser, txt.ClipLog),
|
||||
"message": txt.Clip(message, txt.ClipLog),
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package form
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
)
|
||||
|
||||
// ClientCredentials represents client authentication request credentials.
|
||||
type ClientCredentials struct {
|
||||
ClientID string `form:"client_id" json:"client_id,omitempty"`
|
||||
ClientSecret string `form:"client_secret" json:" client_secret,omitempty"`
|
||||
AuthScope string `form:"scope" json:"scope,omitempty"`
|
||||
}
|
||||
|
||||
// Validate checks the grant type and credentials.
|
||||
func (f ClientCredentials) Validate() error {
|
||||
// Check client ID.
|
||||
if f.ClientID == "" {
|
||||
return fmt.Errorf("missing client id")
|
||||
} else if rnd.InvalidUID(f.ClientID, 'c') {
|
||||
return fmt.Errorf("invalid client id")
|
||||
}
|
||||
|
||||
// Check client secret.
|
||||
if f.ClientSecret == "" {
|
||||
return fmt.Errorf("missing client secret")
|
||||
} else if !rnd.IsAlnum(f.ClientSecret) {
|
||||
return fmt.Errorf("invalid client secret")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Scope returns the client scopes as sanitized string.
|
||||
func (f ClientCredentials) Scope() string {
|
||||
return clean.Scope(f.AuthScope)
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package form
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestClientCredentials_Validate(t *testing.T) {
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
m := ClientCredentials{
|
||||
ClientID: "cs5gfen1bgxz7s9i",
|
||||
ClientSecret: "abc",
|
||||
AuthScope: "*",
|
||||
}
|
||||
|
||||
assert.NoError(t, m.Validate())
|
||||
assert.Equal(t, "*", m.Scope())
|
||||
})
|
||||
t.Run("NoClientID", func(t *testing.T) {
|
||||
m := ClientCredentials{
|
||||
ClientID: "",
|
||||
ClientSecret: "Alice123!",
|
||||
AuthScope: "*",
|
||||
}
|
||||
|
||||
assert.Error(t, m.Validate())
|
||||
})
|
||||
t.Run("InvalidClientID", func(t *testing.T) {
|
||||
m := ClientCredentials{
|
||||
ClientID: "s5gfen1bgxz7s9i",
|
||||
ClientSecret: "Alice123!",
|
||||
AuthScope: "*",
|
||||
}
|
||||
|
||||
assert.Error(t, m.Validate())
|
||||
})
|
||||
t.Run("NoSecret", func(t *testing.T) {
|
||||
m := ClientCredentials{
|
||||
ClientID: "cs5gfen1bgxz7s9i",
|
||||
ClientSecret: "",
|
||||
AuthScope: "*",
|
||||
}
|
||||
|
||||
assert.Error(t, m.Validate())
|
||||
})
|
||||
t.Run("InvalidSecret", func(t *testing.T) {
|
||||
m := ClientCredentials{
|
||||
ClientID: "cs5gfen1bgxz7s9i",
|
||||
ClientSecret: "abc 123",
|
||||
AuthScope: "*",
|
||||
}
|
||||
|
||||
assert.Error(t, m.Validate())
|
||||
})
|
||||
}
|
||||
@@ -2,51 +2,57 @@ package form
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
// Login represents a login form.
|
||||
type Login struct {
|
||||
UserName string `json:"username,omitempty"`
|
||||
UserEmail string `json:"email,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Passcode string `json:"passcode,omitempty"`
|
||||
ShareToken string `json:"token,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Code string `json:"code,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
}
|
||||
|
||||
// Username returns the sanitized username in lowercase.
|
||||
func (f Login) Username() string {
|
||||
return clean.Username(f.UserName)
|
||||
// CleanUsername returns the sanitized and normalized username.
|
||||
func (f Login) CleanUsername() string {
|
||||
return clean.Username(f.Username)
|
||||
}
|
||||
|
||||
// Email returns the sanitized email in lowercase.
|
||||
func (f Login) Email() string {
|
||||
return clean.Email(f.UserEmail)
|
||||
}
|
||||
|
||||
// HasUsername checks if a username is set.
|
||||
func (f Login) HasUsername() bool {
|
||||
if l := len(f.Username()); l == 0 || l > 255 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// HasPasscode checks if a passcode is set.
|
||||
func (f Login) HasPasscode() bool {
|
||||
return f.Passcode != "" && len(f.Passcode) <= 255
|
||||
}
|
||||
|
||||
// HasPassword checks if a password is set.
|
||||
func (f Login) HasPassword() bool {
|
||||
return f.Password != "" && len(f.Password) <= 255
|
||||
}
|
||||
|
||||
// HasShareToken checks if a link share token has been provided.
|
||||
func (f Login) HasShareToken() bool {
|
||||
return f.ShareToken != ""
|
||||
// CleanEmail returns the sanitized and normalized email.
|
||||
func (f Login) CleanEmail() string {
|
||||
return clean.Email(f.Email)
|
||||
}
|
||||
|
||||
// HasCredentials checks if all credentials is set.
|
||||
func (f Login) HasCredentials() bool {
|
||||
return f.HasUsername() && f.HasPassword()
|
||||
}
|
||||
|
||||
// HasUsername checks if a username is set.
|
||||
func (f Login) HasUsername() bool {
|
||||
if l := len(f.CleanUsername()); l == 0 || l > txt.ClipUsername {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// HasPassword checks if a password is set.
|
||||
func (f Login) HasPassword() bool {
|
||||
return f.Password != "" && len(f.Password) <= txt.ClipPassword
|
||||
}
|
||||
|
||||
// HasPasscode checks if a verification passcode has been provided.
|
||||
func (f Login) HasPasscode() bool {
|
||||
return clean.Passcode(f.Code) != ""
|
||||
}
|
||||
|
||||
// Passcode returns the sanitized verification passcode.
|
||||
func (f Login) Passcode() string {
|
||||
return clean.Passcode(f.Code)
|
||||
}
|
||||
|
||||
// HasShareToken checks if a link share token has been provided.
|
||||
func (f Login) HasShareToken() bool {
|
||||
return f.Token != ""
|
||||
}
|
||||
|
||||
@@ -6,68 +6,94 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLogin_Email(t *testing.T) {
|
||||
func TestLogin_CleanEmail(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
form := &Login{UserEmail: "", Password: "passwd", ShareToken: ""}
|
||||
assert.Equal(t, "", form.Email())
|
||||
form := &Login{Email: "", Password: "passwd", Token: ""}
|
||||
assert.Equal(t, "", form.CleanEmail())
|
||||
})
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
form := &Login{UserEmail: "test@test.com", UserName: "John", Password: "passwd", ShareToken: "123"}
|
||||
assert.Equal(t, "test@test.com", form.Email())
|
||||
form := &Login{Email: "test@test.com", Username: "John", Password: "passwd", Token: "123"}
|
||||
assert.Equal(t, "test@test.com", form.CleanEmail())
|
||||
})
|
||||
}
|
||||
|
||||
func TestLogin_HasToken(t *testing.T) {
|
||||
t.Run("false", func(t *testing.T) {
|
||||
form := &Login{UserEmail: "test@test.com", UserName: "John", Password: "passwd", ShareToken: ""}
|
||||
form := &Login{Email: "test@test.com", Username: "John", Password: "passwd", Token: ""}
|
||||
assert.Equal(t, false, form.HasShareToken())
|
||||
})
|
||||
t.Run("true", func(t *testing.T) {
|
||||
form := &Login{UserEmail: "test@test.com", UserName: "John", Password: "passwd", ShareToken: "123"}
|
||||
form := &Login{Email: "test@test.com", Username: "John", Password: "passwd", Token: "123"}
|
||||
assert.Equal(t, true, form.HasShareToken())
|
||||
})
|
||||
}
|
||||
|
||||
func TestLogin_HasName(t *testing.T) {
|
||||
func TestLogin_HasUsername(t *testing.T) {
|
||||
t.Run("false", func(t *testing.T) {
|
||||
form := &Login{UserEmail: "test@test.com", Password: "passwd", ShareToken: ""}
|
||||
form := &Login{Email: "test@test.com", Password: "passwd", Token: ""}
|
||||
assert.Equal(t, false, form.HasUsername())
|
||||
})
|
||||
t.Run("true", func(t *testing.T) {
|
||||
form := &Login{UserEmail: "test@test.com", UserName: "John", Password: "passwd", ShareToken: "123"}
|
||||
form := &Login{Email: "test@test.com", Username: "John", Password: "passwd", Token: "123"}
|
||||
assert.Equal(t, true, form.HasUsername())
|
||||
})
|
||||
}
|
||||
|
||||
func TestLogin_HasPasscode(t *testing.T) {
|
||||
func TestLogin_CleanUsername(t *testing.T) {
|
||||
t.Run("false", func(t *testing.T) {
|
||||
form := &Login{UserEmail: "test@test.com", Passcode: "", ShareToken: ""}
|
||||
assert.Equal(t, false, form.HasPasscode())
|
||||
form := &Login{Email: "test@test.com", Password: "passwd", Token: ""}
|
||||
assert.Equal(t, "", form.CleanUsername())
|
||||
})
|
||||
t.Run("true", func(t *testing.T) {
|
||||
form := &Login{UserEmail: "test@test.com", UserName: "John", Passcode: "123456", ShareToken: "123"}
|
||||
form := &Login{Email: "test@test.com", Username: " John", Password: "passwd", Token: "123"}
|
||||
assert.Equal(t, "john", form.CleanUsername())
|
||||
})
|
||||
}
|
||||
|
||||
func TestLogin_HasPasscode(t *testing.T) {
|
||||
t.Run("False", func(t *testing.T) {
|
||||
form := &Login{Email: "test@test.com", Code: "", Token: ""}
|
||||
assert.Equal(t, false, form.HasPasscode())
|
||||
})
|
||||
t.Run("True", func(t *testing.T) {
|
||||
form := &Login{Email: "test@test.com", Username: "John", Code: "123456", Token: "123"}
|
||||
assert.Equal(t, true, form.HasPasscode())
|
||||
})
|
||||
}
|
||||
|
||||
func TestLogin_Passcode(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
form := &Login{Username: "admin", Password: "passwd1234", Code: "", Token: ""}
|
||||
assert.Equal(t, "", form.Passcode())
|
||||
})
|
||||
t.Run("Recovery", func(t *testing.T) {
|
||||
form := &Login{Username: "admin", Password: "passwd1234", Code: " A23 456 H7l pwf"}
|
||||
assert.Equal(t, "a23456h7lpwf", form.Passcode())
|
||||
})
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
form := &Login{Username: "admin", Password: "passwd1234", Code: "123456", Token: "123"}
|
||||
assert.Equal(t, "123456", form.Passcode())
|
||||
})
|
||||
}
|
||||
|
||||
func TestLogin_HasPassword(t *testing.T) {
|
||||
t.Run("false", func(t *testing.T) {
|
||||
form := &Login{UserEmail: "test@test.com", Password: "", ShareToken: ""}
|
||||
form := &Login{Email: "test@test.com", Password: "", Token: ""}
|
||||
assert.Equal(t, false, form.HasPassword())
|
||||
})
|
||||
t.Run("true", func(t *testing.T) {
|
||||
form := &Login{UserEmail: "test@test.com", UserName: "John", Password: "passwd", ShareToken: "123"}
|
||||
form := &Login{Email: "test@test.com", Username: "John", Password: "passwd", Token: "123"}
|
||||
assert.Equal(t, true, form.HasPassword())
|
||||
})
|
||||
}
|
||||
|
||||
func TestLogin_HasCredentials(t *testing.T) {
|
||||
t.Run("false", func(t *testing.T) {
|
||||
form := &Login{UserEmail: "test@test.com", Password: "passwd123", ShareToken: ""}
|
||||
form := &Login{Email: "test@test.com", Password: "passwd123", Token: ""}
|
||||
assert.Equal(t, false, form.HasCredentials())
|
||||
})
|
||||
t.Run("true", func(t *testing.T) {
|
||||
form := &Login{UserEmail: "test@test.com", UserName: "John", Password: "passwd", ShareToken: "123"}
|
||||
form := &Login{Email: "test@test.com", Username: "John", Password: "passwd", Token: "123"}
|
||||
assert.Equal(t, true, form.HasCredentials())
|
||||
})
|
||||
}
|
||||
|
||||
70
internal/form/oauth_create_token.go
Normal file
70
internal/form/oauth_create_token.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package form
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
// OAuthCreateToken represents a create token request form.
|
||||
type OAuthCreateToken struct {
|
||||
GrantType authn.GrantType `form:"grant_type" json:"grant_type,omitempty"`
|
||||
ClientID string `form:"client_id" json:"client_id,omitempty"`
|
||||
ClientSecret string `form:"client_secret" json:" client_secret,omitempty"`
|
||||
Username string `form:"username" json:"username,omitempty"`
|
||||
Password string `form:"password" json:"password,omitempty"`
|
||||
RefreshToken string `form:"refresh_token" json:"refresh_token,omitempty"`
|
||||
Code string `form:"code" json:"code,omitempty"`
|
||||
CodeVerifier string `form:"code_verifier" json:"code_verifier,omitempty"`
|
||||
RedirectURI string `form:"redirect_uri" json:"redirect_uri,omitempty"`
|
||||
Assertion string `form:"assertion" json:"assertion,omitempty"`
|
||||
Name string `form:"name" json:"name,omitempty"`
|
||||
Scope string `form:"scope" json:"scope,omitempty"`
|
||||
Expires int `form:"expires" json:"expires,omitempty"`
|
||||
}
|
||||
|
||||
// Validate verifies the request parameters depending on the grant type.
|
||||
func (f OAuthCreateToken) Validate() error {
|
||||
switch f.GrantType {
|
||||
case authn.GrantClientCredentials, authn.GrantUndefined:
|
||||
// Validate client id.
|
||||
if f.ClientID == "" {
|
||||
return authn.ErrClientIDRequired
|
||||
} else if rnd.InvalidUID(f.ClientID, 'c') {
|
||||
return authn.ErrInvalidCredentials
|
||||
}
|
||||
|
||||
// Validate client secret.
|
||||
if f.ClientSecret == "" {
|
||||
return authn.ErrClientSecretRequired
|
||||
} else if !rnd.IsAlnum(f.ClientSecret) {
|
||||
return authn.ErrInvalidCredentials
|
||||
}
|
||||
case authn.GrantPassword:
|
||||
// Validate request credentials.
|
||||
if f.Username == "" {
|
||||
return authn.ErrUsernameRequired
|
||||
} else if len(f.Username) > txt.ClipUsername {
|
||||
return authn.ErrInvalidCredentials
|
||||
} else if f.Password == "" {
|
||||
return authn.ErrPasswordRequired
|
||||
} else if len(f.Password) > txt.ClipPassword {
|
||||
return authn.ErrInvalidCredentials
|
||||
} else if f.Name == "" {
|
||||
return authn.ErrNameRequired
|
||||
} else if f.Scope == "" {
|
||||
return authn.ErrScopeRequired
|
||||
}
|
||||
default:
|
||||
// Reject requests with unsupported grant types.
|
||||
return authn.ErrInvalidGrantType
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanScope returns the client scopes as sanitized string.
|
||||
func (f OAuthCreateToken) CleanScope() string {
|
||||
return clean.Scope(f.Scope)
|
||||
}
|
||||
91
internal/form/oauth_create_token_test.go
Normal file
91
internal/form/oauth_create_token_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package form
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
)
|
||||
|
||||
func TestOAuthCreateToken_Validate(t *testing.T) {
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
m := OAuthCreateToken{
|
||||
ClientID: "cs5gfen1bgxz7s9i",
|
||||
ClientSecret: "abc",
|
||||
Scope: "*",
|
||||
}
|
||||
|
||||
assert.NoError(t, m.Validate())
|
||||
assert.Equal(t, "*", m.CleanScope())
|
||||
})
|
||||
t.Run("GrantType", func(t *testing.T) {
|
||||
m := OAuthCreateToken{
|
||||
GrantType: authn.GrantClientCredentials,
|
||||
ClientID: "cs5gfen1bgxz7s9i",
|
||||
ClientSecret: "abc",
|
||||
Scope: "*",
|
||||
}
|
||||
|
||||
assert.NoError(t, m.Validate())
|
||||
assert.Equal(t, "*", m.CleanScope())
|
||||
})
|
||||
t.Run("NoClientID", func(t *testing.T) {
|
||||
m := OAuthCreateToken{
|
||||
ClientID: "",
|
||||
ClientSecret: "Alice123!",
|
||||
Scope: "*",
|
||||
}
|
||||
|
||||
assert.Error(t, m.Validate())
|
||||
})
|
||||
t.Run("InvalidClientID", func(t *testing.T) {
|
||||
m := OAuthCreateToken{
|
||||
ClientID: "s5gfen1bgxz7s9i",
|
||||
ClientSecret: "Alice123!",
|
||||
Scope: "*",
|
||||
}
|
||||
|
||||
assert.Error(t, m.Validate())
|
||||
})
|
||||
t.Run("NoSecret", func(t *testing.T) {
|
||||
m := OAuthCreateToken{
|
||||
ClientID: "cs5gfen1bgxz7s9i",
|
||||
ClientSecret: "",
|
||||
Scope: "*",
|
||||
}
|
||||
|
||||
assert.Error(t, m.Validate())
|
||||
})
|
||||
t.Run("InvalidSecret", func(t *testing.T) {
|
||||
m := OAuthCreateToken{
|
||||
ClientID: "cs5gfen1bgxz7s9i",
|
||||
ClientSecret: "abc 123",
|
||||
Scope: "*",
|
||||
}
|
||||
|
||||
assert.Error(t, m.Validate())
|
||||
})
|
||||
t.Run("Password", func(t *testing.T) {
|
||||
m := OAuthCreateToken{
|
||||
GrantType: authn.GrantPassword,
|
||||
Username: "admin",
|
||||
Password: "cs5gfen1bgxz7s9i",
|
||||
Name: "test",
|
||||
Scope: "*",
|
||||
}
|
||||
|
||||
assert.NoError(t, m.Validate())
|
||||
})
|
||||
t.Run("PasswordRequired", func(t *testing.T) {
|
||||
m := OAuthCreateToken{
|
||||
GrantType: authn.GrantPassword,
|
||||
Username: "admin",
|
||||
Password: "",
|
||||
Name: "test",
|
||||
Scope: "*",
|
||||
}
|
||||
|
||||
assert.Error(t, m.Validate())
|
||||
})
|
||||
}
|
||||
@@ -10,14 +10,14 @@ const (
|
||||
ClientAccessToken = "access_token"
|
||||
)
|
||||
|
||||
// ClientToken represents a client authentication token.
|
||||
type ClientToken struct {
|
||||
// OAuthRevokeToken represents a token revokation form.
|
||||
type OAuthRevokeToken struct {
|
||||
AuthToken string `form:"token" binding:"required" json:"token,omitempty"`
|
||||
TypeHint string `form:"token_type_hint" json:" token_type_hint,omitempty"`
|
||||
}
|
||||
|
||||
// Empty checks if all form values are unset.
|
||||
func (f ClientToken) Empty() bool {
|
||||
func (f OAuthRevokeToken) Empty() bool {
|
||||
switch {
|
||||
case f.AuthToken != "":
|
||||
return false
|
||||
@@ -29,7 +29,7 @@ func (f ClientToken) Empty() bool {
|
||||
}
|
||||
|
||||
// Validate checks the token and token type.
|
||||
func (f ClientToken) Validate() error {
|
||||
func (f OAuthRevokeToken) Validate() error {
|
||||
// Check auth token.
|
||||
if f.AuthToken == "" {
|
||||
return fmt.Errorf("missing token")
|
||||
@@ -6,23 +6,23 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestClientToken_Empty(t *testing.T) {
|
||||
func TestOAuthRevokeToken_Empty(t *testing.T) {
|
||||
t.Run("AuthTokenAndTypeHintEmpty", func(t *testing.T) {
|
||||
m := ClientToken{
|
||||
m := OAuthRevokeToken{
|
||||
AuthToken: "",
|
||||
TypeHint: "",
|
||||
}
|
||||
assert.True(t, m.Empty())
|
||||
})
|
||||
t.Run("AuthTokenNotEmpty", func(t *testing.T) {
|
||||
m := ClientToken{
|
||||
m := OAuthRevokeToken{
|
||||
AuthToken: "abc",
|
||||
TypeHint: "",
|
||||
}
|
||||
assert.False(t, m.Empty())
|
||||
})
|
||||
t.Run("TypeHintNotEmpty", func(t *testing.T) {
|
||||
m := ClientToken{
|
||||
m := OAuthRevokeToken{
|
||||
AuthToken: "",
|
||||
TypeHint: "test",
|
||||
}
|
||||
@@ -30,30 +30,30 @@ func TestClientToken_Empty(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestClientToken_Validate(t *testing.T) {
|
||||
func TestOAuthRevokeToken_Validate(t *testing.T) {
|
||||
t.Run("AuthTokenEmpty", func(t *testing.T) {
|
||||
m := ClientToken{
|
||||
m := OAuthRevokeToken{
|
||||
AuthToken: "",
|
||||
TypeHint: "test",
|
||||
}
|
||||
assert.Error(t, m.Validate())
|
||||
})
|
||||
t.Run("AuthTokenInvalid", func(t *testing.T) {
|
||||
m := ClientToken{
|
||||
m := OAuthRevokeToken{
|
||||
AuthToken: "abc 234",
|
||||
TypeHint: "test",
|
||||
}
|
||||
assert.Error(t, m.Validate())
|
||||
})
|
||||
t.Run("UnsupportedToken", func(t *testing.T) {
|
||||
m := ClientToken{
|
||||
m := OAuthRevokeToken{
|
||||
AuthToken: "abc234",
|
||||
TypeHint: "test",
|
||||
}
|
||||
assert.Error(t, m.Validate())
|
||||
})
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
m := ClientToken{
|
||||
m := OAuthRevokeToken{
|
||||
AuthToken: "abc234",
|
||||
TypeHint: "access_token",
|
||||
}
|
||||
28
internal/form/passcode.go
Normal file
28
internal/form/passcode.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package form
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
// Passcode represents a multi-factor authentication key setup form.
|
||||
type Passcode struct {
|
||||
Type string `form:"type" json:"type,omitempty"`
|
||||
Password string `form:"password" json:"password,omitempty"`
|
||||
Code string `form:"code" json:"code,omitempty"`
|
||||
}
|
||||
|
||||
// HasPassword checks if a password has been provided.
|
||||
func (f Passcode) HasPassword() bool {
|
||||
return f.Password != "" && len(f.Password) <= txt.ClipPassword
|
||||
}
|
||||
|
||||
// HasPasscode checks if a verification code has been provided.
|
||||
func (f Passcode) HasPasscode() bool {
|
||||
return clean.Passcode(f.Code) != ""
|
||||
}
|
||||
|
||||
// Passcode returns the sanitized verification code.
|
||||
func (f Passcode) Passcode() string {
|
||||
return clean.Passcode(f.Code)
|
||||
}
|
||||
44
internal/form/passcode_test.go
Normal file
44
internal/form/passcode_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package form
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPasscode_HasPassword(t *testing.T) {
|
||||
t.Run("True", func(t *testing.T) {
|
||||
form := &Passcode{Type: "totp", Password: "passwd1234", Code: "123456"}
|
||||
assert.Equal(t, true, form.HasPassword())
|
||||
})
|
||||
t.Run("False", func(t *testing.T) {
|
||||
form := &Passcode{Type: "totp", Password: "", Code: "123456"}
|
||||
assert.Equal(t, false, form.HasPassword())
|
||||
})
|
||||
}
|
||||
|
||||
func TestPasscode_HasPasscode(t *testing.T) {
|
||||
t.Run("True", func(t *testing.T) {
|
||||
form := &Passcode{Type: "totp", Password: "passwd1234", Code: "123456"}
|
||||
assert.Equal(t, true, form.HasPasscode())
|
||||
})
|
||||
t.Run("False", func(t *testing.T) {
|
||||
form := &Passcode{Type: "totp", Password: "passwd1234", Code: ""}
|
||||
assert.Equal(t, false, form.HasPasscode())
|
||||
})
|
||||
}
|
||||
|
||||
func TestPasscode_Passcode(t *testing.T) {
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
form := &Passcode{Type: "totp", Password: "passwd1234", Code: "123456"}
|
||||
assert.Equal(t, "123456", form.Passcode())
|
||||
})
|
||||
t.Run("Recovery", func(t *testing.T) {
|
||||
form := &Passcode{Type: "totp", Password: "passwd1234", Code: " A23 456 H7l pwf"}
|
||||
assert.Equal(t, "a23456h7lpwf", form.Passcode())
|
||||
})
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
form := &Passcode{Type: "totp", Password: "passwd1234", Code: ""}
|
||||
assert.Equal(t, "", form.Passcode())
|
||||
})
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package form
|
||||
|
||||
// UserPasscode represents a multi-factor authentication key setup form.
|
||||
type UserPasscode struct {
|
||||
Type string `form:"type" json:"type,omitempty"`
|
||||
Passcode string `form:"passcode" json:"passcode,omitempty"`
|
||||
Password string `form:"password" json:"password,omitempty"`
|
||||
}
|
||||
|
||||
// HasPassword checks if a password is set.
|
||||
func (f UserPasscode) HasPassword() bool {
|
||||
return f.Password != "" && len(f.Password) <= 255
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package form
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUserPasscode_HasPassword(t *testing.T) {
|
||||
t.Run("false", func(t *testing.T) {
|
||||
form := &UserPasscode{Passcode: "123456", Password: ""}
|
||||
assert.Equal(t, false, form.HasPassword())
|
||||
})
|
||||
t.Run("true", func(t *testing.T) {
|
||||
form := &UserPasscode{Passcode: "123456", Password: "passwd1234"}
|
||||
assert.Equal(t, true, form.HasPassword())
|
||||
})
|
||||
}
|
||||
@@ -169,7 +169,7 @@ func WebDAVAuth(conf *config.Config) gin.HandlerFunc {
|
||||
|
||||
// User credentials.
|
||||
f := form.Login{
|
||||
UserName: username,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
|
||||
|
||||
@@ -16,14 +16,19 @@ var (
|
||||
ErrInvalidCredentials = errors.New("invalid credentials")
|
||||
ErrInvalidShareToken = errors.New("invalid share token")
|
||||
ErrInsufficientScope = errors.New("insufficient scope")
|
||||
ErrNameRequired = errors.New("name required")
|
||||
ErrScopeRequired = errors.New("scope required")
|
||||
ErrDisabledInPublicMode = errors.New("disabled in public mode")
|
||||
ErrAuthenticationDisabled = errors.New("authentication disabled")
|
||||
)
|
||||
|
||||
// OAuth2-related error messages:
|
||||
var (
|
||||
ErrInvalidClientID = errors.New("invalid client id")
|
||||
ErrInvalidClientSecret = errors.New("invalid client secret")
|
||||
ErrInvalidGrantType = errors.New("invalid grant type")
|
||||
ErrInvalidClientID = errors.New("invalid client id")
|
||||
ErrClientIDRequired = errors.New("client id required")
|
||||
ErrInvalidClientSecret = errors.New("invalid client secret")
|
||||
ErrClientSecretRequired = errors.New("client secret required")
|
||||
)
|
||||
|
||||
// Username-related error messages:
|
||||
@@ -35,7 +40,7 @@ var (
|
||||
// Passcode-related error messages:
|
||||
var (
|
||||
ErrPasscodeRequired = errors.New("passcode required")
|
||||
ErrPasscodeNotSetUp = errors.New("passcode required, but not set up")
|
||||
ErrPasscodeNotSetUp = errors.New("passcode required, but not configured")
|
||||
ErrPasscodeNotVerified = errors.New("passcode not verified")
|
||||
ErrPasscodeAlreadyActivated = errors.New("passcode already activated")
|
||||
ErrPasscodeNotSupported = errors.New("passcode not supported")
|
||||
|
||||
103
pkg/authn/grants.go
Normal file
103
pkg/authn/grants.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package authn
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
// GrantType represents an authentication grant type.
|
||||
type GrantType string
|
||||
|
||||
// Standard authentication grant types.
|
||||
const (
|
||||
GrantUndefined GrantType = ""
|
||||
GrantClientCredentials GrantType = "client_credentials"
|
||||
GrantPassword GrantType = "password"
|
||||
GrantShareToken GrantType = "share_token"
|
||||
GrantRefreshToken GrantType = "refresh_token"
|
||||
GrantAuthorizationCode GrantType = "authorization_code"
|
||||
GrantJwtBearer GrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
||||
GrantSamlBearer GrantType = "urn:ietf:params:oauth:grant-type:saml2-bearer"
|
||||
GrantTokenExchange GrantType = "urn:ietf:params:oauth:grant-type:token-exchange"
|
||||
)
|
||||
|
||||
// String returns the provider identifier as a string.
|
||||
func (t GrantType) String() string {
|
||||
return clean.TypeLowerUnderscore(string(t))
|
||||
}
|
||||
|
||||
// Is compares the method with another type.
|
||||
func (t GrantType) Is(method GrantType) bool {
|
||||
return t == method
|
||||
}
|
||||
|
||||
// IsNot checks if the method is not the specified type.
|
||||
func (t GrantType) IsNot(method GrantType) bool {
|
||||
return t != method
|
||||
}
|
||||
|
||||
// IsUndefined checks if the method is undefined.
|
||||
func (t GrantType) IsUndefined() bool {
|
||||
return t == ""
|
||||
}
|
||||
|
||||
// Equal checks if the type matches.
|
||||
func (t GrantType) Equal(s string) bool {
|
||||
return strings.EqualFold(s, t.String())
|
||||
}
|
||||
|
||||
// NotEqual checks if the type is different.
|
||||
func (t GrantType) NotEqual(s string) bool {
|
||||
return !t.Equal(s)
|
||||
}
|
||||
|
||||
// Pretty returns the provider identifier in an easy-to-read format.
|
||||
func (t GrantType) Pretty() string {
|
||||
switch t {
|
||||
case GrantShareToken:
|
||||
return "Share Token"
|
||||
case GrantRefreshToken:
|
||||
return "Refresh Token"
|
||||
case GrantClientCredentials:
|
||||
return "Client Credentials"
|
||||
case GrantAuthorizationCode:
|
||||
return "Authorization Code"
|
||||
case GrantJwtBearer:
|
||||
return "JWT Bearer Assertion"
|
||||
case GrantSamlBearer:
|
||||
return "SAML2 Bearer Assertion"
|
||||
case GrantTokenExchange:
|
||||
return "Token Exchange"
|
||||
default:
|
||||
return txt.UpperFirst(t.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Grant casts a string to a normalized grant type.
|
||||
func Grant(s string) GrantType {
|
||||
s = clean.TypeLowerUnderscore(s)
|
||||
switch s {
|
||||
case "", "-", "null", "nil", "0", "false":
|
||||
return GrantUndefined
|
||||
case "client_credentials", "client":
|
||||
return GrantClientCredentials
|
||||
case "password", "passwd", "pass", "user", "username":
|
||||
return GrantPassword
|
||||
case "share_token", "share":
|
||||
return GrantClientCredentials
|
||||
case "refresh_token", "refresh":
|
||||
return GrantRefreshToken
|
||||
case "authorization_code", "auth_code":
|
||||
return GrantAuthorizationCode
|
||||
case "jwt-bearer", "jwt", "jwt_bearer":
|
||||
return GrantJwtBearer
|
||||
case "saml2-bearer", "saml2_bearer", "saml2", "saml":
|
||||
return GrantSamlBearer
|
||||
case "token-exchange", "token_exchange":
|
||||
return GrantTokenExchange
|
||||
default:
|
||||
return GrantType(s)
|
||||
}
|
||||
}
|
||||
93
pkg/authn/grants_test.go
Normal file
93
pkg/authn/grants_test.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package authn
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGrantType_String(t *testing.T) {
|
||||
assert.Equal(t, "", GrantUndefined.String())
|
||||
assert.Equal(t, "client_credentials", GrantClientCredentials.String())
|
||||
assert.Equal(t, "password", GrantPassword.String())
|
||||
assert.Equal(t, "refresh_token", GrantRefreshToken.String())
|
||||
assert.Equal(t, "authorization_code", GrantAuthorizationCode.String())
|
||||
assert.Equal(t, "authorization_code", GrantType("Authorization Code ").String())
|
||||
assert.Equal(t, GrantAuthorizationCode.String(), GrantType("Authorization Code ").String())
|
||||
assert.Equal(t, "urn:ietf:params:oauth:grant-type:jwt-bearer", GrantJwtBearer.String())
|
||||
}
|
||||
|
||||
func TestGrantType_Is(t *testing.T) {
|
||||
assert.Equal(t, true, GrantUndefined.Is(GrantUndefined))
|
||||
assert.Equal(t, true, GrantClientCredentials.Is(GrantClientCredentials))
|
||||
assert.Equal(t, true, GrantPassword.Is(GrantPassword))
|
||||
assert.Equal(t, false, GrantClientCredentials.Is(GrantPassword))
|
||||
assert.Equal(t, false, GrantClientCredentials.Is(GrantRefreshToken))
|
||||
assert.Equal(t, false, GrantClientCredentials.Is(GrantAuthorizationCode))
|
||||
assert.Equal(t, false, GrantClientCredentials.Is(GrantJwtBearer))
|
||||
assert.Equal(t, false, GrantClientCredentials.Is(GrantSamlBearer))
|
||||
assert.Equal(t, false, GrantClientCredentials.Is(GrantTokenExchange))
|
||||
assert.Equal(t, false, GrantClientCredentials.Is(GrantUndefined))
|
||||
}
|
||||
|
||||
func TestGrantType_IsNot(t *testing.T) {
|
||||
assert.Equal(t, false, GrantUndefined.IsNot(GrantUndefined))
|
||||
assert.Equal(t, false, GrantClientCredentials.IsNot(GrantClientCredentials))
|
||||
assert.Equal(t, false, GrantPassword.IsNot(GrantPassword))
|
||||
assert.Equal(t, true, GrantClientCredentials.IsNot(GrantPassword))
|
||||
assert.Equal(t, true, GrantClientCredentials.IsNot(GrantRefreshToken))
|
||||
assert.Equal(t, true, GrantClientCredentials.IsNot(GrantAuthorizationCode))
|
||||
assert.Equal(t, true, GrantClientCredentials.IsNot(GrantJwtBearer))
|
||||
assert.Equal(t, true, GrantClientCredentials.IsNot(GrantSamlBearer))
|
||||
assert.Equal(t, true, GrantClientCredentials.IsNot(GrantTokenExchange))
|
||||
assert.Equal(t, true, GrantClientCredentials.IsNot(GrantUndefined))
|
||||
}
|
||||
|
||||
func TestGrantType_IsUndefined(t *testing.T) {
|
||||
assert.Equal(t, true, GrantUndefined.IsUndefined())
|
||||
assert.Equal(t, false, GrantClientCredentials.IsUndefined())
|
||||
assert.Equal(t, false, GrantPassword.IsUndefined())
|
||||
}
|
||||
|
||||
func TestGrantType_Pretty(t *testing.T) {
|
||||
assert.Equal(t, "", GrantUndefined.Pretty())
|
||||
assert.Equal(t, "Client Credentials", GrantClientCredentials.Pretty())
|
||||
assert.Equal(t, "Password", GrantPassword.Pretty())
|
||||
assert.Equal(t, "Refresh Token", GrantRefreshToken.Pretty())
|
||||
assert.Equal(t, "Authorization Code", GrantAuthorizationCode.Pretty())
|
||||
assert.Equal(t, "JWT Bearer Assertion", GrantJwtBearer.Pretty())
|
||||
assert.Equal(t, "SAML2 Bearer Assertion", GrantSamlBearer.Pretty())
|
||||
}
|
||||
|
||||
func TestGrantType_Equal(t *testing.T) {
|
||||
assert.True(t, GrantClientCredentials.Equal("Client_Credentials"))
|
||||
assert.False(t, GrantClientCredentials.Equal("Client Credentials"))
|
||||
assert.True(t, GrantClientCredentials.Equal("client_credentials"))
|
||||
assert.False(t, GrantClientCredentials.Equal("client"))
|
||||
assert.True(t, GrantUndefined.Equal(""))
|
||||
assert.True(t, GrantPassword.Equal("Password"))
|
||||
assert.True(t, GrantPassword.Equal("password"))
|
||||
assert.False(t, GrantPassword.Equal("pass"))
|
||||
}
|
||||
|
||||
func TestGrantType_NotEqual(t *testing.T) {
|
||||
assert.False(t, GrantClientCredentials.NotEqual("Client_Credentials"))
|
||||
assert.True(t, GrantClientCredentials.NotEqual("Client Credentials"))
|
||||
assert.False(t, GrantClientCredentials.NotEqual("client_credentials"))
|
||||
assert.True(t, GrantClientCredentials.NotEqual("client"))
|
||||
assert.False(t, GrantUndefined.NotEqual(""))
|
||||
assert.False(t, GrantPassword.NotEqual("Password"))
|
||||
assert.False(t, GrantPassword.NotEqual("password"))
|
||||
assert.True(t, GrantPassword.NotEqual("pass"))
|
||||
}
|
||||
|
||||
func TestGrant(t *testing.T) {
|
||||
assert.Equal(t, GrantUndefined, Grant(""))
|
||||
assert.Equal(t, GrantClientCredentials, Grant("client credentials"))
|
||||
assert.Equal(t, GrantPassword, Grant("pass"))
|
||||
assert.Equal(t, GrantRefreshToken, Grant("refresh_token"))
|
||||
assert.Equal(t, GrantAuthorizationCode, Grant("auth_code"))
|
||||
assert.Equal(t, GrantJwtBearer, Grant("jwt-bearer"))
|
||||
assert.Equal(t, GrantSamlBearer, Grant("saml"))
|
||||
assert.Equal(t, GrantTokenExchange, Grant("token-exchange"))
|
||||
}
|
||||
@@ -40,6 +40,11 @@ func TestMethodType_IsNot(t *testing.T) {
|
||||
assert.Equal(t, true, MethodUndefined.IsNot(MethodDefault))
|
||||
}
|
||||
|
||||
func TestMethodType_IsUndefined(t *testing.T) {
|
||||
assert.True(t, MethodUndefined.IsUndefined())
|
||||
assert.False(t, Method2FA.IsUndefined())
|
||||
}
|
||||
|
||||
func TestMethodType_IsDefault(t *testing.T) {
|
||||
assert.Equal(t, true, MethodDefault.IsDefault())
|
||||
assert.Equal(t, false, MethodPersonal.IsDefault())
|
||||
@@ -81,11 +86,6 @@ func TestMethod(t *testing.T) {
|
||||
assert.Equal(t, Method2FA, Method("2FA"))
|
||||
}
|
||||
|
||||
func TestMethodType_IsUnknown(t *testing.T) {
|
||||
assert.True(t, MethodUndefined.IsUndefined())
|
||||
assert.False(t, Method2FA.IsUndefined())
|
||||
}
|
||||
|
||||
func TestMethodType_IsSession(t *testing.T) {
|
||||
assert.True(t, MethodSession.IsSession())
|
||||
assert.False(t, Method2FA.IsSession())
|
||||
|
||||
@@ -41,6 +41,11 @@ func TestProviderType_IsNot(t *testing.T) {
|
||||
assert.False(t, ProviderUndefined.IsNot(ProviderUndefined))
|
||||
}
|
||||
|
||||
func TestProviderType_IsUndefined(t *testing.T) {
|
||||
assert.True(t, ProviderUndefined.IsUndefined())
|
||||
assert.False(t, ProviderLocal.IsUndefined())
|
||||
}
|
||||
|
||||
func TestProviderType_IsRemote(t *testing.T) {
|
||||
assert.False(t, ProviderLocal.IsRemote())
|
||||
assert.True(t, ProviderLDAP.IsRemote())
|
||||
@@ -124,11 +129,6 @@ func TestProvider(t *testing.T) {
|
||||
assert.Equal(t, ProviderClientCredentials, Provider("oauth2"))
|
||||
}
|
||||
|
||||
func TestProviderType_IsUnknown(t *testing.T) {
|
||||
assert.True(t, ProviderUndefined.IsUndefined())
|
||||
assert.False(t, ProviderLocal.IsUndefined())
|
||||
}
|
||||
|
||||
func TestProviderType_IsApplication(t *testing.T) {
|
||||
assert.True(t, ProviderApplication.IsApplication())
|
||||
assert.False(t, ProviderLocal.IsApplication())
|
||||
|
||||
@@ -35,7 +35,7 @@ func Handle(s string) string {
|
||||
}, s)
|
||||
|
||||
// Empty or too long?
|
||||
if s == "" || reject(s, txt.ClipUserName) {
|
||||
if s == "" || reject(s, txt.ClipUsername) {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -113,3 +113,23 @@ func Attr(s string) string {
|
||||
func Password(s string) string {
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
|
||||
// Passcode sanitizes a passcode and returns it in lowercase with all whitespace removed.
|
||||
func Passcode(s string) string {
|
||||
if s == "" || reject(s, txt.ClipPasscode) {
|
||||
return ""
|
||||
} else if s = strings.ToLower(strings.TrimSpace(s)); s == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Remove unwanted characters.
|
||||
s = strings.Map(func(r rune) rune {
|
||||
if (r < '0' || r > '9') && (r < 'a' || r > 'z') {
|
||||
return -1
|
||||
}
|
||||
|
||||
return r
|
||||
}, s)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -120,3 +120,15 @@ func TestPassword(t *testing.T) {
|
||||
assert.Equal(t, "!#$T#)$%I#J$I", Password("!#$T#)$%I#J$I"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestPasscode(t *testing.T) {
|
||||
t.Run("Alnum", func(t *testing.T) {
|
||||
assert.Equal(t, "fgdg5yw4y", Passcode("fgdg5yw4y "))
|
||||
})
|
||||
t.Run("Upper", func(t *testing.T) {
|
||||
assert.Equal(t, "aabdf24245vgfrg", Passcode(" AABDF24245vgfrg "))
|
||||
})
|
||||
t.Run("Special", func(t *testing.T) {
|
||||
assert.Equal(t, "tiji", Passcode("!#$T#)$%I#J$I"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,6 +14,11 @@ func TypeLower(s string) string {
|
||||
return Type(strings.ToLower(s))
|
||||
}
|
||||
|
||||
// TypeLowerUnderscore converts a string to a lowercase type string and replaces spaces with underscores.
|
||||
func TypeLowerUnderscore(s string) string {
|
||||
return strings.ReplaceAll(TypeLower(s), " ", "_")
|
||||
}
|
||||
|
||||
// ShortType omits invalid runes, ensures a maximum length of 8 characters, and returns the result.
|
||||
func ShortType(s string) string {
|
||||
return Clip(ASCII(s), ClipShortType)
|
||||
@@ -23,3 +28,8 @@ func ShortType(s string) string {
|
||||
func ShortTypeLower(s string) string {
|
||||
return ShortType(strings.ToLower(s))
|
||||
}
|
||||
|
||||
// ShortTypeLowerUnderscore converts a string to a short lowercase type string and replaces spaces with underscores.
|
||||
func ShortTypeLowerUnderscore(s string) string {
|
||||
return strings.ReplaceAll(ShortTypeLower(s), " ", "_")
|
||||
}
|
||||
|
||||
@@ -52,6 +52,21 @@ func TestTypeLower(t *testing.T) {
|
||||
assert.Equal(t, ClipType, len(result))
|
||||
}
|
||||
|
||||
func TestTypeLowerUnderscore(t *testing.T) {
|
||||
t.Run("Undefined", func(t *testing.T) {
|
||||
assert.Equal(t, "", TypeLowerUnderscore(" \t "))
|
||||
})
|
||||
t.Run("ClientCredentials", func(t *testing.T) {
|
||||
assert.Equal(t, "client_credentials", TypeLowerUnderscore(" Client Credentials幸"))
|
||||
})
|
||||
t.Run("Clip", func(t *testing.T) {
|
||||
assert.Equal(
|
||||
t,
|
||||
"hanzi_are_logograms_developed_for_the_writing_of_chinese!_expres",
|
||||
TypeLowerUnderscore(" 幸福 Hanzi are logograms developed for the writing of Chinese! Expressions in an index may not ...!"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestShortType(t *testing.T) {
|
||||
result := ShortType(" 幸福 Hanzi are logograms developed for the writing of Chinese! Expressions in an index may not ...!")
|
||||
assert.Equal(t, "Hanzi ar", result)
|
||||
@@ -63,3 +78,17 @@ func TestShortTypeLower(t *testing.T) {
|
||||
assert.Equal(t, "hanzi ar", result)
|
||||
assert.Equal(t, ClipShortType, len(result))
|
||||
}
|
||||
|
||||
func TestShortTypeLowerUnderscore(t *testing.T) {
|
||||
t.Run("Undefined", func(t *testing.T) {
|
||||
assert.Equal(t, "", ShortTypeLowerUnderscore(" \t "))
|
||||
})
|
||||
t.Run("ClientCredentials", func(t *testing.T) {
|
||||
assert.Equal(t, "client_c", ShortTypeLowerUnderscore(" Client Credentials幸"))
|
||||
})
|
||||
t.Run("Clip", func(t *testing.T) {
|
||||
assert.Equal(t,
|
||||
"hanzi_ar",
|
||||
ShortTypeLowerUnderscore(" 幸福 Hanzi are logograms developed for the writing of Chinese! Expressions in an index may not ...!"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,10 +8,11 @@ const (
|
||||
Ellipsis = "…"
|
||||
ClipCountry = 2
|
||||
ClipRole = 32
|
||||
ClipPasscode = 36
|
||||
ClipKeyword = 40
|
||||
ClipIP = 48
|
||||
ClipRealm = 64
|
||||
ClipUserName = 64
|
||||
ClipUsername = 64
|
||||
ClipPassword = 72
|
||||
ClipSlug = 80
|
||||
ClipCategory = 100
|
||||
|
||||
Reference in New Issue
Block a user