diff --git a/.gitignore b/.gitignore index 25851242fda3ea71887611907a5fa39836c782bd..7cd2be0c5bfd5752ee08ea7b9c8d31284698cd69 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ translationtool.phar .php-cs-fixer.cache .phpunit.result.cache junit.xml +css/ +.codex diff --git a/README.md b/README.md index ae3cbfe279036d444d7467776679cad9b24c90ae..2ee0f4ba209124378ef9913b474b04371a8a4d5a 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ ## User Account creation -- This plugin creates an endpoint `/apps/ecloud-accounts/api/set_account_data` that is to be used to set user's email, quota,recovery email and create the user's folder if necessary +- Form for user account creation accessible from `/apps/ecloud-accounts/accounts/{lang}/signup` +- For development info on account creation, see [account creation docs](docs/account-creation.md) ### Captcha Configuration for user account creation @@ -26,6 +27,13 @@ - `ecloud-accounts.hcaptcha_site_key` - `ecloud-accounts.hcaptcha_secret` +### Passwords E2EE setup at signup configuration (disabled by default) + +- If you want to enforce passwords E2EE setup at signup using the account password set by user: + `occ config:app:set ecloud-accounts setupPasswordsE2eeAtSignup --value true --type=boolean` +- To disable: + `occ config:app:set ecloud-accounts setupPasswordsE2eeAtSignup --value false --type=boolean` + ## Drop account - The drop account functionality plugin works in conjunction with the drop_account plugin : https://apps.nextcloud.com/apps/drop_account @@ -151,4 +159,4 @@ Needs configuration variables to be set: ``` oidc_vault_account_delete_url=vault-full-url oidc_vault_account_delete_token=vault-token -``` \ No newline at end of file +``` diff --git a/docs/account-creation.md b/docs/account-creation.md new file mode 100644 index 0000000000000000000000000000000000000000..b950e0539acb305c9b65ed9f0abea57912c177eb --- /dev/null +++ b/docs/account-creation.md @@ -0,0 +1,40 @@ +### Actions done when the create method is triggered + +- **Note: Steps that fail with an error are in bold; other steps fail silently in the background** + +### When passwords E2EE setup at signup is disabled + +```mermaid +flowchart TD + K{Browser} --> L{Server} + L --> A[1. Validation] + A --> B[2. Creation of LDAP record] + B --> C[3. Add username to permanent common data store] + C --> D[4. Set user data locally at nextcloud] + D --> E[5. Create HME and default @murena.io aliases] + E --> F[6. Set user ToS as accepted] + F --> G[7. Set user language] + G --> H[8. Set Newsletter signup choice of user] + H --> I[9. Set recovery email of user and trigger verification email] + I --> J[10. Trigger welcome email] +``` + +### When passwords E2EE setup at signup is enabled + +```mermaid +flowchart TD + K{Browser} --> L{Server} + L --> A[1. Validation] + A --> B[2. Creation of LDAP record] + B --> C[3. Add username to permanent common data store] + C --> D[4. Set user data locally at nextcloud] + D --> E[5. Create HME and default @murena.io aliases] + E --> F[6. Set user ToS as accepted] + F --> G[7. Set user language] + G --> H[8. Set Newsletter signup choice of user] + H --> I[9. Set recovery email of user and trigger verification email] + I --> J[10. Trigger welcome email] + J --> |Success| K + K --> N[12. Compute and setup user encryption challenge at server via API call] + N --> O[13. Setup user keychain at server via API call] +``` diff --git a/l10n/de.js b/l10n/de.js index 7b58fc509174f8d5d2ad928c86074f57bba847c4..5a5a6a83bc48f762b3dccbb64018836a806f0f6d 100644 --- a/l10n/de.js +++ b/l10n/de.js @@ -84,6 +84,7 @@ OC.L10N.register( "Username is too large.": "Der Benutzername ist zu groß.", "Display name is too large.": "Der Anzeigename ist zu groß.", "Password has invalid characters.": "Das Passwort enthält ungültige Zeichen.", - "I want to stay informed about Murena for business offers.": "Ich möchte Informationen über Murenas Angebote erhalten." + "I want to stay informed about Murena for business offers.": "Ich möchte Informationen über Murenas Angebote erhalten.", + "Your browser does not support the required encryption. Please use an up-to-date browser to continue.": "Ihr Browser unterstützt die erforderliche Verschlüsselung nicht. Bitte aktualisieren Sie Ihren Browser, um fortzufahren." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/de.json b/l10n/de.json index 30bd2d7381813dd2e120cd4736dbdf4156cdbdd6..06d7bc6d6a7c1f8fc378a78c37f133557acfa7fc 100644 --- a/l10n/de.json +++ b/l10n/de.json @@ -82,7 +82,8 @@ "Username is too large.": "Der Benutzername ist zu groß.", "Display name is too large.": "Der Anzeigename ist zu groß.", "Password has invalid characters.": "Das Passwort enthält ungültige Zeichen.", - "I want to stay informed about Murena for business offers.": "Ich möchte Informationen über Murenas Angebote erhalten." + "I want to stay informed about Murena for business offers.": "Ich möchte Informationen über Murenas Angebote erhalten.", + "Your browser does not support the required encryption. Please use an up-to-date browser to continue.": "Ihr Browser unterstützt die erforderliche Verschlüsselung nicht. Bitte aktualisieren Sie Ihren Browser, um fortzufahren." }, "pluralForm": "nplurals=2; plural=(n != 1);" } diff --git a/l10n/de_DE.js b/l10n/de_DE.js index 815e0347ae0672e2be39addb517d117419c84263..a45189345291262b3936e8d52e8b7076010a4c31 100644 --- a/l10n/de_DE.js +++ b/l10n/de_DE.js @@ -84,6 +84,7 @@ OC.L10N.register( "Username is too large.": "Der Benutzername ist zu groß.", "Display name is too large.": "Der Anzeigename ist zu groß.", "Password has invalid characters.": "Das Passwort enthält ungültige Zeichen.", - "I want to stay informed about Murena for business offers.": "Ich möchte Informationen über Murenas Angebote erhalten." + "I want to stay informed about Murena for business offers.": "Ich möchte Informationen über Murenas Angebote erhalten.", + "Your browser does not support the required encryption. Please use an up-to-date browser to continue.": "Ihr Browser unterstützt die erforderliche Verschlüsselung nicht. Bitte aktualisieren Sie Ihren Browser, um fortzufahren." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/de_DE.json b/l10n/de_DE.json index 7a3967f970b41afba66acae202f4085aff41cbb4..ae9c75fe7d9a1f5ed6ea646398cd7646725c6f3c 100644 --- a/l10n/de_DE.json +++ b/l10n/de_DE.json @@ -82,7 +82,8 @@ "Username is too large.": "Der Benutzername ist zu groß.", "Display name is too large.": "Der Anzeigename ist zu groß.", "Password has invalid characters.": "Das Passwort enthält ungültige Zeichen.", - "I want to stay informed about Murena for business offers.": "Ich möchte Informationen über Murenas Angebote erhalten." + "I want to stay informed about Murena for business offers.": "Ich möchte Informationen über Murenas Angebote erhalten.", + "Your browser does not support the required encryption. Please use an up-to-date browser to continue.": "Ihr Browser unterstützt die erforderliche Verschlüsselung nicht. Bitte aktualisieren Sie Ihren Browser, um fortzufahren." }, "pluralForm": "nplurals=2; plural=(n != 1);" } diff --git a/l10n/en.js b/l10n/en.js index e8ea893704d93627ebaccfef2ba7756c33dfe508..9209b1ff4550b3636fe50b04881ec6f861f28e9a 100644 --- a/l10n/en.js +++ b/l10n/en.js @@ -87,6 +87,7 @@ OC.L10N.register( "Username is too large.": "Username is too large.", "Display name is too large.": "Display name is too large.", "Password has invalid characters.": "Password has invalid characters.", - "I want to stay informed about Murena for business offers.": "I want to stay informed about Murena for business offers." + "I want to stay informed about Murena for business offers.": "I want to stay informed about Murena for business offers.", + "Your browser does not support the required encryption. Please use an up-to-date browser to continue.": "Your browser does not support the required encryption. Please use an up-to-date browser to continue." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/en.json b/l10n/en.json index d0c84c9c29fbe2b74bfed49fdba64f2af532865f..d6e8e1ad6897ce7e07fd538c5355e5c0a3a0f007 100644 --- a/l10n/en.json +++ b/l10n/en.json @@ -84,7 +84,8 @@ "Username is too large.": "Username is too large.", "Display name is too large.": "Display name is too large.", "Password has invalid characters.": "La contraseña tiene caracteres no válidos.", - "I want to stay informed about Murena for business offers.": "I want to stay informed about Murena for business offers." + "I want to stay informed about Murena for business offers.": "I want to stay informed about Murena for business offers.", + "Your browser does not support the required encryption. Please use an up-to-date browser to continue.": "Your browser does not support the required encryption. Please use an up-to-date browser to continue." }, "pluralForm": "nplurals=2; plural=(n != 1);" } diff --git a/l10n/es.js b/l10n/es.js index 2a134171d2550fb27efcb63659b88798040f9ed6..332318689ce74e0901c9ac916b679c71d111adc5 100644 --- a/l10n/es.js +++ b/l10n/es.js @@ -86,6 +86,7 @@ OC.L10N.register( "Username is too large.": "El nombre de usuario es demasiado grande.", "Display name is too large.": "El nombre para mostrar es demasiado grande.", "Password has invalid characters.": "La contraseña tiene caracteres no válidos.", - "I want to stay informed about Murena for business offers.": "Deseo permanecer informado sobre las ofertas de Murena para empresas." + "I want to stay informed about Murena for business offers.": "Deseo permanecer informado sobre las ofertas de Murena para empresas.", + "Your browser does not support the required encryption. Please use an up-to-date browser to continue.": "Tu navegador no admite el cifrado requerido. Utiliza un navegador actualizado para continuar." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/es.json b/l10n/es.json index de0fde984696c429e8eabac333ac65ec22a62d7a..88c4ba50a1d4c57c25e617a9ef94eb91cba01667 100644 --- a/l10n/es.json +++ b/l10n/es.json @@ -85,7 +85,8 @@ "Username is too large.": "El nombre de usuario es demasiado grande.", "Display name is too large.": "El nombre para mostrar es demasiado grande.", "Password has invalid characters.": "Password has invalid characters.", - "I want to stay informed about Murena for business offers.": "Deseo permanecer informado sobre las ofertas de Murena para empresas." + "I want to stay informed about Murena for business offers.": "Deseo permanecer informado sobre las ofertas de Murena para empresas.", + "Your browser does not support the required encryption. Please use an up-to-date browser to continue.": "Tu navegador no admite el cifrado requerido. Utiliza un navegador actualizado para continuar." }, "pluralForm": "nplurals=2; plural=(n != 1);" } diff --git a/l10n/fr.js b/l10n/fr.js index 6e94f030a4071a45e0581badace999bbd1fd3a70..25481a169e2b1eaac3855a57a6c0eb870a52dcd1 100644 --- a/l10n/fr.js +++ b/l10n/fr.js @@ -85,6 +85,7 @@ OC.L10N.register( "Username is too large.": "Le nom d'utilisateur est trop grand.", "Display name is too large.": "Le nom affiché est trop grand.", "Password has invalid characters.": "Le mot de passe contient des caractères non valides.", - "I want to stay informed about Murena for business offers.": "Je souhaite rester informé·e des offres Murena pour les entreprises." + "I want to stay informed about Murena for business offers.": "Je souhaite rester informé·e des offres Murena pour les entreprises.", + "Your browser does not support the required encryption. Please use an up-to-date browser to continue.": "Votre navigateur ne prend pas en charge le chiffrement requis. Veuillez utiliser un navigateur à jour pour continuer." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/fr.json b/l10n/fr.json index 288c8749b26431e49b0d58cebe1c5c6d59c8d2e1..2c80d3a507ea3b3866bdd583e28d2dc281fc7991 100644 --- a/l10n/fr.json +++ b/l10n/fr.json @@ -84,7 +84,8 @@ "Username is too large.": "Le nom d'utilisateur est trop grand.", "Display name is too large.": "Le nom affiché est trop grand.", "Password has invalid characters.": "Le mot de passe contient des caractères non valides.", - "I want to stay informed about Murena for business offers.": "Je souhaite rester informé·e des offres Murena pour les entreprises." + "I want to stay informed about Murena for business offers.": "Je souhaite rester informé·e des offres Murena pour les entreprises.", + "Your browser does not support the required encryption. Please use an up-to-date browser to continue.": "Votre navigateur ne prend pas en charge le chiffrement requis. Veuillez utiliser un navigateur à jour pour continuer." }, "pluralForm": "nplurals=2; plural=(n != 1);" } diff --git a/l10n/it.js b/l10n/it.js index e804cb1d16baf3dba1eb8c44bda143ebffcb8f61..a18bfeab08728ae99034532bb0b7f8d5f5f42457 100644 --- a/l10n/it.js +++ b/l10n/it.js @@ -85,6 +85,7 @@ OC.L10N.register( "Username is too large.": "Il nome utente è troppo grande.", "Display name is too large.": "Il nome del display è troppo grande.", "Password has invalid characters.": "La password contiene caratteri non validi.", - "I want to stay informed about Murena for business offers.": "Desidero ricevere informazioni sulle offerte commerciali di Murena." + "I want to stay informed about Murena for business offers.": "Desidero ricevere informazioni sulle offerte commerciali di Murena.", + "Your browser does not support the required encryption. Please use an up-to-date browser to continue.": "Il tuo browser non supporta la crittografia richiesta. Utilizza un browser aggiornato per continuare." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/it.json b/l10n/it.json index 1bacf80b539de51d97e40a54ca2fb48929a7798e..3989a8d8fe458036c8803e0a0e35f25149f13147 100644 --- a/l10n/it.json +++ b/l10n/it.json @@ -81,7 +81,8 @@ "Display name is too large.": "Il nome del display è troppo grande.", "Password has invalid characters.": "La password contiene caratteri non validi.", "I have read and accept the Terms of Service.": "Ho letto e accetto i Termini di servizio.", - "I want to stay informed about Murena for business offers.": "Desidero ricevere informazioni sulle offerte commerciali di Murena." + "I want to stay informed about Murena for business offers.": "Desidero ricevere informazioni sulle offerte commerciali di Murena.", + "Your browser does not support the required encryption. Please use an up-to-date browser to continue.": "Il tuo browser non supporta la crittografia richiesta. Utilizza un browser aggiornato per continuare." }, "pluralForm": "nplurals=2; plural=(n != 1);" } diff --git a/lib/Controller/AccountController.php b/lib/Controller/AccountController.php index 54d316accefa021974cf781b6bd63f5a05c68f69..379d2e0fa56e1fa6e37f76fab5b268d17b3a01a3 100644 --- a/lib/Controller/AccountController.php +++ b/lib/Controller/AccountController.php @@ -14,6 +14,7 @@ use OCA\EcloudAccounts\Service\CaptchaService; use OCA\EcloudAccounts\Service\HCaptchaService; use OCA\EcloudAccounts\Service\NewsLetterService; use OCA\EcloudAccounts\Service\UserService; +use OCP\App\IAppManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; @@ -52,7 +53,8 @@ class AccountController extends Controller { private const HCAPTCHA_PROVIDER = 'hcaptcha'; private const HCAPTCHA_DOMAINS = ['https://hcaptcha.com', 'https://*.hcaptcha.com']; private const BLACKLISTED_USERNAMES_FILE_NAME = 'blacklisted_usernames'; - + private const PASSWORDS_APP_ID = 'passwords'; + private LoggerInterface $logger; public function __construct( $AppName, @@ -66,6 +68,7 @@ class AccountController extends Controller { IURLGenerator $urlGenerator, ISession $session, IConfig $config, + private IAppManager $appManager, LoggerInterface $logger, IInitialState $initialState, IAppData $appData, @@ -113,18 +116,27 @@ class AccountController extends Controller { $captchaProvider = $this->getCaptchaProvider(); $this->initialState->provideInitialState('captchaProvider', $captchaProvider); + $csp = $response->getContentSecurityPolicy(); + $setupPasswordsE2ee = false; + + if ($this->shouldSetupPasswordsE2eeAtSignup()) { + $setupPasswordsE2ee = true; + $csp->allowEvalWasm(); + } + $this->initialState->provideInitialState('shouldSetupPasswordsE2ee', $setupPasswordsE2ee); + if ($captchaProvider === self::HCAPTCHA_PROVIDER) { - $csp = $response->getContentSecurityPolicy(); foreach (self::HCAPTCHA_DOMAINS as $domain) { $csp->addAllowedScriptDomain($domain); $csp->addAllowedFrameDomain($domain); $csp->addAllowedStyleDomain($domain); $csp->addAllowedConnectDomain($domain); } - $response->setContentSecurityPolicy($csp); $hcaptchaSiteKey = $this->config->getSystemValue(Application::APP_ID . '.hcaptcha_site_key'); $this->initialState->provideInitialState('hCaptchaSiteKey', $hcaptchaSiteKey); } + $response->setContentSecurityPolicy($csp); + return $response; } @@ -171,7 +183,7 @@ class AccountController extends Controller { $inputData = [ 'username' => ['value' => $username, 'maxLength' => 30], 'display name' => ['value' => $displayname, 'maxLength' => 30], - 'password' => ['value' => $password, 'maxLength' => 1024], + 'password' => ['value' => $password, 'maxLength' => 32], ]; foreach ($inputData as $inputName => $inputInfo) { @@ -218,6 +230,11 @@ class AccountController extends Controller { $this->session->remove(self::SESSION_VERIFIED_DISPLAYNAME); $this->session->remove(self::CAPTCHA_VERIFIED_CHECK); + // Session needed for client to setup E2EE at passwords app via API calls + if ($this->shouldSetupPasswordsE2eeAtSignup()) { + $user = $this->userService->getUser($username); + $this->userSession->setUser($user); + } $response->setStatus(200); $response->setData(['success' => true]); @@ -408,4 +425,8 @@ class AccountController extends Controller { return $captchaProvider; } + private function shouldSetupPasswordsE2eeAtSignup() : bool { + return $this->appManager->isEnabledForUser(self::PASSWORDS_APP_ID) && $this->config->getAppValue(Application::APP_ID, 'setupPasswordsE2eeAtSignup', false); + } + } diff --git a/package-lock.json b/package-lock.json index 3cd4fc6c4da5900612973cd17651f0241f54b8df..a383b43696a37cf3c0ffd20ad1a4006042e9b163 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@nextcloud/l10n": "^3.1.0", "@nextcloud/router": "^3.0.1", "@nextcloud/vue": "^8.16.0", + "passwords-client": "1.0.0-alpha.5731", "vue": "^2.7.0", "vue-password-strength-meter": "^1.7.2" }, @@ -9325,6 +9326,21 @@ "node": ">= 0.8.0" } }, + "node_modules/libsodium": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.10.tgz", + "integrity": "sha512-eY+z7hDrDKxkAK+QKZVNv92A5KYkxfvIshtBJkmg5TSiCnYqZP3i9OO9whE79Pwgm4jGaoHgkM4ao/b9Cyu4zQ==", + "license": "ISC" + }, + "node_modules/libsodium-wrappers": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz", + "integrity": "sha512-pO3F1Q9NPLB/MWIhehim42b/Fwb30JNScCNh8TcQ/kIc+qGLQch8ag8wb0keK3EP5kbGakk1H8Wwo7v+36rNQg==", + "license": "ISC", + "dependencies": { + "libsodium": "^0.7.0" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -11130,6 +11146,26 @@ "node": ">= 0.8" } }, + "node_modules/passwords-client": { + "version": "1.0.0-alpha.5731", + "resolved": "https://registry.npmjs.org/passwords-client/-/passwords-client-1.0.0-alpha.5731.tgz", + "integrity": "sha512-Pl36mcHhUlbN9IjJAkPmcunXPvC5Rz/hVZZgUDKM+GOUIzVSsH8ltPliUJCN+2tugc0x/aINmO987CXNptWcYw==", + "license": "ISC", + "dependencies": { + "eventemitter3": "^3.1.2", + "libsodium": "0.7.10", + "libsodium-wrappers": "0.7.10", + "process": "^0.11.10", + "url-parse": "^1.5.3", + "uuid": "^8.3.2" + } + }, + "node_modules/passwords-client/node_modules/eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", + "license": "MIT" + }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -11566,8 +11602,6 @@ "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "peer": true, "engines": { "node": ">= 0.6.0" } @@ -14418,8 +14452,6 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "peer": true, "bin": { "uuid": "dist/bin/uuid" } diff --git a/package.json b/package.json index 58337c9e399aa671bbba6a69aa503dbda148c2ec..0337d1710e0b80cbb44af2e4b852a56ef19f6584 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "@nextcloud/router": "^3.0.1", "@nextcloud/vue": "^8.16.0", "vue": "^2.7.0", - "vue-password-strength-meter": "^1.7.2" + "vue-password-strength-meter": "^1.7.2", + "passwords-client": "1.0.0-alpha.5731" }, "browserslist": [ "extends @nextcloud/browserslist-config" diff --git a/src/Signup.vue b/src/Signup.vue index 9f0d163c0547d2ab2c2c6c5cebeac2e8cbb9b705..e3615469f7c09694213e1ef7948b5c284ed9dc57 100644 --- a/src/Signup.vue +++ b/src/Signup.vue @@ -2,7 +2,10 @@