aboutsummaryrefslogtreecommitdiffstats
path: root/apps/oauth2
diff options
context:
space:
mode:
Diffstat (limited to 'apps/oauth2')
-rw-r--r--apps/oauth2/.l10nignore4
-rw-r--r--apps/oauth2/appinfo/info.xml43
-rw-r--r--apps/oauth2/appinfo/routes.php30
-rw-r--r--apps/oauth2/composer/autoload.php25
-rw-r--r--apps/oauth2/composer/composer.json13
-rw-r--r--apps/oauth2/composer/composer.lock18
-rw-r--r--apps/oauth2/composer/composer/ClassLoader.php579
-rw-r--r--apps/oauth2/composer/composer/InstalledVersions.php359
-rw-r--r--apps/oauth2/composer/composer/LICENSE21
-rw-r--r--apps/oauth2/composer/composer/autoload_classmap.php29
-rw-r--r--apps/oauth2/composer/composer/autoload_namespaces.php9
-rw-r--r--apps/oauth2/composer/composer/autoload_psr4.php10
-rw-r--r--apps/oauth2/composer/composer/autoload_real.php37
-rw-r--r--apps/oauth2/composer/composer/autoload_static.php55
-rw-r--r--apps/oauth2/composer/composer/installed.json5
-rw-r--r--apps/oauth2/composer/composer/installed.php23
-rw-r--r--apps/oauth2/l10n/.gitkeep0
-rw-r--r--apps/oauth2/l10n/ar.js23
-rw-r--r--apps/oauth2/l10n/ar.json21
-rw-r--r--apps/oauth2/l10n/ast.js22
-rw-r--r--apps/oauth2/l10n/ast.json20
-rw-r--r--apps/oauth2/l10n/bg.js20
-rw-r--r--apps/oauth2/l10n/bg.json18
-rw-r--r--apps/oauth2/l10n/br.js20
-rw-r--r--apps/oauth2/l10n/br.json18
-rw-r--r--apps/oauth2/l10n/ca.js23
-rw-r--r--apps/oauth2/l10n/ca.json21
-rw-r--r--apps/oauth2/l10n/cs.js23
-rw-r--r--apps/oauth2/l10n/cs.json21
-rw-r--r--apps/oauth2/l10n/da.js23
-rw-r--r--apps/oauth2/l10n/da.json21
-rw-r--r--apps/oauth2/l10n/de.js23
-rw-r--r--apps/oauth2/l10n/de.json21
-rw-r--r--apps/oauth2/l10n/de_DE.js23
-rw-r--r--apps/oauth2/l10n/de_DE.json21
-rw-r--r--apps/oauth2/l10n/el.js20
-rw-r--r--apps/oauth2/l10n/el.json18
-rw-r--r--apps/oauth2/l10n/en_GB.js23
-rw-r--r--apps/oauth2/l10n/en_GB.json21
-rw-r--r--apps/oauth2/l10n/es.js23
-rw-r--r--apps/oauth2/l10n/es.json21
-rw-r--r--apps/oauth2/l10n/es_AR.js14
-rw-r--r--apps/oauth2/l10n/es_AR.json12
-rw-r--r--apps/oauth2/l10n/es_CL.js14
-rw-r--r--apps/oauth2/l10n/es_CL.json12
-rw-r--r--apps/oauth2/l10n/es_CO.js14
-rw-r--r--apps/oauth2/l10n/es_CO.json12
-rw-r--r--apps/oauth2/l10n/es_CR.js14
-rw-r--r--apps/oauth2/l10n/es_CR.json12
-rw-r--r--apps/oauth2/l10n/es_DO.js14
-rw-r--r--apps/oauth2/l10n/es_DO.json12
-rw-r--r--apps/oauth2/l10n/es_EC.js20
-rw-r--r--apps/oauth2/l10n/es_EC.json18
-rw-r--r--apps/oauth2/l10n/es_GT.js20
-rw-r--r--apps/oauth2/l10n/es_GT.json18
-rw-r--r--apps/oauth2/l10n/es_MX.js22
-rw-r--r--apps/oauth2/l10n/es_MX.json20
-rw-r--r--apps/oauth2/l10n/es_SV.js14
-rw-r--r--apps/oauth2/l10n/es_SV.json12
-rw-r--r--apps/oauth2/l10n/et_EE.js23
-rw-r--r--apps/oauth2/l10n/et_EE.json21
-rw-r--r--apps/oauth2/l10n/eu.js23
-rw-r--r--apps/oauth2/l10n/eu.json21
-rw-r--r--apps/oauth2/l10n/fa.js20
-rw-r--r--apps/oauth2/l10n/fa.json18
-rw-r--r--apps/oauth2/l10n/fi.js20
-rw-r--r--apps/oauth2/l10n/fi.json18
-rw-r--r--apps/oauth2/l10n/fr.js23
-rw-r--r--apps/oauth2/l10n/fr.json21
-rw-r--r--apps/oauth2/l10n/ga.js23
-rw-r--r--apps/oauth2/l10n/ga.json21
-rw-r--r--apps/oauth2/l10n/gl.js23
-rw-r--r--apps/oauth2/l10n/gl.json21
-rw-r--r--apps/oauth2/l10n/he.js20
-rw-r--r--apps/oauth2/l10n/he.json18
-rw-r--r--apps/oauth2/l10n/hr.js20
-rw-r--r--apps/oauth2/l10n/hr.json18
-rw-r--r--apps/oauth2/l10n/hu.js23
-rw-r--r--apps/oauth2/l10n/hu.json21
-rw-r--r--apps/oauth2/l10n/is.js23
-rw-r--r--apps/oauth2/l10n/is.json21
-rw-r--r--apps/oauth2/l10n/it.js23
-rw-r--r--apps/oauth2/l10n/it.json21
-rw-r--r--apps/oauth2/l10n/ja.js23
-rw-r--r--apps/oauth2/l10n/ja.json21
-rw-r--r--apps/oauth2/l10n/ka.js22
-rw-r--r--apps/oauth2/l10n/ka.json20
-rw-r--r--apps/oauth2/l10n/ka_GE.js15
-rw-r--r--apps/oauth2/l10n/ka_GE.json13
-rw-r--r--apps/oauth2/l10n/ko.js20
-rw-r--r--apps/oauth2/l10n/ko.json18
-rw-r--r--apps/oauth2/l10n/lt_LT.js17
-rw-r--r--apps/oauth2/l10n/lt_LT.json15
-rw-r--r--apps/oauth2/l10n/lv.js17
-rw-r--r--apps/oauth2/l10n/lv.json15
-rw-r--r--apps/oauth2/l10n/mk.js20
-rw-r--r--apps/oauth2/l10n/mk.json18
-rw-r--r--apps/oauth2/l10n/nb.js23
-rw-r--r--apps/oauth2/l10n/nb.json21
-rw-r--r--apps/oauth2/l10n/nl.js23
-rw-r--r--apps/oauth2/l10n/nl.json21
-rw-r--r--apps/oauth2/l10n/oc.js17
-rw-r--r--apps/oauth2/l10n/oc.json15
-rw-r--r--apps/oauth2/l10n/pl.js23
-rw-r--r--apps/oauth2/l10n/pl.json21
-rw-r--r--apps/oauth2/l10n/pt_BR.js23
-rw-r--r--apps/oauth2/l10n/pt_BR.json21
-rw-r--r--apps/oauth2/l10n/pt_PT.js20
-rw-r--r--apps/oauth2/l10n/pt_PT.json18
-rw-r--r--apps/oauth2/l10n/ru.js23
-rw-r--r--apps/oauth2/l10n/ru.json21
-rw-r--r--apps/oauth2/l10n/sc.js20
-rw-r--r--apps/oauth2/l10n/sc.json18
-rw-r--r--apps/oauth2/l10n/si.js17
-rw-r--r--apps/oauth2/l10n/si.json15
-rw-r--r--apps/oauth2/l10n/sk.js23
-rw-r--r--apps/oauth2/l10n/sk.json21
-rw-r--r--apps/oauth2/l10n/sl.js20
-rw-r--r--apps/oauth2/l10n/sl.json18
-rw-r--r--apps/oauth2/l10n/sq.js14
-rw-r--r--apps/oauth2/l10n/sq.json12
-rw-r--r--apps/oauth2/l10n/sr.js23
-rw-r--r--apps/oauth2/l10n/sr.json21
-rw-r--r--apps/oauth2/l10n/sv.js23
-rw-r--r--apps/oauth2/l10n/sv.json21
-rw-r--r--apps/oauth2/l10n/sw.js23
-rw-r--r--apps/oauth2/l10n/sw.json21
-rw-r--r--apps/oauth2/l10n/tr.js23
-rw-r--r--apps/oauth2/l10n/tr.json21
-rw-r--r--apps/oauth2/l10n/ug.js23
-rw-r--r--apps/oauth2/l10n/ug.json21
-rw-r--r--apps/oauth2/l10n/uk.js23
-rw-r--r--apps/oauth2/l10n/uk.json21
-rw-r--r--apps/oauth2/l10n/vi.js19
-rw-r--r--apps/oauth2/l10n/vi.json17
-rw-r--r--apps/oauth2/l10n/zh_CN.js23
-rw-r--r--apps/oauth2/l10n/zh_CN.json21
-rw-r--r--apps/oauth2/l10n/zh_HK.js23
-rw-r--r--apps/oauth2/l10n/zh_HK.json21
-rw-r--r--apps/oauth2/l10n/zh_TW.js23
-rw-r--r--apps/oauth2/l10n/zh_TW.json21
-rw-r--r--apps/oauth2/lib/BackgroundJob/CleanupExpiredAuthorizationCode.php43
-rw-r--r--apps/oauth2/lib/Command/ImportLegacyOcClient.php76
-rw-r--r--apps/oauth2/lib/Controller/LoginRedirectorController.php123
-rw-r--r--apps/oauth2/lib/Controller/OauthApiController.php214
-rw-r--r--apps/oauth2/lib/Controller/SettingsController.php80
-rw-r--r--apps/oauth2/lib/Db/AccessToken.php49
-rw-r--r--apps/oauth2/lib/Db/AccessTokenMapper.php85
-rw-r--r--apps/oauth2/lib/Db/Client.php39
-rw-r--r--apps/oauth2/lib/Db/ClientMapper.php80
-rw-r--r--apps/oauth2/lib/Exceptions/AccessTokenNotFoundException.php12
-rw-r--r--apps/oauth2/lib/Exceptions/ClientNotFoundException.php12
-rw-r--r--apps/oauth2/lib/Migration/SetTokenExpiration.php51
-rw-r--r--apps/oauth2/lib/Migration/Version010401Date20181207190718.php82
-rw-r--r--apps/oauth2/lib/Migration/Version010402Date20190107124745.php36
-rw-r--r--apps/oauth2/lib/Migration/Version011601Date20230522143227.php65
-rw-r--r--apps/oauth2/lib/Migration/Version011602Date20230613160650.php39
-rw-r--r--apps/oauth2/lib/Migration/Version011603Date20230620111039.php69
-rw-r--r--apps/oauth2/lib/Migration/Version011901Date20240829164356.php49
-rw-r--r--apps/oauth2/lib/Settings/Admin.php63
-rw-r--r--apps/oauth2/openapi.json227
-rw-r--r--apps/oauth2/openapi.json.license2
-rw-r--r--apps/oauth2/src/App.vue161
-rw-r--r--apps/oauth2/src/components/OAuthItem.vue113
-rw-r--r--apps/oauth2/src/main.js21
-rw-r--r--apps/oauth2/templates/admin.php11
-rw-r--r--apps/oauth2/tests/Controller/LoginRedirectorControllerTest.php255
-rw-r--r--apps/oauth2/tests/Controller/OauthApiControllerTest.php607
-rw-r--r--apps/oauth2/tests/Controller/SettingsControllerTest.php188
-rw-r--r--apps/oauth2/tests/Db/AccessTokenMapperTest.php59
-rw-r--r--apps/oauth2/tests/Db/ClientMapperTest.php82
-rw-r--r--apps/oauth2/tests/Settings/AdminTest.php60
172 files changed, 6753 insertions, 0 deletions
diff --git a/apps/oauth2/.l10nignore b/apps/oauth2/.l10nignore
new file mode 100644
index 00000000000..4b3060dbc2c
--- /dev/null
+++ b/apps/oauth2/.l10nignore
@@ -0,0 +1,4 @@
+# SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+# SPDX-License-Identifier: AGPL-3.0-or-later
+# compiled vue templates
+js/
diff --git a/apps/oauth2/appinfo/info.xml b/apps/oauth2/appinfo/info.xml
new file mode 100644
index 00000000000..6ab30292ab0
--- /dev/null
+++ b/apps/oauth2/appinfo/info.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!--
+ - SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
+ <id>oauth2</id>
+ <name>OAuth 2.0</name>
+ <summary>Allows OAuth2 compatible authentication from other web applications.</summary>
+ <description>The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications.</description>
+ <version>1.20.0</version>
+ <licence>agpl</licence>
+ <author>Lukas Reschke</author>
+ <namespace>OAuth2</namespace>
+ <types>
+ <authentication/>
+ </types>
+
+ <category>integration</category>
+ <bugs>https://github.com/nextcloud/server/issues</bugs>
+ <dependencies>
+ <nextcloud min-version="32" max-version="32"/>
+ </dependencies>
+
+ <background-jobs>
+ <job>OCA\OAuth2\BackgroundJob\CleanupExpiredAuthorizationCode</job>
+ </background-jobs>
+
+ <repair-steps>
+ <post-migration>
+ <step>OCA\OAuth2\Migration\SetTokenExpiration</step>
+ </post-migration>
+ </repair-steps>
+
+ <commands>
+ <command>OCA\OAuth2\Command\ImportLegacyOcClient</command>
+ </commands>
+
+ <settings>
+ <admin>OCA\OAuth2\Settings\Admin</admin>
+ </settings>
+</info>
diff --git a/apps/oauth2/appinfo/routes.php b/apps/oauth2/appinfo/routes.php
new file mode 100644
index 00000000000..b34aff05552
--- /dev/null
+++ b/apps/oauth2/appinfo/routes.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+return [
+ 'routes' => [
+ [
+ 'name' => 'Settings#addClient',
+ 'url' => '/clients',
+ 'verb' => 'POST',
+ ],
+ [
+ 'name' => 'Settings#deleteClient',
+ 'url' => '/clients/{id}',
+ 'verb' => 'DELETE'
+ ],
+ [
+ 'name' => 'LoginRedirector#authorize',
+ 'url' => '/authorize',
+ 'verb' => 'GET',
+ ],
+ [
+ 'name' => 'OauthApi#getToken',
+ 'url' => '/api/v1/token',
+ 'verb' => 'POST'
+ ],
+ ],
+];
diff --git a/apps/oauth2/composer/autoload.php b/apps/oauth2/composer/autoload.php
new file mode 100644
index 00000000000..02011f1eb55
--- /dev/null
+++ b/apps/oauth2/composer/autoload.php
@@ -0,0 +1,25 @@
+<?php
+
+// autoload.php @generated by Composer
+
+if (PHP_VERSION_ID < 50600) {
+ if (!headers_sent()) {
+ header('HTTP/1.1 500 Internal Server Error');
+ }
+ $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
+ if (!ini_get('display_errors')) {
+ if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
+ fwrite(STDERR, $err);
+ } elseif (!headers_sent()) {
+ echo $err;
+ }
+ }
+ trigger_error(
+ $err,
+ E_USER_ERROR
+ );
+}
+
+require_once __DIR__ . '/composer/autoload_real.php';
+
+return ComposerAutoloaderInitOAuth2::getLoader();
diff --git a/apps/oauth2/composer/composer.json b/apps/oauth2/composer/composer.json
new file mode 100644
index 00000000000..78f235f3c2e
--- /dev/null
+++ b/apps/oauth2/composer/composer.json
@@ -0,0 +1,13 @@
+{
+ "config" : {
+ "vendor-dir": ".",
+ "optimize-autoloader": true,
+ "classmap-authoritative": true,
+ "autoloader-suffix": "OAuth2"
+ },
+ "autoload" : {
+ "psr-4": {
+ "OCA\\OAuth2\\": "../lib/"
+ }
+ }
+}
diff --git a/apps/oauth2/composer/composer.lock b/apps/oauth2/composer/composer.lock
new file mode 100644
index 00000000000..fd0bcbcb753
--- /dev/null
+++ b/apps/oauth2/composer/composer.lock
@@ -0,0 +1,18 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "d751713988987e9331980363e24189ce",
+ "packages": [],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": [],
+ "plugin-api-version": "2.1.0"
+}
diff --git a/apps/oauth2/composer/composer/ClassLoader.php b/apps/oauth2/composer/composer/ClassLoader.php
new file mode 100644
index 00000000000..7824d8f7eaf
--- /dev/null
+++ b/apps/oauth2/composer/composer/ClassLoader.php
@@ -0,0 +1,579 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ * Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @see https://www.php-fig.org/psr/psr-0/
+ * @see https://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ /** @var \Closure(string):void */
+ private static $includeFile;
+
+ /** @var string|null */
+ private $vendorDir;
+
+ // PSR-4
+ /**
+ * @var array<string, array<string, int>>
+ */
+ private $prefixLengthsPsr4 = array();
+ /**
+ * @var array<string, list<string>>
+ */
+ private $prefixDirsPsr4 = array();
+ /**
+ * @var list<string>
+ */
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ /**
+ * List of PSR-0 prefixes
+ *
+ * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
+ *
+ * @var array<string, array<string, list<string>>>
+ */
+ private $prefixesPsr0 = array();
+ /**
+ * @var list<string>
+ */
+ private $fallbackDirsPsr0 = array();
+
+ /** @var bool */
+ private $useIncludePath = false;
+
+ /**
+ * @var array<string, string>
+ */
+ private $classMap = array();
+
+ /** @var bool */
+ private $classMapAuthoritative = false;
+
+ /**
+ * @var array<string, bool>
+ */
+ private $missingClasses = array();
+
+ /** @var string|null */
+ private $apcuPrefix;
+
+ /**
+ * @var array<string, self>
+ */
+ private static $registeredLoaders = array();
+
+ /**
+ * @param string|null $vendorDir
+ */
+ public function __construct($vendorDir = null)
+ {
+ $this->vendorDir = $vendorDir;
+ self::initializeIncludeClosure();
+ }
+
+ /**
+ * @return array<string, list<string>>
+ */
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
+ }
+
+ return array();
+ }
+
+ /**
+ * @return array<string, list<string>>
+ */
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ /**
+ * @return list<string>
+ */
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ /**
+ * @return list<string>
+ */
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ /**
+ * @return array<string, string> Array of classname => path
+ */
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array<string, string> $classMap Class to filename map
+ *
+ * @return void
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param list<string>|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @return void
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ $paths = (array) $paths;
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param list<string>|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ $paths = (array) $paths;
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param list<string>|string $paths The PSR-0 base directories
+ *
+ * @return void
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param list<string>|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ *
+ * @return void
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ *
+ * @return void
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ *
+ * @return void
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ *
+ * @return void
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+
+ if (null === $this->vendorDir) {
+ return;
+ }
+
+ if ($prepend) {
+ self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
+ } else {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ self::$registeredLoaders[$this->vendorDir] = $this;
+ }
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ *
+ * @return void
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+
+ if (null !== $this->vendorDir) {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ }
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return true|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ $includeFile = self::$includeFile;
+ $includeFile($file);
+
+ return true;
+ }
+
+ return null;
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+ return false;
+ }
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if (false === $file && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
+ if (false === $file) {
+ // Remember that this class does not exist.
+ $this->missingClasses[$class] = true;
+ }
+
+ return $file;
+ }
+
+ /**
+ * Returns the currently registered loaders keyed by their corresponding vendor directories.
+ *
+ * @return array<string, self>
+ */
+ public static function getRegisteredLoaders()
+ {
+ return self::$registeredLoaders;
+ }
+
+ /**
+ * @param string $class
+ * @param string $ext
+ * @return string|false
+ */
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath . '\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ if (file_exists($file = $dir . $pathEnd)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+
+ return false;
+ }
+
+ /**
+ * @return void
+ */
+ private static function initializeIncludeClosure()
+ {
+ if (self::$includeFile !== null) {
+ return;
+ }
+
+ /**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ *
+ * @param string $file
+ * @return void
+ */
+ self::$includeFile = \Closure::bind(static function($file) {
+ include $file;
+ }, null, null);
+ }
+}
diff --git a/apps/oauth2/composer/composer/InstalledVersions.php b/apps/oauth2/composer/composer/InstalledVersions.php
new file mode 100644
index 00000000000..51e734a774b
--- /dev/null
+++ b/apps/oauth2/composer/composer/InstalledVersions.php
@@ -0,0 +1,359 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ * Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer;
+
+use Composer\Autoload\ClassLoader;
+use Composer\Semver\VersionParser;
+
+/**
+ * This class is copied in every Composer installed project and available to all
+ *
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
+ *
+ * To require its presence, you can require `composer-runtime-api ^2.0`
+ *
+ * @final
+ */
+class InstalledVersions
+{
+ /**
+ * @var mixed[]|null
+ * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
+ */
+ private static $installed;
+
+ /**
+ * @var bool|null
+ */
+ private static $canGetVendors;
+
+ /**
+ * @var array[]
+ * @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
+ */
+ private static $installedByVendor = array();
+
+ /**
+ * Returns a list of all package names which are present, either by being installed, replaced or provided
+ *
+ * @return string[]
+ * @psalm-return list<string>
+ */
+ public static function getInstalledPackages()
+ {
+ $packages = array();
+ foreach (self::getInstalled() as $installed) {
+ $packages[] = array_keys($installed['versions']);
+ }
+
+ if (1 === \count($packages)) {
+ return $packages[0];
+ }
+
+ return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+ }
+
+ /**
+ * Returns a list of all package names with a specific type e.g. 'library'
+ *
+ * @param string $type
+ * @return string[]
+ * @psalm-return list<string>
+ */
+ public static function getInstalledPackagesByType($type)
+ {
+ $packagesByType = array();
+
+ foreach (self::getInstalled() as $installed) {
+ foreach ($installed['versions'] as $name => $package) {
+ if (isset($package['type']) && $package['type'] === $type) {
+ $packagesByType[] = $name;
+ }
+ }
+ }
+
+ return $packagesByType;
+ }
+
+ /**
+ * Checks whether the given package is installed
+ *
+ * This also returns true if the package name is provided or replaced by another package
+ *
+ * @param string $packageName
+ * @param bool $includeDevRequirements
+ * @return bool
+ */
+ public static function isInstalled($packageName, $includeDevRequirements = true)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (isset($installed['versions'][$packageName])) {
+ return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the given package satisfies a version constraint
+ *
+ * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
+ *
+ * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
+ *
+ * @param VersionParser $parser Install composer/semver to have access to this class and functionality
+ * @param string $packageName
+ * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
+ * @return bool
+ */
+ public static function satisfies(VersionParser $parser, $packageName, $constraint)
+ {
+ $constraint = $parser->parseConstraints((string) $constraint);
+ $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+ return $provided->matches($constraint);
+ }
+
+ /**
+ * Returns a version constraint representing all the range(s) which are installed for a given package
+ *
+ * It is easier to use this via isInstalled() with the $constraint argument if you need to check
+ * whether a given version of a package is installed, and not just whether it exists
+ *
+ * @param string $packageName
+ * @return string Version constraint usable with composer/semver
+ */
+ public static function getVersionRanges($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ $ranges = array();
+ if (isset($installed['versions'][$packageName]['pretty_version'])) {
+ $ranges[] = $installed['versions'][$packageName]['pretty_version'];
+ }
+ if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+ }
+ if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+ }
+ if (array_key_exists('provided', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+ }
+
+ return implode(' || ', $ranges);
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getPrettyVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['pretty_version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
+ */
+ public static function getReference($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['reference'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['reference'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
+ */
+ public static function getInstallPath($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @return array
+ * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
+ */
+ public static function getRootPackage()
+ {
+ $installed = self::getInstalled();
+
+ return $installed[0]['root'];
+ }
+
+ /**
+ * Returns the raw installed.php data for custom implementations
+ *
+ * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
+ * @return array[]
+ * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
+ */
+ public static function getRawData()
+ {
+ @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ self::$installed = include __DIR__ . '/installed.php';
+ } else {
+ self::$installed = array();
+ }
+ }
+
+ return self::$installed;
+ }
+
+ /**
+ * Returns the raw data of all installed.php which are currently loaded for custom implementations
+ *
+ * @return array[]
+ * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
+ */
+ public static function getAllRawData()
+ {
+ return self::getInstalled();
+ }
+
+ /**
+ * Lets you reload the static array from another file
+ *
+ * This is only useful for complex integrations in which a project needs to use
+ * this class but then also needs to execute another project's autoloader in process,
+ * and wants to ensure both projects have access to their version of installed.php.
+ *
+ * A typical case would be PHPUnit, where it would need to make sure it reads all
+ * the data it needs from this class, then call reload() with
+ * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
+ * the project in which it runs can then also use this class safely, without
+ * interference between PHPUnit's dependencies and the project's dependencies.
+ *
+ * @param array[] $data A vendor/composer/installed.php data set
+ * @return void
+ *
+ * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
+ */
+ public static function reload($data)
+ {
+ self::$installed = $data;
+ self::$installedByVendor = array();
+ }
+
+ /**
+ * @return array[]
+ * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
+ */
+ private static function getInstalled()
+ {
+ if (null === self::$canGetVendors) {
+ self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+ }
+
+ $installed = array();
+
+ if (self::$canGetVendors) {
+ foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+ if (isset(self::$installedByVendor[$vendorDir])) {
+ $installed[] = self::$installedByVendor[$vendorDir];
+ } elseif (is_file($vendorDir.'/composer/installed.php')) {
+ /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
+ $required = require $vendorDir.'/composer/installed.php';
+ $installed[] = self::$installedByVendor[$vendorDir] = $required;
+ if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
+ self::$installed = $installed[count($installed) - 1];
+ }
+ }
+ }
+ }
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
+ $required = require __DIR__ . '/installed.php';
+ self::$installed = $required;
+ } else {
+ self::$installed = array();
+ }
+ }
+
+ if (self::$installed !== array()) {
+ $installed[] = self::$installed;
+ }
+
+ return $installed;
+ }
+}
diff --git a/apps/oauth2/composer/composer/LICENSE b/apps/oauth2/composer/composer/LICENSE
new file mode 100644
index 00000000000..f27399a042d
--- /dev/null
+++ b/apps/oauth2/composer/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/apps/oauth2/composer/composer/autoload_classmap.php b/apps/oauth2/composer/composer/autoload_classmap.php
new file mode 100644
index 00000000000..f5fc5bfe251
--- /dev/null
+++ b/apps/oauth2/composer/composer/autoload_classmap.php
@@ -0,0 +1,29 @@
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(__DIR__);
+$baseDir = $vendorDir;
+
+return array(
+ 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
+ 'OCA\\OAuth2\\BackgroundJob\\CleanupExpiredAuthorizationCode' => $baseDir . '/../lib/BackgroundJob/CleanupExpiredAuthorizationCode.php',
+ 'OCA\\OAuth2\\Command\\ImportLegacyOcClient' => $baseDir . '/../lib/Command/ImportLegacyOcClient.php',
+ 'OCA\\OAuth2\\Controller\\LoginRedirectorController' => $baseDir . '/../lib/Controller/LoginRedirectorController.php',
+ 'OCA\\OAuth2\\Controller\\OauthApiController' => $baseDir . '/../lib/Controller/OauthApiController.php',
+ 'OCA\\OAuth2\\Controller\\SettingsController' => $baseDir . '/../lib/Controller/SettingsController.php',
+ 'OCA\\OAuth2\\Db\\AccessToken' => $baseDir . '/../lib/Db/AccessToken.php',
+ 'OCA\\OAuth2\\Db\\AccessTokenMapper' => $baseDir . '/../lib/Db/AccessTokenMapper.php',
+ 'OCA\\OAuth2\\Db\\Client' => $baseDir . '/../lib/Db/Client.php',
+ 'OCA\\OAuth2\\Db\\ClientMapper' => $baseDir . '/../lib/Db/ClientMapper.php',
+ 'OCA\\OAuth2\\Exceptions\\AccessTokenNotFoundException' => $baseDir . '/../lib/Exceptions/AccessTokenNotFoundException.php',
+ 'OCA\\OAuth2\\Exceptions\\ClientNotFoundException' => $baseDir . '/../lib/Exceptions/ClientNotFoundException.php',
+ 'OCA\\OAuth2\\Migration\\SetTokenExpiration' => $baseDir . '/../lib/Migration/SetTokenExpiration.php',
+ 'OCA\\OAuth2\\Migration\\Version010401Date20181207190718' => $baseDir . '/../lib/Migration/Version010401Date20181207190718.php',
+ 'OCA\\OAuth2\\Migration\\Version010402Date20190107124745' => $baseDir . '/../lib/Migration/Version010402Date20190107124745.php',
+ 'OCA\\OAuth2\\Migration\\Version011601Date20230522143227' => $baseDir . '/../lib/Migration/Version011601Date20230522143227.php',
+ 'OCA\\OAuth2\\Migration\\Version011602Date20230613160650' => $baseDir . '/../lib/Migration/Version011602Date20230613160650.php',
+ 'OCA\\OAuth2\\Migration\\Version011603Date20230620111039' => $baseDir . '/../lib/Migration/Version011603Date20230620111039.php',
+ 'OCA\\OAuth2\\Migration\\Version011901Date20240829164356' => $baseDir . '/../lib/Migration/Version011901Date20240829164356.php',
+ 'OCA\\OAuth2\\Settings\\Admin' => $baseDir . '/../lib/Settings/Admin.php',
+);
diff --git a/apps/oauth2/composer/composer/autoload_namespaces.php b/apps/oauth2/composer/composer/autoload_namespaces.php
new file mode 100644
index 00000000000..3f5c9296251
--- /dev/null
+++ b/apps/oauth2/composer/composer/autoload_namespaces.php
@@ -0,0 +1,9 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(__DIR__);
+$baseDir = $vendorDir;
+
+return array(
+);
diff --git a/apps/oauth2/composer/composer/autoload_psr4.php b/apps/oauth2/composer/composer/autoload_psr4.php
new file mode 100644
index 00000000000..6c3c791a23c
--- /dev/null
+++ b/apps/oauth2/composer/composer/autoload_psr4.php
@@ -0,0 +1,10 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(__DIR__);
+$baseDir = $vendorDir;
+
+return array(
+ 'OCA\\OAuth2\\' => array($baseDir . '/../lib'),
+);
diff --git a/apps/oauth2/composer/composer/autoload_real.php b/apps/oauth2/composer/composer/autoload_real.php
new file mode 100644
index 00000000000..0253518be84
--- /dev/null
+++ b/apps/oauth2/composer/composer/autoload_real.php
@@ -0,0 +1,37 @@
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInitOAuth2
+{
+ private static $loader;
+
+ public static function loadClassLoader($class)
+ {
+ if ('Composer\Autoload\ClassLoader' === $class) {
+ require __DIR__ . '/ClassLoader.php';
+ }
+ }
+
+ /**
+ * @return \Composer\Autoload\ClassLoader
+ */
+ public static function getLoader()
+ {
+ if (null !== self::$loader) {
+ return self::$loader;
+ }
+
+ spl_autoload_register(array('ComposerAutoloaderInitOAuth2', 'loadClassLoader'), true, true);
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
+ spl_autoload_unregister(array('ComposerAutoloaderInitOAuth2', 'loadClassLoader'));
+
+ require __DIR__ . '/autoload_static.php';
+ call_user_func(\Composer\Autoload\ComposerStaticInitOAuth2::getInitializer($loader));
+
+ $loader->setClassMapAuthoritative(true);
+ $loader->register(true);
+
+ return $loader;
+ }
+}
diff --git a/apps/oauth2/composer/composer/autoload_static.php b/apps/oauth2/composer/composer/autoload_static.php
new file mode 100644
index 00000000000..c4843fa1990
--- /dev/null
+++ b/apps/oauth2/composer/composer/autoload_static.php
@@ -0,0 +1,55 @@
+<?php
+
+// autoload_static.php @generated by Composer
+
+namespace Composer\Autoload;
+
+class ComposerStaticInitOAuth2
+{
+ public static $prefixLengthsPsr4 = array (
+ 'O' =>
+ array (
+ 'OCA\\OAuth2\\' => 11,
+ ),
+ );
+
+ public static $prefixDirsPsr4 = array (
+ 'OCA\\OAuth2\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/../lib',
+ ),
+ );
+
+ public static $classMap = array (
+ 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+ 'OCA\\OAuth2\\BackgroundJob\\CleanupExpiredAuthorizationCode' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupExpiredAuthorizationCode.php',
+ 'OCA\\OAuth2\\Command\\ImportLegacyOcClient' => __DIR__ . '/..' . '/../lib/Command/ImportLegacyOcClient.php',
+ 'OCA\\OAuth2\\Controller\\LoginRedirectorController' => __DIR__ . '/..' . '/../lib/Controller/LoginRedirectorController.php',
+ 'OCA\\OAuth2\\Controller\\OauthApiController' => __DIR__ . '/..' . '/../lib/Controller/OauthApiController.php',
+ 'OCA\\OAuth2\\Controller\\SettingsController' => __DIR__ . '/..' . '/../lib/Controller/SettingsController.php',
+ 'OCA\\OAuth2\\Db\\AccessToken' => __DIR__ . '/..' . '/../lib/Db/AccessToken.php',
+ 'OCA\\OAuth2\\Db\\AccessTokenMapper' => __DIR__ . '/..' . '/../lib/Db/AccessTokenMapper.php',
+ 'OCA\\OAuth2\\Db\\Client' => __DIR__ . '/..' . '/../lib/Db/Client.php',
+ 'OCA\\OAuth2\\Db\\ClientMapper' => __DIR__ . '/..' . '/../lib/Db/ClientMapper.php',
+ 'OCA\\OAuth2\\Exceptions\\AccessTokenNotFoundException' => __DIR__ . '/..' . '/../lib/Exceptions/AccessTokenNotFoundException.php',
+ 'OCA\\OAuth2\\Exceptions\\ClientNotFoundException' => __DIR__ . '/..' . '/../lib/Exceptions/ClientNotFoundException.php',
+ 'OCA\\OAuth2\\Migration\\SetTokenExpiration' => __DIR__ . '/..' . '/../lib/Migration/SetTokenExpiration.php',
+ 'OCA\\OAuth2\\Migration\\Version010401Date20181207190718' => __DIR__ . '/..' . '/../lib/Migration/Version010401Date20181207190718.php',
+ 'OCA\\OAuth2\\Migration\\Version010402Date20190107124745' => __DIR__ . '/..' . '/../lib/Migration/Version010402Date20190107124745.php',
+ 'OCA\\OAuth2\\Migration\\Version011601Date20230522143227' => __DIR__ . '/..' . '/../lib/Migration/Version011601Date20230522143227.php',
+ 'OCA\\OAuth2\\Migration\\Version011602Date20230613160650' => __DIR__ . '/..' . '/../lib/Migration/Version011602Date20230613160650.php',
+ 'OCA\\OAuth2\\Migration\\Version011603Date20230620111039' => __DIR__ . '/..' . '/../lib/Migration/Version011603Date20230620111039.php',
+ 'OCA\\OAuth2\\Migration\\Version011901Date20240829164356' => __DIR__ . '/..' . '/../lib/Migration/Version011901Date20240829164356.php',
+ 'OCA\\OAuth2\\Settings\\Admin' => __DIR__ . '/..' . '/../lib/Settings/Admin.php',
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixLengthsPsr4 = ComposerStaticInitOAuth2::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInitOAuth2::$prefixDirsPsr4;
+ $loader->classMap = ComposerStaticInitOAuth2::$classMap;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/apps/oauth2/composer/composer/installed.json b/apps/oauth2/composer/composer/installed.json
new file mode 100644
index 00000000000..f20a6c47c6d
--- /dev/null
+++ b/apps/oauth2/composer/composer/installed.json
@@ -0,0 +1,5 @@
+{
+ "packages": [],
+ "dev": false,
+ "dev-package-names": []
+}
diff --git a/apps/oauth2/composer/composer/installed.php b/apps/oauth2/composer/composer/installed.php
new file mode 100644
index 00000000000..1a66c7f2416
--- /dev/null
+++ b/apps/oauth2/composer/composer/installed.php
@@ -0,0 +1,23 @@
+<?php return array(
+ 'root' => array(
+ 'name' => '__root__',
+ 'pretty_version' => 'dev-master',
+ 'version' => 'dev-master',
+ 'reference' => 'b1797842784b250fb01ed5e3bf130705eb94751b',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../',
+ 'aliases' => array(),
+ 'dev' => false,
+ ),
+ 'versions' => array(
+ '__root__' => array(
+ 'pretty_version' => 'dev-master',
+ 'version' => 'dev-master',
+ 'reference' => 'b1797842784b250fb01ed5e3bf130705eb94751b',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ ),
+);
diff --git a/apps/oauth2/l10n/.gitkeep b/apps/oauth2/l10n/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/apps/oauth2/l10n/.gitkeep
diff --git a/apps/oauth2/l10n/ar.js b/apps/oauth2/l10n/ar.js
new file mode 100644
index 00000000000..0ccccefa3b1
--- /dev/null
+++ b/apps/oauth2/l10n/ar.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "عميلك ليس مُخوّلاً بالربط. رجاءً، أبلغ مسؤول النظام.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "عنوان URL المُعاد توجيهه يجب أن يكون عنواناً كاملاً كما في المثال التالي: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "يسمح بالتحقّق من الهويّة عن طريق تطبيقات وب أخرى متوافقة مع OAuth2.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "التطبيق OAuth2 يسمح لمسؤولي النظام بتهيئة الإجراء الأساسي للتحقّق من الهويّةبحيث يسمح بالتحقّق من الهويّة عن طريق تطبيقات ويب أخرى متوافقة مع OAuth2.",
+ "OAuth 2.0 clients" : "عملاء OAuth2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "يسمح OAuth2.0 لخدمات خارجية بطلب الوصول إلى {instanceName}",
+ "Name" : "الاسم",
+ "Redirection URI" : "رابط إعادة التوجيه",
+ "Client Identifier" : "مُعرِّف العميل",
+ "Secret key" : "المفتاح السّرّي",
+ "Delete client" : "حذف عميل ",
+ "Make sure you store the secret key, it cannot be recovered." : "تأكد من تخزين المفتاح السري، إذ أنه لا يمكن استعادته.",
+ "Add client" : "إضافة عميل",
+ "Add" : "إضافة",
+ "Show client secret" : "عرض كلمة سر العميل",
+ "Hide client secret" : "إخْفِ سِرَّ العميل",
+ "Delete" : "حذف"
+},
+"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;");
diff --git a/apps/oauth2/l10n/ar.json b/apps/oauth2/l10n/ar.json
new file mode 100644
index 00000000000..a7ea13b979f
--- /dev/null
+++ b/apps/oauth2/l10n/ar.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "عميلك ليس مُخوّلاً بالربط. رجاءً، أبلغ مسؤول النظام.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "عنوان URL المُعاد توجيهه يجب أن يكون عنواناً كاملاً كما في المثال التالي: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "يسمح بالتحقّق من الهويّة عن طريق تطبيقات وب أخرى متوافقة مع OAuth2.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "التطبيق OAuth2 يسمح لمسؤولي النظام بتهيئة الإجراء الأساسي للتحقّق من الهويّةبحيث يسمح بالتحقّق من الهويّة عن طريق تطبيقات ويب أخرى متوافقة مع OAuth2.",
+ "OAuth 2.0 clients" : "عملاء OAuth2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "يسمح OAuth2.0 لخدمات خارجية بطلب الوصول إلى {instanceName}",
+ "Name" : "الاسم",
+ "Redirection URI" : "رابط إعادة التوجيه",
+ "Client Identifier" : "مُعرِّف العميل",
+ "Secret key" : "المفتاح السّرّي",
+ "Delete client" : "حذف عميل ",
+ "Make sure you store the secret key, it cannot be recovered." : "تأكد من تخزين المفتاح السري، إذ أنه لا يمكن استعادته.",
+ "Add client" : "إضافة عميل",
+ "Add" : "إضافة",
+ "Show client secret" : "عرض كلمة سر العميل",
+ "Hide client secret" : "إخْفِ سِرَّ العميل",
+ "Delete" : "حذف"
+},"pluralForm" :"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/ast.js b/apps/oauth2/l10n/ast.js
new file mode 100644
index 00000000000..5a71f29b226
--- /dev/null
+++ b/apps/oauth2/l10n/ast.js
@@ -0,0 +1,22 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "El veceru nun tien autorización pa conectase. Informa al alministrador de l'aplicación.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "La URL de redireición tien de ser una URL completa, por exemplu: https://dominiu.com/camin",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite l'autenticación compatible con OAuth2 dende otres aplicaciones web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "L'aplicación OAuth2 permite a l'alministración configurar el fluxu de trabayu d'autenticación integráu pa que tamién permita l'autenticación compatible con OAuth2 dende otres aplicaciones web.",
+ "OAuth 2.0 clients" : "Veceros d'OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permite que los servicios esternos soliciten l'accesu a «{instanceName}».",
+ "Name" : "Nome",
+ "Redirection URI" : "URI de redireición",
+ "Client Identifier" : "Identificador del veceru",
+ "Secret key" : "Clave del secretu",
+ "Delete client" : "Desaniciar el veceru",
+ "Add client" : "Amestar el veceru",
+ "Add" : "Amestar",
+ "Show client secret" : "Amosar el secretu del veceru",
+ "Hide client secret" : "Anubrir el secretu del veceru",
+ "Delete" : "Desaniciar"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/ast.json b/apps/oauth2/l10n/ast.json
new file mode 100644
index 00000000000..07995fc9268
--- /dev/null
+++ b/apps/oauth2/l10n/ast.json
@@ -0,0 +1,20 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "El veceru nun tien autorización pa conectase. Informa al alministrador de l'aplicación.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "La URL de redireición tien de ser una URL completa, por exemplu: https://dominiu.com/camin",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite l'autenticación compatible con OAuth2 dende otres aplicaciones web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "L'aplicación OAuth2 permite a l'alministración configurar el fluxu de trabayu d'autenticación integráu pa que tamién permita l'autenticación compatible con OAuth2 dende otres aplicaciones web.",
+ "OAuth 2.0 clients" : "Veceros d'OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permite que los servicios esternos soliciten l'accesu a «{instanceName}».",
+ "Name" : "Nome",
+ "Redirection URI" : "URI de redireición",
+ "Client Identifier" : "Identificador del veceru",
+ "Secret key" : "Clave del secretu",
+ "Delete client" : "Desaniciar el veceru",
+ "Add client" : "Amestar el veceru",
+ "Add" : "Amestar",
+ "Show client secret" : "Amosar el secretu del veceru",
+ "Hide client secret" : "Anubrir el secretu del veceru",
+ "Delete" : "Desaniciar"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/bg.js b/apps/oauth2/l10n/bg.js
new file mode 100644
index 00000000000..c5bdfa109f5
--- /dev/null
+++ b/apps/oauth2/l10n/bg.js
@@ -0,0 +1,20 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Вашият клиент не е упълномощен да се свързва. Моля, информирайте администратора на вашия клиент.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Вашият URL за пренасочване трябва да е пълен URL, например: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Позволява удостоверяване от други уеб приложения, съвместими с OAuth2 .",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Приложението OAuth2 позволява на администраторите да конфигурират вградения работен поток за удостоверяване, за да позволят и удостоверяване от други уеб приложения, съвместими с OAuth2 .",
+ "OAuth 2.0 clients" : "OAuth 2.0 клиенти",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 позволява на външни услуги да изискват достъп до {instanceName}.",
+ "Name" : "Име",
+ "Redirection URI" : "URI за пренасочване",
+ "Client Identifier" : "Идентификатор на клиента",
+ "Secret key" : "Секретен ключ",
+ "Add client" : "Добавяне на клиент",
+ "Add" : "Добавяне",
+ "Show client secret" : "Покажете тайната на клиента",
+ "Delete" : "Изтриване"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/bg.json b/apps/oauth2/l10n/bg.json
new file mode 100644
index 00000000000..5a11b54b85b
--- /dev/null
+++ b/apps/oauth2/l10n/bg.json
@@ -0,0 +1,18 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Вашият клиент не е упълномощен да се свързва. Моля, информирайте администратора на вашия клиент.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Вашият URL за пренасочване трябва да е пълен URL, например: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Позволява удостоверяване от други уеб приложения, съвместими с OAuth2 .",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Приложението OAuth2 позволява на администраторите да конфигурират вградения работен поток за удостоверяване, за да позволят и удостоверяване от други уеб приложения, съвместими с OAuth2 .",
+ "OAuth 2.0 clients" : "OAuth 2.0 клиенти",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 позволява на външни услуги да изискват достъп до {instanceName}.",
+ "Name" : "Име",
+ "Redirection URI" : "URI за пренасочване",
+ "Client Identifier" : "Идентификатор на клиента",
+ "Secret key" : "Секретен ключ",
+ "Add client" : "Добавяне на клиент",
+ "Add" : "Добавяне",
+ "Show client secret" : "Покажете тайната на клиента",
+ "Delete" : "Изтриване"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/br.js b/apps/oauth2/l10n/br.js
new file mode 100644
index 00000000000..d04244a3353
--- /dev/null
+++ b/apps/oauth2/l10n/br.js
@@ -0,0 +1,20 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "N'eo ket aotreet ho kliant d'en em kenstagañ. Galvit merour ho kliant.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Ho adhenter URL en deus ezhomm bezhañ un URL klog da skouer : https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Oatreit an dilesa a glot gant OAuth 2.0 diouzh ho meziant web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Aotreant a raio ar meziant OAuth 2.0 ar merour da arventennoù evit an dilesa red-labour e-barzh evit aotreañ an dilesa a glot gant OAuth 2.0 eus meziantoù web all.",
+ "OAuth 2.0 clients" : "Kliant OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "Aotreañ a ra OAuth 2.0 ar servijourienn diavaez da goulenn tizhout {instanceName}.",
+ "Name" : "Anv",
+ "Redirection URI" : "Adhent URL",
+ "Client Identifier" : "Anavezer Kliant",
+ "Secret key" : "Alc'hwez sekred",
+ "Add client" : "Ouzhoennañ kliant",
+ "Add" : "Ouzhpennañ",
+ "Show client secret" : "Diskouez sekret ar kliant",
+ "Delete" : "Dilemel"
+},
+"nplurals=5; plural=((n%10 == 1) && (n%100 != 11) && (n%100 !=71) && (n%100 !=91) ? 0 :(n%10 == 2) && (n%100 != 12) && (n%100 !=72) && (n%100 !=92) ? 1 :(n%10 ==3 || n%10==4 || n%10==9) && (n%100 < 10 || n% 100 > 19) && (n%100 < 70 || n%100 > 79) && (n%100 < 90 || n%100 > 99) ? 2 :(n != 0 && n % 1000000 == 0) ? 3 : 4);");
diff --git a/apps/oauth2/l10n/br.json b/apps/oauth2/l10n/br.json
new file mode 100644
index 00000000000..f55e5b3f1ac
--- /dev/null
+++ b/apps/oauth2/l10n/br.json
@@ -0,0 +1,18 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "N'eo ket aotreet ho kliant d'en em kenstagañ. Galvit merour ho kliant.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Ho adhenter URL en deus ezhomm bezhañ un URL klog da skouer : https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Oatreit an dilesa a glot gant OAuth 2.0 diouzh ho meziant web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Aotreant a raio ar meziant OAuth 2.0 ar merour da arventennoù evit an dilesa red-labour e-barzh evit aotreañ an dilesa a glot gant OAuth 2.0 eus meziantoù web all.",
+ "OAuth 2.0 clients" : "Kliant OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "Aotreañ a ra OAuth 2.0 ar servijourienn diavaez da goulenn tizhout {instanceName}.",
+ "Name" : "Anv",
+ "Redirection URI" : "Adhent URL",
+ "Client Identifier" : "Anavezer Kliant",
+ "Secret key" : "Alc'hwez sekred",
+ "Add client" : "Ouzhoennañ kliant",
+ "Add" : "Ouzhpennañ",
+ "Show client secret" : "Diskouez sekret ar kliant",
+ "Delete" : "Dilemel"
+},"pluralForm" :"nplurals=5; plural=((n%10 == 1) && (n%100 != 11) && (n%100 !=71) && (n%100 !=91) ? 0 :(n%10 == 2) && (n%100 != 12) && (n%100 !=72) && (n%100 !=92) ? 1 :(n%10 ==3 || n%10==4 || n%10==9) && (n%100 < 10 || n% 100 > 19) && (n%100 < 70 || n%100 > 79) && (n%100 < 90 || n%100 > 99) ? 2 :(n != 0 && n % 1000000 == 0) ? 3 : 4);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/ca.js b/apps/oauth2/l10n/ca.js
new file mode 100644
index 00000000000..cb74993c9fd
--- /dev/null
+++ b/apps/oauth2/l10n/ca.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "El vostre client no té autorització per a connectar-se. Informeu-ne a l'administrador del client.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "L'URL de redirecció ha de ser un URL complet, com ara: https://domini.com/camí",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permet l'autenticació compatible amb OAuth2 des d'altres aplicacions web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "L'aplicació OAuth2 permet als administradors configurar el flux de treball d'autenticació integrat perquè també permeti l'autenticació compatible amb OAuth2 des d'altres aplicacions web.",
+ "OAuth 2.0 clients" : "Clients OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permet que serveis externs sol·licitin accés a {instanceName}.",
+ "Name" : "Nom",
+ "Redirection URI" : "URI de redirecció",
+ "Client Identifier" : "Identificador del client",
+ "Secret key" : "Clau secreta",
+ "Delete client" : "Suprimeix el client",
+ "Make sure you store the secret key, it cannot be recovered." : "Assegureu-vos d'emmagatzemar la clau secreta, no es pot recuperar.",
+ "Add client" : "Afegeix un client",
+ "Add" : "Afegeix",
+ "Show client secret" : "Mostra el secret del client",
+ "Hide client secret" : "Amaga el secret del client",
+ "Delete" : "Suprimeix"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/ca.json b/apps/oauth2/l10n/ca.json
new file mode 100644
index 00000000000..3a064e3639e
--- /dev/null
+++ b/apps/oauth2/l10n/ca.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "El vostre client no té autorització per a connectar-se. Informeu-ne a l'administrador del client.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "L'URL de redirecció ha de ser un URL complet, com ara: https://domini.com/camí",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permet l'autenticació compatible amb OAuth2 des d'altres aplicacions web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "L'aplicació OAuth2 permet als administradors configurar el flux de treball d'autenticació integrat perquè també permeti l'autenticació compatible amb OAuth2 des d'altres aplicacions web.",
+ "OAuth 2.0 clients" : "Clients OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permet que serveis externs sol·licitin accés a {instanceName}.",
+ "Name" : "Nom",
+ "Redirection URI" : "URI de redirecció",
+ "Client Identifier" : "Identificador del client",
+ "Secret key" : "Clau secreta",
+ "Delete client" : "Suprimeix el client",
+ "Make sure you store the secret key, it cannot be recovered." : "Assegureu-vos d'emmagatzemar la clau secreta, no es pot recuperar.",
+ "Add client" : "Afegeix un client",
+ "Add" : "Afegeix",
+ "Show client secret" : "Mostra el secret del client",
+ "Hide client secret" : "Amaga el secret del client",
+ "Delete" : "Suprimeix"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/cs.js b/apps/oauth2/l10n/cs.js
new file mode 100644
index 00000000000..008e1627780
--- /dev/null
+++ b/apps/oauth2/l10n/cs.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Váš klient není autorizován pro připojení se. Informujte správce svého klienta.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "URL adresu přesměrování je třeba uvádět celou, např. https://yourdomain.com/umisteni",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Umožní ověřování kompatibilní s OAuth2 z ostatních webových aplikací.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Aplikace OAuth2 umožňuje správcům nastavit vestavěný postup ověřování tak, aby podporoval také OAuth2 kompatibilní ověřování z ostatních webových aplikací.",
+ "OAuth 2.0 clients" : "OAuth 2.0 klienti",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 umožňuje externím službám vyžádat si přístup k {instanceName}.",
+ "Name" : "Název",
+ "Redirection URI" : "URL pro přesměrování",
+ "Client Identifier" : "Identifikátor klienta",
+ "Secret key" : "Tajný klíč",
+ "Delete client" : "Smazat klienta",
+ "Make sure you store the secret key, it cannot be recovered." : "Nezapomeňte si uložit tajný klíč – není možné ho obnovit.",
+ "Add client" : "Přidat klienta",
+ "Add" : "Přidat",
+ "Show client secret" : "Zobrazit klientské tajemství",
+ "Hide client secret" : "Skrýt tajemství klienta",
+ "Delete" : "Smazat"
+},
+"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;");
diff --git a/apps/oauth2/l10n/cs.json b/apps/oauth2/l10n/cs.json
new file mode 100644
index 00000000000..75f5586ca80
--- /dev/null
+++ b/apps/oauth2/l10n/cs.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Váš klient není autorizován pro připojení se. Informujte správce svého klienta.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "URL adresu přesměrování je třeba uvádět celou, např. https://yourdomain.com/umisteni",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Umožní ověřování kompatibilní s OAuth2 z ostatních webových aplikací.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Aplikace OAuth2 umožňuje správcům nastavit vestavěný postup ověřování tak, aby podporoval také OAuth2 kompatibilní ověřování z ostatních webových aplikací.",
+ "OAuth 2.0 clients" : "OAuth 2.0 klienti",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 umožňuje externím službám vyžádat si přístup k {instanceName}.",
+ "Name" : "Název",
+ "Redirection URI" : "URL pro přesměrování",
+ "Client Identifier" : "Identifikátor klienta",
+ "Secret key" : "Tajný klíč",
+ "Delete client" : "Smazat klienta",
+ "Make sure you store the secret key, it cannot be recovered." : "Nezapomeňte si uložit tajný klíč – není možné ho obnovit.",
+ "Add client" : "Přidat klienta",
+ "Add" : "Přidat",
+ "Show client secret" : "Zobrazit klientské tajemství",
+ "Hide client secret" : "Skrýt tajemství klienta",
+ "Delete" : "Smazat"
+},"pluralForm" :"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/da.js b/apps/oauth2/l10n/da.js
new file mode 100644
index 00000000000..981fd99ab1c
--- /dev/null
+++ b/apps/oauth2/l10n/da.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Din klient er ikke autoriseret til at oprette forbindelse. Informer venligst din klients administrator.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Din omdirigerings-URL skal være en fuld URL, for eksempel: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Tillader OAuth2-kompatibel godkendelse fra andre webapplikationer.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2-appen tillader administratorer mulighed for at konfigurere den indbyggede godkendelsesworkflow til også at tillade OAuth2-kompatibel godkendelse fra andre webapplikationer.",
+ "OAuth 2.0 clients" : "OAuth 2.0 klienter",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 tillader eksterne tjenester at anmode om adgang til {instanceName}.",
+ "Name" : "Navn",
+ "Redirection URI" : "Viderestilling URI",
+ "Client Identifier" : "Klient ID",
+ "Secret key" : "Hemmelig nøgle",
+ "Delete client" : "Slet klient",
+ "Make sure you store the secret key, it cannot be recovered." : "Sørg for at gemme den hemmelige nøgle, den kan ikke gendannes.",
+ "Add client" : "Tilføj klient",
+ "Add" : "Tilføj",
+ "Show client secret" : "Vis klient hemmelighed",
+ "Hide client secret" : "Skjul klient hemmelighed",
+ "Delete" : "Slet"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/da.json b/apps/oauth2/l10n/da.json
new file mode 100644
index 00000000000..289737290fe
--- /dev/null
+++ b/apps/oauth2/l10n/da.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Din klient er ikke autoriseret til at oprette forbindelse. Informer venligst din klients administrator.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Din omdirigerings-URL skal være en fuld URL, for eksempel: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Tillader OAuth2-kompatibel godkendelse fra andre webapplikationer.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2-appen tillader administratorer mulighed for at konfigurere den indbyggede godkendelsesworkflow til også at tillade OAuth2-kompatibel godkendelse fra andre webapplikationer.",
+ "OAuth 2.0 clients" : "OAuth 2.0 klienter",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 tillader eksterne tjenester at anmode om adgang til {instanceName}.",
+ "Name" : "Navn",
+ "Redirection URI" : "Viderestilling URI",
+ "Client Identifier" : "Klient ID",
+ "Secret key" : "Hemmelig nøgle",
+ "Delete client" : "Slet klient",
+ "Make sure you store the secret key, it cannot be recovered." : "Sørg for at gemme den hemmelige nøgle, den kan ikke gendannes.",
+ "Add client" : "Tilføj klient",
+ "Add" : "Tilføj",
+ "Show client secret" : "Vis klient hemmelighed",
+ "Hide client secret" : "Skjul klient hemmelighed",
+ "Delete" : "Slet"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/de.js b/apps/oauth2/l10n/de.js
new file mode 100644
index 00000000000..20d0e79912a
--- /dev/null
+++ b/apps/oauth2/l10n/de.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Dein Client ist nicht berechtigt, eine Verbindung herzustellen. Bitte informiere die Administration deines Clients.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Die Weiterleitungs-URL muss eine vollständige URL sein. Beispiel: https://deinedomain.com/pfad",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Ermöglicht OAuth2 komplatible Authentifizierung durch andere Web-Anwendungen.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Die OAuth2-App ermöglicht es Administratoren den eingebauten Authenztifizierungsablauf dahingehend zu konfigurieren, das auch ein OAuth2 komplatible Authentifizierung von anderen Web-Anwendungen aus möglich ist. ",
+ "OAuth 2.0 clients" : "OAuth 2.0-Clients",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 ermöglicht es externen Diensten, Zugriff auf {instanceName} anzufordern.",
+ "Name" : "Name",
+ "Redirection URI" : "Weiterleitungs-URI",
+ "Client Identifier" : "Client-Identifikationsmerkmal",
+ "Secret key" : "Geheimer Schlüssel",
+ "Delete client" : "Client löschen",
+ "Make sure you store the secret key, it cannot be recovered." : "Bewahre den geheimen Schlüssel unbedingt auf, er kann nicht wiederhergestellt werden.",
+ "Add client" : "Client hinzufügen",
+ "Add" : "Hinzufügen",
+ "Show client secret" : "Geheime Zeichenkette des Clients anzeigen",
+ "Hide client secret" : "Geheime Zeichenkette des Clients ausblenden",
+ "Delete" : " Löschen"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/de.json b/apps/oauth2/l10n/de.json
new file mode 100644
index 00000000000..60c835a800f
--- /dev/null
+++ b/apps/oauth2/l10n/de.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Dein Client ist nicht berechtigt, eine Verbindung herzustellen. Bitte informiere die Administration deines Clients.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Die Weiterleitungs-URL muss eine vollständige URL sein. Beispiel: https://deinedomain.com/pfad",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Ermöglicht OAuth2 komplatible Authentifizierung durch andere Web-Anwendungen.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Die OAuth2-App ermöglicht es Administratoren den eingebauten Authenztifizierungsablauf dahingehend zu konfigurieren, das auch ein OAuth2 komplatible Authentifizierung von anderen Web-Anwendungen aus möglich ist. ",
+ "OAuth 2.0 clients" : "OAuth 2.0-Clients",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 ermöglicht es externen Diensten, Zugriff auf {instanceName} anzufordern.",
+ "Name" : "Name",
+ "Redirection URI" : "Weiterleitungs-URI",
+ "Client Identifier" : "Client-Identifikationsmerkmal",
+ "Secret key" : "Geheimer Schlüssel",
+ "Delete client" : "Client löschen",
+ "Make sure you store the secret key, it cannot be recovered." : "Bewahre den geheimen Schlüssel unbedingt auf, er kann nicht wiederhergestellt werden.",
+ "Add client" : "Client hinzufügen",
+ "Add" : "Hinzufügen",
+ "Show client secret" : "Geheime Zeichenkette des Clients anzeigen",
+ "Hide client secret" : "Geheime Zeichenkette des Clients ausblenden",
+ "Delete" : " Löschen"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/de_DE.js b/apps/oauth2/l10n/de_DE.js
new file mode 100644
index 00000000000..941b430bcb4
--- /dev/null
+++ b/apps/oauth2/l10n/de_DE.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Ihr Client ist nicht berechtigt, eine Verbindung herzustellen. Bitte informieren Sie die Administration Ihres Clients.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Die Weiterleitungs-URL muss eine vollständige URL sein. Beispiel: https://ihredomain.com/pfad",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Ermöglicht OAuth2-kompatible Authentifizierung durch andere Web-Anwendungen.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Die OAuth2-App ermöglicht es Administratoren, den eingebauten Authentifizierungsablauf dahingehend zu konfigurieren, dass auch eine OAuth2-kompatible Authentifizierung durch andere Web-Anwendungen möglich ist. ",
+ "OAuth 2.0 clients" : "OAuth 2.0-Clients",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 ermöglicht es externen Diensten, Zugriff auf {instanceName} anzufordern.",
+ "Name" : "Name",
+ "Redirection URI" : "Weiterleitungs-URI",
+ "Client Identifier" : "Client-Identifikationsmerkmal",
+ "Secret key" : "Geheimer Schlüssel",
+ "Delete client" : "Client löschen",
+ "Make sure you store the secret key, it cannot be recovered." : "Bewahren Sie den geheimen Schlüssel unbedingt auf, er kann nicht wiederhergestellt werden.",
+ "Add client" : "Client hinzufügen",
+ "Add" : "Hinzufügen",
+ "Show client secret" : "Geheime Zeichenkette des Clients anzeigen",
+ "Hide client secret" : "Geheime Zeichenkette des Clients ausblenden",
+ "Delete" : "Löschen"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/de_DE.json b/apps/oauth2/l10n/de_DE.json
new file mode 100644
index 00000000000..b2be0a4dfb4
--- /dev/null
+++ b/apps/oauth2/l10n/de_DE.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Ihr Client ist nicht berechtigt, eine Verbindung herzustellen. Bitte informieren Sie die Administration Ihres Clients.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Die Weiterleitungs-URL muss eine vollständige URL sein. Beispiel: https://ihredomain.com/pfad",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Ermöglicht OAuth2-kompatible Authentifizierung durch andere Web-Anwendungen.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Die OAuth2-App ermöglicht es Administratoren, den eingebauten Authentifizierungsablauf dahingehend zu konfigurieren, dass auch eine OAuth2-kompatible Authentifizierung durch andere Web-Anwendungen möglich ist. ",
+ "OAuth 2.0 clients" : "OAuth 2.0-Clients",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 ermöglicht es externen Diensten, Zugriff auf {instanceName} anzufordern.",
+ "Name" : "Name",
+ "Redirection URI" : "Weiterleitungs-URI",
+ "Client Identifier" : "Client-Identifikationsmerkmal",
+ "Secret key" : "Geheimer Schlüssel",
+ "Delete client" : "Client löschen",
+ "Make sure you store the secret key, it cannot be recovered." : "Bewahren Sie den geheimen Schlüssel unbedingt auf, er kann nicht wiederhergestellt werden.",
+ "Add client" : "Client hinzufügen",
+ "Add" : "Hinzufügen",
+ "Show client secret" : "Geheime Zeichenkette des Clients anzeigen",
+ "Hide client secret" : "Geheime Zeichenkette des Clients ausblenden",
+ "Delete" : "Löschen"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/el.js b/apps/oauth2/l10n/el.js
new file mode 100644
index 00000000000..d576b821c94
--- /dev/null
+++ b/apps/oauth2/l10n/el.js
@@ -0,0 +1,20 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Ο εφαρμογή σας δεν είναι εξουσιοδοτημένη να συνδεθεί. Ενημερώστε τον διαχειριστή σας.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Η διεύθυνση URL ανακατεύθυνσης πρέπει να είναι μια πλήρη διεύθυνση URL, για παράδειγμα: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Επιτρέπει τον συμβατό έλεγχο ταυτότητας OAuth2 από άλλες εφαρμογές ιστού.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Η εφαρμογή OAuth2 επιτρέπει στους διαχειριστές να ρυθμίζουν τις παραμέτρους της ενσωματωμένης ροής εργασιών ελέγχου ταυτότητας για να επιτρέπουν επίσης τον έλεγχο ταυτότητας OAuth2 από άλλες εφαρμογές ιστού.",
+ "OAuth 2.0 clients" : "Πελάτες OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "Το OAuth 2.0 επιτρέπει στις εξωτερικές υπηρεσίες να ζητούν πρόσβαση στο {instanceName}.",
+ "Name" : "Όνομα",
+ "Redirection URI" : "URI ανακατεύθυνσης",
+ "Client Identifier" : "Αναγνωριστικό πελάτη",
+ "Secret key" : "Μυστικό κλειδί",
+ "Add client" : "Προσθήκη πελάτη",
+ "Add" : "Προσθήκη",
+ "Show client secret" : "Εμφάνιση μυστικού εφαρμογής υπολογιστή",
+ "Delete" : "Διαγραφή"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/el.json b/apps/oauth2/l10n/el.json
new file mode 100644
index 00000000000..dd771839bc0
--- /dev/null
+++ b/apps/oauth2/l10n/el.json
@@ -0,0 +1,18 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Ο εφαρμογή σας δεν είναι εξουσιοδοτημένη να συνδεθεί. Ενημερώστε τον διαχειριστή σας.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Η διεύθυνση URL ανακατεύθυνσης πρέπει να είναι μια πλήρη διεύθυνση URL, για παράδειγμα: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Επιτρέπει τον συμβατό έλεγχο ταυτότητας OAuth2 από άλλες εφαρμογές ιστού.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Η εφαρμογή OAuth2 επιτρέπει στους διαχειριστές να ρυθμίζουν τις παραμέτρους της ενσωματωμένης ροής εργασιών ελέγχου ταυτότητας για να επιτρέπουν επίσης τον έλεγχο ταυτότητας OAuth2 από άλλες εφαρμογές ιστού.",
+ "OAuth 2.0 clients" : "Πελάτες OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "Το OAuth 2.0 επιτρέπει στις εξωτερικές υπηρεσίες να ζητούν πρόσβαση στο {instanceName}.",
+ "Name" : "Όνομα",
+ "Redirection URI" : "URI ανακατεύθυνσης",
+ "Client Identifier" : "Αναγνωριστικό πελάτη",
+ "Secret key" : "Μυστικό κλειδί",
+ "Add client" : "Προσθήκη πελάτη",
+ "Add" : "Προσθήκη",
+ "Show client secret" : "Εμφάνιση μυστικού εφαρμογής υπολογιστή",
+ "Delete" : "Διαγραφή"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/en_GB.js b/apps/oauth2/l10n/en_GB.js
new file mode 100644
index 00000000000..fb42df933ca
--- /dev/null
+++ b/apps/oauth2/l10n/en_GB.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Your client is not authorised to connect. Please inform the administrator of your client.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Allows OAuth2 compatible authentication from other web applications.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications.",
+ "OAuth 2.0 clients" : "OAuth 2.0 clients",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 allows external services to request access to {instanceName}.",
+ "Name" : "Name",
+ "Redirection URI" : "Redirection URI",
+ "Client Identifier" : "Client Identifier",
+ "Secret key" : "Secret key",
+ "Delete client" : "Delete client",
+ "Make sure you store the secret key, it cannot be recovered." : "Make sure you store the secret key, it cannot be recovered.",
+ "Add client" : "Add client",
+ "Add" : "Add",
+ "Show client secret" : "Show client secret",
+ "Hide client secret" : "Hide client secret",
+ "Delete" : "Delete"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/en_GB.json b/apps/oauth2/l10n/en_GB.json
new file mode 100644
index 00000000000..85029055a07
--- /dev/null
+++ b/apps/oauth2/l10n/en_GB.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Your client is not authorised to connect. Please inform the administrator of your client.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Allows OAuth2 compatible authentication from other web applications.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications.",
+ "OAuth 2.0 clients" : "OAuth 2.0 clients",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 allows external services to request access to {instanceName}.",
+ "Name" : "Name",
+ "Redirection URI" : "Redirection URI",
+ "Client Identifier" : "Client Identifier",
+ "Secret key" : "Secret key",
+ "Delete client" : "Delete client",
+ "Make sure you store the secret key, it cannot be recovered." : "Make sure you store the secret key, it cannot be recovered.",
+ "Add client" : "Add client",
+ "Add" : "Add",
+ "Show client secret" : "Show client secret",
+ "Hide client secret" : "Hide client secret",
+ "Delete" : "Delete"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/es.js b/apps/oauth2/l10n/es.js
new file mode 100644
index 00000000000..2c7e76a2cbb
--- /dev/null
+++ b/apps/oauth2/l10n/es.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Tu cliente no está autorizado a conectarse. Por favor, informa de tu cliente al administrador.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Tu URL de redirección debe ser una URL completa, por ejemplo: https://tudominio.com/ruta",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite autenticación con OAuth2 desde otras aplicaciones web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "La app OAuth2 permite a los administradores configurar el flujo de trabajo de autenticación incorporado para permitir también autenticación compatible con OAuth2 desde otras aplicaciones web.",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permite a servicios externos solicitar acceso a {instanceName}",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI de redirección",
+ "Client Identifier" : "Identificador de cliente",
+ "Secret key" : "Clave secreta",
+ "Delete client" : "Eliminar cliente",
+ "Make sure you store the secret key, it cannot be recovered." : "Asegúrese de guardar la clave secreta, ya que no podrá ser recuperada.",
+ "Add client" : "Añadir cliente",
+ "Add" : "Añadir",
+ "Show client secret" : "Mostrar secreto del cliente",
+ "Hide client secret" : "Ocultar secreto del cliente",
+ "Delete" : "Eliminar"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/oauth2/l10n/es.json b/apps/oauth2/l10n/es.json
new file mode 100644
index 00000000000..9854ba3aa83
--- /dev/null
+++ b/apps/oauth2/l10n/es.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Tu cliente no está autorizado a conectarse. Por favor, informa de tu cliente al administrador.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Tu URL de redirección debe ser una URL completa, por ejemplo: https://tudominio.com/ruta",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite autenticación con OAuth2 desde otras aplicaciones web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "La app OAuth2 permite a los administradores configurar el flujo de trabajo de autenticación incorporado para permitir también autenticación compatible con OAuth2 desde otras aplicaciones web.",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permite a servicios externos solicitar acceso a {instanceName}",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI de redirección",
+ "Client Identifier" : "Identificador de cliente",
+ "Secret key" : "Clave secreta",
+ "Delete client" : "Eliminar cliente",
+ "Make sure you store the secret key, it cannot be recovered." : "Asegúrese de guardar la clave secreta, ya que no podrá ser recuperada.",
+ "Add client" : "Añadir cliente",
+ "Add" : "Añadir",
+ "Show client secret" : "Mostrar secreto del cliente",
+ "Hide client secret" : "Ocultar secreto del cliente",
+ "Delete" : "Eliminar"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/es_AR.js b/apps/oauth2/l10n/es_AR.js
new file mode 100644
index 00000000000..ec54aae2bea
--- /dev/null
+++ b/apps/oauth2/l10n/es_AR.js
@@ -0,0 +1,14 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI de redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Delete" : "Eliminar"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/oauth2/l10n/es_AR.json b/apps/oauth2/l10n/es_AR.json
new file mode 100644
index 00000000000..31ae3db82be
--- /dev/null
+++ b/apps/oauth2/l10n/es_AR.json
@@ -0,0 +1,12 @@
+{ "translations": {
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI de redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Delete" : "Eliminar"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/es_CL.js b/apps/oauth2/l10n/es_CL.js
new file mode 100644
index 00000000000..079b21192fd
--- /dev/null
+++ b/apps/oauth2/l10n/es_CL.js
@@ -0,0 +1,14 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Delete" : "Borrar"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/oauth2/l10n/es_CL.json b/apps/oauth2/l10n/es_CL.json
new file mode 100644
index 00000000000..40b679cc951
--- /dev/null
+++ b/apps/oauth2/l10n/es_CL.json
@@ -0,0 +1,12 @@
+{ "translations": {
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Delete" : "Borrar"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/es_CO.js b/apps/oauth2/l10n/es_CO.js
new file mode 100644
index 00000000000..079b21192fd
--- /dev/null
+++ b/apps/oauth2/l10n/es_CO.js
@@ -0,0 +1,14 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Delete" : "Borrar"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/oauth2/l10n/es_CO.json b/apps/oauth2/l10n/es_CO.json
new file mode 100644
index 00000000000..40b679cc951
--- /dev/null
+++ b/apps/oauth2/l10n/es_CO.json
@@ -0,0 +1,12 @@
+{ "translations": {
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Delete" : "Borrar"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/es_CR.js b/apps/oauth2/l10n/es_CR.js
new file mode 100644
index 00000000000..079b21192fd
--- /dev/null
+++ b/apps/oauth2/l10n/es_CR.js
@@ -0,0 +1,14 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Delete" : "Borrar"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/oauth2/l10n/es_CR.json b/apps/oauth2/l10n/es_CR.json
new file mode 100644
index 00000000000..40b679cc951
--- /dev/null
+++ b/apps/oauth2/l10n/es_CR.json
@@ -0,0 +1,12 @@
+{ "translations": {
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Delete" : "Borrar"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/es_DO.js b/apps/oauth2/l10n/es_DO.js
new file mode 100644
index 00000000000..079b21192fd
--- /dev/null
+++ b/apps/oauth2/l10n/es_DO.js
@@ -0,0 +1,14 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Delete" : "Borrar"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/oauth2/l10n/es_DO.json b/apps/oauth2/l10n/es_DO.json
new file mode 100644
index 00000000000..40b679cc951
--- /dev/null
+++ b/apps/oauth2/l10n/es_DO.json
@@ -0,0 +1,12 @@
+{ "translations": {
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Delete" : "Borrar"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/es_EC.js b/apps/oauth2/l10n/es_EC.js
new file mode 100644
index 00000000000..38fab4a83a6
--- /dev/null
+++ b/apps/oauth2/l10n/es_EC.js
@@ -0,0 +1,20 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Tu cliente no está autorizado para conectarse. Por favor, informa al administrador de tu cliente.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Tu URL de redirección debe ser una URL completa, por ejemplo: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite la autenticación compatible con OAuth2 desde otras aplicaciones web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "La aplicación OAuth2 permite a los administradores configurar el flujo de autenticación integrado para permitir la autenticación compatible con OAuth2 desde otras aplicaciones web.",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permite a servicios externos solicitar acceso a {instanceName}.",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Show client secret" : "Mostrar el secreto del cliente",
+ "Delete" : "Borrar"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/oauth2/l10n/es_EC.json b/apps/oauth2/l10n/es_EC.json
new file mode 100644
index 00000000000..3dd48dcdcd2
--- /dev/null
+++ b/apps/oauth2/l10n/es_EC.json
@@ -0,0 +1,18 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Tu cliente no está autorizado para conectarse. Por favor, informa al administrador de tu cliente.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Tu URL de redirección debe ser una URL completa, por ejemplo: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite la autenticación compatible con OAuth2 desde otras aplicaciones web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "La aplicación OAuth2 permite a los administradores configurar el flujo de autenticación integrado para permitir la autenticación compatible con OAuth2 desde otras aplicaciones web.",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permite a servicios externos solicitar acceso a {instanceName}.",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Show client secret" : "Mostrar el secreto del cliente",
+ "Delete" : "Borrar"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/es_GT.js b/apps/oauth2/l10n/es_GT.js
new file mode 100644
index 00000000000..38f9a9c8eff
--- /dev/null
+++ b/apps/oauth2/l10n/es_GT.js
@@ -0,0 +1,20 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Su cliente no está autorizado para conectarse. Por favor informe al administrador de su cliente.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Su URL de redireccionamiento debe ser una URL completa, por ejemplo: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite la autenticación compatible con OAuth2 desde otras aplicaciones web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "La aplicación OAuth2 permite a los administradores configurar el flujo de trabajo de autenticación incorporado para permitir también la autenticación compatible con OAuth2 desde otras aplicaciones web.",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permite que los servicios externos soliciten acceso a {instanceName}.",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Show client secret" : "Mostrar secreto del cliente",
+ "Delete" : "Borrar"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/oauth2/l10n/es_GT.json b/apps/oauth2/l10n/es_GT.json
new file mode 100644
index 00000000000..fea09081d1d
--- /dev/null
+++ b/apps/oauth2/l10n/es_GT.json
@@ -0,0 +1,18 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Su cliente no está autorizado para conectarse. Por favor informe al administrador de su cliente.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Su URL de redireccionamiento debe ser una URL completa, por ejemplo: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite la autenticación compatible con OAuth2 desde otras aplicaciones web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "La aplicación OAuth2 permite a los administradores configurar el flujo de trabajo de autenticación incorporado para permitir también la autenticación compatible con OAuth2 desde otras aplicaciones web.",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permite que los servicios externos soliciten acceso a {instanceName}.",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Show client secret" : "Mostrar secreto del cliente",
+ "Delete" : "Borrar"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/es_MX.js b/apps/oauth2/l10n/es_MX.js
new file mode 100644
index 00000000000..ac9270bc6af
--- /dev/null
+++ b/apps/oauth2/l10n/es_MX.js
@@ -0,0 +1,22 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Su cliente no está autorizado para conectarse. Por favor, informe al administrador de su cliente.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Su URL de redireccionamiento debe ser una URL completa, por ejemplo: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite autenticación compatible con OAuth2 desde otras aplicaciones web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "La aplicación OAuth2 le permite a los administradores configurar el flujo de autenticación integrado para también permitir la autenticación OAuth2 de otras aplicaciones web compatibles. ",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permite que servicios externos soliciten acceso a {instanceName}.",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Delete client" : "Eliminar cliente",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Show client secret" : "Mostrar el secreto del cliente",
+ "Hide client secret" : "Ocultar el secreto del cliente",
+ "Delete" : "Borrar"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/oauth2/l10n/es_MX.json b/apps/oauth2/l10n/es_MX.json
new file mode 100644
index 00000000000..6f0d19e414a
--- /dev/null
+++ b/apps/oauth2/l10n/es_MX.json
@@ -0,0 +1,20 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Su cliente no está autorizado para conectarse. Por favor, informe al administrador de su cliente.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Su URL de redireccionamiento debe ser una URL completa, por ejemplo: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite autenticación compatible con OAuth2 desde otras aplicaciones web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "La aplicación OAuth2 le permite a los administradores configurar el flujo de autenticación integrado para también permitir la autenticación OAuth2 de otras aplicaciones web compatibles. ",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permite que servicios externos soliciten acceso a {instanceName}.",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Delete client" : "Eliminar cliente",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Show client secret" : "Mostrar el secreto del cliente",
+ "Hide client secret" : "Ocultar el secreto del cliente",
+ "Delete" : "Borrar"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/es_SV.js b/apps/oauth2/l10n/es_SV.js
new file mode 100644
index 00000000000..079b21192fd
--- /dev/null
+++ b/apps/oauth2/l10n/es_SV.js
@@ -0,0 +1,14 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Delete" : "Borrar"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/oauth2/l10n/es_SV.json b/apps/oauth2/l10n/es_SV.json
new file mode 100644
index 00000000000..40b679cc951
--- /dev/null
+++ b/apps/oauth2/l10n/es_SV.json
@@ -0,0 +1,12 @@
+{ "translations": {
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "Name" : "Nombre",
+ "Redirection URI" : "URI para redirección",
+ "Client Identifier" : "Identificador del cliente",
+ "Secret key" : "Llave secreta",
+ "Add client" : "Agregar cliente",
+ "Add" : "Agregar",
+ "Delete" : "Borrar"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/et_EE.js b/apps/oauth2/l10n/et_EE.js
new file mode 100644
index 00000000000..93a7c7e97bf
--- /dev/null
+++ b/apps/oauth2/l10n/et_EE.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Su klient ei ole ühenduseks autoriseeritud. Palun teavita oma kliendi peakasutajat.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Ümbersuunamise URL peab olema täis URL, näiteks: https://sinudomeen.com/asukoht",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Võimaldab OAuth2-ga ühilduvat autentimist teistest veebirakendustest.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Rakendus OAuth2 võimaldab administraatoritel konfigureerida sisseehitatud autentimise töövoogu, et võimaldada ka OAuth2-ga ühilduvat autentimist teistest veebirakendustest.",
+ "OAuth 2.0 clients" : "OAuth 2.0 kliendid",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 võimaldab välisteenustel taotleda juurdepääsu {instanceName}ile",
+ "Name" : "Nimi",
+ "Redirection URI" : "Suunamise URI",
+ "Client Identifier" : "Kliendi identifikaator",
+ "Secret key" : "Salavõti",
+ "Delete client" : "Kustuta klient",
+ "Make sure you store the secret key, it cannot be recovered." : "Salvesta salajane võti, seda ei saa taastada.",
+ "Add client" : "Lisa klient",
+ "Add" : "Lisa",
+ "Show client secret" : "Näita kliendi salakoodi",
+ "Hide client secret" : "Peida kliendi salakood",
+ "Delete" : "Kustuta"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/et_EE.json b/apps/oauth2/l10n/et_EE.json
new file mode 100644
index 00000000000..0ddc97bea45
--- /dev/null
+++ b/apps/oauth2/l10n/et_EE.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Su klient ei ole ühenduseks autoriseeritud. Palun teavita oma kliendi peakasutajat.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Ümbersuunamise URL peab olema täis URL, näiteks: https://sinudomeen.com/asukoht",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Võimaldab OAuth2-ga ühilduvat autentimist teistest veebirakendustest.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Rakendus OAuth2 võimaldab administraatoritel konfigureerida sisseehitatud autentimise töövoogu, et võimaldada ka OAuth2-ga ühilduvat autentimist teistest veebirakendustest.",
+ "OAuth 2.0 clients" : "OAuth 2.0 kliendid",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 võimaldab välisteenustel taotleda juurdepääsu {instanceName}ile",
+ "Name" : "Nimi",
+ "Redirection URI" : "Suunamise URI",
+ "Client Identifier" : "Kliendi identifikaator",
+ "Secret key" : "Salavõti",
+ "Delete client" : "Kustuta klient",
+ "Make sure you store the secret key, it cannot be recovered." : "Salvesta salajane võti, seda ei saa taastada.",
+ "Add client" : "Lisa klient",
+ "Add" : "Lisa",
+ "Show client secret" : "Näita kliendi salakoodi",
+ "Hide client secret" : "Peida kliendi salakood",
+ "Delete" : "Kustuta"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/eu.js b/apps/oauth2/l10n/eu.js
new file mode 100644
index 00000000000..e195a1ef3ac
--- /dev/null
+++ b/apps/oauth2/l10n/eu.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Zure bezeroak ez du konektatzeko baimenik. Esan iezaiozu zure bezeroaren administratzaileari.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Berbideraketa egiteko URLa URL oso bat izan behar da, adibidez: https://zuredomeinua.eus/karpeta",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Beste web aplikazio batzuen bidez egindako OAuth2 erako autentikazioa egitea baimentzen du",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 aplikazioak, zerbitzu hori eskaintzen duten beste web aplikazio batzuen bidez autentikazioa egiten konfiguratzeko aukera eskaintzen die sistemaren kudeatzaileei.",
+ "OAuth 2.0 clients" : "OAuth 2.0 bezeroak",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0k kanpo zerbitzuei {instanceName} instantziara sartzeko baimena eskatzea baimentzen die.",
+ "Name" : "Izena",
+ "Redirection URI" : "Birbideraketaren URI",
+ "Client Identifier" : "Bezeroaren Identifikadorea",
+ "Secret key" : "Gako sekretua",
+ "Delete client" : "Ezabatu bezeroa",
+ "Make sure you store the secret key, it cannot be recovered." : "Ziurtatu gako sekretua gordetzen duzula, ezin da berreskuratu.",
+ "Add client" : "Gehitu bezeroa",
+ "Add" : "Gehitu",
+ "Show client secret" : "Erakutsi bezeroaren sekretua",
+ "Hide client secret" : "Ezkutatu bezeroaren sekretua",
+ "Delete" : "Ezabatu"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/eu.json b/apps/oauth2/l10n/eu.json
new file mode 100644
index 00000000000..ddfcd437650
--- /dev/null
+++ b/apps/oauth2/l10n/eu.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Zure bezeroak ez du konektatzeko baimenik. Esan iezaiozu zure bezeroaren administratzaileari.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Berbideraketa egiteko URLa URL oso bat izan behar da, adibidez: https://zuredomeinua.eus/karpeta",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Beste web aplikazio batzuen bidez egindako OAuth2 erako autentikazioa egitea baimentzen du",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 aplikazioak, zerbitzu hori eskaintzen duten beste web aplikazio batzuen bidez autentikazioa egiten konfiguratzeko aukera eskaintzen die sistemaren kudeatzaileei.",
+ "OAuth 2.0 clients" : "OAuth 2.0 bezeroak",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0k kanpo zerbitzuei {instanceName} instantziara sartzeko baimena eskatzea baimentzen die.",
+ "Name" : "Izena",
+ "Redirection URI" : "Birbideraketaren URI",
+ "Client Identifier" : "Bezeroaren Identifikadorea",
+ "Secret key" : "Gako sekretua",
+ "Delete client" : "Ezabatu bezeroa",
+ "Make sure you store the secret key, it cannot be recovered." : "Ziurtatu gako sekretua gordetzen duzula, ezin da berreskuratu.",
+ "Add client" : "Gehitu bezeroa",
+ "Add" : "Gehitu",
+ "Show client secret" : "Erakutsi bezeroaren sekretua",
+ "Hide client secret" : "Ezkutatu bezeroaren sekretua",
+ "Delete" : "Ezabatu"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/fa.js b/apps/oauth2/l10n/fa.js
new file mode 100644
index 00000000000..4f21d887051
--- /dev/null
+++ b/apps/oauth2/l10n/fa.js
@@ -0,0 +1,20 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "مشتری خود را برای متصل شدن مجاز نیست. لطفاً به مدیر مشتری خود اطلاع دهید.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "URL هدایت مجدد شما باید به عنوان مثال یک URL کامل باشد: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "اجازه احراز هویت سازگار OAuth2 را از دیگر برنامه های وب می دهد.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "برنامه OAuth2 به مدیران اجازه می دهد تا گردش کار داخلی تأیید اعتبار را پیکربندی کنند ، همچنین اجازه تأیید هویت سازگار OAuth2 را از دیگر برنامه های وب فراهم می کند.",
+ "OAuth 2.0 clients" : "مشتری OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth تأیید 2.0 اجازه می دهد تا خدمات خارجی به درخواست دسترسی به {} instanceName.",
+ "Name" : "نام",
+ "Redirection URI" : "تغییر مسیر URI",
+ "Client Identifier" : "شناسه مشتری",
+ "Secret key" : "کلید مخفی",
+ "Add client" : "افزودن مشتری",
+ "Add" : "افزودن",
+ "Show client secret" : "راز مشتری را نشان دهید",
+ "Delete" : "حذف"
+},
+"nplurals=2; plural=(n > 1);");
diff --git a/apps/oauth2/l10n/fa.json b/apps/oauth2/l10n/fa.json
new file mode 100644
index 00000000000..f7091f5c83e
--- /dev/null
+++ b/apps/oauth2/l10n/fa.json
@@ -0,0 +1,18 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "مشتری خود را برای متصل شدن مجاز نیست. لطفاً به مدیر مشتری خود اطلاع دهید.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "URL هدایت مجدد شما باید به عنوان مثال یک URL کامل باشد: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "اجازه احراز هویت سازگار OAuth2 را از دیگر برنامه های وب می دهد.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "برنامه OAuth2 به مدیران اجازه می دهد تا گردش کار داخلی تأیید اعتبار را پیکربندی کنند ، همچنین اجازه تأیید هویت سازگار OAuth2 را از دیگر برنامه های وب فراهم می کند.",
+ "OAuth 2.0 clients" : "مشتری OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth تأیید 2.0 اجازه می دهد تا خدمات خارجی به درخواست دسترسی به {} instanceName.",
+ "Name" : "نام",
+ "Redirection URI" : "تغییر مسیر URI",
+ "Client Identifier" : "شناسه مشتری",
+ "Secret key" : "کلید مخفی",
+ "Add client" : "افزودن مشتری",
+ "Add" : "افزودن",
+ "Show client secret" : "راز مشتری را نشان دهید",
+ "Delete" : "حذف"
+},"pluralForm" :"nplurals=2; plural=(n > 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/fi.js b/apps/oauth2/l10n/fi.js
new file mode 100644
index 00000000000..c7ad237bc72
--- /dev/null
+++ b/apps/oauth2/l10n/fi.js
@@ -0,0 +1,20 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Asiakastasi ei ole valtuutettu yhdistämään. Ilmoita ylläpidolle asiakkaastasi.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Uudelleenohjauksen URL-osoitteen on oltava täydellinen URL-osoite, esimerkiksi: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Sallii OAuth2-yhteensopivan tunnistautumisen muista verkkosovelluksista.",
+ "OAuth 2.0 clients" : "OAuth 2.0 -asiakkaat",
+ "Name" : "Nimi",
+ "Redirection URI" : "Uudelleenohjaus URI",
+ "Client Identifier" : "Asiakkaan tunniste",
+ "Secret key" : "Salainen avain",
+ "Delete client" : "Poista asiakas",
+ "Add client" : "Lisää asiakas",
+ "Add" : "Lisää",
+ "Show client secret" : "Näytä asiakkaan salaisuus",
+ "Hide client secret" : "Piilota asiakkaan salaisuus",
+ "Delete" : "Poista"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/fi.json b/apps/oauth2/l10n/fi.json
new file mode 100644
index 00000000000..c74e11bef5e
--- /dev/null
+++ b/apps/oauth2/l10n/fi.json
@@ -0,0 +1,18 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Asiakastasi ei ole valtuutettu yhdistämään. Ilmoita ylläpidolle asiakkaastasi.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Uudelleenohjauksen URL-osoitteen on oltava täydellinen URL-osoite, esimerkiksi: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Sallii OAuth2-yhteensopivan tunnistautumisen muista verkkosovelluksista.",
+ "OAuth 2.0 clients" : "OAuth 2.0 -asiakkaat",
+ "Name" : "Nimi",
+ "Redirection URI" : "Uudelleenohjaus URI",
+ "Client Identifier" : "Asiakkaan tunniste",
+ "Secret key" : "Salainen avain",
+ "Delete client" : "Poista asiakas",
+ "Add client" : "Lisää asiakas",
+ "Add" : "Lisää",
+ "Show client secret" : "Näytä asiakkaan salaisuus",
+ "Hide client secret" : "Piilota asiakkaan salaisuus",
+ "Delete" : "Poista"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/fr.js b/apps/oauth2/l10n/fr.js
new file mode 100644
index 00000000000..a60dc05f74c
--- /dev/null
+++ b/apps/oauth2/l10n/fr.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Votre client n'est pas autorisé à se connecter. Veuillez en informer l'administrateur.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Votre adresse de redirection doit être une URL complète, par exemple: https://example.com/chemin",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Autoriser l'authentification compatible OAuth2 depuis d'autres applications web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "L'application OAuth2 permet aux administrateurs de configurer le processus d'authentification intégré afin d'autoriser une authentification compatible OAuth2 depuis d'autres applications web.",
+ "OAuth 2.0 clients" : "Clients OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permet à des services externes de demander l'accès à {instanceName}.",
+ "Name" : "Nom",
+ "Redirection URI" : "URI de redirection",
+ "Client Identifier" : "Identifiant du client",
+ "Secret key" : "Clé secrète",
+ "Delete client" : "Supprimer le client",
+ "Make sure you store the secret key, it cannot be recovered." : "Assurez-vous de stocker la clé secrète, elle ne peut pas être re récupérée.",
+ "Add client" : "Ajouter un client",
+ "Add" : "Ajouter",
+ "Show client secret" : "Afficher le code secret du client",
+ "Hide client secret" : "Masquer le secret du client",
+ "Delete" : "Supprimer"
+},
+"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/oauth2/l10n/fr.json b/apps/oauth2/l10n/fr.json
new file mode 100644
index 00000000000..9be9ea0d408
--- /dev/null
+++ b/apps/oauth2/l10n/fr.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Votre client n'est pas autorisé à se connecter. Veuillez en informer l'administrateur.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Votre adresse de redirection doit être une URL complète, par exemple: https://example.com/chemin",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Autoriser l'authentification compatible OAuth2 depuis d'autres applications web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "L'application OAuth2 permet aux administrateurs de configurer le processus d'authentification intégré afin d'autoriser une authentification compatible OAuth2 depuis d'autres applications web.",
+ "OAuth 2.0 clients" : "Clients OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permet à des services externes de demander l'accès à {instanceName}.",
+ "Name" : "Nom",
+ "Redirection URI" : "URI de redirection",
+ "Client Identifier" : "Identifiant du client",
+ "Secret key" : "Clé secrète",
+ "Delete client" : "Supprimer le client",
+ "Make sure you store the secret key, it cannot be recovered." : "Assurez-vous de stocker la clé secrète, elle ne peut pas être re récupérée.",
+ "Add client" : "Ajouter un client",
+ "Add" : "Ajouter",
+ "Show client secret" : "Afficher le code secret du client",
+ "Hide client secret" : "Masquer le secret du client",
+ "Delete" : "Supprimer"
+},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/ga.js b/apps/oauth2/l10n/ga.js
new file mode 100644
index 00000000000..45199e91f97
--- /dev/null
+++ b/apps/oauth2/l10n/ga.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Níl do chliant údaraithe chun nascadh. Cuir do chliant ar an eolas do riarthóir.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Ní mór do URL athsheolaidh a bheith ina URL iomlán mar shampla: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Ceadaíonn sé fíordheimhniú comhoiriúnach OAuth2 ó fheidhmchláir ghréasáin eile.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Ligeann an aip OAuth2 do riarthóirí an sreabhadh oibre fíordheimhnithe ionsuite a chumrú chun fíordheimhniú comhoiriúnach OAuth2 a cheadú ó fheidhmchláir ghréasáin eile.",
+ "OAuth 2.0 clients" : "Cliant OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "Ligeann OAuth 2.0 do sheirbhísí seachtracha rochtain a iarraidh ar {instanceName}.",
+ "Name" : "Ainm",
+ "Redirection URI" : "Atreo URI",
+ "Client Identifier" : "Aitheantóir Cliant",
+ "Secret key" : "Eochair rúnda",
+ "Delete client" : "Scrios cliant",
+ "Make sure you store the secret key, it cannot be recovered." : "Déan cinnte go stórálann tú an eochair rúnda, ní féidir í a aisghabháil.",
+ "Add client" : "Cuir cliant leis",
+ "Add" : "Cuir",
+ "Show client secret" : "Taispeáin rún an chliaint",
+ "Hide client secret" : "Folaigh rún an chliaint",
+ "Delete" : "Scrios"
+},
+"nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4);");
diff --git a/apps/oauth2/l10n/ga.json b/apps/oauth2/l10n/ga.json
new file mode 100644
index 00000000000..a85a372dc24
--- /dev/null
+++ b/apps/oauth2/l10n/ga.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Níl do chliant údaraithe chun nascadh. Cuir do chliant ar an eolas do riarthóir.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Ní mór do URL athsheolaidh a bheith ina URL iomlán mar shampla: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Ceadaíonn sé fíordheimhniú comhoiriúnach OAuth2 ó fheidhmchláir ghréasáin eile.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Ligeann an aip OAuth2 do riarthóirí an sreabhadh oibre fíordheimhnithe ionsuite a chumrú chun fíordheimhniú comhoiriúnach OAuth2 a cheadú ó fheidhmchláir ghréasáin eile.",
+ "OAuth 2.0 clients" : "Cliant OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "Ligeann OAuth 2.0 do sheirbhísí seachtracha rochtain a iarraidh ar {instanceName}.",
+ "Name" : "Ainm",
+ "Redirection URI" : "Atreo URI",
+ "Client Identifier" : "Aitheantóir Cliant",
+ "Secret key" : "Eochair rúnda",
+ "Delete client" : "Scrios cliant",
+ "Make sure you store the secret key, it cannot be recovered." : "Déan cinnte go stórálann tú an eochair rúnda, ní féidir í a aisghabháil.",
+ "Add client" : "Cuir cliant leis",
+ "Add" : "Cuir",
+ "Show client secret" : "Taispeáin rún an chliaint",
+ "Hide client secret" : "Folaigh rún an chliaint",
+ "Delete" : "Scrios"
+},"pluralForm" :"nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/gl.js b/apps/oauth2/l10n/gl.js
new file mode 100644
index 00000000000..3aaa35e0b00
--- /dev/null
+++ b/apps/oauth2/l10n/gl.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "O seu cliente non ten autorización para conectarse. Informe do seu cliente á administración da instancia",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "o seu URL de redirección debe ser un URL completo, por exemplo: https://omeudominio.com/ruta",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite a autenticación compatíbel con OAuth2 desde outras aplicacións web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "A aplicación OAuth2 permítelle aos administradores configurar o fluxo de traballo de autenticación integrado para permitir tamén autenticación compatíbel con OAuth2 desde outras aplicacións web.",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permítelle aos servizos externos solicitar acceso a {instanceName}.",
+ "Name" : "Nome",
+ "Redirection URI" : "URI de redireccionamento",
+ "Client Identifier" : "Identificador do cliente",
+ "Secret key" : "Chave secreta",
+ "Delete client" : "Eliminar cliente",
+ "Make sure you store the secret key, it cannot be recovered." : "Asegúrese de gardar a chave secreta, non é posíbel recuperala.",
+ "Add client" : "Engadir cliente",
+ "Add" : "Engadir",
+ "Show client secret" : "Amosar o segredo do cliente",
+ "Hide client secret" : "Agochar o segredo do cliente",
+ "Delete" : "Eliminar"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/gl.json b/apps/oauth2/l10n/gl.json
new file mode 100644
index 00000000000..3401883d361
--- /dev/null
+++ b/apps/oauth2/l10n/gl.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "O seu cliente non ten autorización para conectarse. Informe do seu cliente á administración da instancia",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "o seu URL de redirección debe ser un URL completo, por exemplo: https://omeudominio.com/ruta",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite a autenticación compatíbel con OAuth2 desde outras aplicacións web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "A aplicación OAuth2 permítelle aos administradores configurar o fluxo de traballo de autenticación integrado para permitir tamén autenticación compatíbel con OAuth2 desde outras aplicacións web.",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permítelle aos servizos externos solicitar acceso a {instanceName}.",
+ "Name" : "Nome",
+ "Redirection URI" : "URI de redireccionamento",
+ "Client Identifier" : "Identificador do cliente",
+ "Secret key" : "Chave secreta",
+ "Delete client" : "Eliminar cliente",
+ "Make sure you store the secret key, it cannot be recovered." : "Asegúrese de gardar a chave secreta, non é posíbel recuperala.",
+ "Add client" : "Engadir cliente",
+ "Add" : "Engadir",
+ "Show client secret" : "Amosar o segredo do cliente",
+ "Hide client secret" : "Agochar o segredo do cliente",
+ "Delete" : "Eliminar"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/he.js b/apps/oauth2/l10n/he.js
new file mode 100644
index 00000000000..2d32792f86e
--- /dev/null
+++ b/apps/oauth2/l10n/he.js
@@ -0,0 +1,20 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "הלקוח שלך לא אישר את החיבור. נא ליידע את מנהל המערכת שלך על הלקוח שלך.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "כתובת ההפניה שלך חייבת להיות כתובת מלאה, למשל: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "מאפשר אימות תואם OAuth2 מיישומי אינטרנט אחרים.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "יישומון OAuth2 מאפשר למנהלים להגדיר את הסדר האימות המובנה כדי לאפשר אימות תואם OAuth2 מיישומים אחרים.",
+ "OAuth 2.0 clients" : "לקוחות OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 מאפשר לשירותים חיצוניים לבקש גישה אל {instanceName}.",
+ "Name" : "שם",
+ "Redirection URI" : "כתובת הפנייה",
+ "Client Identifier" : "זיהוי לקו",
+ "Secret key" : "מפתח סודי",
+ "Add client" : "הוספת לקוחי",
+ "Add" : "הוספה",
+ "Show client secret" : "הצגת סוד לקוח",
+ "Delete" : "מחיקה"
+},
+"nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: 2;");
diff --git a/apps/oauth2/l10n/he.json b/apps/oauth2/l10n/he.json
new file mode 100644
index 00000000000..d46ae0195f4
--- /dev/null
+++ b/apps/oauth2/l10n/he.json
@@ -0,0 +1,18 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "הלקוח שלך לא אישר את החיבור. נא ליידע את מנהל המערכת שלך על הלקוח שלך.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "כתובת ההפניה שלך חייבת להיות כתובת מלאה, למשל: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "מאפשר אימות תואם OAuth2 מיישומי אינטרנט אחרים.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "יישומון OAuth2 מאפשר למנהלים להגדיר את הסדר האימות המובנה כדי לאפשר אימות תואם OAuth2 מיישומים אחרים.",
+ "OAuth 2.0 clients" : "לקוחות OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 מאפשר לשירותים חיצוניים לבקש גישה אל {instanceName}.",
+ "Name" : "שם",
+ "Redirection URI" : "כתובת הפנייה",
+ "Client Identifier" : "זיהוי לקו",
+ "Secret key" : "מפתח סודי",
+ "Add client" : "הוספת לקוחי",
+ "Add" : "הוספה",
+ "Show client secret" : "הצגת סוד לקוח",
+ "Delete" : "מחיקה"
+},"pluralForm" :"nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/hr.js b/apps/oauth2/l10n/hr.js
new file mode 100644
index 00000000000..396209ee36b
--- /dev/null
+++ b/apps/oauth2/l10n/hr.js
@@ -0,0 +1,20 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Vaš klijent nije ovlašten za povezivanje. Obavijestite administratora svog klijenta.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Vaš URL za preusmjeravanje mora biti cjelovit URL, primjerice: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Omogućuje autentifikaciju kompatibilnu s OAuth2 iz drugih web aplikacija.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Aplikacija OAuth2 omogućuje administratorima da konfiguriraju ugrađeni tijek autentifikacije kako bi također omogućili autentifikaciju kompatibilnu s OAuth2 iz drugih web aplikacija.",
+ "OAuth 2.0 clients" : "OAuth 2.0 klijenti",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 omogućuje vanjskim servisima da zahtijevaju pristup {instanceName}.",
+ "Name" : "Naziv",
+ "Redirection URI" : "URL za preusmjeravanje",
+ "Client Identifier" : "Identifikator klijenta",
+ "Secret key" : "Tajni ključ",
+ "Add client" : "Dodaj klijenta",
+ "Add" : "Dodaj",
+ "Show client secret" : "Prikaži tajni ključ klijenta",
+ "Delete" : "Izbriši"
+},
+"nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;");
diff --git a/apps/oauth2/l10n/hr.json b/apps/oauth2/l10n/hr.json
new file mode 100644
index 00000000000..79303555b6b
--- /dev/null
+++ b/apps/oauth2/l10n/hr.json
@@ -0,0 +1,18 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Vaš klijent nije ovlašten za povezivanje. Obavijestite administratora svog klijenta.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Vaš URL za preusmjeravanje mora biti cjelovit URL, primjerice: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Omogućuje autentifikaciju kompatibilnu s OAuth2 iz drugih web aplikacija.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Aplikacija OAuth2 omogućuje administratorima da konfiguriraju ugrađeni tijek autentifikacije kako bi također omogućili autentifikaciju kompatibilnu s OAuth2 iz drugih web aplikacija.",
+ "OAuth 2.0 clients" : "OAuth 2.0 klijenti",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 omogućuje vanjskim servisima da zahtijevaju pristup {instanceName}.",
+ "Name" : "Naziv",
+ "Redirection URI" : "URL za preusmjeravanje",
+ "Client Identifier" : "Identifikator klijenta",
+ "Secret key" : "Tajni ključ",
+ "Add client" : "Dodaj klijenta",
+ "Add" : "Dodaj",
+ "Show client secret" : "Prikaži tajni ključ klijenta",
+ "Delete" : "Izbriši"
+},"pluralForm" :"nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/hu.js b/apps/oauth2/l10n/hu.js
new file mode 100644
index 00000000000..eddb2928651
--- /dev/null
+++ b/apps/oauth2/l10n/hu.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Az Ön kliensalkalmazása számára nem engedélyezett a kapcsolódás. Értesítse a kliense rendszergazdáját.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Az átirányítási URL-jének teljes URL-nek kell lennie, például: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Lehetővé teszi az OAuth2 kompatibilis hitelesítést más webalkalmazásokból.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Az OAuth2 alkalmazás lehetővé teszi a rendszergazdák számára, hogy beállíthassák a beépített azonosítási munkafolyamat számára az OAuth2 kompatibilis hitelesítést más webalkalmazásokból.",
+ "OAuth 2.0 clients" : "OAuth 2.0 kliensek",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "Az OAuth 2.0 engedélyezi, hogy külső szolgáltatások hozzáférést kérjenek a következőhöz: {instanceName}.",
+ "Name" : "Név",
+ "Redirection URI" : "Átirányítási URI",
+ "Client Identifier" : "Ügyfélazonosító",
+ "Secret key" : "Titkos kulcs",
+ "Delete client" : "Kliens törlése",
+ "Make sure you store the secret key, it cannot be recovered." : "Győződjön meg róla tárolja a titkos kulcsot, mert nem nyerhető vissza.",
+ "Add client" : "Kliens hozzáadása",
+ "Add" : "Hozzáadás",
+ "Show client secret" : "Klienstitok megjelenítése",
+ "Hide client secret" : "Klienstitok elrejtése",
+ "Delete" : "Törlés"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/hu.json b/apps/oauth2/l10n/hu.json
new file mode 100644
index 00000000000..2e0a9c4de02
--- /dev/null
+++ b/apps/oauth2/l10n/hu.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Az Ön kliensalkalmazása számára nem engedélyezett a kapcsolódás. Értesítse a kliense rendszergazdáját.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Az átirányítási URL-jének teljes URL-nek kell lennie, például: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Lehetővé teszi az OAuth2 kompatibilis hitelesítést más webalkalmazásokból.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Az OAuth2 alkalmazás lehetővé teszi a rendszergazdák számára, hogy beállíthassák a beépített azonosítási munkafolyamat számára az OAuth2 kompatibilis hitelesítést más webalkalmazásokból.",
+ "OAuth 2.0 clients" : "OAuth 2.0 kliensek",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "Az OAuth 2.0 engedélyezi, hogy külső szolgáltatások hozzáférést kérjenek a következőhöz: {instanceName}.",
+ "Name" : "Név",
+ "Redirection URI" : "Átirányítási URI",
+ "Client Identifier" : "Ügyfélazonosító",
+ "Secret key" : "Titkos kulcs",
+ "Delete client" : "Kliens törlése",
+ "Make sure you store the secret key, it cannot be recovered." : "Győződjön meg róla tárolja a titkos kulcsot, mert nem nyerhető vissza.",
+ "Add client" : "Kliens hozzáadása",
+ "Add" : "Hozzáadás",
+ "Show client secret" : "Klienstitok megjelenítése",
+ "Hide client secret" : "Klienstitok elrejtése",
+ "Delete" : "Törlés"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/is.js b/apps/oauth2/l10n/is.js
new file mode 100644
index 00000000000..e786a1e409d
--- /dev/null
+++ b/apps/oauth2/l10n/is.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Biðlarinn þinn hefur ekki heimild til að tengjast. Láttu kerfisstjórann þinn vita.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "URL-endurbeiningarslóðin verður að vera full URL-slóð, til dæmis: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Gerir kleift að nota OAuth2-samhæfða auðkenningu frá öðrum vefforritum.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2-forritið gerir kerfisstjórum kleift að stilla innbyggða auðkenningarferlið þannig að einnig sé hægt að nota OAuth2-samhæfða auðkenningu frá öðrum vefforritum.",
+ "OAuth 2.0 clients" : "OAuth 2.0 biðlarar",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 gerir utanaðkomandi þjónustum kleift að biðja um aðgang að {instanceName}.",
+ "Name" : "Nafn",
+ "Redirection URI" : "Endurbeiningarslóð",
+ "Client Identifier" : "Biðlaraauðkenni",
+ "Secret key" : "Leynilykill",
+ "Delete client" : "Eyða biðlara",
+ "Make sure you store the secret key, it cannot be recovered." : "Vertu viss um að geyma leynilykilinn á góðum stað, henn er ekki hægt að endurheimta.",
+ "Add client" : "Bæta við biðlara",
+ "Add" : "Bæta við",
+ "Show client secret" : "Sýna leynilykil biðlara",
+ "Hide client secret" : "Fela leynilykil biðlara",
+ "Delete" : "Eyða"
+},
+"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);");
diff --git a/apps/oauth2/l10n/is.json b/apps/oauth2/l10n/is.json
new file mode 100644
index 00000000000..573b3a496a4
--- /dev/null
+++ b/apps/oauth2/l10n/is.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Biðlarinn þinn hefur ekki heimild til að tengjast. Láttu kerfisstjórann þinn vita.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "URL-endurbeiningarslóðin verður að vera full URL-slóð, til dæmis: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Gerir kleift að nota OAuth2-samhæfða auðkenningu frá öðrum vefforritum.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2-forritið gerir kerfisstjórum kleift að stilla innbyggða auðkenningarferlið þannig að einnig sé hægt að nota OAuth2-samhæfða auðkenningu frá öðrum vefforritum.",
+ "OAuth 2.0 clients" : "OAuth 2.0 biðlarar",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 gerir utanaðkomandi þjónustum kleift að biðja um aðgang að {instanceName}.",
+ "Name" : "Nafn",
+ "Redirection URI" : "Endurbeiningarslóð",
+ "Client Identifier" : "Biðlaraauðkenni",
+ "Secret key" : "Leynilykill",
+ "Delete client" : "Eyða biðlara",
+ "Make sure you store the secret key, it cannot be recovered." : "Vertu viss um að geyma leynilykilinn á góðum stað, henn er ekki hægt að endurheimta.",
+ "Add client" : "Bæta við biðlara",
+ "Add" : "Bæta við",
+ "Show client secret" : "Sýna leynilykil biðlara",
+ "Hide client secret" : "Fela leynilykil biðlara",
+ "Delete" : "Eyða"
+},"pluralForm" :"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/it.js b/apps/oauth2/l10n/it.js
new file mode 100644
index 00000000000..f6365b013d0
--- /dev/null
+++ b/apps/oauth2/l10n/it.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Il tuo client non è autorizzato a collegarsi. Informa l'amministratore del tuo client.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Il tuo URL di redirezione deve essere un URL completo, ad esempio: https://tuodominio.com/percorso",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Consente l'autenticazione compatibile con OAuth2 da altre applicazioni web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "L'applicazione OAuth2 consente agli amministratori di configurare la procedura di autenticazione integrata per consentire anche l'autenticazione compatibile con OAuth2 da altre applicazioni web.",
+ "OAuth 2.0 clients" : "Client OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 consente a servizi esterni di richiedere accesso a {instanceName}.",
+ "Name" : "Nome",
+ "Redirection URI" : "URI di redirezione",
+ "Client Identifier" : "Identificatore client",
+ "Secret key" : "Chiave segreta",
+ "Delete client" : "Elimina client",
+ "Make sure you store the secret key, it cannot be recovered." : "Assicurati di salvare la chiave segreta, non può essere recuperata.",
+ "Add client" : "Aggiungi client",
+ "Add" : "Aggiungi",
+ "Show client secret" : "Mostra segreto del client",
+ "Hide client secret" : "Nascondi segreto del client",
+ "Delete" : "Elimina"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/oauth2/l10n/it.json b/apps/oauth2/l10n/it.json
new file mode 100644
index 00000000000..d7db0bbe00d
--- /dev/null
+++ b/apps/oauth2/l10n/it.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Il tuo client non è autorizzato a collegarsi. Informa l'amministratore del tuo client.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Il tuo URL di redirezione deve essere un URL completo, ad esempio: https://tuodominio.com/percorso",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Consente l'autenticazione compatibile con OAuth2 da altre applicazioni web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "L'applicazione OAuth2 consente agli amministratori di configurare la procedura di autenticazione integrata per consentire anche l'autenticazione compatibile con OAuth2 da altre applicazioni web.",
+ "OAuth 2.0 clients" : "Client OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 consente a servizi esterni di richiedere accesso a {instanceName}.",
+ "Name" : "Nome",
+ "Redirection URI" : "URI di redirezione",
+ "Client Identifier" : "Identificatore client",
+ "Secret key" : "Chiave segreta",
+ "Delete client" : "Elimina client",
+ "Make sure you store the secret key, it cannot be recovered." : "Assicurati di salvare la chiave segreta, non può essere recuperata.",
+ "Add client" : "Aggiungi client",
+ "Add" : "Aggiungi",
+ "Show client secret" : "Mostra segreto del client",
+ "Hide client secret" : "Nascondi segreto del client",
+ "Delete" : "Elimina"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/ja.js b/apps/oauth2/l10n/ja.js
new file mode 100644
index 00000000000..c1418fb2021
--- /dev/null
+++ b/apps/oauth2/l10n/ja.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "このクライアント(認証依頼元)は認証されていないので接続できません。クライアント側の管理者に問い合わせてください。",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "リダイレクトURLは、完全なURL(例えばhttps://yourdomain.com/path)である必要があります。",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "他のWebアプリケーションからのOAuth2互換認証を許可します。",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2アプリは、他のWebサービスからOAuth2互換の認証に接続し、管理者が認証の流れを設定できるようにするものです。",
+ "OAuth 2.0 clients" : "OAuth 2.0クライアント",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0により {instanceName} へのアクセス要求を外部サービスに許可します。",
+ "Name" : "名前",
+ "Redirection URI" : "リダイレクトURI",
+ "Client Identifier" : "クライアントID",
+ "Secret key" : "シークレットキー",
+ "Delete client" : "クライアントを削除",
+ "Make sure you store the secret key, it cannot be recovered." : "秘密鍵は復元できないので、必ず保管してください。",
+ "Add client" : "クライアントの追加",
+ "Add" : "追加",
+ "Show client secret" : "クライアントシークレットキーを表示",
+ "Hide client secret" : "クライアントのシークレットキーを隠す",
+ "Delete" : "削除"
+},
+"nplurals=1; plural=0;");
diff --git a/apps/oauth2/l10n/ja.json b/apps/oauth2/l10n/ja.json
new file mode 100644
index 00000000000..09357aca90f
--- /dev/null
+++ b/apps/oauth2/l10n/ja.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "このクライアント(認証依頼元)は認証されていないので接続できません。クライアント側の管理者に問い合わせてください。",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "リダイレクトURLは、完全なURL(例えばhttps://yourdomain.com/path)である必要があります。",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "他のWebアプリケーションからのOAuth2互換認証を許可します。",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2アプリは、他のWebサービスからOAuth2互換の認証に接続し、管理者が認証の流れを設定できるようにするものです。",
+ "OAuth 2.0 clients" : "OAuth 2.0クライアント",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0により {instanceName} へのアクセス要求を外部サービスに許可します。",
+ "Name" : "名前",
+ "Redirection URI" : "リダイレクトURI",
+ "Client Identifier" : "クライアントID",
+ "Secret key" : "シークレットキー",
+ "Delete client" : "クライアントを削除",
+ "Make sure you store the secret key, it cannot be recovered." : "秘密鍵は復元できないので、必ず保管してください。",
+ "Add client" : "クライアントの追加",
+ "Add" : "追加",
+ "Show client secret" : "クライアントシークレットキーを表示",
+ "Hide client secret" : "クライアントのシークレットキーを隠す",
+ "Delete" : "削除"
+},"pluralForm" :"nplurals=1; plural=0;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/ka.js b/apps/oauth2/l10n/ka.js
new file mode 100644
index 00000000000..c3a7a9385ba
--- /dev/null
+++ b/apps/oauth2/l10n/ka.js
@@ -0,0 +1,22 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Your client is not authorized to connect. Please inform the administrator of your client.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Allows OAuth2 compatible authentication from other web applications.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications.",
+ "OAuth 2.0 clients" : "OAuth 2.0 clients",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 allows external services to request access to {instanceName}.",
+ "Name" : "Name",
+ "Redirection URI" : "Redirection URI",
+ "Client Identifier" : "Client Identifier",
+ "Secret key" : "Secret key",
+ "Delete client" : "Delete client",
+ "Add client" : "Add client",
+ "Add" : "Add",
+ "Show client secret" : "Show client secret",
+ "Hide client secret" : "Hide client secret",
+ "Delete" : "Delete"
+},
+"nplurals=2; plural=(n!=1);");
diff --git a/apps/oauth2/l10n/ka.json b/apps/oauth2/l10n/ka.json
new file mode 100644
index 00000000000..9d634320a17
--- /dev/null
+++ b/apps/oauth2/l10n/ka.json
@@ -0,0 +1,20 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Your client is not authorized to connect. Please inform the administrator of your client.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Allows OAuth2 compatible authentication from other web applications.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications.",
+ "OAuth 2.0 clients" : "OAuth 2.0 clients",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 allows external services to request access to {instanceName}.",
+ "Name" : "Name",
+ "Redirection URI" : "Redirection URI",
+ "Client Identifier" : "Client Identifier",
+ "Secret key" : "Secret key",
+ "Delete client" : "Delete client",
+ "Add client" : "Add client",
+ "Add" : "Add",
+ "Show client secret" : "Show client secret",
+ "Hide client secret" : "Hide client secret",
+ "Delete" : "Delete"
+},"pluralForm" :"nplurals=2; plural=(n!=1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/ka_GE.js b/apps/oauth2/l10n/ka_GE.js
new file mode 100644
index 00000000000..a94015e4803
--- /dev/null
+++ b/apps/oauth2/l10n/ka_GE.js
@@ -0,0 +1,15 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "OAuth 2.0 კლიენტები",
+ "Name" : "სახელი",
+ "Redirection URI" : "გადამისამართების URI",
+ "Client Identifier" : "კლიენტის იდენტიფიკატორი",
+ "Secret key" : "საიდუმლო გასაღები",
+ "Add client" : "კლიენტის დამატება",
+ "Add" : "დამატება",
+ "Show client secret" : "გამოაჩინე კლიენტის საიდუმლო",
+ "Delete" : "წაშლა"
+},
+"nplurals=2; plural=(n!=1);");
diff --git a/apps/oauth2/l10n/ka_GE.json b/apps/oauth2/l10n/ka_GE.json
new file mode 100644
index 00000000000..186b5570c7f
--- /dev/null
+++ b/apps/oauth2/l10n/ka_GE.json
@@ -0,0 +1,13 @@
+{ "translations": {
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "OAuth 2.0 კლიენტები",
+ "Name" : "სახელი",
+ "Redirection URI" : "გადამისამართების URI",
+ "Client Identifier" : "კლიენტის იდენტიფიკატორი",
+ "Secret key" : "საიდუმლო გასაღები",
+ "Add client" : "კლიენტის დამატება",
+ "Add" : "დამატება",
+ "Show client secret" : "გამოაჩინე კლიენტის საიდუმლო",
+ "Delete" : "წაშლა"
+},"pluralForm" :"nplurals=2; plural=(n!=1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/ko.js b/apps/oauth2/l10n/ko.js
new file mode 100644
index 00000000000..8a9da68e958
--- /dev/null
+++ b/apps/oauth2/l10n/ko.js
@@ -0,0 +1,20 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "클라이언트가 연결할 수 있도록 허가되지 않았습니다. 클라이언트 개발자에게 연락하십시오.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "리다이렉트 URL은 예시와 같이 완전한 URL으로 이루어져야 합니다. 예시: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "다른 웹 애플리케이션에서 OAuth2 호환 인증을 허용합니다.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 앱을 사용하면 관리자가 기본 인증 작업 과정을 통해서 다른 웹 애플리케이션에서 OAuth2 호환 인증을 허용합니다.",
+ "OAuth 2.0 clients" : "OAuth 2.0 클라이언트",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0을 사용하여 외부 서비스에서 {instanceName}에 접근할 수 있습니다.",
+ "Name" : "이름",
+ "Redirection URI" : "전환될 URI",
+ "Client Identifier" : "클라이언트 식별자",
+ "Secret key" : "비밀 키",
+ "Add client" : "클라이언트 추가",
+ "Add" : "추가",
+ "Show client secret" : "클라이언트 비밀 값 표시",
+ "Delete" : "삭제"
+},
+"nplurals=1; plural=0;");
diff --git a/apps/oauth2/l10n/ko.json b/apps/oauth2/l10n/ko.json
new file mode 100644
index 00000000000..fd689b591f7
--- /dev/null
+++ b/apps/oauth2/l10n/ko.json
@@ -0,0 +1,18 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "클라이언트가 연결할 수 있도록 허가되지 않았습니다. 클라이언트 개발자에게 연락하십시오.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "리다이렉트 URL은 예시와 같이 완전한 URL으로 이루어져야 합니다. 예시: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "다른 웹 애플리케이션에서 OAuth2 호환 인증을 허용합니다.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 앱을 사용하면 관리자가 기본 인증 작업 과정을 통해서 다른 웹 애플리케이션에서 OAuth2 호환 인증을 허용합니다.",
+ "OAuth 2.0 clients" : "OAuth 2.0 클라이언트",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0을 사용하여 외부 서비스에서 {instanceName}에 접근할 수 있습니다.",
+ "Name" : "이름",
+ "Redirection URI" : "전환될 URI",
+ "Client Identifier" : "클라이언트 식별자",
+ "Secret key" : "비밀 키",
+ "Add client" : "클라이언트 추가",
+ "Add" : "추가",
+ "Show client secret" : "클라이언트 비밀 값 표시",
+ "Delete" : "삭제"
+},"pluralForm" :"nplurals=1; plural=0;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/lt_LT.js b/apps/oauth2/l10n/lt_LT.js
new file mode 100644
index 00000000000..d6ba78db8e1
--- /dev/null
+++ b/apps/oauth2/l10n/lt_LT.js
@@ -0,0 +1,17 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Jūsų kliento programa nėra įgaliota prisijungti. Informuokite administratorių apie savo kliento programą.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Jūsų nukreipimo adresas (redirect URL) turi būti pilnas, pavyzdžiui: https://jusuadresas.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "OAuth 2.0 klientai",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 leidžia išorinėms tarnyboms užklausti prieigą prie {instanceName}.",
+ "Name" : "Pavadinimas",
+ "Redirection URI" : "Peradresavimo URI",
+ "Client Identifier" : "Kliento identifikatorius",
+ "Secret key" : "Slaptasis raktas",
+ "Add client" : "Pridėti klientą",
+ "Add" : "Pridėti",
+ "Delete" : "Ištrinti"
+},
+"nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);");
diff --git a/apps/oauth2/l10n/lt_LT.json b/apps/oauth2/l10n/lt_LT.json
new file mode 100644
index 00000000000..fed347c6a89
--- /dev/null
+++ b/apps/oauth2/l10n/lt_LT.json
@@ -0,0 +1,15 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Jūsų kliento programa nėra įgaliota prisijungti. Informuokite administratorių apie savo kliento programą.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Jūsų nukreipimo adresas (redirect URL) turi būti pilnas, pavyzdžiui: https://jusuadresas.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "OAuth 2.0 clients" : "OAuth 2.0 klientai",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 leidžia išorinėms tarnyboms užklausti prieigą prie {instanceName}.",
+ "Name" : "Pavadinimas",
+ "Redirection URI" : "Peradresavimo URI",
+ "Client Identifier" : "Kliento identifikatorius",
+ "Secret key" : "Slaptasis raktas",
+ "Add client" : "Pridėti klientą",
+ "Add" : "Pridėti",
+ "Delete" : "Ištrinti"
+},"pluralForm" :"nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/lv.js b/apps/oauth2/l10n/lv.js
new file mode 100644
index 00000000000..a6df308f198
--- /dev/null
+++ b/apps/oauth2/l10n/lv.js
@@ -0,0 +1,17 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Tavam pārvirzīšanas URL ir jābūt pilnam URL, piemēram https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Ļauj OAuth2 saderīgu autentifikāciju citām tīmekļa lietotnēm.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 lietotne ļauj pārvaldītājiem konfigurēt iebūvēto autentificēšanās darbplūsmu un ļauj ar OAuth2 saderīgu autentificēšanos no citām tīmekļa lietotnēm.",
+ "OAuth 2.0 clients" : "OAuth 2.0 klients",
+ "Name" : "Nosaukums",
+ "Redirection URI" : "Pārvirzāmais URI",
+ "Client Identifier" : "Klienta identifikators",
+ "Secret key" : "Slepenā atslēga",
+ "Add client" : "Pievienot klientu",
+ "Add" : "Pievienot",
+ "Delete" : "Izdzēst"
+},
+"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);");
diff --git a/apps/oauth2/l10n/lv.json b/apps/oauth2/l10n/lv.json
new file mode 100644
index 00000000000..3fddb2506ac
--- /dev/null
+++ b/apps/oauth2/l10n/lv.json
@@ -0,0 +1,15 @@
+{ "translations": {
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Tavam pārvirzīšanas URL ir jābūt pilnam URL, piemēram https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Ļauj OAuth2 saderīgu autentifikāciju citām tīmekļa lietotnēm.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 lietotne ļauj pārvaldītājiem konfigurēt iebūvēto autentificēšanās darbplūsmu un ļauj ar OAuth2 saderīgu autentificēšanos no citām tīmekļa lietotnēm.",
+ "OAuth 2.0 clients" : "OAuth 2.0 klients",
+ "Name" : "Nosaukums",
+ "Redirection URI" : "Pārvirzāmais URI",
+ "Client Identifier" : "Klienta identifikators",
+ "Secret key" : "Slepenā atslēga",
+ "Add client" : "Pievienot klientu",
+ "Add" : "Pievienot",
+ "Delete" : "Izdzēst"
+},"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/mk.js b/apps/oauth2/l10n/mk.js
new file mode 100644
index 00000000000..3ef703ea529
--- /dev/null
+++ b/apps/oauth2/l10n/mk.js
@@ -0,0 +1,20 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Вашиот клиент не е авторизиран за да се поврзе. Ве молиме, информирајте администратор за вашиот клиент.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Вашето УРЛ со пренасочување треба да биде целосно УРЛ:\nhttps://vasdomen.com/pateka",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Дозволи OAuth2 компатибилна автентификација од други веб апликации.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 апликацијата им овозможува на администраторите да конфигурираат вграден проток за автентификација, истотака дозволува OAuth2 компатибилна автентификација од други веб апликации.",
+ "OAuth 2.0 clients" : "OAuth 2.0 клиенти",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 овозможува на надворешни сервиси да побараат пристап на {instanceName}.",
+ "Name" : "Име",
+ "Redirection URI" : "Пренасочено URI",
+ "Client Identifier" : "Идентификатор на клиент",
+ "Secret key" : "Таен клуч",
+ "Add client" : "Додади клиент",
+ "Add" : "Додади",
+ "Show client secret" : "Прикажи тајна на клиент",
+ "Delete" : "Избриши"
+},
+"nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;");
diff --git a/apps/oauth2/l10n/mk.json b/apps/oauth2/l10n/mk.json
new file mode 100644
index 00000000000..993f4112fb1
--- /dev/null
+++ b/apps/oauth2/l10n/mk.json
@@ -0,0 +1,18 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Вашиот клиент не е авторизиран за да се поврзе. Ве молиме, информирајте администратор за вашиот клиент.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Вашето УРЛ со пренасочување треба да биде целосно УРЛ:\nhttps://vasdomen.com/pateka",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Дозволи OAuth2 компатибилна автентификација од други веб апликации.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 апликацијата им овозможува на администраторите да конфигурираат вграден проток за автентификација, истотака дозволува OAuth2 компатибилна автентификација од други веб апликации.",
+ "OAuth 2.0 clients" : "OAuth 2.0 клиенти",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 овозможува на надворешни сервиси да побараат пристап на {instanceName}.",
+ "Name" : "Име",
+ "Redirection URI" : "Пренасочено URI",
+ "Client Identifier" : "Идентификатор на клиент",
+ "Secret key" : "Таен клуч",
+ "Add client" : "Додади клиент",
+ "Add" : "Додади",
+ "Show client secret" : "Прикажи тајна на клиент",
+ "Delete" : "Избриши"
+},"pluralForm" :"nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/nb.js b/apps/oauth2/l10n/nb.js
new file mode 100644
index 00000000000..9cf61c0eed0
--- /dev/null
+++ b/apps/oauth2/l10n/nb.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Din klient er ikke autorisert til å koble til. Vennligst informer din klients administrator.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "URL for videresending må være en fullstendig URL. For eksempel https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Tillater OAuth2-kompatibel autentisering fra andre nettapplikasjoner.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2-appen gir administratorer mulighet til å konfigurere den innebygde arbeidsflyten for autentisering til å også tillate OAuth2-kompatibel autentisering fra andre nett-applikasjoner.",
+ "OAuth 2.0 clients" : "OAuth 2.0-klienter",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 gir eksterne tjenester tillatelse til å be om tilgang til {instanceName}.",
+ "Name" : "Navn",
+ "Redirection URI" : "Videresendings-URI",
+ "Client Identifier" : "Klient-identifikator",
+ "Secret key" : "Hemmelig nøkkel",
+ "Delete client" : "Slett klient",
+ "Make sure you store the secret key, it cannot be recovered." : "Pass på at du oppbevarer den hemmelige nøkkelen, den kan ikke gjenopprettes.",
+ "Add client" : "Legg til klient",
+ "Add" : "Legg til",
+ "Show client secret" : "Vis klients hemmelighet",
+ "Hide client secret" : "Skjul klienthemmelighet",
+ "Delete" : "Slett"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/nb.json b/apps/oauth2/l10n/nb.json
new file mode 100644
index 00000000000..70154349c8b
--- /dev/null
+++ b/apps/oauth2/l10n/nb.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Din klient er ikke autorisert til å koble til. Vennligst informer din klients administrator.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "URL for videresending må være en fullstendig URL. For eksempel https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Tillater OAuth2-kompatibel autentisering fra andre nettapplikasjoner.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2-appen gir administratorer mulighet til å konfigurere den innebygde arbeidsflyten for autentisering til å også tillate OAuth2-kompatibel autentisering fra andre nett-applikasjoner.",
+ "OAuth 2.0 clients" : "OAuth 2.0-klienter",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 gir eksterne tjenester tillatelse til å be om tilgang til {instanceName}.",
+ "Name" : "Navn",
+ "Redirection URI" : "Videresendings-URI",
+ "Client Identifier" : "Klient-identifikator",
+ "Secret key" : "Hemmelig nøkkel",
+ "Delete client" : "Slett klient",
+ "Make sure you store the secret key, it cannot be recovered." : "Pass på at du oppbevarer den hemmelige nøkkelen, den kan ikke gjenopprettes.",
+ "Add client" : "Legg til klient",
+ "Add" : "Legg til",
+ "Show client secret" : "Vis klients hemmelighet",
+ "Hide client secret" : "Skjul klienthemmelighet",
+ "Delete" : "Slett"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/nl.js b/apps/oauth2/l10n/nl.js
new file mode 100644
index 00000000000..f27d2a062d8
--- /dev/null
+++ b/apps/oauth2/l10n/nl.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Je client is niet geautoriseerd om te verbinden. Informeer de beheerder van je client.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Je doorverwijzings-URL moet een volledige URL zijn, bijvoorbeeld: https://jouwdomein.com/pad",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Staat OAuth2 compatible authenticatie vanaf andere web applicaties toe.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "De OAuth2 app laat beheerders de ingebouwde inlog-workflow configureren om ook OAuth2 compatible authenticatie vanaf andere web applicaties mogelijk te maken.",
+ "OAuth 2.0 clients" : "OAuth 2.0 Clients",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 staat externe services toe om toegang te vragen bij {instanceName}.",
+ "Name" : "Naam",
+ "Redirection URI" : "Omeiding URI",
+ "Client Identifier" : "Client identificatie",
+ "Secret key" : "Geheime sleutel",
+ "Delete client" : "Client verwijderen",
+ "Make sure you store the secret key, it cannot be recovered." : "Zorg ervoor dat je de geheime sleutel opslaat, deze kan niet worden hersteld.",
+ "Add client" : "Voeg client toe",
+ "Add" : "Toevoegen",
+ "Show client secret" : "Geheime sleutel van cliënt weergeven",
+ "Hide client secret" : "Geheime sleutel van cliënt verbergen",
+ "Delete" : "verwijderen"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/nl.json b/apps/oauth2/l10n/nl.json
new file mode 100644
index 00000000000..c66631b5750
--- /dev/null
+++ b/apps/oauth2/l10n/nl.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Je client is niet geautoriseerd om te verbinden. Informeer de beheerder van je client.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Je doorverwijzings-URL moet een volledige URL zijn, bijvoorbeeld: https://jouwdomein.com/pad",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Staat OAuth2 compatible authenticatie vanaf andere web applicaties toe.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "De OAuth2 app laat beheerders de ingebouwde inlog-workflow configureren om ook OAuth2 compatible authenticatie vanaf andere web applicaties mogelijk te maken.",
+ "OAuth 2.0 clients" : "OAuth 2.0 Clients",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 staat externe services toe om toegang te vragen bij {instanceName}.",
+ "Name" : "Naam",
+ "Redirection URI" : "Omeiding URI",
+ "Client Identifier" : "Client identificatie",
+ "Secret key" : "Geheime sleutel",
+ "Delete client" : "Client verwijderen",
+ "Make sure you store the secret key, it cannot be recovered." : "Zorg ervoor dat je de geheime sleutel opslaat, deze kan niet worden hersteld.",
+ "Add client" : "Voeg client toe",
+ "Add" : "Toevoegen",
+ "Show client secret" : "Geheime sleutel van cliënt weergeven",
+ "Hide client secret" : "Geheime sleutel van cliënt verbergen",
+ "Delete" : "verwijderen"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/oc.js b/apps/oauth2/l10n/oc.js
new file mode 100644
index 00000000000..8b114800cd1
--- /dev/null
+++ b/apps/oauth2/l10n/oc.js
@@ -0,0 +1,17 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Vòstre client es pas autorizat a se connectar. Mercés d’informar l’administrator de vòstre client.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Vòstra URL de redireccion deu èsser una URL complèta per exemple : https://vostredomeni.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permet l’autentificacion compatibla OAuth2 a partir d’autras aplicacions web",
+ "OAuth 2.0 clients" : "clients OAuth 2.0",
+ "Name" : "Nom",
+ "Redirection URI" : "URI de redireccion",
+ "Client Identifier" : "Identificador client",
+ "Add client" : "Apondre un client",
+ "Add" : "Apondre",
+ "Show client secret" : "Afichar lo secret del client",
+ "Delete" : "Suprimir"
+},
+"nplurals=2; plural=(n > 1);");
diff --git a/apps/oauth2/l10n/oc.json b/apps/oauth2/l10n/oc.json
new file mode 100644
index 00000000000..cee27e15d72
--- /dev/null
+++ b/apps/oauth2/l10n/oc.json
@@ -0,0 +1,15 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Vòstre client es pas autorizat a se connectar. Mercés d’informar l’administrator de vòstre client.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Vòstra URL de redireccion deu èsser una URL complèta per exemple : https://vostredomeni.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permet l’autentificacion compatibla OAuth2 a partir d’autras aplicacions web",
+ "OAuth 2.0 clients" : "clients OAuth 2.0",
+ "Name" : "Nom",
+ "Redirection URI" : "URI de redireccion",
+ "Client Identifier" : "Identificador client",
+ "Add client" : "Apondre un client",
+ "Add" : "Apondre",
+ "Show client secret" : "Afichar lo secret del client",
+ "Delete" : "Suprimir"
+},"pluralForm" :"nplurals=2; plural=(n > 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/pl.js b/apps/oauth2/l10n/pl.js
new file mode 100644
index 00000000000..50a5f361808
--- /dev/null
+++ b/apps/oauth2/l10n/pl.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Twoja aplikacja klienta nie posiada autoryzacji do połączenia. Poinformuj proszę administratora klienta.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Adres przekierowania musi być pełnym adresem URL, np.: https://twojadomena.com/sciezka",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Umożliwia uwierzytelnianie zgodne z OAuth2 innych aplikacji internetowych.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Aplikacja OAuth2 umożliwia administratorom modyfikację wbudowanego uwierzytelnienia, pozwalając na korzystanie z OAuth2 innych aplikacji internetowych.",
+ "OAuth 2.0 clients" : "Klienci OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 pozwala zewnętrznym serwisom na korzystanie z dostępu do {instanceName}.",
+ "Name" : "Nazwa",
+ "Redirection URI" : "URI przekierowania",
+ "Client Identifier" : "Identyfikator Klienta",
+ "Secret key" : "Tajny klucz",
+ "Delete client" : "Usuń klienta",
+ "Make sure you store the secret key, it cannot be recovered." : "Upewnij się, że przechowujesz klucz tajny, gdyż nie będzie można go odzyskać.",
+ "Add client" : "Dodaj klienta",
+ "Add" : "Dodaj",
+ "Show client secret" : "Pokaż tajny klucz klienta",
+ "Hide client secret" : "Ukryj tajny klucz klienta",
+ "Delete" : "Usuń"
+},
+"nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);");
diff --git a/apps/oauth2/l10n/pl.json b/apps/oauth2/l10n/pl.json
new file mode 100644
index 00000000000..09ac52e38c7
--- /dev/null
+++ b/apps/oauth2/l10n/pl.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Twoja aplikacja klienta nie posiada autoryzacji do połączenia. Poinformuj proszę administratora klienta.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Adres przekierowania musi być pełnym adresem URL, np.: https://twojadomena.com/sciezka",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Umożliwia uwierzytelnianie zgodne z OAuth2 innych aplikacji internetowych.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Aplikacja OAuth2 umożliwia administratorom modyfikację wbudowanego uwierzytelnienia, pozwalając na korzystanie z OAuth2 innych aplikacji internetowych.",
+ "OAuth 2.0 clients" : "Klienci OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 pozwala zewnętrznym serwisom na korzystanie z dostępu do {instanceName}.",
+ "Name" : "Nazwa",
+ "Redirection URI" : "URI przekierowania",
+ "Client Identifier" : "Identyfikator Klienta",
+ "Secret key" : "Tajny klucz",
+ "Delete client" : "Usuń klienta",
+ "Make sure you store the secret key, it cannot be recovered." : "Upewnij się, że przechowujesz klucz tajny, gdyż nie będzie można go odzyskać.",
+ "Add client" : "Dodaj klienta",
+ "Add" : "Dodaj",
+ "Show client secret" : "Pokaż tajny klucz klienta",
+ "Hide client secret" : "Ukryj tajny klucz klienta",
+ "Delete" : "Usuń"
+},"pluralForm" :"nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/pt_BR.js b/apps/oauth2/l10n/pt_BR.js
new file mode 100644
index 00000000000..4d390ef783e
--- /dev/null
+++ b/apps/oauth2/l10n/pt_BR.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Seu cliente não está autorizado a conectar-se. Informe isso ao administrador.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "A URL de redirecionamento precisa ser completa, por exemplo: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite a autenticação compatível com OAuth2 de outros aplicativos da web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "O aplicativo OAuth2 permite que os administradores configurem o fluxo de trabalho de autenticação integrado para permitir também a autenticação compatível com OAuth2 de outros aplicativos da Web.",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permite que serviços externos solicitem acesso a {instanceName}.",
+ "Name" : "Nome",
+ "Redirection URI" : "Redirecionamento URI",
+ "Client Identifier" : "Identificador do Cliente",
+ "Secret key" : "Chave secreta",
+ "Delete client" : "Excluir cliente",
+ "Make sure you store the secret key, it cannot be recovered." : "Certifique-se de armazenar a chave secreta, ela não pode ser recuperada.",
+ "Add client" : "Adicionar cliente",
+ "Add" : "Adicionar",
+ "Show client secret" : "Mostra senha do cliente",
+ "Hide client secret" : "Ocultar segredo do cliente",
+ "Delete" : "Excluir"
+},
+"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/oauth2/l10n/pt_BR.json b/apps/oauth2/l10n/pt_BR.json
new file mode 100644
index 00000000000..4407ca4c591
--- /dev/null
+++ b/apps/oauth2/l10n/pt_BR.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Seu cliente não está autorizado a conectar-se. Informe isso ao administrador.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "A URL de redirecionamento precisa ser completa, por exemplo: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite a autenticação compatível com OAuth2 de outros aplicativos da web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "O aplicativo OAuth2 permite que os administradores configurem o fluxo de trabalho de autenticação integrado para permitir também a autenticação compatível com OAuth2 de outros aplicativos da Web.",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 permite que serviços externos solicitem acesso a {instanceName}.",
+ "Name" : "Nome",
+ "Redirection URI" : "Redirecionamento URI",
+ "Client Identifier" : "Identificador do Cliente",
+ "Secret key" : "Chave secreta",
+ "Delete client" : "Excluir cliente",
+ "Make sure you store the secret key, it cannot be recovered." : "Certifique-se de armazenar a chave secreta, ela não pode ser recuperada.",
+ "Add client" : "Adicionar cliente",
+ "Add" : "Adicionar",
+ "Show client secret" : "Mostra senha do cliente",
+ "Hide client secret" : "Ocultar segredo do cliente",
+ "Delete" : "Excluir"
+},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/pt_PT.js b/apps/oauth2/l10n/pt_PT.js
new file mode 100644
index 00000000000..09f9f952d30
--- /dev/null
+++ b/apps/oauth2/l10n/pt_PT.js
@@ -0,0 +1,20 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "O seu cliente não está autorizado a conectar-se. Por favor, informe o administrador do seu cliente.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "O seu URL de redirecionamento precisa ser um URL completo, por exemplo: https://seudominio.com/caminho",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite autenticação OAuth2 compatível com outras aplicações web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "A app OAuth2 permite que os administradores configurem o fluxo de autenticação para também permitir autenticação compatível com OAuth2 de outras aplicações web.",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth2.0 permite que dispositivos externos peçam acesso a {instanceName}.",
+ "Name" : "Nome",
+ "Redirection URI" : "URI de redirecionamento",
+ "Client Identifier" : "Identificador de Cliente",
+ "Secret key" : "Código secreto",
+ "Add client" : "Adicionar cliente",
+ "Add" : "Adicionar",
+ "Show client secret" : "Mostrar segredo do cliente",
+ "Delete" : "Apagar"
+},
+"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/oauth2/l10n/pt_PT.json b/apps/oauth2/l10n/pt_PT.json
new file mode 100644
index 00000000000..37b9ea94e98
--- /dev/null
+++ b/apps/oauth2/l10n/pt_PT.json
@@ -0,0 +1,18 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "O seu cliente não está autorizado a conectar-se. Por favor, informe o administrador do seu cliente.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "O seu URL de redirecionamento precisa ser um URL completo, por exemplo: https://seudominio.com/caminho",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Permite autenticação OAuth2 compatível com outras aplicações web.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "A app OAuth2 permite que os administradores configurem o fluxo de autenticação para também permitir autenticação compatível com OAuth2 de outras aplicações web.",
+ "OAuth 2.0 clients" : "Clientes OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth2.0 permite que dispositivos externos peçam acesso a {instanceName}.",
+ "Name" : "Nome",
+ "Redirection URI" : "URI de redirecionamento",
+ "Client Identifier" : "Identificador de Cliente",
+ "Secret key" : "Código secreto",
+ "Add client" : "Adicionar cliente",
+ "Add" : "Adicionar",
+ "Show client secret" : "Mostrar segredo do cliente",
+ "Delete" : "Apagar"
+},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/ru.js b/apps/oauth2/l10n/ru.js
new file mode 100644
index 00000000000..0566016ee23
--- /dev/null
+++ b/apps/oauth2/l10n/ru.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Ваш клиент не авторизован для соединения. Пожалуйста, сообщите о вашем клиенте администратору.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Ваш перенаправляющий URL должен быть полным, например: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Разрешает аутентификацию, совместимую с OAuth2, из других веб-приложений.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Приложение OAuth2 позволяет администраторам настроить встроенный процесс проверки подлинности, чтобы также обеспечить совместимость OAuth2 с другими веб-приложениями.",
+ "OAuth 2.0 clients" : "Клиенты OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 позволяет внешним службам запрашивать доступ к {instanceName}.",
+ "Name" : "Имя",
+ "Redirection URI" : "URI перенаправления",
+ "Client Identifier" : "Идентификатор клиента",
+ "Secret key" : "Секретный ключ",
+ "Delete client" : "Удалить клиент",
+ "Make sure you store the secret key, it cannot be recovered." : "Убедитесь, что вы сохранили секретный ключ, он не может быть восстановлен.",
+ "Add client" : "Добавить клиент",
+ "Add" : "Добавить",
+ "Show client secret" : "Показать секретный ключ клиента",
+ "Hide client secret" : "Скрыть секрет клиента",
+ "Delete" : "Удалить"
+},
+"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);");
diff --git a/apps/oauth2/l10n/ru.json b/apps/oauth2/l10n/ru.json
new file mode 100644
index 00000000000..0e34793a1a1
--- /dev/null
+++ b/apps/oauth2/l10n/ru.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Ваш клиент не авторизован для соединения. Пожалуйста, сообщите о вашем клиенте администратору.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Ваш перенаправляющий URL должен быть полным, например: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Разрешает аутентификацию, совместимую с OAuth2, из других веб-приложений.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Приложение OAuth2 позволяет администраторам настроить встроенный процесс проверки подлинности, чтобы также обеспечить совместимость OAuth2 с другими веб-приложениями.",
+ "OAuth 2.0 clients" : "Клиенты OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 позволяет внешним службам запрашивать доступ к {instanceName}.",
+ "Name" : "Имя",
+ "Redirection URI" : "URI перенаправления",
+ "Client Identifier" : "Идентификатор клиента",
+ "Secret key" : "Секретный ключ",
+ "Delete client" : "Удалить клиент",
+ "Make sure you store the secret key, it cannot be recovered." : "Убедитесь, что вы сохранили секретный ключ, он не может быть восстановлен.",
+ "Add client" : "Добавить клиент",
+ "Add" : "Добавить",
+ "Show client secret" : "Показать секретный ключ клиента",
+ "Hide client secret" : "Скрыть секрет клиента",
+ "Delete" : "Удалить"
+},"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/sc.js b/apps/oauth2/l10n/sc.js
new file mode 100644
index 00000000000..833214ed6d7
--- /dev/null
+++ b/apps/oauth2/l10n/sc.js
@@ -0,0 +1,20 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Su cliente tuo no est autorizadu a si connètere. Pro praghere, informa s'amministradore de su cliente tuo.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : " S'URL tuo pro torrare a deretare tocat chi siat unu URL cumpretu, pro nàrrere: https://sudomìniutuo.com/caminu",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Cunsenti s'autenticatzione cumpatìbile cun OAuth2 dae àteras aplicatziones in sa rete.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "S'aplicatzione OAuth2 cunsentit a is amministradores de cunfigurare sa protzedura de autenticatzione integrada pro cunsentire fintzas s'autenticatzione cumpatìbile cun OAuth2 dae àteras aplicatziones in sa rete.",
+ "OAuth 2.0 clients" : "Clientes de OAuth 2.0 ",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 cunsentit a is zservìtzios de foras de pedire s'atzessu a {instanceName}.",
+ "Name" : "Nùmene",
+ "Redirection URI" : "URI de torrada a deretare",
+ "Client Identifier" : "Identificadore cliente",
+ "Secret key" : "Crae segreta",
+ "Add client" : "Agiunghe cliente",
+ "Add" : "Agiunghe",
+ "Show client secret" : "Mustra su segretu de su cliente",
+ "Delete" : "Cantzella"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/sc.json b/apps/oauth2/l10n/sc.json
new file mode 100644
index 00000000000..61e866c2a39
--- /dev/null
+++ b/apps/oauth2/l10n/sc.json
@@ -0,0 +1,18 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Su cliente tuo no est autorizadu a si connètere. Pro praghere, informa s'amministradore de su cliente tuo.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : " S'URL tuo pro torrare a deretare tocat chi siat unu URL cumpretu, pro nàrrere: https://sudomìniutuo.com/caminu",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Cunsenti s'autenticatzione cumpatìbile cun OAuth2 dae àteras aplicatziones in sa rete.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "S'aplicatzione OAuth2 cunsentit a is amministradores de cunfigurare sa protzedura de autenticatzione integrada pro cunsentire fintzas s'autenticatzione cumpatìbile cun OAuth2 dae àteras aplicatziones in sa rete.",
+ "OAuth 2.0 clients" : "Clientes de OAuth 2.0 ",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 cunsentit a is zservìtzios de foras de pedire s'atzessu a {instanceName}.",
+ "Name" : "Nùmene",
+ "Redirection URI" : "URI de torrada a deretare",
+ "Client Identifier" : "Identificadore cliente",
+ "Secret key" : "Crae segreta",
+ "Add client" : "Agiunghe cliente",
+ "Add" : "Agiunghe",
+ "Show client secret" : "Mustra su segretu de su cliente",
+ "Delete" : "Cantzella"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/si.js b/apps/oauth2/l10n/si.js
new file mode 100644
index 00000000000..6dc3386f6ae
--- /dev/null
+++ b/apps/oauth2/l10n/si.js
@@ -0,0 +1,17 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "ඔබගේ අනුග්‍රාහකය සම්බන්ධ කිරීමට අවසර නැත. කරුණාකර අනුග්‍රාහකයේ පරිපාලක ට දන්වන්න.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "ඔබ සම්පූර්ණ ආපසු හරවා යැවීමේ ඒ.ස.නි.(URL) ක් භාවිතා කළ යුතුය උදා: https://yourdomain.lk/maga",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "වෙනත් වියමන යෙදුම් වලින් OAuth2 අනුකූල සත්‍යාපනය සඳහා ඉඩ ලබා දේ.",
+ "OAuth 2.0 clients" : "OAuth 2.0 අනුග්‍රාහකයින්",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 බාහිර සේවාවන්ට {instanceName} වෙත ප්‍රවේශය ඉල්ලීමට ඉඩ දෙයි.",
+ "Name" : "නම",
+ "Client Identifier" : "අනුග්‍රාහක හඳුන්වනය",
+ "Secret key" : "රහස්‍ය යතුර",
+ "Add client" : "අනුග්‍රාහකයක් එකතු කරන්න",
+ "Add" : "එකතු කරන්න",
+ "Show client secret" : "අනුග්‍රාහකයේ රහස්‍යතාව පෙන්වන්න"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/si.json b/apps/oauth2/l10n/si.json
new file mode 100644
index 00000000000..335d13c7a0c
--- /dev/null
+++ b/apps/oauth2/l10n/si.json
@@ -0,0 +1,15 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "ඔබගේ අනුග්‍රාහකය සම්බන්ධ කිරීමට අවසර නැත. කරුණාකර අනුග්‍රාහකයේ පරිපාලක ට දන්වන්න.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "ඔබ සම්පූර්ණ ආපසු හරවා යැවීමේ ඒ.ස.නි.(URL) ක් භාවිතා කළ යුතුය උදා: https://yourdomain.lk/maga",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "වෙනත් වියමන යෙදුම් වලින් OAuth2 අනුකූල සත්‍යාපනය සඳහා ඉඩ ලබා දේ.",
+ "OAuth 2.0 clients" : "OAuth 2.0 අනුග්‍රාහකයින්",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 බාහිර සේවාවන්ට {instanceName} වෙත ප්‍රවේශය ඉල්ලීමට ඉඩ දෙයි.",
+ "Name" : "නම",
+ "Client Identifier" : "අනුග්‍රාහක හඳුන්වනය",
+ "Secret key" : "රහස්‍ය යතුර",
+ "Add client" : "අනුග්‍රාහකයක් එකතු කරන්න",
+ "Add" : "එකතු කරන්න",
+ "Show client secret" : "අනුග්‍රාහකයේ රහස්‍යතාව පෙන්වන්න"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/sk.js b/apps/oauth2/l10n/sk.js
new file mode 100644
index 00000000000..5bd63fde0ff
--- /dev/null
+++ b/apps/oauth2/l10n/sk.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Váš klient nemá oprávnenie na pripojenie. Prosím informujte administrátora svojho klienta.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Vaša URL pre presmerovanie musí byť úplná. Napríklad: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Povoliť overenie kompatibilné s OAuth2 iných webových aplikácií.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Aplikácia OAuth2 umožňuje správcom nakonfigurovať vstavaný postup overovania tak, aby povolil autentifikáciu kompatibilnú s protokolom OAuth2 aj z iných webových aplikácií.",
+ "OAuth 2.0 clients" : "Klienti OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 umožňuje externým službám požiadať o prístup k {instanceName}.",
+ "Name" : "Názov",
+ "Redirection URI" : "URI presmerovania",
+ "Client Identifier" : "Identifikátor klienta",
+ "Secret key" : "Tajný kľúč",
+ "Delete client" : "Odstrániť klienta",
+ "Make sure you store the secret key, it cannot be recovered." : "Uistite sa, že ste uložili tajný kľúč. Nedá sa obnoviť.",
+ "Add client" : "Pridať klienta",
+ "Add" : "Pridať",
+ "Show client secret" : "Zobraziť tajomstvo klienta",
+ "Hide client secret" : "Skryť klienta",
+ "Delete" : "Zmazať"
+},
+"nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);");
diff --git a/apps/oauth2/l10n/sk.json b/apps/oauth2/l10n/sk.json
new file mode 100644
index 00000000000..f7f9ac19229
--- /dev/null
+++ b/apps/oauth2/l10n/sk.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Váš klient nemá oprávnenie na pripojenie. Prosím informujte administrátora svojho klienta.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Vaša URL pre presmerovanie musí byť úplná. Napríklad: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Povoliť overenie kompatibilné s OAuth2 iných webových aplikácií.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Aplikácia OAuth2 umožňuje správcom nakonfigurovať vstavaný postup overovania tak, aby povolil autentifikáciu kompatibilnú s protokolom OAuth2 aj z iných webových aplikácií.",
+ "OAuth 2.0 clients" : "Klienti OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 umožňuje externým službám požiadať o prístup k {instanceName}.",
+ "Name" : "Názov",
+ "Redirection URI" : "URI presmerovania",
+ "Client Identifier" : "Identifikátor klienta",
+ "Secret key" : "Tajný kľúč",
+ "Delete client" : "Odstrániť klienta",
+ "Make sure you store the secret key, it cannot be recovered." : "Uistite sa, že ste uložili tajný kľúč. Nedá sa obnoviť.",
+ "Add client" : "Pridať klienta",
+ "Add" : "Pridať",
+ "Show client secret" : "Zobraziť tajomstvo klienta",
+ "Hide client secret" : "Skryť klienta",
+ "Delete" : "Zmazať"
+},"pluralForm" :"nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/sl.js b/apps/oauth2/l10n/sl.js
new file mode 100644
index 00000000000..5786c7d8b36
--- /dev/null
+++ b/apps/oauth2/l10n/sl.js
@@ -0,0 +1,20 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Nameščen odjemalec ni overjen za vzpostavljanje povezave. Stopite v stik s skrbnikom odjemalca.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Preusmeritveni naslov URL mora biti zapisan v polni obliki: https://domena.si/celotna-pot",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Dovoli skladno overitev OAuth2 prek drugih spletnih programov.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Program OAuth2 omogoča skrbnikom nastavitev vgrajenega sistema overitve za skladnost z drugimi spletnimi programi.",
+ "OAuth 2.0 clients" : "Odjemalci OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "Program OAuth 2.0 omogoča zunanjim storitvam odobritev dostopa do {instanceName}.",
+ "Name" : "Ime",
+ "Redirection URI" : "Preusmeritveni naslov URI",
+ "Client Identifier" : "ID odjemalca",
+ "Secret key" : "Skrivni ključ",
+ "Add client" : "Dodaj odjemalca",
+ "Add" : "Dodaj",
+ "Show client secret" : "Pokaži skrivno kodo odjemalca",
+ "Delete" : "Izbriši"
+},
+"nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);");
diff --git a/apps/oauth2/l10n/sl.json b/apps/oauth2/l10n/sl.json
new file mode 100644
index 00000000000..a419d4c3263
--- /dev/null
+++ b/apps/oauth2/l10n/sl.json
@@ -0,0 +1,18 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Nameščen odjemalec ni overjen za vzpostavljanje povezave. Stopite v stik s skrbnikom odjemalca.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Preusmeritveni naslov URL mora biti zapisan v polni obliki: https://domena.si/celotna-pot",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Dovoli skladno overitev OAuth2 prek drugih spletnih programov.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Program OAuth2 omogoča skrbnikom nastavitev vgrajenega sistema overitve za skladnost z drugimi spletnimi programi.",
+ "OAuth 2.0 clients" : "Odjemalci OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "Program OAuth 2.0 omogoča zunanjim storitvam odobritev dostopa do {instanceName}.",
+ "Name" : "Ime",
+ "Redirection URI" : "Preusmeritveni naslov URI",
+ "Client Identifier" : "ID odjemalca",
+ "Secret key" : "Skrivni ključ",
+ "Add client" : "Dodaj odjemalca",
+ "Add" : "Dodaj",
+ "Show client secret" : "Pokaži skrivno kodo odjemalca",
+ "Delete" : "Izbriši"
+},"pluralForm" :"nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/sq.js b/apps/oauth2/l10n/sq.js
new file mode 100644
index 00000000000..fe81fb5069d
--- /dev/null
+++ b/apps/oauth2/l10n/sq.js
@@ -0,0 +1,14 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "OAuth 2.0" : "O.Auth 2.0",
+ "OAuth 2.0 clients" : "Klientë OAuth 2.0",
+ "Name" : "Emri",
+ "Redirection URI" : "URI Ridrejtimi",
+ "Client Identifier" : "Identifikues Klienti",
+ "Secret key" : "Kyç i fshehtë",
+ "Add client" : "Shto klient",
+ "Add" : "Shto ",
+ "Delete" : "Delete"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/sq.json b/apps/oauth2/l10n/sq.json
new file mode 100644
index 00000000000..c6623df185a
--- /dev/null
+++ b/apps/oauth2/l10n/sq.json
@@ -0,0 +1,12 @@
+{ "translations": {
+ "OAuth 2.0" : "O.Auth 2.0",
+ "OAuth 2.0 clients" : "Klientë OAuth 2.0",
+ "Name" : "Emri",
+ "Redirection URI" : "URI Ridrejtimi",
+ "Client Identifier" : "Identifikues Klienti",
+ "Secret key" : "Kyç i fshehtë",
+ "Add client" : "Shto klient",
+ "Add" : "Shto ",
+ "Delete" : "Delete"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/sr.js b/apps/oauth2/l10n/sr.js
new file mode 100644
index 00000000000..9ec881c1a2a
--- /dev/null
+++ b/apps/oauth2/l10n/sr.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Вашем клијенту није одобрено повезивање. Обавестите администратора који је Ваш клијент.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Адреса за преусмеравање мора бити пуна адреса, нпр: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Дозвољава OAuth2 компатибилну идентификацију са других веб апликација,",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 апликација дозвољава администраторима да подесе уграђени след индетификације тако да дозвољава и OAuth2 компатибилну идентификацију са других веб апликација.",
+ "OAuth 2.0 clients" : "OAuth 2.0 клијенти",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 омогућава спољним сервисима да захтевају приступ инстанци {instanceName}.",
+ "Name" : "Име",
+ "Redirection URI" : "Адреса за преусмеравање",
+ "Client Identifier" : "Идентификација клијента",
+ "Secret key" : "Тајни кључ ",
+ "Delete client" : "Обриши клијента",
+ "Make sure you store the secret key, it cannot be recovered." : "Сачувајте тајни кључ, нећете моћи поново да му приступите.",
+ "Add client" : "Додај клијента",
+ "Add" : "Додај",
+ "Show client secret" : "Прикажи клијентску лозинку",
+ "Hide client secret" : "Сакриј клијентску тајну",
+ "Delete" : "Обриши"
+},
+"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);");
diff --git a/apps/oauth2/l10n/sr.json b/apps/oauth2/l10n/sr.json
new file mode 100644
index 00000000000..815e73ada5a
--- /dev/null
+++ b/apps/oauth2/l10n/sr.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Вашем клијенту није одобрено повезивање. Обавестите администратора који је Ваш клијент.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Адреса за преусмеравање мора бити пуна адреса, нпр: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Дозвољава OAuth2 компатибилну идентификацију са других веб апликација,",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 апликација дозвољава администраторима да подесе уграђени след индетификације тако да дозвољава и OAuth2 компатибилну идентификацију са других веб апликација.",
+ "OAuth 2.0 clients" : "OAuth 2.0 клијенти",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 омогућава спољним сервисима да захтевају приступ инстанци {instanceName}.",
+ "Name" : "Име",
+ "Redirection URI" : "Адреса за преусмеравање",
+ "Client Identifier" : "Идентификација клијента",
+ "Secret key" : "Тајни кључ ",
+ "Delete client" : "Обриши клијента",
+ "Make sure you store the secret key, it cannot be recovered." : "Сачувајте тајни кључ, нећете моћи поново да му приступите.",
+ "Add client" : "Додај клијента",
+ "Add" : "Додај",
+ "Show client secret" : "Прикажи клијентску лозинку",
+ "Hide client secret" : "Сакриј клијентску тајну",
+ "Delete" : "Обриши"
+},"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/sv.js b/apps/oauth2/l10n/sv.js
new file mode 100644
index 00000000000..4d55cf91c3a
--- /dev/null
+++ b/apps/oauth2/l10n/sv.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Din klient har inte behörighet att ansluta. Vänligen meddela din administratör.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Din omdirigeringsadress måste vara en fullständig webbadress till exempel: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Tillåter OAuth2-kompatibel autentisering från andra webbapplikationer.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "I appen OAuth2 kan administratörer konfigurera det inbyggda autentiseringsflödet för att även tillåta OAuth2-kompatibel autentisering från andra webbapplikationer.",
+ "OAuth 2.0 clients" : "OAuth 2.0 klienter",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 tillåter externa tjänster att begära åtkomst till {instanceName}.",
+ "Name" : "Namn",
+ "Redirection URI" : "Omdirigerings-URI",
+ "Client Identifier" : "Klientidentifierare",
+ "Secret key" : "Hemlig nyckel",
+ "Delete client" : "Ta bort klient",
+ "Make sure you store the secret key, it cannot be recovered." : "Se till att du lagrar den hemliga nyckeln, den kan inte återställas.",
+ "Add client" : "Lägg till klient",
+ "Add" : "Lägg till",
+ "Show client secret" : "Visa klienthemlighet",
+ "Hide client secret" : "Dölj klienthemlighet",
+ "Delete" : "Ta bort"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/sv.json b/apps/oauth2/l10n/sv.json
new file mode 100644
index 00000000000..064e6923f96
--- /dev/null
+++ b/apps/oauth2/l10n/sv.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Din klient har inte behörighet att ansluta. Vänligen meddela din administratör.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Din omdirigeringsadress måste vara en fullständig webbadress till exempel: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Tillåter OAuth2-kompatibel autentisering från andra webbapplikationer.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "I appen OAuth2 kan administratörer konfigurera det inbyggda autentiseringsflödet för att även tillåta OAuth2-kompatibel autentisering från andra webbapplikationer.",
+ "OAuth 2.0 clients" : "OAuth 2.0 klienter",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 tillåter externa tjänster att begära åtkomst till {instanceName}.",
+ "Name" : "Namn",
+ "Redirection URI" : "Omdirigerings-URI",
+ "Client Identifier" : "Klientidentifierare",
+ "Secret key" : "Hemlig nyckel",
+ "Delete client" : "Ta bort klient",
+ "Make sure you store the secret key, it cannot be recovered." : "Se till att du lagrar den hemliga nyckeln, den kan inte återställas.",
+ "Add client" : "Lägg till klient",
+ "Add" : "Lägg till",
+ "Show client secret" : "Visa klienthemlighet",
+ "Hide client secret" : "Dölj klienthemlighet",
+ "Delete" : "Ta bort"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/sw.js b/apps/oauth2/l10n/sw.js
new file mode 100644
index 00000000000..44e346ffa4f
--- /dev/null
+++ b/apps/oauth2/l10n/sw.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Mteja wako hajaidhinishwa kuunganisha. Tafadhali mjulishe msimamizi wa mteja wako.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "URL yako ya kuelekeza kwingine inahitaji kuwa URL kamili kwa mfano: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Huruhusu uthibitishaji tangamanifu wa OAuth2 kutoka kwa programu zingine za wavuti.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Programu ya OAuth2 huruhusu wasimamizi kusanidi utendakazi wa uthibitishaji uliojengewa ndani ili kuruhusu pia uthibitishaji unaooana na OAuth2 kutoka kwa programu zingine za wavuti.",
+ "OAuth 2.0 clients" : "OAuth 2.0 clients",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 inaruhusu huduma za nje kuomba ufikiaji {instanceName}.",
+ "Name" : "Jina",
+ "Redirection URI" : "URI ya kuelekeza kwingine",
+ "Client Identifier" : "Kitambulisho cha Mteja",
+ "Secret key" : "Ufunguo wa siri",
+ "Delete client" : "Futa mteja",
+ "Make sure you store the secret key, it cannot be recovered." : "Hakikisha umehifadhi ufunguo wa siri, hauwezi kurejeshwa.",
+ "Add client" : "Ongeza mteja",
+ "Add" : "Ongeza",
+ "Show client secret" : "Onyesha siri ya mteja",
+ "Hide client secret" : "Ficha siri ya mteja",
+ "Delete" : "Futa"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/sw.json b/apps/oauth2/l10n/sw.json
new file mode 100644
index 00000000000..0bdbdd3a440
--- /dev/null
+++ b/apps/oauth2/l10n/sw.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Mteja wako hajaidhinishwa kuunganisha. Tafadhali mjulishe msimamizi wa mteja wako.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "URL yako ya kuelekeza kwingine inahitaji kuwa URL kamili kwa mfano: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Huruhusu uthibitishaji tangamanifu wa OAuth2 kutoka kwa programu zingine za wavuti.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Programu ya OAuth2 huruhusu wasimamizi kusanidi utendakazi wa uthibitishaji uliojengewa ndani ili kuruhusu pia uthibitishaji unaooana na OAuth2 kutoka kwa programu zingine za wavuti.",
+ "OAuth 2.0 clients" : "OAuth 2.0 clients",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 inaruhusu huduma za nje kuomba ufikiaji {instanceName}.",
+ "Name" : "Jina",
+ "Redirection URI" : "URI ya kuelekeza kwingine",
+ "Client Identifier" : "Kitambulisho cha Mteja",
+ "Secret key" : "Ufunguo wa siri",
+ "Delete client" : "Futa mteja",
+ "Make sure you store the secret key, it cannot be recovered." : "Hakikisha umehifadhi ufunguo wa siri, hauwezi kurejeshwa.",
+ "Add client" : "Ongeza mteja",
+ "Add" : "Ongeza",
+ "Show client secret" : "Onyesha siri ya mteja",
+ "Hide client secret" : "Ficha siri ya mteja",
+ "Delete" : "Futa"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/tr.js b/apps/oauth2/l10n/tr.js
new file mode 100644
index 00000000000..eb672477de8
--- /dev/null
+++ b/apps/oauth2/l10n/tr.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "İstemcinizin bağlanma izni yok. Lütfen BT yöneticinize istemciniz ile ilgili bilgi verin.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Yönlendirme adresi https://sitem.com/yol gibi tam bir adres olmalıdır",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Diğer site uygulamaları için OAuth2 uyumlu kimlik doğrulaması sağlar.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 uygulaması, BT yöneticilerinin iç kimlik doğrulama iş akışını yapılandırabilmesini ve diğer site uygulamaları için OAuth2 uyumlu kimlik doğrulaması kullanılabilmesini sağlar.",
+ "OAuth 2.0 clients" : "OAuth 2.0 istemcileri",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 dış hizmetlerin {instanceName} için erişim isteğinde bulunmasını sağlar.",
+ "Name" : "Ad",
+ "Redirection URI" : "Yönlendirme adresi",
+ "Client Identifier" : "İstemci belirteci",
+ "Secret key" : "Gizli anahtar",
+ "Delete client" : "İstemciyi sil",
+ "Make sure you store the secret key, it cannot be recovered." : "Yeniden öğrenilemeyeceği için gizli anahtarı kaydettiğinizden emin olun.",
+ "Add client" : "İstemci ekle",
+ "Add" : "Ekle",
+ "Show client secret" : "İstemci parolasını görüntüle",
+ "Hide client secret" : "İstemci parolasını gizle",
+ "Delete" : "Sil"
+},
+"nplurals=2; plural=(n > 1);");
diff --git a/apps/oauth2/l10n/tr.json b/apps/oauth2/l10n/tr.json
new file mode 100644
index 00000000000..ec9e8fc1e83
--- /dev/null
+++ b/apps/oauth2/l10n/tr.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "İstemcinizin bağlanma izni yok. Lütfen BT yöneticinize istemciniz ile ilgili bilgi verin.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Yönlendirme adresi https://sitem.com/yol gibi tam bir adres olmalıdır",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Diğer site uygulamaları için OAuth2 uyumlu kimlik doğrulaması sağlar.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 uygulaması, BT yöneticilerinin iç kimlik doğrulama iş akışını yapılandırabilmesini ve diğer site uygulamaları için OAuth2 uyumlu kimlik doğrulaması kullanılabilmesini sağlar.",
+ "OAuth 2.0 clients" : "OAuth 2.0 istemcileri",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 dış hizmetlerin {instanceName} için erişim isteğinde bulunmasını sağlar.",
+ "Name" : "Ad",
+ "Redirection URI" : "Yönlendirme adresi",
+ "Client Identifier" : "İstemci belirteci",
+ "Secret key" : "Gizli anahtar",
+ "Delete client" : "İstemciyi sil",
+ "Make sure you store the secret key, it cannot be recovered." : "Yeniden öğrenilemeyeceği için gizli anahtarı kaydettiğinizden emin olun.",
+ "Add client" : "İstemci ekle",
+ "Add" : "Ekle",
+ "Show client secret" : "İstemci parolasını görüntüle",
+ "Hide client secret" : "İstemci parolasını gizle",
+ "Delete" : "Sil"
+},"pluralForm" :"nplurals=2; plural=(n > 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/ug.js b/apps/oauth2/l10n/ug.js
new file mode 100644
index 00000000000..89db1a948ef
--- /dev/null
+++ b/apps/oauth2/l10n/ug.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "خېرىدارىڭىزنىڭ ئۇلىنىش ھوقۇقى يوق. خېرىدارىڭىزنىڭ باشقۇرغۇچىغا خەۋەر قىلىڭ.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "قايتا نىشانلانغان URL ئادرېسىڭىز تولۇق URL بولۇشى كېرەك: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "باشقا تور پروگراممىلىرىدىن OAuth2 ماسلاشتۇرۇلغان دەلىللەشكە يول قويىدۇ.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 دېتالى باشقۇرغۇچىنىڭ ئىچىگە دەلىللەش خىزمەت ئېقىمىنى سەپلەپ ، باشقا تور قوللىنىشچان پروگراممىلىرىنىڭ OAuth2 ماسلاشتۇرۇلغان دەلىللىشىگە يول قويىدۇ.",
+ "OAuth 2.0 clients" : "OAuth 2.0 خېرىدارى",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 سىرتقى مۇلازىمەتلەرنىڭ {instanceName} access نى زىيارەت قىلىشىنى تەلەپ قىلىدۇ.",
+ "Name" : "ئاتى",
+ "Redirection URI" : "قايتا نىشانلاش URI",
+ "Client Identifier" : "Client Identifier",
+ "Secret key" : "مەخپىي ئاچقۇچ",
+ "Delete client" : "خېرىدارنى ئۆچۈرۈڭ",
+ "Make sure you store the secret key, it cannot be recovered." : "مەخپىي ئاچقۇچنى ساقلىغانلىقىڭىزنى جەزملەشتۈرۈڭ ، ئەسلىگە كەلتۈرگىلى بولمايدۇ.",
+ "Add client" : "خېرىدار قوشۇڭ",
+ "Add" : "قوش",
+ "Show client secret" : "خېرىدارلارنىڭ مەخپىيىتىنى كۆرسىتىڭ",
+ "Hide client secret" : "خېرىدارلارنىڭ مەخپىيىتىنى يوشۇرۇش",
+ "Delete" : "ئۆچۈر"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/oauth2/l10n/ug.json b/apps/oauth2/l10n/ug.json
new file mode 100644
index 00000000000..4dac11f85c2
--- /dev/null
+++ b/apps/oauth2/l10n/ug.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "خېرىدارىڭىزنىڭ ئۇلىنىش ھوقۇقى يوق. خېرىدارىڭىزنىڭ باشقۇرغۇچىغا خەۋەر قىلىڭ.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "قايتا نىشانلانغان URL ئادرېسىڭىز تولۇق URL بولۇشى كېرەك: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "باشقا تور پروگراممىلىرىدىن OAuth2 ماسلاشتۇرۇلغان دەلىللەشكە يول قويىدۇ.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 دېتالى باشقۇرغۇچىنىڭ ئىچىگە دەلىللەش خىزمەت ئېقىمىنى سەپلەپ ، باشقا تور قوللىنىشچان پروگراممىلىرىنىڭ OAuth2 ماسلاشتۇرۇلغان دەلىللىشىگە يول قويىدۇ.",
+ "OAuth 2.0 clients" : "OAuth 2.0 خېرىدارى",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 سىرتقى مۇلازىمەتلەرنىڭ {instanceName} access نى زىيارەت قىلىشىنى تەلەپ قىلىدۇ.",
+ "Name" : "ئاتى",
+ "Redirection URI" : "قايتا نىشانلاش URI",
+ "Client Identifier" : "Client Identifier",
+ "Secret key" : "مەخپىي ئاچقۇچ",
+ "Delete client" : "خېرىدارنى ئۆچۈرۈڭ",
+ "Make sure you store the secret key, it cannot be recovered." : "مەخپىي ئاچقۇچنى ساقلىغانلىقىڭىزنى جەزملەشتۈرۈڭ ، ئەسلىگە كەلتۈرگىلى بولمايدۇ.",
+ "Add client" : "خېرىدار قوشۇڭ",
+ "Add" : "قوش",
+ "Show client secret" : "خېرىدارلارنىڭ مەخپىيىتىنى كۆرسىتىڭ",
+ "Hide client secret" : "خېرىدارلارنىڭ مەخپىيىتىنى يوشۇرۇش",
+ "Delete" : "ئۆچۈر"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/uk.js b/apps/oauth2/l10n/uk.js
new file mode 100644
index 00000000000..8c3b6433b20
--- /dev/null
+++ b/apps/oauth2/l10n/uk.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Ваш клієнт не авторизований для підключення. Будь ласка, повідомте адміністратора вашого клієнта.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Ваша URL-адреса переспрямування має бути повною URL-адресою, наприклад: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Дозволяє авторизацію OAuth2, яка підтримується іншими вебзастосунками.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Застосунок OAuth2 дозволяє адміністраторам налаштувати вбудований процес авторизації, який дозволить використовувати сумісний з OAuth2 метод авторизації для сторонніз вебзастосунків.",
+ "OAuth 2.0 clients" : "Клієнти OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 дозволяє зовнішнім службам запитувати доступ до {instanceName}.",
+ "Name" : "Ім'я",
+ "Redirection URI" : "URI перенаправлення",
+ "Client Identifier" : "Ідентифікатор клієнта",
+ "Secret key" : "Пароль",
+ "Delete client" : "Вилучити клієнта",
+ "Make sure you store the secret key, it cannot be recovered." : "Обов'язково збережіть секретний ключ, його неможливо відновити.",
+ "Add client" : "Додати клієнта",
+ "Add" : "Додати",
+ "Show client secret" : "Показати ключ клієнта",
+ "Hide client secret" : "Приховати ключ клієнта",
+ "Delete" : "Вилучити"
+},
+"nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);");
diff --git a/apps/oauth2/l10n/uk.json b/apps/oauth2/l10n/uk.json
new file mode 100644
index 00000000000..ad7906e1795
--- /dev/null
+++ b/apps/oauth2/l10n/uk.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Ваш клієнт не авторизований для підключення. Будь ласка, повідомте адміністратора вашого клієнта.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "Ваша URL-адреса переспрямування має бути повною URL-адресою, наприклад: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Дозволяє авторизацію OAuth2, яка підтримується іншими вебзастосунками.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Застосунок OAuth2 дозволяє адміністраторам налаштувати вбудований процес авторизації, який дозволить використовувати сумісний з OAuth2 метод авторизації для сторонніз вебзастосунків.",
+ "OAuth 2.0 clients" : "Клієнти OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 дозволяє зовнішнім службам запитувати доступ до {instanceName}.",
+ "Name" : "Ім'я",
+ "Redirection URI" : "URI перенаправлення",
+ "Client Identifier" : "Ідентифікатор клієнта",
+ "Secret key" : "Пароль",
+ "Delete client" : "Вилучити клієнта",
+ "Make sure you store the secret key, it cannot be recovered." : "Обов'язково збережіть секретний ключ, його неможливо відновити.",
+ "Add client" : "Додати клієнта",
+ "Add" : "Додати",
+ "Show client secret" : "Показати ключ клієнта",
+ "Hide client secret" : "Приховати ключ клієнта",
+ "Delete" : "Вилучити"
+},"pluralForm" :"nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/vi.js b/apps/oauth2/l10n/vi.js
new file mode 100644
index 00000000000..3800d4efd5f
--- /dev/null
+++ b/apps/oauth2/l10n/vi.js
@@ -0,0 +1,19 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Ứng dụng khách của bạn không được ủy quyền để kết nối. Vui lòng thông báo cho quản trị viên của ứng dụng khách.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "URl chuyển hướng của bạn phải là URL đầy đủ, vd: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Cho phép xác thực tương thích với OAuth2 từ các ứng dụng web khác.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Ứng dụng OAuth2 cho phép các quản trị viên thiết lập dòng chảy công việc xác thực để cũng cho phép xác thực tương thích với OAuth2 từ các ứng dụng web khác.",
+ "OAuth 2.0 clients" : "kết nối OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 cho phép các dịch vụ ở ngoài yêu cầu quyền truy cập vào {instanceName}.",
+ "Name" : "Tên",
+ "Redirection URI" : "Liên kết chuyển tiếp",
+ "Client Identifier" : "Mã định danh ứng dụng khách",
+ "Add client" : "Thêm kết nối",
+ "Add" : "Thêm",
+ "Show client secret" : "Hiện bí mật ứng dụng khách",
+ "Delete" : "Xóa"
+},
+"nplurals=1; plural=0;");
diff --git a/apps/oauth2/l10n/vi.json b/apps/oauth2/l10n/vi.json
new file mode 100644
index 00000000000..c845ec0e30d
--- /dev/null
+++ b/apps/oauth2/l10n/vi.json
@@ -0,0 +1,17 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "Ứng dụng khách của bạn không được ủy quyền để kết nối. Vui lòng thông báo cho quản trị viên của ứng dụng khách.",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "URl chuyển hướng của bạn phải là URL đầy đủ, vd: https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "Cho phép xác thực tương thích với OAuth2 từ các ứng dụng web khác.",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "Ứng dụng OAuth2 cho phép các quản trị viên thiết lập dòng chảy công việc xác thực để cũng cho phép xác thực tương thích với OAuth2 từ các ứng dụng web khác.",
+ "OAuth 2.0 clients" : "kết nối OAuth 2.0",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 cho phép các dịch vụ ở ngoài yêu cầu quyền truy cập vào {instanceName}.",
+ "Name" : "Tên",
+ "Redirection URI" : "Liên kết chuyển tiếp",
+ "Client Identifier" : "Mã định danh ứng dụng khách",
+ "Add client" : "Thêm kết nối",
+ "Add" : "Thêm",
+ "Show client secret" : "Hiện bí mật ứng dụng khách",
+ "Delete" : "Xóa"
+},"pluralForm" :"nplurals=1; plural=0;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/zh_CN.js b/apps/oauth2/l10n/zh_CN.js
new file mode 100644
index 00000000000..5f2ee894089
--- /dev/null
+++ b/apps/oauth2/l10n/zh_CN.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "您的客户端程序未获得连接授权。请联系您客户端程序的管理员",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "您的跳转网址应为完整网址,例如:\nhttps://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "允许其它网站应用的 OAuth2 兼容验证。",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 应用允许管理员配置内置的验证工作流,同时允许其它网站应用的OAuth2 兼容验证。",
+ "OAuth 2.0 clients" : "OAuth 2.0 客户端",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 允许外部服务请求访问 {instanceName}。",
+ "Name" : "名称",
+ "Redirection URI" : "跳转URI",
+ "Client Identifier" : "客户端标识",
+ "Secret key" : "密钥",
+ "Delete client" : "删除客户端",
+ "Make sure you store the secret key, it cannot be recovered." : "请确保您存储了密钥,它无法恢复。",
+ "Add client" : "添加客户端",
+ "Add" : "添加",
+ "Show client secret" : "显示客户端密钥",
+ "Hide client secret" : "隐藏客户端密钥",
+ "Delete" : "删除"
+},
+"nplurals=1; plural=0;");
diff --git a/apps/oauth2/l10n/zh_CN.json b/apps/oauth2/l10n/zh_CN.json
new file mode 100644
index 00000000000..4b1c4270e48
--- /dev/null
+++ b/apps/oauth2/l10n/zh_CN.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "您的客户端程序未获得连接授权。请联系您客户端程序的管理员",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "您的跳转网址应为完整网址,例如:\nhttps://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "允许其它网站应用的 OAuth2 兼容验证。",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 应用允许管理员配置内置的验证工作流,同时允许其它网站应用的OAuth2 兼容验证。",
+ "OAuth 2.0 clients" : "OAuth 2.0 客户端",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 允许外部服务请求访问 {instanceName}。",
+ "Name" : "名称",
+ "Redirection URI" : "跳转URI",
+ "Client Identifier" : "客户端标识",
+ "Secret key" : "密钥",
+ "Delete client" : "删除客户端",
+ "Make sure you store the secret key, it cannot be recovered." : "请确保您存储了密钥,它无法恢复。",
+ "Add client" : "添加客户端",
+ "Add" : "添加",
+ "Show client secret" : "显示客户端密钥",
+ "Hide client secret" : "隐藏客户端密钥",
+ "Delete" : "删除"
+},"pluralForm" :"nplurals=1; plural=0;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/zh_HK.js b/apps/oauth2/l10n/zh_HK.js
new file mode 100644
index 00000000000..727019e492c
--- /dev/null
+++ b/apps/oauth2/l10n/zh_HK.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "此客戶端無權連結,請通知此客戶端的管理員。",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "重導向 URL 必須為完整的 URL,如範例所示:https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "允許相容於Oauth2的外部網頁應用程式驗證。",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 APPs允許管理員為其他支援OAuth2的網頁應用程式啟用原生的驗證流程。",
+ "OAuth 2.0 clients" : "OAuth 2.0 客戶端",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 允許外部服務要求存取 {instanceName}。",
+ "Name" : "名稱",
+ "Redirection URI" : "重導向 URI",
+ "Client Identifier" : "用戶識別",
+ "Secret key" : "私密密鑰",
+ "Delete client" : "刪除客戶端",
+ "Make sure you store the secret key, it cannot be recovered." : "請確保妥善保存密鑰,因為它無法復得。",
+ "Add client" : "新增第三方應用程式",
+ "Add" : "新增",
+ "Show client secret" : "顯示客戶端密碼",
+ "Hide client secret" : "隱藏客戶端密碼",
+ "Delete" : "刪除"
+},
+"nplurals=1; plural=0;");
diff --git a/apps/oauth2/l10n/zh_HK.json b/apps/oauth2/l10n/zh_HK.json
new file mode 100644
index 00000000000..1773d8ec559
--- /dev/null
+++ b/apps/oauth2/l10n/zh_HK.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "此客戶端無權連結,請通知此客戶端的管理員。",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "重導向 URL 必須為完整的 URL,如範例所示:https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "允許相容於Oauth2的外部網頁應用程式驗證。",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 APPs允許管理員為其他支援OAuth2的網頁應用程式啟用原生的驗證流程。",
+ "OAuth 2.0 clients" : "OAuth 2.0 客戶端",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 允許外部服務要求存取 {instanceName}。",
+ "Name" : "名稱",
+ "Redirection URI" : "重導向 URI",
+ "Client Identifier" : "用戶識別",
+ "Secret key" : "私密密鑰",
+ "Delete client" : "刪除客戶端",
+ "Make sure you store the secret key, it cannot be recovered." : "請確保妥善保存密鑰,因為它無法復得。",
+ "Add client" : "新增第三方應用程式",
+ "Add" : "新增",
+ "Show client secret" : "顯示客戶端密碼",
+ "Hide client secret" : "隱藏客戶端密碼",
+ "Delete" : "刪除"
+},"pluralForm" :"nplurals=1; plural=0;"
+} \ No newline at end of file
diff --git a/apps/oauth2/l10n/zh_TW.js b/apps/oauth2/l10n/zh_TW.js
new file mode 100644
index 00000000000..e61fe31d36b
--- /dev/null
+++ b/apps/oauth2/l10n/zh_TW.js
@@ -0,0 +1,23 @@
+OC.L10N.register(
+ "oauth2",
+ {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "您的客戶端無連線授權。請通知您客戶端的管理員。",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "您的重新導向 URL 必須為完整的 URL,例如:https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "允許來自其他網路應用程式的 OAuth2 相容身份認證。",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 應用程式讓管理員可以設定內建的身份認證工作流程,以讓其他網路應用程式可以進行相容於 OAuth2 的身份認證。",
+ "OAuth 2.0 clients" : "OAuth 2.0 客戶端",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 讓外部服務請求能存取 {instanceName}。",
+ "Name" : "名稱",
+ "Redirection URI" : "重新導向 URI",
+ "Client Identifier" : "客戶端識別",
+ "Secret key" : "私密金鑰",
+ "Delete client" : "刪除客戶端",
+ "Make sure you store the secret key, it cannot be recovered." : "請確定您儲存了私鑰,其無法還原。",
+ "Add client" : "新增客戶端",
+ "Add" : "新增",
+ "Show client secret" : "顯示客戶端密碼",
+ "Hide client secret" : "隱藏客戶端密碼",
+ "Delete" : "刪除"
+},
+"nplurals=1; plural=0;");
diff --git a/apps/oauth2/l10n/zh_TW.json b/apps/oauth2/l10n/zh_TW.json
new file mode 100644
index 00000000000..ba0a879f152
--- /dev/null
+++ b/apps/oauth2/l10n/zh_TW.json
@@ -0,0 +1,21 @@
+{ "translations": {
+ "Your client is not authorized to connect. Please inform the administrator of your client." : "您的客戶端無連線授權。請通知您客戶端的管理員。",
+ "Your redirect URL needs to be a full URL for example: https://yourdomain.com/path" : "您的重新導向 URL 必須為完整的 URL,例如:https://yourdomain.com/path",
+ "OAuth 2.0" : "OAuth 2.0",
+ "Allows OAuth2 compatible authentication from other web applications." : "允許來自其他網路應用程式的 OAuth2 相容身份認證。",
+ "The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications." : "OAuth2 應用程式讓管理員可以設定內建的身份認證工作流程,以讓其他網路應用程式可以進行相容於 OAuth2 的身份認證。",
+ "OAuth 2.0 clients" : "OAuth 2.0 客戶端",
+ "OAuth 2.0 allows external services to request access to {instanceName}." : "OAuth 2.0 讓外部服務請求能存取 {instanceName}。",
+ "Name" : "名稱",
+ "Redirection URI" : "重新導向 URI",
+ "Client Identifier" : "客戶端識別",
+ "Secret key" : "私密金鑰",
+ "Delete client" : "刪除客戶端",
+ "Make sure you store the secret key, it cannot be recovered." : "請確定您儲存了私鑰,其無法還原。",
+ "Add client" : "新增客戶端",
+ "Add" : "新增",
+ "Show client secret" : "顯示客戶端密碼",
+ "Hide client secret" : "隱藏客戶端密碼",
+ "Delete" : "刪除"
+},"pluralForm" :"nplurals=1; plural=0;"
+} \ No newline at end of file
diff --git a/apps/oauth2/lib/BackgroundJob/CleanupExpiredAuthorizationCode.php b/apps/oauth2/lib/BackgroundJob/CleanupExpiredAuthorizationCode.php
new file mode 100644
index 00000000000..b819a45ace2
--- /dev/null
+++ b/apps/oauth2/lib/BackgroundJob/CleanupExpiredAuthorizationCode.php
@@ -0,0 +1,43 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+
+namespace OCA\OAuth2\BackgroundJob;
+
+use OCA\OAuth2\Db\AccessTokenMapper;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\TimedJob;
+use OCP\DB\Exception;
+use Psr\Log\LoggerInterface;
+
+class CleanupExpiredAuthorizationCode extends TimedJob {
+
+ public function __construct(
+ ITimeFactory $timeFactory,
+ private AccessTokenMapper $accessTokenMapper,
+ private LoggerInterface $logger,
+ ) {
+ parent::__construct($timeFactory);
+ // 30 days
+ $this->setInterval(60 * 60 * 24 * 30);
+ $this->setTimeSensitivity(self::TIME_INSENSITIVE);
+ }
+
+ /**
+ * @param mixed $argument
+ * @inheritDoc
+ */
+ protected function run($argument): void {
+ try {
+ $this->accessTokenMapper->cleanupExpiredAuthorizationCode();
+ } catch (Exception $e) {
+ $this->logger->warning('Failed to cleanup tokens with expired authorization code', ['exception' => $e]);
+ }
+ }
+}
diff --git a/apps/oauth2/lib/Command/ImportLegacyOcClient.php b/apps/oauth2/lib/Command/ImportLegacyOcClient.php
new file mode 100644
index 00000000000..acdc57cf991
--- /dev/null
+++ b/apps/oauth2/lib/Command/ImportLegacyOcClient.php
@@ -0,0 +1,76 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\OAuth2\Command;
+
+use OCA\OAuth2\Db\Client;
+use OCA\OAuth2\Db\ClientMapper;
+use OCP\IConfig;
+use OCP\Security\ICrypto;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ImportLegacyOcClient extends Command {
+ private const ARGUMENT_CLIENT_ID = 'client-id';
+ private const ARGUMENT_CLIENT_SECRET = 'client-secret';
+
+ public function __construct(
+ private readonly IConfig $config,
+ private readonly ICrypto $crypto,
+ private readonly ClientMapper $clientMapper,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ $this->setName('oauth2:import-legacy-oc-client');
+ $this->setDescription('This command is only required to be run on instances which were migrated from ownCloud without the oauth2.enable_oc_clients system config! Import a legacy Oauth2 client from an ownCloud instance and migrate it. The data is expected to be straight out of the database table oc_oauth2_clients.');
+ $this->addArgument(
+ self::ARGUMENT_CLIENT_ID,
+ InputArgument::REQUIRED,
+ 'Value of the "identifier" column',
+ );
+ $this->addArgument(
+ self::ARGUMENT_CLIENT_SECRET,
+ InputArgument::REQUIRED,
+ 'Value of the "secret" column',
+ );
+ }
+
+ public function isEnabled(): bool {
+ return $this->config->getSystemValueBool('oauth2.enable_oc_clients', false);
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ /** @var string $clientId */
+ $clientId = $input->getArgument(self::ARGUMENT_CLIENT_ID);
+
+ /** @var string $clientSecret */
+ $clientSecret = $input->getArgument(self::ARGUMENT_CLIENT_SECRET);
+
+ // Should not happen but just to be sure
+ if (empty($clientId) || empty($clientSecret)) {
+ return 1;
+ }
+
+ $hashedClientSecret = bin2hex($this->crypto->calculateHMAC($clientSecret));
+
+ $client = new Client();
+ $client->setName('ownCloud Desktop Client');
+ $client->setRedirectUri('http://localhost:*');
+ $client->setClientIdentifier($clientId);
+ $client->setSecret($hashedClientSecret);
+ $this->clientMapper->insert($client);
+
+ $output->writeln('<info>Client imported successfully</info>');
+ return 0;
+ }
+}
diff --git a/apps/oauth2/lib/Controller/LoginRedirectorController.php b/apps/oauth2/lib/Controller/LoginRedirectorController.php
new file mode 100644
index 00000000000..7241b35cdcf
--- /dev/null
+++ b/apps/oauth2/lib/Controller/LoginRedirectorController.php
@@ -0,0 +1,123 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Controller;
+
+use OC\Core\Controller\ClientFlowLoginController;
+use OCA\OAuth2\Db\ClientMapper;
+use OCA\OAuth2\Exceptions\ClientNotFoundException;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
+use OCP\AppFramework\Http\Attribute\OpenAPI;
+use OCP\AppFramework\Http\Attribute\PublicPage;
+use OCP\AppFramework\Http\Attribute\UseSession;
+use OCP\AppFramework\Http\RedirectResponse;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\IAppConfig;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IRequest;
+use OCP\ISession;
+use OCP\IURLGenerator;
+use OCP\Security\ISecureRandom;
+
+#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)]
+class LoginRedirectorController extends Controller {
+ /**
+ * @param string $appName
+ * @param IRequest $request
+ * @param IURLGenerator $urlGenerator
+ * @param ClientMapper $clientMapper
+ * @param ISession $session
+ * @param IL10N $l
+ */
+ public function __construct(
+ string $appName,
+ IRequest $request,
+ private IURLGenerator $urlGenerator,
+ private ClientMapper $clientMapper,
+ private ISession $session,
+ private IL10N $l,
+ private ISecureRandom $random,
+ private IAppConfig $appConfig,
+ private IConfig $config,
+ ) {
+ parent::__construct($appName, $request);
+ }
+
+ /**
+ * Authorize the user
+ *
+ * @param string $client_id Client ID
+ * @param string $state State of the flow
+ * @param string $response_type Response type for the flow
+ * @param string $redirect_uri URI to redirect to after the flow (is only used for legacy ownCloud clients)
+ * @return TemplateResponse<Http::STATUS_OK, array{}>|RedirectResponse<Http::STATUS_SEE_OTHER, array{}>
+ *
+ * 200: Client not found
+ * 303: Redirect to login URL
+ */
+ #[PublicPage]
+ #[NoCSRFRequired]
+ #[UseSession]
+ public function authorize($client_id,
+ $state,
+ $response_type,
+ string $redirect_uri = ''): TemplateResponse|RedirectResponse {
+ try {
+ $client = $this->clientMapper->getByIdentifier($client_id);
+ } catch (ClientNotFoundException $e) {
+ $params = [
+ 'content' => $this->l->t('Your client is not authorized to connect. Please inform the administrator of your client.'),
+ ];
+ return new TemplateResponse('core', '404', $params, 'guest');
+ }
+
+ if ($response_type !== 'code') {
+ //Fail
+ $url = $client->getRedirectUri() . '?error=unsupported_response_type&state=' . $state;
+ return new RedirectResponse($url);
+ }
+
+ $enableOcClients = $this->config->getSystemValueBool('oauth2.enable_oc_clients', false);
+
+ $providedRedirectUri = '';
+ if ($enableOcClients && $client->getRedirectUri() === 'http://localhost:*') {
+ $providedRedirectUri = $redirect_uri;
+ }
+
+ $this->session->set('oauth.state', $state);
+
+ if (in_array($client->getName(), $this->appConfig->getValueArray('oauth2', 'skipAuthPickerApplications', []))) {
+ /** @see ClientFlowLoginController::showAuthPickerPage **/
+ $stateToken = $this->random->generate(
+ 64,
+ ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS
+ );
+ $this->session->set(ClientFlowLoginController::STATE_NAME, $stateToken);
+ $targetUrl = $this->urlGenerator->linkToRouteAbsolute(
+ 'core.ClientFlowLogin.grantPage',
+ [
+ 'stateToken' => $stateToken,
+ 'clientIdentifier' => $client->getClientIdentifier(),
+ 'providedRedirectUri' => $providedRedirectUri,
+ ]
+ );
+ } else {
+ $targetUrl = $this->urlGenerator->linkToRouteAbsolute(
+ 'core.ClientFlowLogin.showAuthPickerPage',
+ [
+ 'clientIdentifier' => $client->getClientIdentifier(),
+ 'providedRedirectUri' => $providedRedirectUri,
+ ]
+ );
+ }
+ return new RedirectResponse($targetUrl);
+ }
+}
diff --git a/apps/oauth2/lib/Controller/OauthApiController.php b/apps/oauth2/lib/Controller/OauthApiController.php
new file mode 100644
index 00000000000..11f17fda4bf
--- /dev/null
+++ b/apps/oauth2/lib/Controller/OauthApiController.php
@@ -0,0 +1,214 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Controller;
+
+use OC\Authentication\Token\IProvider as TokenProvider;
+use OCA\OAuth2\Db\AccessTokenMapper;
+use OCA\OAuth2\Db\ClientMapper;
+use OCA\OAuth2\Exceptions\AccessTokenNotFoundException;
+use OCA\OAuth2\Exceptions\ClientNotFoundException;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\Attribute\BruteForceProtection;
+use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
+use OCP\AppFramework\Http\Attribute\OpenAPI;
+use OCP\AppFramework\Http\Attribute\PublicPage;
+use OCP\AppFramework\Http\JSONResponse;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Authentication\Exceptions\ExpiredTokenException;
+use OCP\Authentication\Exceptions\InvalidTokenException;
+use OCP\DB\Exception;
+use OCP\IRequest;
+use OCP\Security\Bruteforce\IThrottler;
+use OCP\Security\ICrypto;
+use OCP\Security\ISecureRandom;
+use Psr\Log\LoggerInterface;
+
+#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)]
+class OauthApiController extends Controller {
+ // the authorization code expires after 10 minutes
+ public const AUTHORIZATION_CODE_EXPIRES_AFTER = 10 * 60;
+
+ public function __construct(
+ string $appName,
+ IRequest $request,
+ private ICrypto $crypto,
+ private AccessTokenMapper $accessTokenMapper,
+ private ClientMapper $clientMapper,
+ private TokenProvider $tokenProvider,
+ private ISecureRandom $secureRandom,
+ private ITimeFactory $time,
+ private LoggerInterface $logger,
+ private IThrottler $throttler,
+ private ITimeFactory $timeFactory,
+ ) {
+ parent::__construct($appName, $request);
+ }
+
+ /**
+ * Get a token
+ *
+ * @param string $grant_type Token type that should be granted
+ * @param ?string $code Code of the flow
+ * @param ?string $refresh_token Refresh token
+ * @param ?string $client_id Client ID
+ * @param ?string $client_secret Client secret
+ * @throws Exception
+ * @return JSONResponse<Http::STATUS_OK, array{access_token: string, token_type: string, expires_in: int, refresh_token: string, user_id: string}, array{}>|JSONResponse<Http::STATUS_BAD_REQUEST, array{error: string}, array{}>
+ *
+ * 200: Token returned
+ * 400: Getting token is not possible
+ */
+ #[PublicPage]
+ #[NoCSRFRequired]
+ #[BruteForceProtection(action: 'oauth2GetToken')]
+ public function getToken(
+ string $grant_type, ?string $code, ?string $refresh_token,
+ ?string $client_id, ?string $client_secret,
+ ): JSONResponse {
+
+ // We only handle two types
+ if ($grant_type !== 'authorization_code' && $grant_type !== 'refresh_token') {
+ $response = new JSONResponse([
+ 'error' => 'invalid_grant',
+ ], Http::STATUS_BAD_REQUEST);
+ $response->throttle(['invalid_grant' => $grant_type]);
+ return $response;
+ }
+
+ // We handle the initial and refresh tokens the same way
+ if ($grant_type === 'refresh_token') {
+ $code = $refresh_token;
+ }
+
+ try {
+ $accessToken = $this->accessTokenMapper->getByCode($code);
+ } catch (AccessTokenNotFoundException $e) {
+ $response = new JSONResponse([
+ 'error' => 'invalid_request',
+ ], Http::STATUS_BAD_REQUEST);
+ $response->throttle(['invalid_request' => 'token not found', 'code' => $code]);
+ return $response;
+ }
+
+ if ($grant_type === 'authorization_code') {
+ // check this token is in authorization code state
+ $deliveredTokenCount = $accessToken->getTokenCount();
+ if ($deliveredTokenCount > 0) {
+ $response = new JSONResponse([
+ 'error' => 'invalid_request',
+ ], Http::STATUS_BAD_REQUEST);
+ $response->throttle(['invalid_request' => 'authorization_code_received_for_active_token']);
+ return $response;
+ }
+
+ // check authorization code expiration
+ $now = $this->timeFactory->now()->getTimestamp();
+ $codeCreatedAt = $accessToken->getCodeCreatedAt();
+ if ($codeCreatedAt < $now - self::AUTHORIZATION_CODE_EXPIRES_AFTER) {
+ // we know this token is not useful anymore
+ $this->accessTokenMapper->delete($accessToken);
+
+ $response = new JSONResponse([
+ 'error' => 'invalid_request',
+ ], Http::STATUS_BAD_REQUEST);
+ $expiredSince = $now - self::AUTHORIZATION_CODE_EXPIRES_AFTER - $codeCreatedAt;
+ $response->throttle(['invalid_request' => 'authorization_code_expired', 'expired_since' => $expiredSince]);
+ return $response;
+ }
+ }
+
+ try {
+ $client = $this->clientMapper->getByUid($accessToken->getClientId());
+ } catch (ClientNotFoundException $e) {
+ $response = new JSONResponse([
+ 'error' => 'invalid_request',
+ ], Http::STATUS_BAD_REQUEST);
+ $response->throttle(['invalid_request' => 'client not found', 'client_id' => $accessToken->getClientId()]);
+ return $response;
+ }
+
+ if (isset($this->request->server['PHP_AUTH_USER'])) {
+ $client_id = $this->request->server['PHP_AUTH_USER'];
+ $client_secret = $this->request->server['PHP_AUTH_PW'];
+ }
+
+ try {
+ $storedClientSecretHash = $client->getSecret();
+ $clientSecretHash = bin2hex($this->crypto->calculateHMAC($client_secret));
+ } catch (\Exception $e) {
+ $this->logger->error('OAuth client secret decryption error', ['exception' => $e]);
+ // we don't throttle here because it might not be a bruteforce attack
+ return new JSONResponse([
+ 'error' => 'invalid_client',
+ ], Http::STATUS_BAD_REQUEST);
+ }
+ // The client id and secret must match. Else we don't provide an access token!
+ if ($client->getClientIdentifier() !== $client_id || $storedClientSecretHash !== $clientSecretHash) {
+ $response = new JSONResponse([
+ 'error' => 'invalid_client',
+ ], Http::STATUS_BAD_REQUEST);
+ $response->throttle(['invalid_client' => 'client ID or secret does not match']);
+ return $response;
+ }
+
+ $decryptedToken = $this->crypto->decrypt($accessToken->getEncryptedToken(), $code);
+
+ // Obtain the appToken associated
+ try {
+ $appToken = $this->tokenProvider->getTokenById($accessToken->getTokenId());
+ } catch (ExpiredTokenException $e) {
+ $appToken = $e->getToken();
+ } catch (InvalidTokenException $e) {
+ //We can't do anything...
+ $this->accessTokenMapper->delete($accessToken);
+ $response = new JSONResponse([
+ 'error' => 'invalid_request',
+ ], Http::STATUS_BAD_REQUEST);
+ $response->throttle(['invalid_request' => 'token is invalid']);
+ return $response;
+ }
+
+ // Rotate the apptoken (so the old one becomes invalid basically)
+ $newToken = $this->secureRandom->generate(72, ISecureRandom::CHAR_ALPHANUMERIC);
+
+ $appToken = $this->tokenProvider->rotate(
+ $appToken,
+ $decryptedToken,
+ $newToken
+ );
+
+ // Expiration is in 1 hour again
+ $appToken->setExpires($this->time->getTime() + 3600);
+ $this->tokenProvider->updateToken($appToken);
+
+ // Generate a new refresh token and encrypt the new apptoken in the DB
+ $newCode = $this->secureRandom->generate(128, ISecureRandom::CHAR_ALPHANUMERIC);
+ $accessToken->setHashedCode(hash('sha512', $newCode));
+ $accessToken->setEncryptedToken($this->crypto->encrypt($newToken, $newCode));
+ // increase the number of delivered oauth token
+ // this helps with cleaning up DB access token when authorization code has expired
+ // and it never delivered any oauth token
+ $tokenCount = $accessToken->getTokenCount();
+ $accessToken->setTokenCount($tokenCount + 1);
+ $this->accessTokenMapper->update($accessToken);
+
+ $this->throttler->resetDelay($this->request->getRemoteAddress(), 'login', ['user' => $appToken->getUID()]);
+
+ return new JSONResponse(
+ [
+ 'access_token' => $newToken,
+ 'token_type' => 'Bearer',
+ 'expires_in' => 3600,
+ 'refresh_token' => $newCode,
+ 'user_id' => $appToken->getUID(),
+ ]
+ );
+ }
+}
diff --git a/apps/oauth2/lib/Controller/SettingsController.php b/apps/oauth2/lib/Controller/SettingsController.php
new file mode 100644
index 00000000000..9bd02c8a2cd
--- /dev/null
+++ b/apps/oauth2/lib/Controller/SettingsController.php
@@ -0,0 +1,80 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Controller;
+
+use OCA\OAuth2\Db\AccessTokenMapper;
+use OCA\OAuth2\Db\Client;
+use OCA\OAuth2\Db\ClientMapper;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\JSONResponse;
+use OCP\Authentication\Token\IProvider as IAuthTokenProvider;
+use OCP\IL10N;
+use OCP\IRequest;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\Security\ICrypto;
+use OCP\Security\ISecureRandom;
+
+class SettingsController extends Controller {
+
+ public const validChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+
+ public function __construct(
+ string $appName,
+ IRequest $request,
+ private ClientMapper $clientMapper,
+ private ISecureRandom $secureRandom,
+ private AccessTokenMapper $accessTokenMapper,
+ private IL10N $l,
+ private IAuthTokenProvider $tokenProvider,
+ private IUserManager $userManager,
+ private ICrypto $crypto,
+ ) {
+ parent::__construct($appName, $request);
+ }
+
+ public function addClient(string $name,
+ string $redirectUri): JSONResponse {
+ if (filter_var($redirectUri, FILTER_VALIDATE_URL) === false) {
+ return new JSONResponse(['message' => $this->l->t('Your redirect URL needs to be a full URL for example: https://yourdomain.com/path')], Http::STATUS_BAD_REQUEST);
+ }
+
+ $client = new Client();
+ $client->setName($name);
+ $client->setRedirectUri($redirectUri);
+ $secret = $this->secureRandom->generate(64, self::validChars);
+ $hashedSecret = bin2hex($this->crypto->calculateHMAC($secret));
+ $client->setSecret($hashedSecret);
+ $client->setClientIdentifier($this->secureRandom->generate(64, self::validChars));
+ $client = $this->clientMapper->insert($client);
+
+ $result = [
+ 'id' => $client->getId(),
+ 'name' => $client->getName(),
+ 'redirectUri' => $client->getRedirectUri(),
+ 'clientId' => $client->getClientIdentifier(),
+ 'clientSecret' => $secret,
+ ];
+
+ return new JSONResponse($result);
+ }
+
+ public function deleteClient(int $id): JSONResponse {
+ $client = $this->clientMapper->getByUid($id);
+
+ $this->userManager->callForSeenUsers(function (IUser $user) use ($client): void {
+ $this->tokenProvider->invalidateTokensOfUser($user->getUID(), $client->getName());
+ });
+
+ $this->accessTokenMapper->deleteByClientId($id);
+ $this->clientMapper->delete($client);
+ return new JSONResponse([]);
+ }
+}
diff --git a/apps/oauth2/lib/Db/AccessToken.php b/apps/oauth2/lib/Db/AccessToken.php
new file mode 100644
index 00000000000..34adc4f4797
--- /dev/null
+++ b/apps/oauth2/lib/Db/AccessToken.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Db;
+
+use OCP\AppFramework\Db\Entity;
+use OCP\DB\Types;
+
+/**
+ * @method int getTokenId()
+ * @method void setTokenId(int $identifier)
+ * @method int getClientId()
+ * @method void setClientId(int $identifier)
+ * @method string getEncryptedToken()
+ * @method void setEncryptedToken(string $token)
+ * @method string getHashedCode()
+ * @method void setHashedCode(string $token)
+ * @method int getCodeCreatedAt()
+ * @method void setCodeCreatedAt(int $createdAt)
+ * @method int getTokenCount()
+ * @method void setTokenCount(int $tokenCount)
+ */
+class AccessToken extends Entity {
+ /** @var int */
+ protected $tokenId;
+ /** @var int */
+ protected $clientId;
+ /** @var string */
+ protected $hashedCode;
+ /** @var string */
+ protected $encryptedToken;
+ /** @var int */
+ protected $codeCreatedAt;
+ /** @var int */
+ protected $tokenCount;
+
+ public function __construct() {
+ $this->addType('id', Types::INTEGER);
+ $this->addType('tokenId', Types::INTEGER);
+ $this->addType('clientId', Types::INTEGER);
+ $this->addType('hashedCode', 'string');
+ $this->addType('encryptedToken', 'string');
+ $this->addType('codeCreatedAt', Types::INTEGER);
+ $this->addType('tokenCount', Types::INTEGER);
+ }
+}
diff --git a/apps/oauth2/lib/Db/AccessTokenMapper.php b/apps/oauth2/lib/Db/AccessTokenMapper.php
new file mode 100644
index 00000000000..8d5f6cf1da1
--- /dev/null
+++ b/apps/oauth2/lib/Db/AccessTokenMapper.php
@@ -0,0 +1,85 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Db;
+
+use OCA\OAuth2\Controller\OauthApiController;
+use OCA\OAuth2\Exceptions\AccessTokenNotFoundException;
+use OCP\AppFramework\Db\IMapperException;
+use OCP\AppFramework\Db\QBMapper;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\DB\Exception;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+
+/**
+ * @template-extends QBMapper<AccessToken>
+ */
+class AccessTokenMapper extends QBMapper {
+
+ public function __construct(
+ IDBConnection $db,
+ private ITimeFactory $timeFactory,
+ ) {
+ parent::__construct($db, 'oauth2_access_tokens');
+ }
+
+ /**
+ * @param string $code
+ * @return AccessToken
+ * @throws AccessTokenNotFoundException
+ */
+ public function getByCode(string $code): AccessToken {
+ $qb = $this->db->getQueryBuilder();
+ $qb
+ ->select('*')
+ ->from($this->tableName)
+ ->where($qb->expr()->eq('hashed_code', $qb->createNamedParameter(hash('sha512', $code))));
+
+ try {
+ $token = $this->findEntity($qb);
+ } catch (IMapperException $e) {
+ throw new AccessTokenNotFoundException('Could not find access token', 0, $e);
+ }
+
+ return $token;
+ }
+
+ /**
+ * delete all access token from a given client
+ *
+ * @param int $id
+ */
+ public function deleteByClientId(int $id) {
+ $qb = $this->db->getQueryBuilder();
+ $qb
+ ->delete($this->tableName)
+ ->where($qb->expr()->eq('client_id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
+ $qb->executeStatement();
+ }
+
+ /**
+ * Delete access tokens that have an expired authorization code
+ * -> those that are old enough
+ * and which never delivered any oauth token (still in authorization state)
+ *
+ * @return void
+ * @throws Exception
+ */
+ public function cleanupExpiredAuthorizationCode(): void {
+ $now = $this->timeFactory->now()->getTimestamp();
+ $maxTokenCreationTs = $now - OauthApiController::AUTHORIZATION_CODE_EXPIRES_AFTER;
+
+ $qb = $this->db->getQueryBuilder();
+ $qb
+ ->delete($this->tableName)
+ ->where($qb->expr()->eq('token_count', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
+ ->andWhere($qb->expr()->lt('code_created_at', $qb->createNamedParameter($maxTokenCreationTs, IQueryBuilder::PARAM_INT)));
+ $qb->executeStatement();
+ }
+}
diff --git a/apps/oauth2/lib/Db/Client.php b/apps/oauth2/lib/Db/Client.php
new file mode 100644
index 00000000000..8fce0040c96
--- /dev/null
+++ b/apps/oauth2/lib/Db/Client.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Db;
+
+use OCP\AppFramework\Db\Entity;
+use OCP\DB\Types;
+
+/**
+ * @method string getClientIdentifier()
+ * @method void setClientIdentifier(string $identifier)
+ * @method string getSecret()
+ * @method void setSecret(string $secret)
+ * @method string getRedirectUri()
+ * @method void setRedirectUri(string $redirectUri)
+ * @method string getName()
+ * @method void setName(string $name)
+ */
+class Client extends Entity {
+ /** @var string */
+ protected $name;
+ /** @var string */
+ protected $redirectUri;
+ /** @var string */
+ protected $clientIdentifier;
+ /** @var string */
+ protected $secret;
+
+ public function __construct() {
+ $this->addType('id', Types::INTEGER);
+ $this->addType('name', 'string');
+ $this->addType('redirectUri', 'string');
+ $this->addType('clientIdentifier', 'string');
+ $this->addType('secret', 'string');
+ }
+}
diff --git a/apps/oauth2/lib/Db/ClientMapper.php b/apps/oauth2/lib/Db/ClientMapper.php
new file mode 100644
index 00000000000..c5ca2989d0f
--- /dev/null
+++ b/apps/oauth2/lib/Db/ClientMapper.php
@@ -0,0 +1,80 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Db;
+
+use OCA\OAuth2\Exceptions\ClientNotFoundException;
+use OCP\AppFramework\Db\IMapperException;
+use OCP\AppFramework\Db\QBMapper;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+
+/**
+ * @template-extends QBMapper<Client>
+ */
+class ClientMapper extends QBMapper {
+
+ /**
+ * @param IDBConnection $db
+ */
+ public function __construct(IDBConnection $db) {
+ parent::__construct($db, 'oauth2_clients');
+ }
+
+ /**
+ * @param string $clientIdentifier
+ * @return Client
+ * @throws ClientNotFoundException
+ */
+ public function getByIdentifier(string $clientIdentifier): Client {
+ $qb = $this->db->getQueryBuilder();
+ $qb
+ ->select('*')
+ ->from($this->tableName)
+ ->where($qb->expr()->eq('client_identifier', $qb->createNamedParameter($clientIdentifier)));
+
+ try {
+ $client = $this->findEntity($qb);
+ } catch (IMapperException $e) {
+ throw new ClientNotFoundException('could not find client ' . $clientIdentifier, 0, $e);
+ }
+ return $client;
+ }
+
+ /**
+ * @param int $id internal id of the client
+ * @return Client
+ * @throws ClientNotFoundException
+ */
+ public function getByUid(int $id): Client {
+ $qb = $this->db->getQueryBuilder();
+ $qb
+ ->select('*')
+ ->from($this->tableName)
+ ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
+
+ try {
+ $client = $this->findEntity($qb);
+ } catch (IMapperException $e) {
+ throw new ClientNotFoundException('could not find client with id ' . $id, 0, $e);
+ }
+ return $client;
+ }
+
+ /**
+ * @return Client[]
+ */
+ public function getClients(): array {
+ $qb = $this->db->getQueryBuilder();
+ $qb
+ ->select('*')
+ ->from($this->tableName);
+
+ return $this->findEntities($qb);
+ }
+}
diff --git a/apps/oauth2/lib/Exceptions/AccessTokenNotFoundException.php b/apps/oauth2/lib/Exceptions/AccessTokenNotFoundException.php
new file mode 100644
index 00000000000..809598e258e
--- /dev/null
+++ b/apps/oauth2/lib/Exceptions/AccessTokenNotFoundException.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Exceptions;
+
+class AccessTokenNotFoundException extends \Exception {
+}
diff --git a/apps/oauth2/lib/Exceptions/ClientNotFoundException.php b/apps/oauth2/lib/Exceptions/ClientNotFoundException.php
new file mode 100644
index 00000000000..cec7a24e22d
--- /dev/null
+++ b/apps/oauth2/lib/Exceptions/ClientNotFoundException.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Exceptions;
+
+class ClientNotFoundException extends \Exception {
+}
diff --git a/apps/oauth2/lib/Migration/SetTokenExpiration.php b/apps/oauth2/lib/Migration/SetTokenExpiration.php
new file mode 100644
index 00000000000..dc925e26bb2
--- /dev/null
+++ b/apps/oauth2/lib/Migration/SetTokenExpiration.php
@@ -0,0 +1,51 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Migration;
+
+use OC\Authentication\Token\IProvider as TokenProvider;
+use OCA\OAuth2\Db\AccessToken;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Authentication\Exceptions\InvalidTokenException;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class SetTokenExpiration implements IRepairStep {
+
+ public function __construct(
+ private IDBConnection $connection,
+ private ITimeFactory $time,
+ private TokenProvider $tokenProvider,
+ ) {
+ }
+
+ public function getName(): string {
+ return 'Update OAuth token expiration times';
+ }
+
+ public function run(IOutput $output) {
+ $qb = $this->connection->getQueryBuilder();
+ $qb->select('*')
+ ->from('oauth2_access_tokens');
+
+ $cursor = $qb->executeQuery();
+
+ while ($row = $cursor->fetch()) {
+ $token = AccessToken::fromRow($row);
+ try {
+ $appToken = $this->tokenProvider->getTokenById($token->getTokenId());
+ $appToken->setExpires($this->time->getTime() + 3600);
+ $this->tokenProvider->updateToken($appToken);
+ } catch (InvalidTokenException $e) {
+ //Skip this token
+ }
+ }
+ $cursor->closeCursor();
+ }
+}
diff --git a/apps/oauth2/lib/Migration/Version010401Date20181207190718.php b/apps/oauth2/lib/Migration/Version010401Date20181207190718.php
new file mode 100644
index 00000000000..8648826d53c
--- /dev/null
+++ b/apps/oauth2/lib/Migration/Version010401Date20181207190718.php
@@ -0,0 +1,82 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version010401Date20181207190718 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ if (!$schema->hasTable('oauth2_clients')) {
+ $table = $schema->createTable('oauth2_clients');
+ $table->addColumn('id', 'integer', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('name', 'string', [
+ 'notnull' => true,
+ 'length' => 64,
+ ]);
+ $table->addColumn('redirect_uri', 'string', [
+ 'notnull' => true,
+ 'length' => 2000,
+ ]);
+ $table->addColumn('client_identifier', 'string', [
+ 'notnull' => true,
+ 'length' => 64,
+ ]);
+ $table->addColumn('secret', 'string', [
+ 'notnull' => true,
+ 'length' => 64,
+ ]);
+ $table->setPrimaryKey(['id']);
+ $table->addIndex(['client_identifier'], 'oauth2_client_id_idx');
+ }
+
+ if (!$schema->hasTable('oauth2_access_tokens')) {
+ $table = $schema->createTable('oauth2_access_tokens');
+ $table->addColumn('id', 'integer', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('token_id', 'integer', [
+ 'notnull' => true,
+ ]);
+ $table->addColumn('client_id', 'integer', [
+ 'notnull' => true,
+ ]);
+ $table->addColumn('hashed_code', 'string', [
+ 'notnull' => true,
+ 'length' => 128,
+ ]);
+ $table->addColumn('encrypted_token', 'string', [
+ 'notnull' => true,
+ 'length' => 786,
+ ]);
+ $table->setPrimaryKey(['id']);
+ $table->addUniqueIndex(['hashed_code'], 'oauth2_access_hash_idx');
+ $table->addIndex(['client_id'], 'oauth2_access_client_id_idx');
+ }
+ return $schema;
+ }
+}
diff --git a/apps/oauth2/lib/Migration/Version010402Date20190107124745.php b/apps/oauth2/lib/Migration/Version010402Date20190107124745.php
new file mode 100644
index 00000000000..08099c625f7
--- /dev/null
+++ b/apps/oauth2/lib/Migration/Version010402Date20190107124745.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version010402Date20190107124745 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ // During an ownCloud migration, the client_identifier column identifier might not exist yet.
+ if ($schema->getTable('oauth2_clients')->hasColumn('client_identifier')) {
+ $table = $schema->getTable('oauth2_clients');
+ $table->dropIndex('oauth2_client_id_idx');
+ $table->addUniqueIndex(['client_identifier'], 'oauth2_client_id_idx');
+ return $schema;
+ }
+ }
+}
diff --git a/apps/oauth2/lib/Migration/Version011601Date20230522143227.php b/apps/oauth2/lib/Migration/Version011601Date20230522143227.php
new file mode 100644
index 00000000000..f2998202e02
--- /dev/null
+++ b/apps/oauth2/lib/Migration/Version011601Date20230522143227.php
@@ -0,0 +1,65 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+use OCP\Security\ICrypto;
+
+class Version011601Date20230522143227 extends SimpleMigrationStep {
+
+ public function __construct(
+ private IDBConnection $connection,
+ private ICrypto $crypto,
+ ) {
+ }
+
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ if ($schema->hasTable('oauth2_clients')) {
+ $table = $schema->getTable('oauth2_clients');
+ if ($table->hasColumn('secret')) {
+ $column = $table->getColumn('secret');
+ $column->setLength(512);
+ return $schema;
+ }
+ }
+
+ return null;
+ }
+
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) {
+ $qbUpdate = $this->connection->getQueryBuilder();
+ $qbUpdate->update('oauth2_clients')
+ ->set('secret', $qbUpdate->createParameter('updateSecret'))
+ ->where(
+ $qbUpdate->expr()->eq('id', $qbUpdate->createParameter('updateId'))
+ );
+
+ $qbSelect = $this->connection->getQueryBuilder();
+ $qbSelect->select('id', 'secret')
+ ->from('oauth2_clients');
+ $req = $qbSelect->executeQuery();
+ while ($row = $req->fetch()) {
+ $id = $row['id'];
+ $secret = $row['secret'];
+ $encryptedSecret = $this->crypto->encrypt($secret);
+ $qbUpdate->setParameter('updateSecret', $encryptedSecret, IQueryBuilder::PARAM_STR);
+ $qbUpdate->setParameter('updateId', $id, IQueryBuilder::PARAM_INT);
+ $qbUpdate->executeStatement();
+ }
+ $req->closeCursor();
+ }
+}
diff --git a/apps/oauth2/lib/Migration/Version011602Date20230613160650.php b/apps/oauth2/lib/Migration/Version011602Date20230613160650.php
new file mode 100644
index 00000000000..06efce324b2
--- /dev/null
+++ b/apps/oauth2/lib/Migration/Version011602Date20230613160650.php
@@ -0,0 +1,39 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version011602Date20230613160650 extends SimpleMigrationStep {
+
+ public function __construct(
+ ) {
+ }
+
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ if ($schema->hasTable('oauth2_clients')) {
+ $table = $schema->getTable('oauth2_clients');
+ if ($table->hasColumn('secret')) {
+ $column = $table->getColumn('secret');
+ // we still change the column length in case Version011601Date20230522143227
+ // has run before it was changed to set the length to 512
+ $column->setLength(512);
+ return $schema;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/apps/oauth2/lib/Migration/Version011603Date20230620111039.php b/apps/oauth2/lib/Migration/Version011603Date20230620111039.php
new file mode 100644
index 00000000000..853eacd2873
--- /dev/null
+++ b/apps/oauth2/lib/Migration/Version011603Date20230620111039.php
@@ -0,0 +1,69 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\DB\Types;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version011603Date20230620111039 extends SimpleMigrationStep {
+
+ public function __construct(
+ private IDBConnection $connection,
+ ) {
+ }
+
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ if ($schema->hasTable('oauth2_access_tokens')) {
+ $table = $schema->getTable('oauth2_access_tokens');
+ $dbChanged = false;
+ if (!$table->hasColumn('code_created_at')) {
+ $table->addColumn('code_created_at', Types::BIGINT, [
+ 'notnull' => true,
+ 'default' => 0,
+ 'unsigned' => true,
+ ]);
+ $dbChanged = true;
+ }
+ if (!$table->hasColumn('token_count')) {
+ $table->addColumn('token_count', Types::BIGINT, [
+ 'notnull' => true,
+ 'default' => 0,
+ 'unsigned' => true,
+ ]);
+ $dbChanged = true;
+ }
+ if (!$table->hasIndex('oauth2_tk_c_created_idx')) {
+ $table->addIndex(['token_count', 'code_created_at'], 'oauth2_tk_c_created_idx');
+ $dbChanged = true;
+ }
+ if ($dbChanged) {
+ return $schema;
+ }
+ }
+
+ return null;
+ }
+
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
+ // we consider that existing access_tokens have already produced at least one oauth token
+ // which prevents cleaning them up
+ $qbUpdate = $this->connection->getQueryBuilder();
+ $qbUpdate->update('oauth2_access_tokens')
+ ->set('token_count', $qbUpdate->createNamedParameter(1, IQueryBuilder::PARAM_INT));
+ $qbUpdate->executeStatement();
+ }
+}
diff --git a/apps/oauth2/lib/Migration/Version011901Date20240829164356.php b/apps/oauth2/lib/Migration/Version011901Date20240829164356.php
new file mode 100644
index 00000000000..20f5754bf11
--- /dev/null
+++ b/apps/oauth2/lib/Migration/Version011901Date20240829164356.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Migration;
+
+use Closure;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+use OCP\Security\ICrypto;
+
+class Version011901Date20240829164356 extends SimpleMigrationStep {
+
+ public function __construct(
+ private IDBConnection $connection,
+ private ICrypto $crypto,
+ ) {
+ }
+
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
+ $qbUpdate = $this->connection->getQueryBuilder();
+ $qbUpdate->update('oauth2_clients')
+ ->set('secret', $qbUpdate->createParameter('updateSecret'))
+ ->where(
+ $qbUpdate->expr()->eq('id', $qbUpdate->createParameter('updateId'))
+ );
+
+ $qbSelect = $this->connection->getQueryBuilder();
+ $qbSelect->select('id', 'secret')
+ ->from('oauth2_clients');
+ $req = $qbSelect->executeQuery();
+ while ($row = $req->fetch()) {
+ $id = $row['id'];
+ $storedEncryptedSecret = $row['secret'];
+ $secret = $this->crypto->decrypt($storedEncryptedSecret);
+ $hashedSecret = bin2hex($this->crypto->calculateHMAC($secret));
+ $qbUpdate->setParameter('updateSecret', $hashedSecret, IQueryBuilder::PARAM_STR);
+ $qbUpdate->setParameter('updateId', $id, IQueryBuilder::PARAM_INT);
+ $qbUpdate->executeStatement();
+ }
+ $req->closeCursor();
+ }
+}
diff --git a/apps/oauth2/lib/Settings/Admin.php b/apps/oauth2/lib/Settings/Admin.php
new file mode 100644
index 00000000000..93b6b7bcc3f
--- /dev/null
+++ b/apps/oauth2/lib/Settings/Admin.php
@@ -0,0 +1,63 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Settings;
+
+use OCA\OAuth2\Db\ClientMapper;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
+use OCP\IURLGenerator;
+use OCP\Settings\ISettings;
+use Psr\Log\LoggerInterface;
+
+class Admin implements ISettings {
+
+ public function __construct(
+ private IInitialState $initialState,
+ private ClientMapper $clientMapper,
+ private IURLGenerator $urlGenerator,
+ private LoggerInterface $logger,
+ ) {
+ }
+
+ public function getForm(): TemplateResponse {
+ $clients = $this->clientMapper->getClients();
+ $result = [];
+
+ foreach ($clients as $client) {
+ try {
+ $result[] = [
+ 'id' => $client->getId(),
+ 'name' => $client->getName(),
+ 'redirectUri' => $client->getRedirectUri(),
+ 'clientId' => $client->getClientIdentifier(),
+ 'clientSecret' => '',
+ ];
+ } catch (\Exception $e) {
+ $this->logger->error('[Settings] OAuth client secret decryption error', ['exception' => $e]);
+ }
+ }
+ $this->initialState->provideInitialState('clients', $result);
+ $this->initialState->provideInitialState('oauth2-doc-link', $this->urlGenerator->linkToDocs('admin-oauth2'));
+
+ return new TemplateResponse(
+ 'oauth2',
+ 'admin',
+ [],
+ ''
+ );
+ }
+
+ public function getSection(): string {
+ return 'security';
+ }
+
+ public function getPriority(): int {
+ return 100;
+ }
+}
diff --git a/apps/oauth2/openapi.json b/apps/oauth2/openapi.json
new file mode 100644
index 00000000000..76cd2da3150
--- /dev/null
+++ b/apps/oauth2/openapi.json
@@ -0,0 +1,227 @@
+{
+ "openapi": "3.0.3",
+ "info": {
+ "title": "oauth2",
+ "version": "0.0.1",
+ "description": "Allows OAuth2 compatible authentication from other web applications.",
+ "license": {
+ "name": "agpl"
+ }
+ },
+ "components": {
+ "securitySchemes": {
+ "basic_auth": {
+ "type": "http",
+ "scheme": "basic"
+ },
+ "bearer_auth": {
+ "type": "http",
+ "scheme": "bearer"
+ }
+ },
+ "schemas": {}
+ },
+ "paths": {
+ "/index.php/apps/oauth2/authorize": {
+ "get": {
+ "operationId": "login_redirector-authorize",
+ "summary": "Authorize the user",
+ "tags": [
+ "login_redirector"
+ ],
+ "security": [
+ {},
+ {
+ "bearer_auth": []
+ },
+ {
+ "basic_auth": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "client_id",
+ "in": "query",
+ "description": "Client ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "state",
+ "in": "query",
+ "description": "State of the flow",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "response_type",
+ "in": "query",
+ "description": "Response type for the flow",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "redirect_uri",
+ "in": "query",
+ "description": "URI to redirect to after the flow (is only used for legacy ownCloud clients)",
+ "schema": {
+ "type": "string",
+ "default": ""
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Client not found",
+ "content": {
+ "text/html": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "303": {
+ "description": "Redirect to login URL",
+ "headers": {
+ "Location": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/index.php/apps/oauth2/api/v1/token": {
+ "post": {
+ "operationId": "oauth_api-get-token",
+ "summary": "Get a token",
+ "tags": [
+ "oauth_api"
+ ],
+ "security": [
+ {},
+ {
+ "bearer_auth": []
+ },
+ {
+ "basic_auth": []
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": [
+ "grant_type"
+ ],
+ "properties": {
+ "grant_type": {
+ "type": "string",
+ "description": "Token type that should be granted"
+ },
+ "code": {
+ "type": "string",
+ "nullable": true,
+ "description": "Code of the flow"
+ },
+ "refresh_token": {
+ "type": "string",
+ "nullable": true,
+ "description": "Refresh token"
+ },
+ "client_id": {
+ "type": "string",
+ "nullable": true,
+ "description": "Client ID"
+ },
+ "client_secret": {
+ "type": "string",
+ "nullable": true,
+ "description": "Client secret"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "500": {
+ "description": "",
+ "content": {
+ "text/plain": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "200": {
+ "description": "Token returned",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": [
+ "access_token",
+ "token_type",
+ "expires_in",
+ "refresh_token",
+ "user_id"
+ ],
+ "properties": {
+ "access_token": {
+ "type": "string"
+ },
+ "token_type": {
+ "type": "string"
+ },
+ "expires_in": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "refresh_token": {
+ "type": "string"
+ },
+ "user_id": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Getting token is not possible",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": [
+ "error"
+ ],
+ "properties": {
+ "error": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "tags": []
+}
diff --git a/apps/oauth2/openapi.json.license b/apps/oauth2/openapi.json.license
new file mode 100644
index 00000000000..83559daa9dc
--- /dev/null
+++ b/apps/oauth2/openapi.json.license
@@ -0,0 +1,2 @@
+SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+SPDX-License-Identifier: AGPL-3.0-or-later \ No newline at end of file
diff --git a/apps/oauth2/src/App.vue b/apps/oauth2/src/App.vue
new file mode 100644
index 00000000000..941317a3e64
--- /dev/null
+++ b/apps/oauth2/src/App.vue
@@ -0,0 +1,161 @@
+<!--
+ - SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+<template>
+ <NcSettingsSection :name="t('oauth2', 'OAuth 2.0 clients')"
+ :description="t('oauth2', 'OAuth 2.0 allows external services to request access to {instanceName}.', { instanceName })"
+ :doc-url="oauthDocLink">
+ <table v-if="clients.length > 0" class="grid">
+ <thead>
+ <tr>
+ <th>
+ {{ t('oauth2', 'Name') }}
+ </th>
+ <th>
+ {{ t('oauth2', 'Redirection URI') }}
+ </th>
+ <th>
+ {{ t('oauth2', 'Client Identifier') }}
+ </th>
+ <th>
+ {{ t('oauth2', 'Secret key') }}
+ </th>
+ <th>
+ {{ t('oauth2', 'Delete client') }}
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <OAuthItem v-for="client in clients"
+ :key="client.id"
+ :client="client"
+ @delete="deleteClient" />
+ </tbody>
+ </table>
+ <NcNoteCard v-if="showSecretWarning"
+ type="warning">
+ {{ t('oauth2', 'Make sure you store the secret key, it cannot be recovered.') }}
+ </NcNoteCard>
+
+ <br>
+ <h3>{{ t('oauth2', 'Add client') }}</h3>
+ <span v-if="newClient.error" class="msg error">{{ newClient.errorMsg }}</span>
+ <form class="oauth2-form" @submit.prevent="addClient">
+ <NcTextField id="name"
+ :value.sync="newClient.name"
+ type="text"
+ class="oauth2-form--input"
+ name="name"
+ :label="t('oauth2', 'Name')"
+ :placeholder="t('oauth2', 'Name')" />
+ <NcTextField id="redirectUri"
+ :value.sync="newClient.redirectUri"
+ type="url"
+ class="oauth2-form--input"
+ name="redirectUri"
+ :label="t('oauth2', 'Redirection URI')"
+ :placeholder="t('oauth2', 'Redirection URI')" />
+ <NcButton native-type="submit" class="inline-button">
+ {{ t('oauth2', 'Add') }}
+ </NcButton>
+ </form>
+ </NcSettingsSection>
+</template>
+
+<script>
+import axios from '@nextcloud/axios'
+import OAuthItem from './components/OAuthItem.vue'
+import { generateUrl } from '@nextcloud/router'
+import { getCapabilities } from '@nextcloud/capabilities'
+import NcSettingsSection from '@nextcloud/vue/components/NcSettingsSection'
+import NcButton from '@nextcloud/vue/components/NcButton'
+import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
+import { loadState } from '@nextcloud/initial-state'
+import NcTextField from '@nextcloud/vue/components/NcTextField'
+
+export default {
+ name: 'App',
+ components: {
+ OAuthItem,
+ NcSettingsSection,
+ NcButton,
+ NcTextField,
+ NcNoteCard,
+ },
+ props: {
+ clients: {
+ type: Array,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ newClient: {
+ name: '',
+ redirectUri: '',
+ errorMsg: '',
+ error: false,
+ },
+ oauthDocLink: loadState('oauth2', 'oauth2-doc-link'),
+ showSecretWarning: false,
+ }
+ },
+ computed: {
+ instanceName() {
+ return getCapabilities().theming.name
+ },
+ },
+ methods: {
+ deleteClient(id) {
+ axios.delete(generateUrl('apps/oauth2/clients/{id}', { id }))
+ .then(() => {
+ // eslint-disable-next-line vue/no-mutating-props
+ this.clients = this.clients.filter(client => client.id !== id)
+ })
+ },
+ addClient() {
+ this.newClient.error = false
+
+ axios.post(
+ generateUrl('apps/oauth2/clients'),
+ {
+ name: this.newClient.name,
+ redirectUri: this.newClient.redirectUri,
+ },
+ ).then(response => {
+ // eslint-disable-next-line vue/no-mutating-props
+ this.clients.push(response.data)
+ this.showSecretWarning = true
+
+ this.newClient.name = ''
+ this.newClient.redirectUri = ''
+ }).catch(reason => {
+ this.newClient.error = true
+ this.newClient.errorMsg = reason.response.data.message
+ })
+ },
+ },
+}
+</script>
+<style scoped>
+ table {
+ max-width: 800px;
+ }
+
+ /** Overwrite button height and position to be aligned with the text input */
+ .inline-button {
+ min-height: 34px !important;
+ display: inline-flex !important;
+ }
+
+ .oauth2-form {
+ display: flex;
+ flex-direction: row;
+ }
+
+ .oauth2-form--input {
+ max-width: 200px;
+ margin-inline-end: 10px;
+ }
+</style>
diff --git a/apps/oauth2/src/components/OAuthItem.vue b/apps/oauth2/src/components/OAuthItem.vue
new file mode 100644
index 00000000000..5a8f1556203
--- /dev/null
+++ b/apps/oauth2/src/components/OAuthItem.vue
@@ -0,0 +1,113 @@
+<!--
+ - SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+<template>
+ <tr>
+ <td>{{ name }}</td>
+ <td>{{ redirectUri }}</td>
+ <td><code>{{ clientId }}</code></td>
+ <td>
+ <div class="action-secret">
+ <code>{{ renderedSecret }}</code>
+ <NcButton v-if="clientSecret !== ''"
+ type="tertiary-no-background"
+ :aria-label="toggleAriaLabel"
+ @click="toggleSecret">
+ <template #icon>
+ <EyeOutline :size="20" />
+ </template>
+ </NcButton>
+ </div>
+ </td>
+ <td class="action-column">
+ <NcButton type="tertiary-no-background"
+ :aria-label="t('oauth2', 'Delete')"
+ @click="$emit('delete', id)">
+ <template #icon>
+ <Delete :size="20"
+ :title="t('oauth2', 'Delete')" />
+ </template>
+ </NcButton>
+ </td>
+ </tr>
+</template>
+
+<script>
+
+import Delete from 'vue-material-design-icons/DeleteOutline.vue'
+import EyeOutline from 'vue-material-design-icons/EyeOutline.vue'
+import NcButton from '@nextcloud/vue/components/NcButton'
+
+export default {
+ name: 'OAuthItem',
+ components: {
+ Delete,
+ NcButton,
+ EyeOutline,
+ },
+ props: {
+ client: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ id: this.client.id,
+ name: this.client.name,
+ redirectUri: this.client.redirectUri,
+ clientId: this.client.clientId,
+ clientSecret: this.client.clientSecret,
+ renderSecret: false,
+ }
+ },
+ computed: {
+ renderedSecret() {
+ if (this.renderSecret) {
+ return this.clientSecret
+ } else {
+ return '****'
+ }
+ },
+ toggleAriaLabel() {
+ if (!this.renderSecret) {
+ return t('oauth2', 'Show client secret')
+ }
+ return t('oauth2', 'Hide client secret')
+ },
+ },
+ methods: {
+ toggleSecret() {
+ this.renderSecret = !this.renderSecret
+ },
+ },
+}
+</script>
+
+<style scoped>
+ .action-secret {
+ display: flex;
+ align-items: center;
+ }
+
+ .action-secret code {
+ padding-top: 5px;
+ }
+
+ td code {
+ display: inline-block;
+ vertical-align: middle;
+ }
+
+ table.inline td {
+ border: none;
+ padding: 5px;
+ }
+
+ .action-column {
+ display: flex;
+ justify-content: flex-end;
+ padding-inline-end: 0;
+ }
+</style>
diff --git a/apps/oauth2/src/main.js b/apps/oauth2/src/main.js
new file mode 100644
index 00000000000..10d537455df
--- /dev/null
+++ b/apps/oauth2/src/main.js
@@ -0,0 +1,21 @@
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import Vue from 'vue'
+import App from './App.vue'
+import { loadState } from '@nextcloud/initial-state'
+
+Vue.prototype.t = t
+Vue.prototype.OC = OC
+
+const clients = loadState('oauth2', 'clients')
+
+const View = Vue.extend(App)
+const oauth = new View({
+ propsData: {
+ clients,
+ },
+})
+oauth.$mount('#oauth2')
diff --git a/apps/oauth2/templates/admin.php b/apps/oauth2/templates/admin.php
new file mode 100644
index 00000000000..844eeee2c08
--- /dev/null
+++ b/apps/oauth2/templates/admin.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+\OCP\Util::addScript('oauth2', 'oauth2', 'core');
+
+?>
+
+<div id="oauth2"></div>
diff --git a/apps/oauth2/tests/Controller/LoginRedirectorControllerTest.php b/apps/oauth2/tests/Controller/LoginRedirectorControllerTest.php
new file mode 100644
index 00000000000..04ac0bfbd28
--- /dev/null
+++ b/apps/oauth2/tests/Controller/LoginRedirectorControllerTest.php
@@ -0,0 +1,255 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Tests\Controller;
+
+use OC\Core\Controller\ClientFlowLoginController;
+use OCA\OAuth2\Controller\LoginRedirectorController;
+use OCA\OAuth2\Db\Client;
+use OCA\OAuth2\Db\ClientMapper;
+use OCA\OAuth2\Exceptions\ClientNotFoundException;
+use OCP\AppFramework\Http\RedirectResponse;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\IAppConfig;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IRequest;
+use OCP\ISession;
+use OCP\IURLGenerator;
+use OCP\Security\ISecureRandom;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+/**
+ * @group DB
+ */
+class LoginRedirectorControllerTest extends TestCase {
+ private IRequest&MockObject $request;
+ private IURLGenerator&MockObject $urlGenerator;
+ private ClientMapper&MockObject $clientMapper;
+ private ISession&MockObject $session;
+ private IL10N&MockObject $l;
+ private ISecureRandom&MockObject $random;
+ private IAppConfig&MockObject $appConfig;
+ private IConfig&MockObject $config;
+
+ private LoginRedirectorController $loginRedirectorController;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->request = $this->createMock(IRequest::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->clientMapper = $this->createMock(ClientMapper::class);
+ $this->session = $this->createMock(ISession::class);
+ $this->l = $this->createMock(IL10N::class);
+ $this->random = $this->createMock(ISecureRandom::class);
+ $this->appConfig = $this->createMock(IAppConfig::class);
+ $this->config = $this->createMock(IConfig::class);
+
+ $this->loginRedirectorController = new LoginRedirectorController(
+ 'oauth2',
+ $this->request,
+ $this->urlGenerator,
+ $this->clientMapper,
+ $this->session,
+ $this->l,
+ $this->random,
+ $this->appConfig,
+ $this->config,
+ );
+ }
+
+ public function testAuthorize(): void {
+ $client = new Client();
+ $client->setClientIdentifier('MyClientIdentifier');
+ $this->clientMapper
+ ->expects($this->once())
+ ->method('getByIdentifier')
+ ->with('MyClientId')
+ ->willReturn($client);
+ $this->session
+ ->expects($this->once())
+ ->method('set')
+ ->with('oauth.state', 'MyState');
+ $this->urlGenerator
+ ->expects($this->once())
+ ->method('linkToRouteAbsolute')
+ ->with(
+ 'core.ClientFlowLogin.showAuthPickerPage',
+ [
+ 'clientIdentifier' => 'MyClientIdentifier',
+ 'providedRedirectUri' => '',
+ ]
+ )
+ ->willReturn('https://example.com/?clientIdentifier=foo');
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValueBool')
+ ->with('oauth2.enable_oc_clients', false)
+ ->willReturn(false);
+
+ $expected = new RedirectResponse('https://example.com/?clientIdentifier=foo');
+ $this->assertEquals($expected, $this->loginRedirectorController->authorize('MyClientId', 'MyState', 'code'));
+ }
+
+ public function testAuthorizeSkipPicker(): void {
+ $client = new Client();
+ $client->setName('MyClientName');
+ $client->setClientIdentifier('MyClientIdentifier');
+ $this->clientMapper
+ ->expects($this->once())
+ ->method('getByIdentifier')
+ ->with('MyClientId')
+ ->willReturn($client);
+ $this->session
+ ->expects(static::exactly(2))
+ ->method('set')
+ ->willReturnCallback(function (string $key, string $value): void {
+ switch ([$key, $value]) {
+ case ['oauth.state', 'MyState']:
+ case [ClientFlowLoginController::STATE_NAME, 'MyStateToken']:
+ /* Expected */
+ break;
+ default:
+ throw new LogicException();
+ }
+ });
+ $this->appConfig
+ ->expects(static::once())
+ ->method('getValueArray')
+ ->with('oauth2', 'skipAuthPickerApplications', [])
+ ->willReturn(['MyClientName']);
+ $this->random
+ ->expects(static::once())
+ ->method('generate')
+ ->willReturn('MyStateToken');
+ $this->urlGenerator
+ ->expects($this->once())
+ ->method('linkToRouteAbsolute')
+ ->with(
+ 'core.ClientFlowLogin.grantPage',
+ [
+ 'stateToken' => 'MyStateToken',
+ 'clientIdentifier' => 'MyClientIdentifier',
+ 'providedRedirectUri' => '',
+ ]
+ )
+ ->willReturn('https://example.com/?clientIdentifier=foo');
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValueBool')
+ ->with('oauth2.enable_oc_clients', false)
+ ->willReturn(false);
+
+ $expected = new RedirectResponse('https://example.com/?clientIdentifier=foo');
+ $this->assertEquals($expected, $this->loginRedirectorController->authorize('MyClientId', 'MyState', 'code'));
+ }
+
+ public function testAuthorizeWrongResponseType(): void {
+ $client = new Client();
+ $client->setClientIdentifier('MyClientIdentifier');
+ $client->setRedirectUri('http://foo.bar');
+ $this->clientMapper
+ ->expects($this->once())
+ ->method('getByIdentifier')
+ ->with('MyClientId')
+ ->willReturn($client);
+ $this->session
+ ->expects($this->never())
+ ->method('set');
+
+
+ $expected = new RedirectResponse('http://foo.bar?error=unsupported_response_type&state=MyState');
+ $this->assertEquals($expected, $this->loginRedirectorController->authorize('MyClientId', 'MyState', 'wrongcode'));
+ }
+
+ public function testAuthorizeWithLegacyOcClient(): void {
+ $client = new Client();
+ $client->setClientIdentifier('MyClientIdentifier');
+ $client->setRedirectUri('http://localhost:*');
+ $this->clientMapper
+ ->expects($this->once())
+ ->method('getByIdentifier')
+ ->with('MyClientId')
+ ->willReturn($client);
+ $this->session
+ ->expects($this->once())
+ ->method('set')
+ ->with('oauth.state', 'MyState');
+ $this->urlGenerator
+ ->expects($this->once())
+ ->method('linkToRouteAbsolute')
+ ->with(
+ 'core.ClientFlowLogin.showAuthPickerPage',
+ [
+ 'clientIdentifier' => 'MyClientIdentifier',
+ 'providedRedirectUri' => 'http://localhost:30000',
+ ]
+ )
+ ->willReturn('https://example.com/?clientIdentifier=foo&providedRedirectUri=http://localhost:30000');
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValueBool')
+ ->with('oauth2.enable_oc_clients', false)
+ ->willReturn(true);
+
+ $expected = new RedirectResponse('https://example.com/?clientIdentifier=foo&providedRedirectUri=http://localhost:30000');
+ $this->assertEquals($expected, $this->loginRedirectorController->authorize('MyClientId', 'MyState', 'code', 'http://localhost:30000'));
+ }
+
+ public function testAuthorizeNotForwardingUntrustedURIs(): void {
+ $client = new Client();
+ $client->setClientIdentifier('MyClientIdentifier');
+ $this->clientMapper
+ ->expects($this->once())
+ ->method('getByIdentifier')
+ ->with('MyClientId')
+ ->willReturn($client);
+ $this->session
+ ->expects($this->once())
+ ->method('set')
+ ->with('oauth.state', 'MyState');
+ $this->urlGenerator
+ ->expects($this->once())
+ ->method('linkToRouteAbsolute')
+ ->with(
+ 'core.ClientFlowLogin.showAuthPickerPage',
+ [
+ 'clientIdentifier' => 'MyClientIdentifier',
+ 'providedRedirectUri' => '',
+ ]
+ )
+ ->willReturn('https://example.com/?clientIdentifier=foo');
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValueBool')
+ ->with('oauth2.enable_oc_clients', false)
+ ->willReturn(false);
+
+ $expected = new RedirectResponse('https://example.com/?clientIdentifier=foo');
+ $this->assertEquals($expected, $this->loginRedirectorController->authorize('MyClientId', 'MyState', 'code', 'http://untrusted-uri.com'));
+ }
+
+
+ public function testClientNotFound(): void {
+ $clientNotFound = new ClientNotFoundException('could not find client test123', 0);
+ $this->clientMapper
+ ->expects($this->once())
+ ->method('getByIdentifier')
+ ->willThrowException($clientNotFound);
+ $this->session
+ ->expects($this->never())
+ ->method('set');
+
+ $response = $this->loginRedirectorController->authorize('MyClientId', 'MyState', 'wrongcode');
+ $this->assertInstanceOf(TemplateResponse::class, $response);
+
+ /** @var TemplateResponse $response */
+ $this->assertEquals('404', $response->getTemplateName());
+ $this->assertEquals('guest', $response->getRenderAs());
+ }
+}
diff --git a/apps/oauth2/tests/Controller/OauthApiControllerTest.php b/apps/oauth2/tests/Controller/OauthApiControllerTest.php
new file mode 100644
index 00000000000..53dd8549196
--- /dev/null
+++ b/apps/oauth2/tests/Controller/OauthApiControllerTest.php
@@ -0,0 +1,607 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Tests\Controller;
+
+use OC\Authentication\Exceptions\ExpiredTokenException;
+use OC\Authentication\Exceptions\InvalidTokenException;
+use OC\Authentication\Token\IProvider as TokenProvider;
+use OC\Authentication\Token\PublicKeyToken;
+use OCA\OAuth2\Controller\OauthApiController;
+use OCA\OAuth2\Db\AccessToken;
+use OCA\OAuth2\Db\AccessTokenMapper;
+use OCA\OAuth2\Db\Client;
+use OCA\OAuth2\Db\ClientMapper;
+use OCA\OAuth2\Exceptions\AccessTokenNotFoundException;
+use OCA\OAuth2\Exceptions\ClientNotFoundException;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\JSONResponse;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\IRequest;
+use OCP\Security\Bruteforce\IThrottler;
+use OCP\Security\ICrypto;
+use OCP\Security\ISecureRandom;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+/* We have to use this to add a property to the mocked request and avoid warnings about dynamic properties on PHP>=8.2 */
+abstract class RequestMock implements IRequest {
+ public array $server = [];
+}
+
+class OauthApiControllerTest extends TestCase {
+ /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject */
+ private $request;
+ /** @var ICrypto|\PHPUnit\Framework\MockObject\MockObject */
+ private $crypto;
+ /** @var AccessTokenMapper|\PHPUnit\Framework\MockObject\MockObject */
+ private $accessTokenMapper;
+ /** @var ClientMapper|\PHPUnit\Framework\MockObject\MockObject */
+ private $clientMapper;
+ /** @var TokenProvider|\PHPUnit\Framework\MockObject\MockObject */
+ private $tokenProvider;
+ /** @var ISecureRandom|\PHPUnit\Framework\MockObject\MockObject */
+ private $secureRandom;
+ /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
+ private $time;
+ /** @var IThrottler|\PHPUnit\Framework\MockObject\MockObject */
+ private $throttler;
+ /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */
+ private $logger;
+ /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
+ private $timeFactory;
+ /** @var OauthApiController */
+ private $oauthApiController;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->request = $this->createMock(RequestMock::class);
+ $this->crypto = $this->createMock(ICrypto::class);
+ $this->accessTokenMapper = $this->createMock(AccessTokenMapper::class);
+ $this->clientMapper = $this->createMock(ClientMapper::class);
+ $this->tokenProvider = $this->createMock(TokenProvider::class);
+ $this->secureRandom = $this->createMock(ISecureRandom::class);
+ $this->time = $this->createMock(ITimeFactory::class);
+ $this->throttler = $this->createMock(IThrottler::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+ $this->timeFactory = $this->createMock(ITimeFactory::class);
+
+ $this->oauthApiController = new OauthApiController(
+ 'oauth2',
+ $this->request,
+ $this->crypto,
+ $this->accessTokenMapper,
+ $this->clientMapper,
+ $this->tokenProvider,
+ $this->secureRandom,
+ $this->time,
+ $this->logger,
+ $this->throttler,
+ $this->timeFactory
+ );
+ }
+
+ public function testGetTokenInvalidGrantType(): void {
+ $expected = new JSONResponse([
+ 'error' => 'invalid_grant',
+ ], Http::STATUS_BAD_REQUEST);
+ $expected->throttle(['invalid_grant' => 'foo']);
+
+ $this->assertEquals($expected, $this->oauthApiController->getToken('foo', null, null, null, null));
+ }
+
+ public function testGetTokenInvalidCode(): void {
+ $expected = new JSONResponse([
+ 'error' => 'invalid_request',
+ ], Http::STATUS_BAD_REQUEST);
+ $expected->throttle(['invalid_request' => 'token not found', 'code' => 'invalidcode']);
+
+ $this->accessTokenMapper->method('getByCode')
+ ->with('invalidcode')
+ ->willThrowException(new AccessTokenNotFoundException());
+
+ $this->assertEquals($expected, $this->oauthApiController->getToken('authorization_code', 'invalidcode', null, null, null));
+ }
+
+ public function testGetTokenExpiredCode(): void {
+ $codeCreatedAt = 100;
+ $expiredSince = 123;
+
+ $expected = new JSONResponse([
+ 'error' => 'invalid_request',
+ ], Http::STATUS_BAD_REQUEST);
+ $expected->throttle(['invalid_request' => 'authorization_code_expired', 'expired_since' => $expiredSince]);
+
+ $accessToken = new AccessToken();
+ $accessToken->setClientId(42);
+ $accessToken->setCodeCreatedAt($codeCreatedAt);
+
+ $this->accessTokenMapper->method('getByCode')
+ ->with('validcode')
+ ->willReturn($accessToken);
+
+ $tsNow = $codeCreatedAt + OauthApiController::AUTHORIZATION_CODE_EXPIRES_AFTER + $expiredSince;
+ $dateNow = (new \DateTimeImmutable())->setTimestamp($tsNow);
+ $this->timeFactory->method('now')
+ ->willReturn($dateNow);
+
+ $this->assertEquals($expected, $this->oauthApiController->getToken('authorization_code', 'validcode', null, null, null));
+ }
+
+ public function testGetTokenWithCodeForActiveToken(): void {
+ // if a token has already delivered oauth tokens,
+ // it should not be possible to get a new oauth token from a valid authorization code
+ $codeCreatedAt = 100;
+
+ $expected = new JSONResponse([
+ 'error' => 'invalid_request',
+ ], Http::STATUS_BAD_REQUEST);
+ $expected->throttle(['invalid_request' => 'authorization_code_received_for_active_token']);
+
+ $accessToken = new AccessToken();
+ $accessToken->setClientId(42);
+ $accessToken->setCodeCreatedAt($codeCreatedAt);
+ $accessToken->setTokenCount(1);
+
+ $this->accessTokenMapper->method('getByCode')
+ ->with('validcode')
+ ->willReturn($accessToken);
+
+ $tsNow = $codeCreatedAt + 1;
+ $dateNow = (new \DateTimeImmutable())->setTimestamp($tsNow);
+ $this->timeFactory->method('now')
+ ->willReturn($dateNow);
+
+ $this->assertEquals($expected, $this->oauthApiController->getToken('authorization_code', 'validcode', null, null, null));
+ }
+
+ public function testGetTokenClientDoesNotExist(): void {
+ // In this test, the token's authorization code is valid and has not expired
+ // and we check what happens when the associated Oauth client does not exist
+ $codeCreatedAt = 100;
+
+ $expected = new JSONResponse([
+ 'error' => 'invalid_request',
+ ], Http::STATUS_BAD_REQUEST);
+ $expected->throttle(['invalid_request' => 'client not found', 'client_id' => 42]);
+
+ $accessToken = new AccessToken();
+ $accessToken->setClientId(42);
+ $accessToken->setCodeCreatedAt($codeCreatedAt);
+
+ $this->accessTokenMapper->method('getByCode')
+ ->with('validcode')
+ ->willReturn($accessToken);
+
+ // 'now' is before the token's authorization code expiration
+ $tsNow = $codeCreatedAt + OauthApiController::AUTHORIZATION_CODE_EXPIRES_AFTER - 1;
+ $dateNow = (new \DateTimeImmutable())->setTimestamp($tsNow);
+ $this->timeFactory->method('now')
+ ->willReturn($dateNow);
+
+ $this->clientMapper->method('getByUid')
+ ->with(42)
+ ->willThrowException(new ClientNotFoundException());
+
+ $this->assertEquals($expected, $this->oauthApiController->getToken('authorization_code', 'validcode', null, null, null));
+ }
+
+ public function testRefreshTokenInvalidRefreshToken(): void {
+ $expected = new JSONResponse([
+ 'error' => 'invalid_request',
+ ], Http::STATUS_BAD_REQUEST);
+ $expected->throttle(['invalid_request' => 'token not found', 'code' => 'invalidrefresh']);
+
+ $this->accessTokenMapper->method('getByCode')
+ ->with('invalidrefresh')
+ ->willThrowException(new AccessTokenNotFoundException());
+
+ $this->assertEquals($expected, $this->oauthApiController->getToken('refresh_token', null, 'invalidrefresh', null, null));
+ }
+
+ public function testRefreshTokenClientDoesNotExist(): void {
+ $expected = new JSONResponse([
+ 'error' => 'invalid_request',
+ ], Http::STATUS_BAD_REQUEST);
+ $expected->throttle(['invalid_request' => 'client not found', 'client_id' => 42]);
+
+ $accessToken = new AccessToken();
+ $accessToken->setClientId(42);
+
+ $this->accessTokenMapper->method('getByCode')
+ ->with('validrefresh')
+ ->willReturn($accessToken);
+
+ $this->clientMapper->method('getByUid')
+ ->with(42)
+ ->willThrowException(new ClientNotFoundException());
+
+ $this->assertEquals($expected, $this->oauthApiController->getToken('refresh_token', null, 'validrefresh', null, null));
+ }
+
+ public static function invalidClientProvider() {
+ return [
+ ['invalidClientId', 'invalidClientSecret'],
+ ['clientId', 'invalidClientSecret'],
+ ['invalidClientId', 'clientSecret'],
+ ];
+ }
+
+ /**
+ *
+ * @param string $clientId
+ * @param string $clientSecret
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('invalidClientProvider')]
+ public function testRefreshTokenInvalidClient($clientId, $clientSecret): void {
+ $expected = new JSONResponse([
+ 'error' => 'invalid_client',
+ ], Http::STATUS_BAD_REQUEST);
+ $expected->throttle(['invalid_client' => 'client ID or secret does not match']);
+
+ $accessToken = new AccessToken();
+ $accessToken->setClientId(42);
+
+ $this->accessTokenMapper->method('getByCode')
+ ->with('validrefresh')
+ ->willReturn($accessToken);
+
+ $this->crypto
+ ->method('calculateHMAC')
+ ->with($this->callback(function (string $text) {
+ return $text === 'clientSecret' || $text === 'invalidClientSecret';
+ }))
+ ->willReturnCallback(function (string $text) {
+ return $text === 'clientSecret'
+ ? 'hashedClientSecret'
+ : 'hashedInvalidClientSecret';
+ });
+
+ $client = new Client();
+ $client->setClientIdentifier('clientId');
+ $client->setSecret(bin2hex('hashedClientSecret'));
+ $this->clientMapper->method('getByUid')
+ ->with(42)
+ ->willReturn($client);
+
+ $this->assertEquals($expected, $this->oauthApiController->getToken('refresh_token', null, 'validrefresh', $clientId, $clientSecret));
+ }
+
+ public function testRefreshTokenInvalidAppToken(): void {
+ $expected = new JSONResponse([
+ 'error' => 'invalid_request',
+ ], Http::STATUS_BAD_REQUEST);
+ $expected->throttle(['invalid_request' => 'token is invalid']);
+
+ $accessToken = new AccessToken();
+ $accessToken->setClientId(42);
+ $accessToken->setTokenId(1337);
+ $accessToken->setEncryptedToken('encryptedToken');
+
+ $this->accessTokenMapper->method('getByCode')
+ ->with('validrefresh')
+ ->willReturn($accessToken);
+
+ $client = new Client();
+ $client->setClientIdentifier('clientId');
+ $client->setSecret(bin2hex('hashedClientSecret'));
+ $this->clientMapper->method('getByUid')
+ ->with(42)
+ ->willReturn($client);
+
+ $this->crypto
+ ->method('decrypt')
+ ->with('encryptedToken')
+ ->willReturn('decryptedToken');
+
+ $this->crypto
+ ->method('calculateHMAC')
+ ->with('clientSecret')
+ ->willReturn('hashedClientSecret');
+
+ $this->tokenProvider->method('getTokenById')
+ ->with(1337)
+ ->willThrowException(new InvalidTokenException());
+
+ $this->accessTokenMapper->expects($this->once())
+ ->method('delete')
+ ->with($accessToken);
+
+ $this->assertEquals($expected, $this->oauthApiController->getToken('refresh_token', null, 'validrefresh', 'clientId', 'clientSecret'));
+ }
+
+ public function testRefreshTokenValidAppToken(): void {
+ $accessToken = new AccessToken();
+ $accessToken->setClientId(42);
+ $accessToken->setTokenId(1337);
+ $accessToken->setEncryptedToken('encryptedToken');
+
+ $this->accessTokenMapper->method('getByCode')
+ ->with('validrefresh')
+ ->willReturn($accessToken);
+
+ $client = new Client();
+ $client->setClientIdentifier('clientId');
+ $client->setSecret(bin2hex('hashedClientSecret'));
+ $this->clientMapper->method('getByUid')
+ ->with(42)
+ ->willReturn($client);
+
+ $this->crypto
+ ->method('decrypt')
+ ->with('encryptedToken')
+ ->willReturn('decryptedToken');
+
+ $this->crypto
+ ->method('calculateHMAC')
+ ->with('clientSecret')
+ ->willReturn('hashedClientSecret');
+
+ $appToken = new PublicKeyToken();
+ $appToken->setUid('userId');
+ $this->tokenProvider->method('getTokenById')
+ ->with(1337)
+ ->willThrowException(new ExpiredTokenException($appToken));
+
+ $this->accessTokenMapper->expects($this->never())
+ ->method('delete')
+ ->with($accessToken);
+
+ $this->secureRandom->method('generate')
+ ->willReturnCallback(function ($len) {
+ return 'random' . $len;
+ });
+
+ $this->tokenProvider->expects($this->once())
+ ->method('rotate')
+ ->with(
+ $appToken,
+ 'decryptedToken',
+ 'random72'
+ )->willReturn($appToken);
+
+ $this->time->method('getTime')
+ ->willReturn(1000);
+
+ $this->tokenProvider->expects($this->once())
+ ->method('updateToken')
+ ->with(
+ $this->callback(function (PublicKeyToken $token) {
+ return $token->getExpires() === 4600;
+ })
+ );
+
+ $this->crypto->method('encrypt')
+ ->with('random72', 'random128')
+ ->willReturn('newEncryptedToken');
+
+ $this->accessTokenMapper->expects($this->once())
+ ->method('update')
+ ->with(
+ $this->callback(function (AccessToken $token) {
+ return $token->getHashedCode() === hash('sha512', 'random128')
+ && $token->getEncryptedToken() === 'newEncryptedToken';
+ })
+ );
+
+ $expected = new JSONResponse([
+ 'access_token' => 'random72',
+ 'token_type' => 'Bearer',
+ 'expires_in' => 3600,
+ 'refresh_token' => 'random128',
+ 'user_id' => 'userId',
+ ]);
+
+ $this->request->method('getRemoteAddress')
+ ->willReturn('1.2.3.4');
+
+ $this->throttler->expects($this->once())
+ ->method('resetDelay')
+ ->with(
+ '1.2.3.4',
+ 'login',
+ ['user' => 'userId']
+ );
+
+ $this->assertEquals($expected, $this->oauthApiController->getToken('refresh_token', null, 'validrefresh', 'clientId', 'clientSecret'));
+ }
+
+ public function testRefreshTokenValidAppTokenBasicAuth(): void {
+ $accessToken = new AccessToken();
+ $accessToken->setClientId(42);
+ $accessToken->setTokenId(1337);
+ $accessToken->setEncryptedToken('encryptedToken');
+
+ $this->accessTokenMapper->method('getByCode')
+ ->with('validrefresh')
+ ->willReturn($accessToken);
+
+ $client = new Client();
+ $client->setClientIdentifier('clientId');
+ $client->setSecret(bin2hex('hashedClientSecret'));
+ $this->clientMapper->method('getByUid')
+ ->with(42)
+ ->willReturn($client);
+
+ $this->crypto
+ ->method('decrypt')
+ ->with('encryptedToken')
+ ->willReturn('decryptedToken');
+
+ $this->crypto
+ ->method('calculateHMAC')
+ ->with('clientSecret')
+ ->willReturn('hashedClientSecret');
+
+ $appToken = new PublicKeyToken();
+ $appToken->setUid('userId');
+ $this->tokenProvider->method('getTokenById')
+ ->with(1337)
+ ->willThrowException(new ExpiredTokenException($appToken));
+
+ $this->accessTokenMapper->expects($this->never())
+ ->method('delete')
+ ->with($accessToken);
+
+ $this->secureRandom->method('generate')
+ ->willReturnCallback(function ($len) {
+ return 'random' . $len;
+ });
+
+ $this->tokenProvider->expects($this->once())
+ ->method('rotate')
+ ->with(
+ $appToken,
+ 'decryptedToken',
+ 'random72'
+ )->willReturn($appToken);
+
+ $this->time->method('getTime')
+ ->willReturn(1000);
+
+ $this->tokenProvider->expects($this->once())
+ ->method('updateToken')
+ ->with(
+ $this->callback(function (PublicKeyToken $token) {
+ return $token->getExpires() === 4600;
+ })
+ );
+
+ $this->crypto->method('encrypt')
+ ->with('random72', 'random128')
+ ->willReturn('newEncryptedToken');
+
+ $this->accessTokenMapper->expects($this->once())
+ ->method('update')
+ ->with(
+ $this->callback(function (AccessToken $token) {
+ return $token->getHashedCode() === hash('sha512', 'random128')
+ && $token->getEncryptedToken() === 'newEncryptedToken';
+ })
+ );
+
+ $expected = new JSONResponse([
+ 'access_token' => 'random72',
+ 'token_type' => 'Bearer',
+ 'expires_in' => 3600,
+ 'refresh_token' => 'random128',
+ 'user_id' => 'userId',
+ ]);
+
+ $this->request->server['PHP_AUTH_USER'] = 'clientId';
+ $this->request->server['PHP_AUTH_PW'] = 'clientSecret';
+
+ $this->request->method('getRemoteAddress')
+ ->willReturn('1.2.3.4');
+
+ $this->throttler->expects($this->once())
+ ->method('resetDelay')
+ ->with(
+ '1.2.3.4',
+ 'login',
+ ['user' => 'userId']
+ );
+
+ $this->assertEquals($expected, $this->oauthApiController->getToken('refresh_token', null, 'validrefresh', null, null));
+ }
+
+ public function testRefreshTokenExpiredAppToken(): void {
+ $accessToken = new AccessToken();
+ $accessToken->setClientId(42);
+ $accessToken->setTokenId(1337);
+ $accessToken->setEncryptedToken('encryptedToken');
+
+ $this->accessTokenMapper->method('getByCode')
+ ->with('validrefresh')
+ ->willReturn($accessToken);
+
+ $client = new Client();
+ $client->setClientIdentifier('clientId');
+ $client->setSecret(bin2hex('hashedClientSecret'));
+ $this->clientMapper->method('getByUid')
+ ->with(42)
+ ->willReturn($client);
+
+ $this->crypto
+ ->method('decrypt')
+ ->with('encryptedToken')
+ ->willReturn('decryptedToken');
+
+ $this->crypto
+ ->method('calculateHMAC')
+ ->with('clientSecret')
+ ->willReturn('hashedClientSecret');
+
+ $appToken = new PublicKeyToken();
+ $appToken->setUid('userId');
+ $this->tokenProvider->method('getTokenById')
+ ->with(1337)
+ ->willReturn($appToken);
+
+ $this->accessTokenMapper->expects($this->never())
+ ->method('delete')
+ ->with($accessToken);
+
+ $this->secureRandom->method('generate')
+ ->willReturnCallback(function ($len) {
+ return 'random' . $len;
+ });
+
+ $this->tokenProvider->expects($this->once())
+ ->method('rotate')
+ ->with(
+ $appToken,
+ 'decryptedToken',
+ 'random72'
+ )->willReturn($appToken);
+
+ $this->time->method('getTime')
+ ->willReturn(1000);
+
+ $this->tokenProvider->expects($this->once())
+ ->method('updateToken')
+ ->with(
+ $this->callback(function (PublicKeyToken $token) {
+ return $token->getExpires() === 4600;
+ })
+ );
+
+ $this->crypto->method('encrypt')
+ ->with('random72', 'random128')
+ ->willReturn('newEncryptedToken');
+
+ $this->accessTokenMapper->expects($this->once())
+ ->method('update')
+ ->with(
+ $this->callback(function (AccessToken $token) {
+ return $token->getHashedCode() === hash('sha512', 'random128')
+ && $token->getEncryptedToken() === 'newEncryptedToken';
+ })
+ );
+
+ $expected = new JSONResponse([
+ 'access_token' => 'random72',
+ 'token_type' => 'Bearer',
+ 'expires_in' => 3600,
+ 'refresh_token' => 'random128',
+ 'user_id' => 'userId',
+ ]);
+
+ $this->request->method('getRemoteAddress')
+ ->willReturn('1.2.3.4');
+
+ $this->throttler->expects($this->once())
+ ->method('resetDelay')
+ ->with(
+ '1.2.3.4',
+ 'login',
+ ['user' => 'userId']
+ );
+
+ $this->assertEquals($expected, $this->oauthApiController->getToken('refresh_token', null, 'validrefresh', 'clientId', 'clientSecret'));
+ }
+}
diff --git a/apps/oauth2/tests/Controller/SettingsControllerTest.php b/apps/oauth2/tests/Controller/SettingsControllerTest.php
new file mode 100644
index 00000000000..030a220e3d7
--- /dev/null
+++ b/apps/oauth2/tests/Controller/SettingsControllerTest.php
@@ -0,0 +1,188 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Tests\Controller;
+
+use OCA\OAuth2\Controller\SettingsController;
+use OCA\OAuth2\Db\AccessTokenMapper;
+use OCA\OAuth2\Db\Client;
+use OCA\OAuth2\Db\ClientMapper;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\JSONResponse;
+use OCP\Authentication\Token\IProvider as IAuthTokenProvider;
+use OCP\IL10N;
+use OCP\IRequest;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\Security\ICrypto;
+use OCP\Security\ISecureRandom;
+use OCP\Server;
+use Test\TestCase;
+
+/**
+ * @group DB
+ */
+class SettingsControllerTest extends TestCase {
+ /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject */
+ private $request;
+ /** @var ClientMapper|\PHPUnit\Framework\MockObject\MockObject */
+ private $clientMapper;
+ /** @var ISecureRandom|\PHPUnit\Framework\MockObject\MockObject */
+ private $secureRandom;
+ /** @var AccessTokenMapper|\PHPUnit\Framework\MockObject\MockObject */
+ private $accessTokenMapper;
+ /** @var IAuthTokenProvider|\PHPUnit\Framework\MockObject\MockObject */
+ private $authTokenProvider;
+ /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */
+ private $userManager;
+ /** @var SettingsController */
+ private $settingsController;
+ /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
+ private $l;
+ /** @var ICrypto|\PHPUnit\Framework\MockObject\MockObject */
+ private $crypto;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->request = $this->createMock(IRequest::class);
+ $this->clientMapper = $this->createMock(ClientMapper::class);
+ $this->secureRandom = $this->createMock(ISecureRandom::class);
+ $this->accessTokenMapper = $this->createMock(AccessTokenMapper::class);
+ $this->authTokenProvider = $this->createMock(IAuthTokenProvider::class);
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->crypto = $this->createMock(ICrypto::class);
+ $this->l = $this->createMock(IL10N::class);
+ $this->l->method('t')
+ ->willReturnArgument(0);
+ $this->settingsController = new SettingsController(
+ 'oauth2',
+ $this->request,
+ $this->clientMapper,
+ $this->secureRandom,
+ $this->accessTokenMapper,
+ $this->l,
+ $this->authTokenProvider,
+ $this->userManager,
+ $this->crypto
+ );
+
+ }
+
+ public function testAddClient(): void {
+ $this->secureRandom
+ ->expects($this->exactly(2))
+ ->method('generate')
+ ->with(64, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')
+ ->willReturnOnConsecutiveCalls(
+ 'MySecret',
+ 'MyClientIdentifier');
+
+ $this->crypto
+ ->expects($this->once())
+ ->method('calculateHMAC')
+ ->willReturn('MyHashedSecret');
+
+ $client = new Client();
+ $client->setName('My Client Name');
+ $client->setRedirectUri('https://example.com/');
+ $client->setSecret(bin2hex('MyHashedSecret'));
+ $client->setClientIdentifier('MyClientIdentifier');
+
+ $this->clientMapper
+ ->expects($this->once())
+ ->method('insert')
+ ->with($this->callback(function (Client $c) {
+ return $c->getName() === 'My Client Name'
+ && $c->getRedirectUri() === 'https://example.com/'
+ && $c->getSecret() === bin2hex('MyHashedSecret')
+ && $c->getClientIdentifier() === 'MyClientIdentifier';
+ }))->willReturnCallback(function (Client $c) {
+ $c->setId(42);
+ return $c;
+ });
+
+ $result = $this->settingsController->addClient('My Client Name', 'https://example.com/');
+ $this->assertInstanceOf(JSONResponse::class, $result);
+
+ $data = $result->getData();
+
+ $this->assertEquals([
+ 'id' => 42,
+ 'name' => 'My Client Name',
+ 'redirectUri' => 'https://example.com/',
+ 'clientId' => 'MyClientIdentifier',
+ 'clientSecret' => 'MySecret',
+ ], $data);
+ }
+
+ public function testDeleteClient(): void {
+
+ $userManager = Server::get(IUserManager::class);
+ // count other users in the db before adding our own
+ $count = 0;
+ $function = function (IUser $user) use (&$count): void {
+ if ($user->getLastLogin() > 0) {
+ $count++;
+ }
+ };
+ $userManager->callForAllUsers($function);
+ $user1 = $userManager->createUser('test101', 'test101');
+ $user1->updateLastLoginTimestamp();
+ $tokenProviderMock = $this->getMockBuilder(IAuthTokenProvider::class)->getMock();
+
+ // expect one call per user and ensure the correct client name
+ $tokenProviderMock
+ ->expects($this->exactly($count + 1))
+ ->method('invalidateTokensOfUser')
+ ->with($this->isType('string'), 'My Client Name');
+
+ $client = new Client();
+ $client->setId(123);
+ $client->setName('My Client Name');
+ $client->setRedirectUri('https://example.com/');
+ $client->setSecret(bin2hex('MyHashedSecret'));
+ $client->setClientIdentifier('MyClientIdentifier');
+
+ $this->clientMapper
+ ->method('getByUid')
+ ->with(123)
+ ->willReturn($client);
+ $this->accessTokenMapper
+ ->expects($this->once())
+ ->method('deleteByClientId')
+ ->with(123);
+ $this->clientMapper
+ ->expects($this->once())
+ ->method('delete')
+ ->with($client);
+
+ $settingsController = new SettingsController(
+ 'oauth2',
+ $this->request,
+ $this->clientMapper,
+ $this->secureRandom,
+ $this->accessTokenMapper,
+ $this->l,
+ $tokenProviderMock,
+ $userManager,
+ $this->crypto
+ );
+
+ $result = $settingsController->deleteClient(123);
+ $this->assertInstanceOf(JSONResponse::class, $result);
+ $this->assertEquals([], $result->getData());
+
+ $user1->delete();
+ }
+
+ public function testInvalidRedirectUri(): void {
+ $result = $this->settingsController->addClient('test', 'invalidurl');
+
+ $this->assertEquals(Http::STATUS_BAD_REQUEST, $result->getStatus());
+ $this->assertSame(['message' => 'Your redirect URL needs to be a full URL for example: https://yourdomain.com/path'], $result->getData());
+ }
+}
diff --git a/apps/oauth2/tests/Db/AccessTokenMapperTest.php b/apps/oauth2/tests/Db/AccessTokenMapperTest.php
new file mode 100644
index 00000000000..41a79fe725b
--- /dev/null
+++ b/apps/oauth2/tests/Db/AccessTokenMapperTest.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Tests\Db;
+
+use OCA\OAuth2\Db\AccessToken;
+use OCA\OAuth2\Db\AccessTokenMapper;
+use OCA\OAuth2\Exceptions\AccessTokenNotFoundException;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\IDBConnection;
+use OCP\Server;
+use Test\TestCase;
+
+/**
+ * @group DB
+ */
+class AccessTokenMapperTest extends TestCase {
+ /** @var AccessTokenMapper */
+ private $accessTokenMapper;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->accessTokenMapper = new AccessTokenMapper(Server::get(IDBConnection::class), Server::get(ITimeFactory::class));
+ }
+
+ public function testGetByCode(): void {
+ $this->accessTokenMapper->deleteByClientId(1234);
+ $token = new AccessToken();
+ $token->setClientId(1234);
+ $token->setTokenId((string)time());
+ $token->setEncryptedToken('MyEncryptedToken');
+ $token->setHashedCode(hash('sha512', 'MyAwesomeToken'));
+ $this->accessTokenMapper->insert($token);
+ $token->resetUpdatedFields();
+
+ $result = $this->accessTokenMapper->getByCode('MyAwesomeToken');
+ $this->assertEquals($token, $result);
+ $this->accessTokenMapper->delete($token);
+ }
+
+
+ public function testDeleteByClientId(): void {
+ $this->expectException(AccessTokenNotFoundException::class);
+
+ $this->accessTokenMapper->deleteByClientId(1234);
+ $token = new AccessToken();
+ $token->setClientId(1234);
+ $token->setTokenId((string)time());
+ $token->setEncryptedToken('MyEncryptedToken');
+ $token->setHashedCode(hash('sha512', 'MyAwesomeToken'));
+ $this->accessTokenMapper->insert($token);
+ $token->resetUpdatedFields();
+ $this->accessTokenMapper->deleteByClientId(1234);
+ $this->accessTokenMapper->getByCode('MyAwesomeToken');
+ }
+}
diff --git a/apps/oauth2/tests/Db/ClientMapperTest.php b/apps/oauth2/tests/Db/ClientMapperTest.php
new file mode 100644
index 00000000000..2e8d20ad200
--- /dev/null
+++ b/apps/oauth2/tests/Db/ClientMapperTest.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Tests\Db;
+
+use OCA\OAuth2\Db\Client;
+use OCA\OAuth2\Db\ClientMapper;
+use OCA\OAuth2\Exceptions\ClientNotFoundException;
+use OCP\IDBConnection;
+use OCP\Server;
+use Test\TestCase;
+
+/**
+ * @group DB
+ */
+class ClientMapperTest extends TestCase {
+ /** @var ClientMapper */
+ private $clientMapper;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->clientMapper = new ClientMapper(Server::get(IDBConnection::class));
+ }
+
+ protected function tearDown(): void {
+ $query = Server::get(IDBConnection::class)->getQueryBuilder();
+ $query->delete('oauth2_clients')->execute();
+
+ parent::tearDown();
+ }
+
+ public function testGetByIdentifier(): void {
+ $client = new Client();
+ $client->setClientIdentifier('MyAwesomeClientIdentifier');
+ $client->setName('Client Name');
+ $client->setRedirectUri('https://example.com/');
+ $client->setSecret('TotallyNotSecret');
+ $this->clientMapper->insert($client);
+ $client->resetUpdatedFields();
+ $this->assertEquals($client, $this->clientMapper->getByIdentifier('MyAwesomeClientIdentifier'));
+ }
+
+ public function testGetByIdentifierNotExisting(): void {
+ $this->expectException(ClientNotFoundException::class);
+
+ $this->clientMapper->getByIdentifier('MyTotallyNotExistingClient');
+ }
+
+ public function testGetByUid(): void {
+ $client = new Client();
+ $client->setClientIdentifier('MyNewClient');
+ $client->setName('Client Name');
+ $client->setRedirectUri('https://example.com/');
+ $client->setSecret('TotallyNotSecret');
+ $this->clientMapper->insert($client);
+ $client->resetUpdatedFields();
+ $this->assertEquals($client, $this->clientMapper->getByUid($client->getId()));
+ }
+
+ public function testGetByUidNotExisting(): void {
+ $this->expectException(ClientNotFoundException::class);
+
+ $this->clientMapper->getByUid(1234);
+ }
+
+ public function testGetClients(): void {
+ $this->assertSame('array', gettype($this->clientMapper->getClients()));
+ }
+
+ public function testInsertLongEncryptedSecret(): void {
+ $client = new Client();
+ $client->setClientIdentifier('MyNewClient');
+ $client->setName('Client Name');
+ $client->setRedirectUri('https://example.com/');
+ $client->setSecret('b81dc8e2dc178817bf28ca7b37265aa96559ca02e6dcdeb74b42221d096ed5ef63681e836ae0ba1077b5fb5e6c2fa7748c78463f66fe0110c8dcb8dd7eb0305b16d0cd993e2ae275879994a2abf88c68|e466d9befa6b0102341458e45ecd551a|013af9e277374483123437f180a3b0371a411ad4f34c451547909769181a7d7cc191f0f5c2de78376d124dd7751b8c9660aabdd913f5e071fc6b819ba2e3d919|3');
+ $this->clientMapper->insert($client);
+ $this->assertTrue(true);
+ }
+}
diff --git a/apps/oauth2/tests/Settings/AdminTest.php b/apps/oauth2/tests/Settings/AdminTest.php
new file mode 100644
index 00000000000..0f08bb30276
--- /dev/null
+++ b/apps/oauth2/tests/Settings/AdminTest.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\OAuth2\Tests\Settings;
+
+use OCA\OAuth2\Db\ClientMapper;
+use OCA\OAuth2\Settings\Admin;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
+use OCP\IURLGenerator;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+class AdminTest extends TestCase {
+
+ /** @var Admin|MockObject */
+ private $admin;
+
+ /** @var IInitialState|MockObject */
+ private $initialState;
+
+ /** @var ClientMapper|MockObject */
+ private $clientMapper;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->initialState = $this->createMock(IInitialState::class);
+ $this->clientMapper = $this->createMock(ClientMapper::class);
+
+ $this->admin = new Admin(
+ $this->initialState,
+ $this->clientMapper,
+ $this->createMock(IURLGenerator::class),
+ $this->createMock(LoggerInterface::class)
+ );
+ }
+
+ public function testGetForm(): void {
+ $expected = new TemplateResponse(
+ 'oauth2',
+ 'admin',
+ [],
+ ''
+ );
+ $this->assertEquals($expected, $this->admin->getForm());
+ }
+
+ public function testGetSection(): void {
+ $this->assertSame('security', $this->admin->getSection());
+ }
+
+ public function testGetPriority(): void {
+ $this->assertSame(100, $this->admin->getPriority());
+ }
+}