├── .gitignore
├── Resources
└── Private
│ ├── Fusion
│ ├── Root.fusion
│ └── NodeTypes
│ │ ├── Registration.fusion
│ │ ├── Profile.fusion
│ │ └── LoginForm.fusion
│ ├── Layouts
│ ├── Neos
│ │ └── Default.html
│ └── Default.html
│ ├── EmailTemplates
│ ├── ActivationToken.txt
│ ├── ResetPasswordToken.txt
│ ├── ActivationToken.html
│ └── ResetPasswordToken.html
│ ├── Templates
│ ├── Login
│ │ ├── Authenticate.html
│ │ └── Login.html
│ ├── Registration
│ │ ├── Register.html
│ │ ├── ActivateAccount.html
│ │ └── Index.html
│ ├── ResetPassword
│ │ ├── RequestToken.html
│ │ ├── UpdatePassword.html
│ │ ├── Index.html
│ │ └── InsertNewPassword.html
│ └── Profile
│ │ └── Index.html
│ ├── Translations
│ ├── en
│ │ ├── NodeTypes
│ │ │ ├── Registration.xlf
│ │ │ ├── LoginForm.xlf
│ │ │ └── Profile.xlf
│ │ ├── Partials
│ │ │ └── Profile
│ │ │ │ ├── PersonalForm.xlf
│ │ │ │ ├── PasswordForm.xlf
│ │ │ │ └── AccountData.xlf
│ │ ├── Templates
│ │ │ └── Partials
│ │ │ │ └── Profile.xlf
│ │ └── Main.xlf
│ ├── de
│ │ ├── NodeTypes
│ │ │ ├── Registration.xlf
│ │ │ ├── LoginForm.xlf
│ │ │ └── Profile.xlf
│ │ ├── Partials
│ │ │ └── Profile
│ │ │ │ ├── PersonalForm.xlf
│ │ │ │ ├── PasswordForm.xlf
│ │ │ │ └── AccountData.xlf
│ │ ├── Templates
│ │ │ └── Partials
│ │ │ │ └── Profile.xlf
│ │ └── Main.xlf
│ └── fr
│ │ ├── NodeTypes
│ │ ├── Registration.xlf
│ │ ├── LoginForm.xlf
│ │ └── Profile.xlf
│ │ ├── Partials
│ │ └── Profile
│ │ │ ├── PersonalForm.xlf
│ │ │ ├── PasswordForm.xlf
│ │ │ └── AccountData.xlf
│ │ ├── Templates
│ │ └── Partials
│ │ │ └── Profile.xlf
│ │ └── Main.xlf
│ └── Partials
│ ├── LogoutForm.html
│ ├── FormErrors.html
│ └── Profile
│ ├── AccountData.html
│ ├── PasswordForm.html
│ └── PersonalForm.html
├── .editorconfig
├── Configuration
├── Views.yaml
├── NodeTypes.Registration.yaml
├── Objects.yaml
├── NodeTypes.Profile.yaml
├── NodeTypes.LoginForm.yaml
├── Policy.yaml
├── Routes.yaml
└── Settings.yaml
├── Classes
├── Domain
│ ├── Service
│ │ ├── UserCreationServiceInterface.php
│ │ ├── RegistrationFlowValidationServiceInterface.php
│ │ ├── RedirectTargetServiceInterface.php
│ │ ├── Flow
│ │ │ ├── FlowUserCreationService.php
│ │ │ └── FlowRedirectTargetService.php
│ │ └── Neos
│ │ │ ├── NeosUserCreationService.php
│ │ │ └── NeosRedirectTargetService.php
│ ├── Repository
│ │ ├── UserRepository.php
│ │ ├── ResetPasswordFlowRepository.php
│ │ └── RegistrationFlowRepository.php
│ ├── Validator
│ │ ├── RegistrationFlowValidator.php
│ │ └── CustomPasswordDtoValidator.php
│ └── Model
│ │ ├── ResetPasswordFlow.php
│ │ ├── PasswordDto.php
│ │ ├── User.php
│ │ └── RegistrationFlow.php
├── Security
│ └── NeosRequestPattern.php
├── ViewHelpers
│ └── IfAuthenticatedViewHelper.php
├── Controller
│ ├── ProfileController.php
│ ├── RegistrationController.php
│ ├── ResetPasswordController.php
│ └── LoginController.php
└── Command
│ └── SandstormUserCommandController.php
├── LICENSE
├── composer.json
├── Migrations
└── Mysql
│ ├── Version20161201174312.php
│ ├── Version20160526150442.php
│ ├── Version20160530132725.php
│ ├── Version20161101091328.php
│ ├── Version20160526150536.php
│ ├── Version20160524125855.php
│ ├── Version20160607125833.php
│ └── Version20160519150828.php
├── Tests
└── Unit
│ └── Domain
│ └── PasswordDtoTest.php
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 |
--------------------------------------------------------------------------------
/Resources/Private/Fusion/Root.fusion:
--------------------------------------------------------------------------------
1 | include: NodeTypes/*
--------------------------------------------------------------------------------
/Resources/Private/Layouts/Neos/Default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | indent_style = space
3 | indent_size = 4
4 | end_of_line = lf
5 | charset = utf-8
6 | trim_trailing_whitespace = true
7 | insert_final_newline = true
8 |
9 | [*.{yaml,html}]
10 | indent_size = 2
11 |
--------------------------------------------------------------------------------
/Configuration/Views.yaml:
--------------------------------------------------------------------------------
1 | -
2 | requestFilter: 'mainRequest.isPackage("Neos.Neos") && isPackage("Sandstorm.UserManagement")'
3 | options:
4 | layoutRootPaths: ['resource://Sandstorm.UserManagement/Private/Layouts/Neos']
5 |
--------------------------------------------------------------------------------
/Resources/Private/Fusion/NodeTypes/Registration.fusion:
--------------------------------------------------------------------------------
1 | prototype(Sandstorm.UserManagement:Registration) < prototype(Neos.Neos:Plugin) {
2 | package = 'Sandstorm.UserManagement'
3 | controller = 'Registration'
4 | action = 'index'
5 | node = ${node}
6 | }
7 |
--------------------------------------------------------------------------------
/Resources/Private/EmailTemplates/ActivationToken.txt:
--------------------------------------------------------------------------------
1 | Dear User,
2 |
3 | you signed up for an account at our service. To confirm your account,
4 | please click the link below or copy it into your browser:
5 |
6 | {activationLink}
7 |
8 | Thank you for your interest in our service!
9 |
--------------------------------------------------------------------------------
/Resources/Private/EmailTemplates/ResetPasswordToken.txt:
--------------------------------------------------------------------------------
1 | Dear User,
2 |
3 | you want to reset your password for our service. To continue,
4 | please click the link below or copy it into your browser:
5 |
6 | {resetPasswordLink}
7 |
8 | If you did not want to reset your password, please ignore this mail.
9 |
--------------------------------------------------------------------------------
/Configuration/NodeTypes.Registration.yaml:
--------------------------------------------------------------------------------
1 | ##
2 | # A simple "Login form" plugin that demonstrates "Frontend authorization"
3 | #
4 | 'Sandstorm.UserManagement:Registration':
5 | superTypes:
6 | 'Neos.Neos:Plugin': TRUE
7 | ui:
8 | label: i18n
9 | icon: 'icon-key'
10 | inspector:
11 | groups:
12 | pluginSettings:
13 | label: i18n
14 |
--------------------------------------------------------------------------------
/Resources/Private/EmailTemplates/ActivationToken.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dear User,
4 |
5 | you signed up for an account at our service. To confirm your account,
6 | please click the link below.
7 |
8 | Click here to activate your account
9 |
10 | Thank you for your interest in our service!
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Resources/Private/EmailTemplates/ResetPasswordToken.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dear User,
4 |
5 | you want to reset your password for our service. To continue,
6 | please click the link below:
7 |
8 | Click here to reset your password
9 |
10 | If you did not want to reset your password, please ignore this mail.
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Configuration/Objects.yaml:
--------------------------------------------------------------------------------
1 | # Use the Flow services as default and switch to Neos as necessary
2 | Sandstorm\UserManagement\Domain\Service\RedirectTargetServiceInterface:
3 | className: 'Sandstorm\UserManagement\Domain\Service\Flow\FlowRedirectTargetService'
4 | Sandstorm\UserManagement\Domain\Service\UserCreationServiceInterface:
5 | className: 'Sandstorm\UserManagement\Domain\Service\Flow\FlowUserCreationService'
6 |
--------------------------------------------------------------------------------
/Resources/Private/Templates/Login/Authenticate.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | Login
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Resources/Private/Fusion/NodeTypes/Profile.fusion:
--------------------------------------------------------------------------------
1 | prototype(Sandstorm.UserManagement:Profile) < prototype(Neos.Neos:Plugin) {
2 | package = 'Sandstorm.UserManagement'
3 | controller = 'Profile'
4 | action = 'index'
5 | showPersonalInformation = ${q(node).property('showPersonalInformation')}
6 | showAccountInformation = ${q(node).property('showAccountInformation')}
7 | enableNewPassword = ${q(node).property('enableNewPassword')}
8 | }
9 |
--------------------------------------------------------------------------------
/Resources/Private/Fusion/NodeTypes/LoginForm.fusion:
--------------------------------------------------------------------------------
1 | ##
2 | # "LoginForm" element, extending "Plugin"
3 | #
4 | prototype(Sandstorm.UserManagement:LoginForm) < prototype(Neos.Neos:Plugin) {
5 | package = 'Sandstorm.UserManagement'
6 | controller = 'Login'
7 | action = 'login'
8 |
9 | redirectAfterLogin = ${q(node).property('redirectAfterLogin')}
10 | redirectAfterLogout = ${q(node).property('redirectAfterLogout')}
11 | node = ${node}
12 | }
13 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/en/NodeTypes/Registration.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Registration Form
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Resources/Private/Templates/Registration/Register.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | Registration
9 |
10 |
11 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/de/NodeTypes/Registration.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Registration Form
7 | Registrierungsformular
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/fr/NodeTypes/Registration.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Registration Form
7 | Formulaire d'inscription
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Resources/Private/Layouts/Default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Resources/Private/Partials/LogoutForm.html:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Resources/Private/Templates/ResetPassword/RequestToken.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | Passwort vergessen
9 |
10 |
11 |
12 |
13 |
14 | Eine Email wurde an {resetPasswordFlow.email} versendet,
15 | falls dieser Account existiert. Klicken Sie auf
16 | den enthaltenen Link, um Ihr Passwort zurückzusetzen.
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Resources/Private/Partials/FormErrors.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | {error}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Classes/Domain/Service/UserCreationServiceInterface.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | First name
7 |
8 |
9 | Last name
10 |
11 |
12 | Change personal data
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Classes/Domain/Service/RegistrationFlowValidationServiceInterface.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | New Password
7 |
8 |
9 | Repeat new password
10 |
11 |
12 | Change password
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Classes/Domain/Repository/UserRepository.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Profile Settings
7 |
8 |
9 | User
10 |
11 |
12 | Account
13 |
14 |
15 | Password
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/en/Partials/Profile/AccountData.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | E-mail address:
7 |
8 |
9 | Role(s):
10 |
11 |
12 | Created on:
13 |
14 |
15 | Y-m-d
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Configuration/NodeTypes.LoginForm.yaml:
--------------------------------------------------------------------------------
1 | ##
2 | # A simple "Login form" plugin that demonstrates "Frontend authorization"
3 | #
4 | 'Sandstorm.UserManagement:LoginForm':
5 | superTypes:
6 | 'Neos.Neos:Plugin': TRUE
7 | ui:
8 | label: i18n
9 | icon: 'icon-key'
10 | inspector:
11 | groups:
12 | pluginSettings:
13 | label: i18n
14 |
15 | properties:
16 | redirectAfterLogin:
17 | type: reference
18 | ui:
19 | label: i18n
20 | reloadIfChanged: false
21 | inspector:
22 | group: pluginSettings
23 | editorOptions:
24 | nodeTypes:
25 | - 'Neos.Neos:Document'
26 | redirectAfterLogout:
27 | type: reference
28 | ui:
29 | label: i18n
30 | reloadIfChanged: false
31 | inspector:
32 | group: pluginSettings
33 | editorOptions:
34 | nodeTypes:
35 | - 'Neos.Neos:Document'
36 |
--------------------------------------------------------------------------------
/Classes/Domain/Repository/ResetPasswordFlowRepository.php:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | Passwort vergessen
9 |
10 |
11 |
12 |
13 |
14 |
Das Passwort wurde erfolgreich aktualisiert.
15 |
16 | Zum Login
17 |
18 |
19 |
20 |
21 |
Es gab einen Fehler beim zurücksetzen Ihres Passworts.
22 |
23 | Nochmals versuchen
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/en/NodeTypes/LoginForm.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Login Form
7 |
8 |
9 | Login Settings
10 |
11 |
12 | Redirect after Login
13 |
14 |
15 | Redirect after Logout
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/de/Partials/Profile/PersonalForm.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | First name
7 | Vorname
8 |
9 |
10 | Last name
11 | Nachname
12 |
13 |
14 | Change personal data
15 | Persönliche Daten ändern
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/fr/Partials/Profile/PersonalForm.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | First name
7 | Prénom
8 |
9 |
10 | Last name
11 | Nom de famille
12 |
13 |
14 | Change personal data
15 | Modifier les informations personnelles
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Classes/Domain/Repository/RegistrationFlowRepository.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | New Password
7 | Neues Passwort
8 |
9 |
10 | Repeat new password
11 | Neues Passwort wiederholen
12 |
13 |
14 | Change password
15 | Passwort ändern
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/fr/Partials/Profile/PasswordForm.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | New Password
7 | Nouveau mot de passe
8 |
9 |
10 | Repeat new password
11 | Répéter le nouveau mot de passe
12 |
13 |
14 | Change password
15 | Modifier le mot de passe
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/de/Templates/Partials/Profile.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Profile Settings
7 | Profileinstellungen
8 |
9 |
10 | User
11 | Benutzer
12 |
13 |
14 | Account
15 | Konto
16 |
17 |
18 | Password
19 | Passwort
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/fr/Templates/Partials/Profile.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Profile Settings
7 | Réglages du profil
8 |
9 |
10 | User
11 | Utilisateur
12 |
13 |
14 | Account
15 | Compte
16 |
17 |
18 | Password
19 | Mot de passe
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 sandstorm|media
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/fr/Partials/Profile/AccountData.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | E-mail address:
7 | Adresse email:
8 |
9 |
10 | Role(s):
11 | Rôle(s):
12 |
13 |
14 | Created on:
15 | Créé le:
16 |
17 |
18 | Y-m-d
19 | d.m.Y
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/de/Partials/Profile/AccountData.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | E-mail address:
7 | E-Mail Adresse:
8 |
9 |
10 | Role(s):
11 | Rolle(n):
12 |
13 |
14 | Created on:
15 | Erstellt am:
16 |
17 |
18 | Y-m-d
19 | d.m.Y
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/en/NodeTypes/Profile.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | User Profile
7 |
8 |
9 | User Profile Settings
10 |
11 |
12 | Show Personal Information
13 |
14 |
15 | Show Account Information
16 |
17 |
18 | Enable Setting of a new Password
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/de/NodeTypes/LoginForm.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Login Form
7 | Login-Formular
8 |
9 |
10 | Login Settings
11 | Login-Einstellungen
12 |
13 |
14 | Redirect after Login
15 | Weiterleitung nach Login
16 |
17 |
18 | Redirect after Logout
19 | Weiterleitung nach Logout
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Resources/Private/Partials/Profile/AccountData.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 | {account.accountIdentifier}
11 |
12 |
13 |
14 |
15 | {role.name}
16 |
17 |
18 |
19 |
20 |
21 | {account.creationDate.date}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sandstorm/usermanagement",
3 | "description": "Neos and Flow package for user management, login/logout, password reset and user activation",
4 | "type": "neos-package",
5 | "keywords": ["user", "login", "logout", "registration", "frontend login", "forgot password", "account", "Neos", "Flow"],
6 | "homepage": "https://github.com/sandstorm/UserManagement",
7 | "license": "MIT",
8 | "authors": [
9 | {
10 | "name": "Bastian Heist",
11 | "homepage": "https://www.sandstorm.de",
12 | "role": "Developer"
13 | },
14 | {
15 | "name": "Sebastian Kurfuerst",
16 | "homepage": "https://www.sandstorm.de",
17 | "role": "Developer"
18 | }
19 | ],
20 | "support": {
21 | "issues": "https://github.com/sandstorm/UserManagement/issues",
22 | "source": "https://github.com/sandstorm/UserManagement"
23 | },
24 | "require": {
25 | "neos/flow": "^6.0 || ^7.0 || ^8.0 || dev-master",
26 | "sandstorm/templatemailer": "^2.0.2"
27 | },
28 | "autoload": {
29 | "psr-4": {
30 | "Sandstorm\\UserManagement\\": "Classes"
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/fr/NodeTypes/LoginForm.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Login Form
7 | Formulaire de connexion
8 |
9 |
10 | Login Settings
11 | Réglages de connexion
12 |
13 |
14 | Redirect after Login
15 | Redirection après connexion
16 |
17 |
18 | Redirect after Logout
19 | Redirection après déconnexion
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Resources/Private/Partials/Profile/PasswordForm.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 | :
10 |
11 |
12 |
13 | :
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Resources/Private/Templates/ResetPassword/Index.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | Passwort vergessen
9 |
10 |
11 |
12 | Bitte geben Sie Ihre Email-Adresse ein. Wir schicken Ihnen dann einen Link, mit dem Sie Ihr Passwort zurücksetzen können.
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {propertyName}:
21 |
22 |
23 | {error}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Classes/Security/NeosRequestPattern.php:
--------------------------------------------------------------------------------
1 | options = $options;
25 | }
26 |
27 | /**
28 | * Matches a \Neos\Flow\Mvc\ActionRequest against its set pattern rules
29 | *
30 | * @param ActionRequest $request The request that should be matched
31 | * @return boolean TRUE if the pattern matched, FALSE otherwise
32 | */
33 | public function matchRequest(ActionRequest $request)
34 | {
35 | $shouldMatchBackend = ($this->options['area'] === self::AREA_FRONTEND) ? false : true;
36 |
37 | $requestPath = $request->getHttpRequest()->getUri()->getPath();
38 | $requestPathMatchesBackend = substr($requestPath, 0, 5) === '/neos' || substr($requestPath, 0, 6) === '/setup' || strpos($requestPath, '@') !== false;
39 |
40 | return $shouldMatchBackend === $requestPathMatchesBackend;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Resources/Private/Templates/Login/Login.html:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 | Login
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/de/NodeTypes/Profile.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | User Profile
7 | Benutzerprofil
8 |
9 |
10 | User Profile Settings
11 | Benutzerprofileinstellungen
12 |
13 |
14 | Show Personal Information
15 | Pesönliche Informationen anzeigen
16 |
17 |
18 | Show Account Information
19 | Kontoinformazionen anzeigen
20 |
21 |
22 | Enable Setting of a new Password
23 | Setzen eines neuen Passwortes aktivieren
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/fr/NodeTypes/Profile.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | User Profile
7 | Profil utilisateur
8 |
9 |
10 | User Profile Settings
11 | Réglage du profil utilisateur
12 |
13 |
14 | Show Personal Information
15 | Afficher les informations personnelles
16 |
17 |
18 | Show Account Information
19 | Afficher les informations du compte
20 |
21 |
22 | Enable Setting of a new Password
23 | Afficher la création d'un nouveau mot de passe
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Classes/Domain/Service/RedirectTargetServiceInterface.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".');
28 | $this->addSql("ALTER TABLE sandstorm_usermanagement_domain_model_registrationflow CHANGE attributes attributes LONGTEXT NOT NULL COMMENT '(DC2Type:flow_json_array)'");
29 | }
30 |
31 | /**
32 | * @param Schema $schema
33 | * @return void
34 | */
35 | public function down(Schema $schema): void
36 | {
37 | // this down() migration is autogenerated, please modify it to your needs
38 | $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".');
39 | $this->addSql("ALTER TABLE sandstorm_usermanagement_domain_model_registrationflow CHANGE attributes attributes LONGTEXT NOT NULL COMMENT '(DC2Type:json_array)'");
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Configuration/Policy.yaml:
--------------------------------------------------------------------------------
1 | privilegeTargets:
2 |
3 | # We allow access to "everybody" to the Registration process, as well as login and also logout, because otherwise a user
4 | # that has no roles yet cannot logout anymore.
5 |
6 | 'Neos\Flow\Security\Authorization\Privilege\Method\MethodPrivilege':
7 | 'Sandstorm.UserManagement:Login':
8 | matcher: 'method(Sandstorm\UserManagement\Controller\(Login|ResetPassword)Controller->(?!initialize).*Action())'
9 | 'Sandstorm.UserManagement:Logout':
10 | matcher: 'method(Neos\Flow\Security\Authentication\Controller\AbstractAuthenticationController->logoutAction())'
11 | 'Sandstorm.UserManagement:Registration':
12 | matcher: 'method(Sandstorm\UserManagement\Controller\RegistrationController->(?!initialize).*Action())'
13 | 'Sandstorm.UserManagement:Profile':
14 | matcher: 'method(Sandstorm\UserManagement\Controller\ProfileController->(?!initialize).*Action())'
15 |
16 | roles:
17 | 'Neos.Flow:Everybody':
18 | privileges:
19 | -
20 | privilegeTarget: 'Sandstorm.UserManagement:Login'
21 | permission: GRANT
22 | -
23 | privilegeTarget: 'Sandstorm.UserManagement:Registration'
24 | permission: GRANT
25 | -
26 | privilegeTarget: 'Sandstorm.UserManagement:Logout'
27 | permission: GRANT
28 | 'Neos.Neos:FrontendUser':
29 | privileges:
30 | -
31 | privilegeTarget: 'Sandstorm.UserManagement:Profile'
32 | permission: GRANT
33 | 'Neos.Neos:Editor':
34 | privileges:
35 | -
36 | privilegeTarget: 'Sandstorm.UserManagement:Profile'
37 | permission: GRANT
38 |
39 |
--------------------------------------------------------------------------------
/Migrations/Mysql/Version20160526150442.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".');
29 |
30 | $this->addSql('ALTER TABLE sandstorm_usermanagement_domain_model_registrationflow DROP newactivationtokenrequested');
31 | }
32 |
33 | /**
34 | * @param Schema $schema
35 | * @return void
36 | */
37 | public function down(Schema $schema): void
38 | {
39 | // this down() migration is autogenerated, please modify it to your needs
40 | $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".');
41 |
42 | $this->addSql('ALTER TABLE sandstorm_usermanagement_domain_model_registrationflow ADD newactivationtokenrequested TINYINT(1) DEFAULT NULL');
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Migrations/Mysql/Version20160530132725.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".');
29 |
30 | $this->addSql('ALTER TABLE sandstorm_usermanagement_domain_model_registrationflow ADD firstname VARCHAR(255) DEFAULT NULL, ADD lastname VARCHAR(255) DEFAULT NULL');
31 | }
32 |
33 | /**
34 | * @param Schema $schema
35 | * @return void
36 | */
37 | public function down(Schema $schema): void
38 | {
39 | // this down() migration is autogenerated, please modify it to your needs
40 | $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".');
41 |
42 | $this->addSql('ALTER TABLE sandstorm_usermanagement_domain_model_registrationflow DROP firstname, lastname');
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Migrations/Mysql/Version20161101091328.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".');
29 | $this->addSql('ALTER TABLE sandstorm_usermanagement_domain_model_registrationflow DROP firstname, DROP lastname');
30 | }
31 |
32 | /**
33 | * @param Schema $schema
34 | * @return void
35 | */
36 | public function down(Schema $schema): void
37 | {
38 | // this down() migration is autogenerated, please modify it to your needs
39 | $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".');
40 | $this->addSql('ALTER TABLE sandstorm_usermanagement_domain_model_registrationflow ADD firstname VARCHAR(255) NOT NULL COLLATE utf8_unicode_ci, ADD lastname VARCHAR(255) NOT NULL COLLATE utf8_unicode_ci');
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Resources/Private/Templates/ResetPassword/InsertNewPassword.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | Passwort-Änderung
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Neues Passwort
17 |
18 |
19 |
20 |
21 |
22 | Passwortbestätigung
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
Entschuldigung, dieser Link zur Passwort-Änderung ist nicht mehr gültig!
33 |
34 |
35 | Neuen Passwortlink abrufen
36 |
37 |
38 |
39 |
Entschuldigung, dieser Link zur Passwort-Änderung ist nicht gültig!
40 |
41 |
42 | Neuen Passwortlink abrufen
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Resources/Private/Partials/Profile/PersonalForm.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 | :
10 |
11 |
16 |
17 | :
18 |
19 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Migrations/Mysql/Version20160526150536.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".');
29 |
30 | $this->addSql('CREATE TABLE sandstorm_usermanagement_domain_model_resetpasswordflow (persistence_object_identifier VARCHAR(40) NOT NULL, email VARCHAR(255) NOT NULL, resetpasswordtoken VARCHAR(255) DEFAULT NULL, resetpasswordtokenvaliduntil DATETIME DEFAULT NULL, PRIMARY KEY(persistence_object_identifier)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB');
31 | }
32 |
33 | /**
34 | * @param Schema $schema
35 | * @return void
36 | */
37 | public function down(Schema $schema): void
38 | {
39 | // this down() migration is autogenerated, please modify it to your needs
40 | $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".');
41 |
42 | $this->addSql('DROP TABLE sandstorm_usermanagement_domain_model_resetpasswordflow');
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Resources/Private/Templates/Registration/ActivateAccount.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | Activate Account
9 |
10 |
11 | VARIABLES:
12 |
13 | - tokenNotFound: if set, the auth token was not found.
14 | - tokenTimeout: if set, the token had a timeout.
15 | - success: all worked.
16 |
17 |
18 |
19 |
20 |
25 |
26 |
27 |
28 |
33 |
34 |
35 |
36 |
40 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Migrations/Mysql/Version20160524125855.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".');
29 |
30 | $this->addSql('CREATE TABLE sandstorm_usermanagement_domain_model_registrationflow (persistence_object_identifier VARCHAR(40) NOT NULL, email VARCHAR(255) NOT NULL, encryptedpassword VARCHAR(255) NOT NULL, attributes LONGTEXT NOT NULL COMMENT \'(DC2Type:json_array)\', activationtoken VARCHAR(255) DEFAULT NULL, activationtokenvaliduntil DATETIME DEFAULT NULL, newactivationtokenrequested TINYINT(1) DEFAULT NULL, PRIMARY KEY(persistence_object_identifier)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB');
31 | }
32 |
33 | /**
34 | * @param Schema $schema
35 | * @return void
36 | */
37 | public function down(Schema $schema): void
38 | {
39 | // this down() migration is autogenerated, please modify it to your needs
40 | $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".');
41 |
42 | $this->addSql('DROP TABLE sandstorm_usermanagement_domain_model_registrationflow');
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Classes/ViewHelpers/IfAuthenticatedViewHelper.php:
--------------------------------------------------------------------------------
1 | registerArgument('authenticationProviderName', 'string', 'Use a different Authentication Provider than the default one', false,'Sandstorm.UserManagement:Login');
22 | }
23 |
24 | /**
25 | * Renders child if any account is currently authenticated, otherwise renders child.
26 | *
27 | * @param string $authenticationProviderName
28 | * @return string the rendered string
29 | * @api
30 | */
31 | public function render()
32 | {
33 | $authenticationProviderName = $this->arguments['authenticationProviderName'];
34 | if (static::evaluateCondition($this->arguments, $this->renderingContext)) {
35 | return $this->renderThenChild();
36 | }
37 |
38 | return $this->renderElseChild();
39 | }
40 |
41 | /**
42 | * @param array $arguments
43 | * @param RenderingContextInterface $renderingContext
44 | * @return bool
45 | */
46 | protected static function evaluateCondition($arguments, RenderingContextInterface $renderingContext)
47 | {
48 | $objectManager = $renderingContext->getObjectManager();
49 | /** @var Context $securityContext */
50 | $securityContext = $objectManager->get(Context::class);
51 | $activeTokens = $securityContext->getAuthenticationTokens();
52 |
53 |
54 | /** @var $token TokenInterface */
55 | foreach ($activeTokens as $token) {
56 | if ($token->getAuthenticationProviderName() === $arguments['authenticationProviderName'] && $token->isAuthenticated()) {
57 | return true;
58 | }
59 | }
60 | return false;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Migrations/Mysql/Version20160607125833.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".');
29 |
30 | $this->addSql('ALTER TABLE sandstorm_usermanagement_domain_model_registrationflow CHANGE firstname firstname VARCHAR(255) NOT NULL, CHANGE lastname lastname VARCHAR(255) NOT NULL');
31 | $this->addSql('ALTER TABLE sandstorm_usermanagement_domain_model_user DROP companyname, DROP resetpasswordtoken, DROP activationtoken, DROP activationtokenvaliduntil, DROP resetpasswordtokenvaliduntil, DROP newactivationtokenrequested');
32 | }
33 |
34 | /**
35 | * @param Schema $schema
36 | * @return void
37 | */
38 | public function down(Schema $schema): void
39 | {
40 | // this down() migration is autogenerated, please modify it to your needs
41 | $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".');
42 |
43 | $this->addSql('ALTER TABLE sandstorm_usermanagement_domain_model_registrationflow CHANGE firstname firstname VARCHAR(255) DEFAULT NULL COLLATE utf8_unicode_ci, CHANGE lastname lastname VARCHAR(255) DEFAULT NULL COLLATE utf8_unicode_ci');
44 | $this->addSql('ALTER TABLE sandstorm_usermanagement_domain_model_user ADD companyname VARCHAR(255) DEFAULT NULL COLLATE utf8_unicode_ci, ADD resetpasswordtoken VARCHAR(255) DEFAULT NULL COLLATE utf8_unicode_ci, ADD activationtoken VARCHAR(255) DEFAULT NULL COLLATE utf8_unicode_ci, ADD activationtokenvaliduntil DATETIME DEFAULT NULL, ADD resetpasswordtokenvaliduntil DATETIME DEFAULT NULL, ADD newactivationtokenrequested TINYINT(1) DEFAULT NULL');
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Classes/Domain/Validator/RegistrationFlowValidator.php:
--------------------------------------------------------------------------------
1 | accountRepository->findOneByAccountIdentifier($value->getEmail());
49 |
50 | if ($existingAccount) {
51 | $message = $this->translator->translateById('validations.registrationflow.email', [$value->getEmail()], null, null, 'Main', 'Sandstorm.UserManagement');
52 | $this->getResult()->forProperty('email')->addError(new Error($message, 1336499566));
53 | }
54 |
55 | // If a custom validation service is registered, call its validate method to allow custom validations during registration
56 | if ($this->objectManager->isRegistered(RegistrationFlowValidationServiceInterface::class)) {
57 | $instance = $this->objectManager->get(RegistrationFlowValidationServiceInterface::class);
58 | $instance->validateRegistrationFlow($value, $this);
59 | }
60 |
61 | }
62 |
63 | /**
64 | * The custom validation service might need to access the result directly, so it is exposed here
65 | *
66 | * @return Result
67 | */
68 | public function getResult()
69 | {
70 | return parent::getResult();
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/Resources/Private/Templates/Profile/Index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 | Profile Settings
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/Classes/Domain/Service/Flow/FlowUserCreationService.php:
--------------------------------------------------------------------------------
1 | setAccountIdentifier($registrationFlow->getEmail());
50 | $account->setCredentialsSource($registrationFlow->getEncryptedPassword());
51 | $account->setAuthenticationProviderName('Sandstorm.UserManagement:Login');
52 |
53 | // Assign pre-configured roles
54 | foreach ($this->rolesForNewUsers as $roleString) {
55 | $account->addRole(new Role($roleString));
56 | }
57 |
58 | // Create the user
59 | $user = new User();
60 | $user->setAccount($account);
61 | $user->setEmail($registrationFlow->getEmail());
62 | if (array_key_exists('salutation', $registrationFlow->getAttributes())) {
63 | $user->setGender($registrationFlow->getAttributes()['salutation']);
64 | }
65 | if (array_key_exists('firstName', $registrationFlow->getAttributes())) {
66 | $user->setFirstName($registrationFlow->getAttributes()['firstName']);
67 | }
68 | if (array_key_exists('lastName', $registrationFlow->getAttributes())) {
69 | $user->setLastName($registrationFlow->getAttributes()['lastName']);
70 | }
71 |
72 | // Persist user
73 | $this->userRepository->add($user);
74 | $this->persistenceManager->allowObject($user);
75 | $this->persistenceManager->allowObject($account);
76 |
77 | // Return the user so the controller can directly use it
78 | return $user;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Resources/Private/Templates/Registration/Index.html:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 | Registration
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Configuration/Routes.yaml:
--------------------------------------------------------------------------------
1 | # #
2 | # Default routes configuration for the User Management package #
3 | # #
4 |
5 | ## Login/Logout ----------------------------------------------------------
6 |
7 | -
8 | name: 'Login Screen'
9 | uriPattern: 'login'
10 | defaults:
11 | '@package': 'Sandstorm.UserManagement'
12 | '@controller': 'Login'
13 | '@action': 'login'
14 | '@format': 'html'
15 |
16 | -
17 | name: 'Login: Authenticate'
18 | uriPattern: 'login/authenticate'
19 | defaults:
20 | '@package': 'Sandstorm.UserManagement'
21 | '@controller': 'Login'
22 | '@action': 'authenticate'
23 | '@format': 'html'
24 |
25 | -
26 | name: 'Logout'
27 | uriPattern: 'logout'
28 | defaults:
29 | '@package': 'Sandstorm.UserManagement'
30 | '@controller': 'Login'
31 | '@action': 'logout'
32 | '@format': 'html'
33 |
34 | ## Registration & account activation ---------------------------------------
35 |
36 | -
37 | name: 'Account: Registration: show form'
38 | uriPattern: 'account/signup/index'
39 | defaults:
40 | '@package': 'Sandstorm.UserManagement'
41 | '@controller': 'Registration'
42 | '@action': 'index'
43 | '@format': 'html'
44 |
45 | -
46 | name: 'Account: submit signup form'
47 | uriPattern: 'account/signup/submit'
48 | defaults:
49 | '@package': 'Sandstorm.UserManagement'
50 | '@controller': 'Registration'
51 | '@action': 'register'
52 | '@format': 'html'
53 |
54 | -
55 | name: 'Account: activate'
56 | uriPattern: 'account/activate/{token}'
57 | defaults:
58 | '@package': 'Sandstorm.UserManagement'
59 | '@controller': 'Registration'
60 | '@action': 'activateAccount'
61 | '@format': 'html'
62 |
63 | ## Reset password -----------------------------------------------------------
64 |
65 | -
66 | name: 'User: send new password link'
67 | uriPattern: 'account/forgotpassword'
68 | defaults:
69 | '@package': 'Sandstorm.UserManagement'
70 | '@controller': 'ResetPassword'
71 | '@action': 'index'
72 | '@format': 'html'
73 |
74 | -
75 | name: 'User: request new password token'
76 | uriPattern: 'account/requestpasswordtoken'
77 | defaults:
78 | '@package': 'Sandstorm.UserManagement'
79 | '@controller': 'ResetPassword'
80 | '@action': 'requestToken'
81 | '@format': 'html'
82 |
83 | -
84 | name: 'User: reset password'
85 | uriPattern: 'account/resetpassword/{token}'
86 | defaults:
87 | '@package': 'Sandstorm.UserManagement'
88 | '@controller': 'ResetPassword'
89 | '@action': 'insertNewPassword'
90 | '@format': 'html'
91 |
92 | -
93 | name: 'User: reset password'
94 | uriPattern: 'account/updatepassword'
95 | defaults:
96 | '@package': 'Sandstorm.UserManagement'
97 | '@controller': 'ResetPassword'
98 | '@action': 'updatePassword'
99 | '@format': 'html'
100 |
--------------------------------------------------------------------------------
/Classes/Domain/Service/Flow/FlowRedirectTargetService.php:
--------------------------------------------------------------------------------
1 | redirectAfterLogin)
33 | && array_key_exists('action', $this->redirectAfterLogin)
34 | && array_key_exists('controller', $this->redirectAfterLogin)
35 | && array_key_exists('package', $this->redirectAfterLogin)
36 | ) {
37 | $controllerArguments = [];
38 | if (array_key_exists('controllerArguments', $this->redirectAfterLogin) && is_array($this->redirectAfterLogin['controllerArguments'])) {
39 | $controllerArguments = $this->redirectAfterLogin['controllerArguments'];
40 | }
41 |
42 | return $controllerContext->getUriBuilder()
43 | ->reset()
44 | ->setCreateAbsoluteUri(true)
45 | ->uriFor($this->redirectAfterLogin['action'], $controllerArguments,
46 | $this->redirectAfterLogin['controller'], $this->redirectAfterLogin['package']);
47 | }
48 | }
49 |
50 | /**
51 | * @param ControllerContext $controllerContext
52 | * @return string|ActionRequest|NULL
53 | */
54 | public function onLogout(ControllerContext $controllerContext)
55 | {
56 | if (is_array($this->redirectAfterLogout)
57 | && array_key_exists('action', $this->redirectAfterLogout)
58 | && array_key_exists('controller', $this->redirectAfterLogout)
59 | && array_key_exists('package', $this->redirectAfterLogout)
60 | ) {
61 | $controllerArguments = [];
62 | if (array_key_exists('controllerArguments', $this->redirectAfterLogout) && is_array($this->redirectAfterLogout['controllerArguments'])) {
63 | $controllerArguments = $this->redirectAfterLogout['controllerArguments'];
64 | }
65 |
66 | return $controllerContext->getUriBuilder()
67 | ->reset()
68 | ->setCreateAbsoluteUri(true)
69 | ->uriFor($this->redirectAfterLogout['action'], $controllerArguments,
70 | $this->redirectAfterLogout['controller'], $this->redirectAfterLogout['package']);
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Migrations/Mysql/Version20160519150828.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() != "mysql");
20 |
21 | $this->addSql("CREATE TABLE sandstorm_usermanagement_domain_model_user (persistence_object_identifier VARCHAR(40) NOT NULL, account VARCHAR(40) DEFAULT NULL, email VARCHAR(255) NOT NULL, gender VARCHAR(255) DEFAULT NULL, firstname VARCHAR(255) DEFAULT NULL, lastname VARCHAR(255) DEFAULT NULL, companyname VARCHAR(255) DEFAULT NULL, resetpasswordtoken VARCHAR(255) DEFAULT NULL, activationtoken VARCHAR(255) DEFAULT NULL, activationtokenvaliduntil DATETIME DEFAULT NULL, resetpasswordtokenvaliduntil DATETIME DEFAULT NULL, newactivationtokenrequested TINYINT(1) DEFAULT NULL, dtype VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_5DEB8A977D3656A4 (account), PRIMARY KEY(persistence_object_identifier)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB");
22 |
23 | /**
24 | * We need to check what the name of table flow accounts table is. If you install flow, run migrations, then add usermananagement,
25 | * then run the UserManagement migrations, the name will will be neos_flow_...
26 | * However, if you install everything in one go and run migrations then, the order will be different because this migration
27 | * comes before the Flow migration where the table is renamed (Version20161124185047). So we need to check which of these two
28 | * tables exist and set the FK relation accordingly.
29 | **/
30 | if ($this->sm->tablesExist('neos_flow_security_account')) {
31 | // "neos_" table is there - this means flow migrations have already been run.
32 | {
33 | $this->addSql("ALTER TABLE sandstorm_usermanagement_domain_model_user ADD CONSTRAINT FK_5DEB8A977D3656A4 FOREIGN KEY (account) REFERENCES neos_flow_security_account (persistence_object_identifier)");
34 | }
35 | } else if ($this->sm->tablesExist('typo3_flow_security_account')) {
36 | // Flow migrations have not been run fully yet, table still has the old name.
37 | $this->addSql("ALTER TABLE sandstorm_usermanagement_domain_model_user ADD CONSTRAINT FK_5DEB8A977D3656A4 FOREIGN KEY (account) REFERENCES typo3_flow_security_account (persistence_object_identifier)");
38 | }
39 | }
40 |
41 | /**
42 | * @param Schema $schema
43 | * @return void
44 | */
45 | public function down(Schema $schema): void
46 | {
47 | // this down() migration is autogenerated, please modify it to your needs
48 | $this->abortIf($this->connection->getDatabasePlatform()->getName() != "mysql");
49 |
50 | $this->addSql("DROP TABLE sandstorm_usermanagement_domain_model_user");
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Configuration/Settings.yaml:
--------------------------------------------------------------------------------
1 | Sandstorm:
2 | UserManagement:
3 | # Validity timespan for the activation token for newly registered users.
4 | activationTokenTimeout: '2 days'
5 | # Validity timespan for the token used to reset passwords.
6 | resetPasswordTokenTimeout: '4 hours'
7 | # The message that appears if a user could not be logged in.
8 | # Set the value to 'i18n' to enable translations
9 | # using 'authFailedMessage.title and authFailedMessage.body as id.
10 | authFailedMessage:
11 | title: 'Login nicht möglich'
12 | body: 'Sie haben ungültige Zugangsdaten eingegeben. Bitte versuchen Sie es noch einmal.'
13 | # Email settings
14 | # Set the value to 'i18n' to enable translations
15 | # using 'email.subjectActivation' and 'email.subjectResetPassword' as id.
16 | email:
17 | subjectActivation: 'Please confirm your account'
18 | # Subject line for the password reset email
19 | subjectResetPassword: 'Password reset'
20 | # An array of roles which are assigned to users after they activate their account.
21 | rolesForNewUsers: []
22 |
23 | # You can make constraints on allowed passwords here.
24 | passwordConstraints:
25 | minLength: 8
26 | maxLength: 128
27 | # per default, we do not prescribe anything about the password content - feel free to change these.
28 | # make sure that the sum of these values does not exceed the minLength, otherwise users can't register.
29 | minNumberOfLowercaseLetters: 0
30 | minNumberOfUppercaseLetters: 0
31 | minNumberOfNumbers: 0
32 | minNumberOfSpecialCharacters: 0
33 |
34 | # Redirect settings after logout/login
35 | redirect:
36 | afterLogin: []
37 | afterLogout: []
38 | # To activate redirection, make these settings. controllerArguments are optional.
39 | # afterLogin:
40 | # action: 'action'
41 | # controller: 'Controller'
42 | # package: 'Your.Package'
43 | # controllerArguments:
44 | # yourAdditionalArgument: 'test'
45 | # afterLogout:
46 | # action: 'action'
47 | # controller: 'Controller'
48 | # package: 'Your.Package'
49 | # controllerArguments:
50 | # yourAdditionalArgument: 'test1'
51 |
52 | TemplateMailer:
53 | templatePackages:
54 | 99999: 'Sandstorm.UserManagement'
55 | senderAddresses:
56 | 'sandstorm_usermanagement_sender_email':
57 | name: 'Sandstorm Usermanagement Package'
58 | address: 'test@example.com'
59 | replyToAddresses:
60 | 'sandstorm_usermanagement_replyTo_email':
61 | name: 'Sandstorm Usermanagement Package Reply-To Email'
62 | address: 'test@example.com'
63 |
64 | Neos:
65 | Flow:
66 | mvc:
67 | routes:
68 | 'Sandstorm.UserManagement': TRUE
69 |
70 |
71 | # The auth provider settings below are needed for the standalone case only.
72 | security:
73 | authentication:
74 | providers:
75 | 'Sandstorm.UserManagement:Login':
76 | provider: 'PersistedUsernamePasswordProvider'
77 | entryPoint: 'WebRedirect'
78 | entryPointOptions:
79 | routeValues:
80 | '@package': 'Sandstorm.UserManagement'
81 | '@controller': 'Login'
82 | '@action': 'login'
83 |
84 | Neos:
85 | fusion:
86 | autoInclude:
87 | 'Sandstorm.UserManagement': TRUE
88 |
89 | userInterface:
90 | translation:
91 | autoInclude:
92 | 'Sandstorm.UserManagement': ['NodeTypes/*']
93 |
--------------------------------------------------------------------------------
/Classes/Domain/Model/ResetPasswordFlow.php:
--------------------------------------------------------------------------------
1 | generateResetPasswordToken();
65 | }
66 | }
67 |
68 | /**
69 | * Generate a new password reset token
70 | *
71 | * @throws Exception If the user doesn't have an account yet
72 | */
73 | protected function generateResetPasswordToken()
74 | {
75 | $this->resetPasswordToken = Algorithms::generateRandomString(30);
76 | $this->resetPasswordTokenValidUntil = (new \DateTime())->add(\DateInterval::createFromDateString($this->resetPasswordTokenTimeout));
77 | }
78 |
79 | /**
80 | * Check if the user has a valid reset password token.
81 | *
82 | * @return bool
83 | */
84 | public function hasValidResetPasswordToken()
85 | {
86 | if ($this->resetPasswordTokenValidUntil == null) {
87 | return false;
88 | }
89 |
90 | return $this->resetPasswordTokenValidUntil->getTimestamp() > time();
91 | }
92 |
93 | /**
94 | * @return string
95 | */
96 | public function getEmail()
97 | {
98 | return $this->email;
99 | }
100 |
101 | /**
102 | * @param string $email
103 | */
104 | public function setEmail($email)
105 | {
106 | $this->email = $email;
107 | }
108 |
109 | /**
110 | * @return string
111 | */
112 | public function getResetPasswordToken()
113 | {
114 | return $this->resetPasswordToken;
115 | }
116 |
117 | /**
118 | * @param PasswordDto $passwordDto
119 | */
120 | public function setPasswordDto(PasswordDto $passwordDto)
121 | {
122 | $this->passwordDto = $passwordDto;
123 | }
124 |
125 | public function getEncryptedPassword()
126 | {
127 | return $this->passwordDto->getEncryptedPasswordAndRemoveNonencryptedVersion();
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/Classes/Domain/Model/PasswordDto.php:
--------------------------------------------------------------------------------
1 | password = $password;
32 | }
33 |
34 | /**
35 | * @param string $passwordConfirmation
36 | */
37 | public function setPasswordConfirmation($passwordConfirmation)
38 | {
39 | $this->passwordConfirmation = $passwordConfirmation;
40 | }
41 |
42 | /**
43 | * @return bool
44 | */
45 | public function arePasswordsEqual()
46 | {
47 | return !empty($this->password) && !empty(trim($this->password)) &&
48 | ($this->password === $this->passwordConfirmation);
49 | }
50 |
51 | /**
52 | * @param $length
53 | * @return bool
54 | */
55 | public function isPasswordMinLength($length)
56 | {
57 | return strlen($this->password) >= $length;
58 | }
59 |
60 | /**
61 | * @param $length
62 | * @return bool
63 | */
64 | public function isPasswordMaxLength($length)
65 | {
66 | return strlen($this->password) <= $length;
67 | }
68 |
69 | /**
70 | * @param int $minAmount
71 | * @return bool
72 | */
73 | public function doesPasswordContainLowercaseLetters($minAmount)
74 | {
75 | return $this->doesPasswordContain('/[a-z]{1}/', $minAmount);
76 | }
77 |
78 | /**
79 | * @param int $minAmount
80 | * @return bool
81 | */
82 | public function doesPasswordContainUppercaseLetters($minAmount)
83 | {
84 | return $this->doesPasswordContain('/[A-Z]{1}/', $minAmount);
85 | }
86 |
87 | /**
88 | * @param int $minAmount
89 | * @return bool
90 | */
91 | public function doesPasswordContainNumbers($minAmount)
92 | {
93 | return $this->doesPasswordContain('/[\d]{1}/', $minAmount);
94 | }
95 |
96 | /**
97 | * @param int $minAmount
98 | * @return bool
99 | */
100 | public function doesPasswordContainSpecialCharacters($minAmount)
101 | {
102 | // Regexes do not work reliably here - we simply kick out all letters and numbers
103 | $replacedPassword = preg_replace('/[a-zA-Z\d]/', '', $this->password);
104 | return strlen($replacedPassword) >= $minAmount;
105 | }
106 |
107 | /**
108 | * Helper method
109 | *
110 | * @param string $regex
111 | * @return bool
112 | */
113 | private function doesPasswordContain($regex, $minAmount)
114 | {
115 | $matches = [];
116 | preg_match_all($regex, $this->password, $matches);
117 | return count($matches[0]) >= $minAmount;
118 | }
119 |
120 | public function getEncryptedPasswordAndRemoveNonencryptedVersion()
121 | {
122 | if (!$this->arePasswordsEqual()) {
123 | throw new Exception('Passwords are not equal; so it is not allowed to call getEncryptedPassword().',
124 | 1464087097);
125 | }
126 |
127 | $encrypted = $this->hashService->hashPassword($this->password);
128 | $this->password = null;
129 | $this->passwordConfirmation = null;
130 |
131 | return $encrypted;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/Classes/Domain/Model/User.php:
--------------------------------------------------------------------------------
1 | account == null) {
60 | return $this->email;
61 | } else {
62 | return $this->account->getAccountIdentifier();
63 | }
64 | }
65 |
66 | /**
67 | * Get the full name of an user.
68 | *
69 | * @return string
70 | */
71 | public function getFullName()
72 | {
73 | return $this->firstName . ' ' . $this->lastName;
74 | }
75 |
76 | /**
77 | * Check if the user is active.
78 | *
79 | * @return bool
80 | */
81 | public function isActive()
82 | {
83 | return $this->account !== null;
84 | }
85 |
86 |
87 | // ---- Only getters and setters follow
88 |
89 |
90 | /**
91 | * @return string
92 | */
93 | public function getFirstName()
94 | {
95 | return $this->firstName;
96 | }
97 |
98 | /**
99 | * @param string $firstName
100 | */
101 | public function setFirstName($firstName)
102 | {
103 | $this->firstName = $firstName;
104 | }
105 |
106 | /**
107 | * @return string
108 | */
109 | public function getLastName()
110 | {
111 | return $this->lastName;
112 | }
113 |
114 | /**
115 | * @param string $lastName
116 | */
117 | public function setLastName($lastName)
118 | {
119 | $this->lastName = $lastName;
120 | }
121 |
122 | /**
123 | * @return string
124 | */
125 | public function getGender()
126 | {
127 | return $this->gender;
128 | }
129 |
130 | /**
131 | * @param string $gender
132 | */
133 | public function setGender($gender)
134 | {
135 | $this->gender = $gender;
136 | }
137 |
138 | /**
139 | * @return string
140 | */
141 | public function getEmail()
142 | {
143 | return $this->email;
144 | }
145 |
146 | /**
147 | * @param string $email
148 | */
149 | public function setEmail($email)
150 | {
151 | $this->email = $email;
152 | }
153 |
154 | /**
155 | * @return \Neos\Flow\Security\Account
156 | */
157 | public function getAccount()
158 | {
159 | return $this->account;
160 | }
161 |
162 | /**
163 | * @param \Neos\Flow\Security\Account $account
164 | */
165 | public function setAccount($account)
166 | {
167 | $this->account = $account;
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/Classes/Domain/Service/Neos/NeosUserCreationService.php:
--------------------------------------------------------------------------------
1 | setAccountIdentifier($registrationFlow->getEmail());
60 | $account->setCredentialsSource($registrationFlow->getEncryptedPassword());
61 | $account->setAuthenticationProviderName('Sandstorm.UserManagement:Login');
62 |
63 | // Assign preconfigured roles
64 | foreach ($this->rolesForNewUsers as $roleString) {
65 | $account->addRole(new Role($roleString));
66 | }
67 |
68 | // Create the user
69 | $user = new User();
70 | $name = new PersonName('', $registrationFlow->getAttributes()['firstName'], '',
71 | $registrationFlow->getAttributes()['lastName'], '', $registrationFlow->getEmail());
72 | $user->setName($name);
73 |
74 | // Assign them to each other and persist
75 | $this->getPartyService()->assignAccountToParty($account, $user);
76 | $this->getPartyRepository()->add($user);
77 | $this->accountRepository->add($account);
78 | $this->persistenceManager->allowObject($user);
79 | $this->persistenceManager->allowObject($user->getPreferences());
80 | $this->persistenceManager->allowObject($name);
81 | $this->persistenceManager->allowObject($account);
82 |
83 | // Return the user so the controller can directly use it
84 | return $user;
85 | }
86 |
87 | /**
88 | * This method exists to ensure the code runs outside Neos.
89 | * We do not fetch this via injection so it works also in Flow when the class is not present
90 | *
91 | * @return PartyService
92 | */
93 | protected function getPartyService()
94 | {
95 | return $this->objectManager->get(PartyService::class);
96 | }
97 |
98 | /**
99 | * This method exists to ensure the code runs outside Neos.
100 | * We do not fetch this via injection so it works also in Flow when the class is not present
101 | *
102 | * @return PartyRepository
103 | */
104 | protected function getPartyRepository()
105 | {
106 | return $this->objectManager->get(PartyRepository::class);
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/Classes/Controller/ProfileController.php:
--------------------------------------------------------------------------------
1 | request->getInternalArguments();
56 | $account = $this->securityContext->getAccount();
57 | $user = $this->userRepository->findOneByAccount($account);
58 | $this->view->assign('account', $account);
59 | $this->view->assign('user', $user);
60 | $this->view->assign('pluginArguments', $pluginArguments);
61 | }
62 |
63 | /**
64 | * @param User $user
65 | */
66 | public function editProfileAction(User $user)
67 | {
68 | $this->userRepository->update($user);
69 | $this->redirect('index');
70 | }
71 |
72 | /**
73 | * @param Account $account
74 | * @param array $password Expects an array in the format array('', '')
75 | * @Flow\Validate(argumentName="password", type="\Neos\Neos\Validation\Validator\PasswordValidator", options={ "allowEmpty"=1, "minimum"=1, "maximum"=255 })
76 | */
77 | public function setNewPasswordAction(Account $account, array $password = array()) {
78 | $user = $this->userRepository->findOneByAccount($account);
79 | $password = array_shift($password);
80 | if (strlen(trim(strval($password))) > 0) {
81 | $this->setPassword($account, $password);
82 | }
83 | $this->redirect('index');
84 |
85 | }
86 |
87 | /**
88 | * Disable the technical error flash message
89 | *
90 | * @return boolean
91 | */
92 | protected function getErrorFlashMessage()
93 | {
94 | return false;
95 | }
96 |
97 | /**
98 | * Sets a new password for the given account
99 | *
100 | * @param Account $account The user to set the password for
101 | * @param string $password A new password
102 | * @return void
103 | * @api
104 | */
105 | protected function setPassword(Account $account, $password)
106 | {
107 | $tokens = $this->tokenAndProviderFactory->getTokens();
108 | $indexedTokens = array();
109 | foreach ($tokens as $token) {
110 | /** @var TokenInterface $token */
111 | $indexedTokens[$token->getAuthenticationProviderName()] = $token;
112 | }
113 |
114 | /** @var Account $account */
115 | $authenticationProviderName = $account->getAuthenticationProviderName();
116 | if (isset($indexedTokens[$authenticationProviderName]) && $indexedTokens[$authenticationProviderName] instanceof UsernamePassword) {
117 | $account->setCredentialsSource($this->hashService->hashPassword($password));
118 | $this->accountRepository->update($account);
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/Tests/Unit/Domain/PasswordDtoTest.php:
--------------------------------------------------------------------------------
1 | setPassword('foobar');
24 | $passwordDto->setPasswordConfirmation('foobar');
25 |
26 | $this->assertTrue($passwordDto->arePasswordsEqual());
27 | }
28 |
29 | /**
30 | * @test
31 | */
32 | public function inequalPasswordsAreNotEqual()
33 | {
34 | $passwordDto = new PasswordDto();
35 | $passwordDto->setPassword('FOOBAR');
36 | $passwordDto->setPasswordConfirmation('foobar');
37 |
38 | $this->assertFalse($passwordDto->arePasswordsEqual());
39 | }
40 |
41 | /**
42 | * @test
43 | */
44 | public function passwordMinLength()
45 | {
46 | $passwordDto = new PasswordDto();
47 | $passwordDto->setPassword('6chars');
48 | $passwordDto->setPasswordConfirmation('6chars');
49 |
50 | $this->assertTrue($passwordDto->isPasswordMinLength(6));
51 | $this->assertFalse($passwordDto->isPasswordMinLength(7));
52 | }
53 |
54 | /**
55 | * @test
56 | */
57 | public function passwordMaxLength()
58 | {
59 | $passwordDto = new PasswordDto();
60 | $passwordDto->setPassword('6chars');
61 | $passwordDto->setPasswordConfirmation('6chars');
62 |
63 | $this->assertTrue($passwordDto->isPasswordMaxLength(6));
64 | $this->assertFalse($passwordDto->isPasswordMaxLength(5));
65 | }
66 |
67 | /**
68 | * @test
69 | */
70 | public function passwordContainsLowercaseLetters()
71 | {
72 | $passwordDto = new PasswordDto();
73 | $passwordDto->setPassword('4loweRCASELETTERS');
74 | $passwordDto->setPasswordConfirmation('4loweRCASELETTERS');
75 |
76 | $this->assertTrue($passwordDto->doesPasswordContainLowercaseLetters(3));
77 | $this->assertTrue($passwordDto->doesPasswordContainLowercaseLetters(4));
78 | $this->assertFalse($passwordDto->doesPasswordContainLowercaseLetters(5));
79 | }
80 |
81 | /**
82 | * @test
83 | */
84 | public function passwordContainsUppercaseLetters()
85 | {
86 | $passwordDto = new PasswordDto();
87 | $passwordDto->setPassword('4UPPErcaseletters');
88 | $passwordDto->setPasswordConfirmation('4UPPErcaseletters');
89 |
90 | $this->assertTrue($passwordDto->doesPasswordContainUppercaseLetters(3));
91 | $this->assertTrue($passwordDto->doesPasswordContainUppercaseLetters(4));
92 | $this->assertFalse($passwordDto->doesPasswordContainUppercaseLetters(5));
93 | }
94 |
95 | /**
96 | * @test
97 | */
98 | public function passwordContainsNumbers()
99 | {
100 | $passwordDto = new PasswordDto();
101 | $passwordDto->setPassword('fournumbers1234');
102 | $passwordDto->setPasswordConfirmation('fournumbers1234');
103 |
104 | $this->assertTrue($passwordDto->doesPasswordContainNumbers(3));
105 | $this->assertTrue($passwordDto->doesPasswordContainNumbers(4));
106 | $this->assertFalse($passwordDto->doesPasswordContainNumbers(5));
107 | }
108 |
109 | /**
110 | * @test
111 | */
112 | public function passwordContainsSpecialCharacters()
113 | {
114 | $passwordDto = new PasswordDto();
115 | $passwordDto->setPassword('4specialCHARS!"%$');
116 | $passwordDto->setPasswordConfirmation('4specialCHARS!"%$');
117 |
118 | $this->assertTrue($passwordDto->doesPasswordContainSpecialCharacters(3));
119 | $this->assertTrue($passwordDto->doesPasswordContainSpecialCharacters(4));
120 | $this->assertFalse($passwordDto->doesPasswordContainSpecialCharacters(5));
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/Classes/Domain/Model/RegistrationFlow.php:
--------------------------------------------------------------------------------
1 | generateActivationToken();
68 | }
69 | }
70 |
71 | /**
72 | * @param PasswordDto $passwordDto
73 | */
74 | public function setPasswordDto(PasswordDto $passwordDto)
75 | {
76 | $this->passwordDto = $passwordDto;
77 | }
78 |
79 | /**
80 | * Generate a new activation token
81 | *
82 | * @throws Exception If the user has an account already
83 | */
84 | public function generateActivationToken()
85 | {
86 | $this->activationToken = Algorithms::generateRandomString(30);
87 | $this->activationTokenValidUntil = (new \DateTime())->add(\DateInterval::createFromDateString($this->activationTokenTimeout));
88 | }
89 |
90 | /**
91 | * Check if the user has a valid activation token.
92 | *
93 | * @return bool
94 | */
95 | public function hasValidActivationToken()
96 | {
97 | if ($this->activationTokenValidUntil == null) {
98 | return false;
99 | }
100 |
101 | return $this->activationTokenValidUntil->getTimestamp() > time();
102 | }
103 |
104 | /**
105 | * @return string
106 | */
107 | public function getEmail()
108 | {
109 | return $this->email;
110 | }
111 |
112 | /**
113 | * @param string $email
114 | */
115 | public function setEmail($email)
116 | {
117 | $this->email = $email;
118 | }
119 |
120 | /**
121 | * @return array
122 | */
123 | public function getAttributes()
124 | {
125 | return $this->attributes;
126 | }
127 |
128 | /**
129 | * @param array $attributes
130 | */
131 | public function setAttributes($attributes)
132 | {
133 | $this->attributes = $attributes;
134 | }
135 |
136 | public function storeEncryptedPassword()
137 | {
138 | $this->encryptedPassword = $this->passwordDto->getEncryptedPasswordAndRemoveNonencryptedVersion();
139 | }
140 |
141 | /**
142 | * @return string
143 | */
144 | public function getEncryptedPassword()
145 | {
146 | return $this->encryptedPassword;
147 | }
148 |
149 | /**
150 | * @return string
151 | */
152 | public function getActivationToken()
153 | {
154 | return $this->activationToken;
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/Classes/Domain/Service/Neos/NeosRedirectTargetService.php:
--------------------------------------------------------------------------------
1 | redirectAfterLogin)
36 | && array_key_exists('action', $this->redirectAfterLogin)
37 | && array_key_exists('controller', $this->redirectAfterLogin)
38 | && array_key_exists('package', $this->redirectAfterLogin)
39 | ) {
40 | $controllerArguments = [];
41 | if (array_key_exists('controllerArguments', $this->redirectAfterLogin) &&
42 | is_array($this->redirectAfterLogin['controllerArguments'])
43 | ) {
44 | $controllerArguments = $this->redirectAfterLogin['controllerArguments'];
45 | }
46 |
47 | return $controllerContext->getUriBuilder()
48 | ->reset()
49 | ->setCreateAbsoluteUri(true)
50 | ->uriFor($this->redirectAfterLogin['action'], $controllerArguments,
51 | $this->redirectAfterLogin['controller'], $this->redirectAfterLogin['package']);
52 | }
53 |
54 | // Neos only logic (configuration at node or via TS)
55 | /** @var ActionRequest $actionRequest */
56 | $actionRequest = $controllerContext->getRequest();
57 | if ($actionRequest->getInternalArgument('__redirectAfterLogin')) {
58 | return $this->getNodeLinkingService()
59 | ->createNodeUri($controllerContext, $actionRequest->getInternalArgument('__redirectAfterLogin'));
60 | }
61 | }
62 |
63 | public function onLogout(ControllerContext $controllerContext)
64 | {
65 | // Check if config for redirect is done
66 | if (is_array($this->redirectAfterLogout)
67 | && array_key_exists('action', $this->redirectAfterLogout)
68 | && array_key_exists('controller', $this->redirectAfterLogout)
69 | && array_key_exists('package', $this->redirectAfterLogout)
70 | ) {
71 | $controllerArguments = [];
72 | if (array_key_exists('controllerArguments', $this->redirectAfterLogout) &&
73 | is_array($this->redirectAfterLogout['controllerArguments'])
74 | ) {
75 | $controllerArguments = $this->redirectAfterLogout['controllerArguments'];
76 | }
77 |
78 | return $controllerContext->getUriBuilder()
79 | ->reset()
80 | ->setCreateAbsoluteUri(true)
81 | ->uriFor($this->redirectAfterLogout['action'], $controllerArguments,
82 | $this->redirectAfterLogout['controller'], $this->redirectAfterLogout['package']);
83 | }
84 |
85 | // Neos only logic (configuration at node or via TS)
86 | /** @var ActionRequest $actionRequest */
87 | $actionRequest = $controllerContext->getRequest();
88 | if ($actionRequest->getInternalArgument('__redirectAfterLogout')) {
89 | return $this->getNodeLinkingService()
90 | ->createNodeUri($controllerContext, $actionRequest->getInternalArgument('__redirectAfterLogout'));
91 | }
92 | }
93 |
94 | /**
95 | * @return LinkingService
96 | */
97 | protected function getNodeLinkingService()
98 | {
99 | return $this->objectManager->get(LinkingService::class);
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/Classes/Domain/Validator/CustomPasswordDtoValidator.php:
--------------------------------------------------------------------------------
1 | getResult();
45 |
46 | // Matching PW and PW confirmation
47 | if (!$value->arePasswordsEqual()) {
48 | $message = $this->translator->translateById('validations.password.matching', [], null, null, 'Main', 'Sandstorm.UserManagement');
49 | $result->forProperty('password')->addError(new Error($message, 1464086581));
50 | }
51 |
52 | // Min length
53 | if (!$value->isPasswordMinLength($this->passwordConstraints['minLength'])) {
54 | $message = $this->translator->translateById('validations.password.minlength', [$this->passwordConstraints['minLength']], null, null, 'Main', 'Sandstorm.UserManagement');
55 | $result->forProperty('password')->addError(new Error($message, 1542220177));
56 | }
57 |
58 | // Max length
59 | if (!$value->isPasswordMaxLength($this->passwordConstraints['maxLength'])) {
60 | $message = $this->translator->translateById('validations.password.maxlength', [$this->passwordConstraints['maxLength']], null, null, 'Main', 'Sandstorm.UserManagement');
61 | $result->forProperty('password')->addError(new Error($message, 1542220177));
62 | }
63 |
64 | // minNumberOfLowercaseLetters
65 | if (!$value->doesPasswordContainLowercaseLetters($this->passwordConstraints['minNumberOfLowercaseLetters'])) {
66 | $message = $this->translator->translateById('validations.password.lowercase', [$this->passwordConstraints['minNumberOfLowercaseLetters']], $this->passwordConstraints['minNumberOfLowercaseLetters'], null, 'Main', 'Sandstorm.UserManagement');
67 | $result->forProperty('password')->addError(new Error($message, 1542220177));
68 | }
69 |
70 | // minNumberOfUppercaseLetters
71 | if (!$value->doesPasswordContainUppercaseLetters($this->passwordConstraints['minNumberOfUppercaseLetters'])) {
72 | $message = $this->translator->translateById('validations.password.uppercase', [$this->passwordConstraints['minNumberOfUppercaseLetters']], $this->passwordConstraints['minNumberOfUppercaseLetters'], null, 'Main', 'Sandstorm.UserManagement');
73 | $result->forProperty('password')->addError(new Error($message, 1542220177));
74 | }
75 |
76 | // minNumberOfNumbers
77 | if (!$value->doesPasswordContainNumbers($this->passwordConstraints['minNumberOfNumbers'])) {
78 | $message = $this->translator->translateById('validations.password.numbers', [$this->passwordConstraints['minNumberOfNumbers']], $this->passwordConstraints['minNumberOfNumbers'], null, 'Main', 'Sandstorm.UserManagement');
79 | $result->forProperty('password')->addError(new Error($message, 1542220177));
80 | }
81 |
82 | // minNumberOfSpecialCharacters
83 | if (!$value->doesPasswordContainSpecialCharacters($this->passwordConstraints['minNumberOfSpecialCharacters'])) {
84 | $message = $this->translator->translateById('validations.password.special', [$this->passwordConstraints['minNumberOfSpecialCharacters']], $this->passwordConstraints['minNumberOfSpecialCharacters'], null, 'Main', 'Sandstorm.UserManagement');
85 | $result->forProperty('password')->addError(new Error($message, 1542220177));
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Classes/Controller/RegistrationController.php:
--------------------------------------------------------------------------------
1 | subjectActivation === 'i18n'
54 | ? $this->translator->translateById(
55 | 'email.subjectActivation',
56 | [],
57 | null,
58 | null,
59 | 'Main',
60 | 'Sandstorm.UserManagement'
61 | )
62 | : $this->subjectActivation;
63 | }
64 |
65 |
66 | /**
67 | * @Flow\SkipCsrfProtection
68 | */
69 | public function indexAction()
70 | {
71 | $this->view->assign('node', $this->request->getInternalArgument('__node'));
72 | }
73 |
74 | /**
75 | * @param RegistrationFlow $registrationFlow
76 | */
77 | public function registerAction(RegistrationFlow $registrationFlow)
78 | {
79 | // We remove already existing flows
80 | $alreadyExistingFlows = $this->registrationFlowRepository->findByEmail($registrationFlow->getEmail());
81 | if (count($alreadyExistingFlows) > 0) {
82 | foreach ($alreadyExistingFlows as $alreadyExistingFlow) {
83 | $this->registrationFlowRepository->remove($alreadyExistingFlow);
84 | }
85 | }
86 | $registrationFlow->storeEncryptedPassword();
87 |
88 | // Send out a confirmation mail
89 | $activationLink = $this->uriBuilder->reset()->setCreateAbsoluteUri(true)->uriFor(
90 | 'activateAccount',
91 | ['token' => $registrationFlow->getActivationToken()],
92 | 'Registration');
93 |
94 | $this->emailService->sendTemplateEmail(
95 | 'ActivationToken',
96 | $this->getSubjectActivation(),
97 | [$registrationFlow->getEmail()],
98 | [
99 | 'activationLink' => $activationLink,
100 | 'registrationFlow' => $registrationFlow
101 | ],
102 | 'sandstorm_usermanagement_sender_email',
103 | [], // cc
104 | [], // bcc
105 | [], // attachments
106 | 'sandstorm_usermanagement_replyTo_email'
107 | );
108 |
109 | $this->registrationFlowRepository->add($registrationFlow);
110 |
111 | $this->view->assign('registrationFlow', $registrationFlow);
112 | $this->view->assign('node', $this->request->getInternalArgument('__node'));
113 | }
114 |
115 | /**
116 | * @param string $token
117 | */
118 | public function activateAccountAction($token)
119 | {
120 | /* @var $registrationFlow \Sandstorm\UserManagement\Domain\Model\RegistrationFlow */
121 | $registrationFlow = $this->registrationFlowRepository->findOneByActivationToken($token);
122 | if (!$registrationFlow) {
123 | $this->view->assign('tokenNotFound', true);
124 |
125 | return;
126 | }
127 |
128 | if (!$registrationFlow->hasValidActivationToken()) {
129 | $this->view->assign('tokenTimeout', true);
130 |
131 | return;
132 | }
133 |
134 | $user = $this->userCreationService->createUserAndAccount($registrationFlow);
135 | $this->registrationFlowRepository->remove($registrationFlow);
136 | $this->persistenceManager->allowObject($registrationFlow);
137 |
138 | $this->view->assign('success', true);
139 | $this->view->assign('user', $user);
140 | $this->view->assign('node', $this->request->getInternalArgument('__node'));
141 | }
142 |
143 | /**
144 | * Disable the technical error flash message
145 | *
146 | * @return boolean
147 | */
148 | protected function getErrorFlashMessage()
149 | {
150 | return false;
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/Classes/Controller/ResetPasswordController.php:
--------------------------------------------------------------------------------
1 | subjectResetPassword === 'i18n'
61 | ? $this->translator->translateById(
62 | 'email.subjectResetPassword',
63 | [],
64 | null,
65 | null,
66 | 'Main',
67 | 'Sandstorm.UserManagement'
68 | )
69 | : $this->subjectResetPassword;
70 | }
71 |
72 | /**
73 | * @Flow\SkipCsrfProtection
74 | */
75 | public function indexAction(string $email = '')
76 | {
77 | $this->view->assign('prefilledEmail', $email);
78 | }
79 |
80 | public function initializeRequestTokenAction()
81 | {
82 | $config = $this->arguments->getArgument('resetPasswordFlow')->getPropertyMappingConfiguration();
83 | $config->allowProperties('email');
84 | $config->setTypeConverterOption(
85 | PersistentObjectConverter::class,
86 | PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED,
87 | TRUE
88 | );
89 | }
90 |
91 | /**
92 | * @param ResetPasswordFlow $resetPasswordFlow
93 | */
94 | public function requestTokenAction(ResetPasswordFlow $resetPasswordFlow)
95 | {
96 | $account = $this->accountRepository->findActiveByAccountIdentifierAndAuthenticationProviderName($resetPasswordFlow->getEmail(),
97 | 'Sandstorm.UserManagement:Login');
98 |
99 | if ($account !== null) {
100 | $alreadyExistingFlows = $this->resetPasswordFlowRepository->findByEmail($resetPasswordFlow->getEmail());
101 | if (count($alreadyExistingFlows) > 0) {
102 | foreach ($alreadyExistingFlows as $alreadyExistingFlow) {
103 | $this->resetPasswordFlowRepository->remove($alreadyExistingFlow);
104 | }
105 | }
106 |
107 | // Send out a confirmation mail
108 | $resetPasswordLink = $this->uriBuilder->reset()->setCreateAbsoluteUri(true)->uriFor(
109 | 'insertNewPassword',
110 | ['token' => $resetPasswordFlow->getResetPasswordToken()],
111 | 'ResetPassword');
112 |
113 | $this->emailService->sendTemplateEmail(
114 | 'ResetPasswordToken',
115 | $this->getSubjectResetPassword(),
116 | [$resetPasswordFlow->getEmail()],
117 | [
118 | 'resetPasswordLink' => $resetPasswordLink,
119 | 'resetPasswordFlow' => $resetPasswordFlow
120 | ],
121 | 'sandstorm_usermanagement_sender_email',
122 | [], // cc
123 | [], // bcc
124 | [], // attachments
125 | 'sandstorm_usermanagement_replyTo_email'
126 | );
127 |
128 | $this->resetPasswordFlowRepository->add($resetPasswordFlow);
129 | }
130 |
131 |
132 | $this->view->assign('resetPasswordFlow', $resetPasswordFlow);
133 | $this->view->assign('account', $account);
134 | }
135 |
136 | /**
137 | * @param string $token
138 | */
139 | public function insertNewPasswordAction($token)
140 | {
141 | /* @var $resetPasswordFlow ResetPasswordFlow */
142 | $resetPasswordFlow = $this->resetPasswordFlowRepository->findOneByResetPasswordToken($token);
143 | if (!$resetPasswordFlow) {
144 | $this->view->assign('tokenNotFound', true);
145 |
146 | return;
147 | }
148 |
149 | if (!$resetPasswordFlow->hasValidResetPasswordToken()) {
150 | $this->view->assign('tokenTimeout', true);
151 |
152 | return;
153 | }
154 |
155 | $this->view->assign('success', true);
156 |
157 | $this->view->assign('resetPasswordFlow', $resetPasswordFlow);
158 | }
159 |
160 |
161 | /**
162 | * @param ResetPasswordFlow $resetPasswordFlow
163 | */
164 | public function updatePasswordAction(ResetPasswordFlow $resetPasswordFlow)
165 | {
166 | $account = $this->accountRepository->findActiveByAccountIdentifierAndAuthenticationProviderName($resetPasswordFlow->getEmail(),
167 | 'Sandstorm.UserManagement:Login');
168 |
169 | if (!$account) {
170 | $this->view->assign('accountNotFound', true);
171 |
172 | return;
173 | }
174 |
175 | $this->view->assign('success', true);
176 | $account->setCredentialsSource($resetPasswordFlow->getEncryptedPassword());
177 | $this->accountRepository->update($account);
178 | $this->resetPasswordFlowRepository->remove($resetPasswordFlow);
179 | }
180 |
181 | /**
182 | * Disable the default error flash message
183 | *
184 | * @return boolean
185 | */
186 | protected function getErrorFlashMessage()
187 | {
188 | return false;
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/en/Main.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Email address
7 |
8 |
9 | Login
10 |
11 |
12 | To the login
13 |
14 |
15 | Password
16 |
17 |
18 | Your password
19 |
20 |
21 | Password forgotten
22 |
23 |
24 | Logout
25 |
26 |
27 | You are logged in as {0}.
28 |
29 |
30 | First name
31 |
32 |
33 | Last name
34 |
35 |
36 | Confirm password
37 |
38 |
39 | Repeat password
40 |
41 |
42 | Salutation
43 |
44 |
45 | Ms.
46 |
47 |
48 | Mr.
49 |
50 |
51 | Register
52 |
53 |
54 | We have sent an email to confirm the registration to %s . Please follow the instructions in the email to confirm the registration.
55 |
56 |
57 | The activation link was not valid.
58 |
59 |
60 | Please register again.
61 |
62 |
63 | The activation link has expired.
64 |
65 |
66 | Your account is has been activated. You can now log in to our website.
67 |
68 |
69 | Unable to login.
70 |
71 |
72 | You have entered invalid access data. Please try again.
73 |
74 |
75 | Please confirm your account
76 |
77 |
78 | Password reset
79 |
80 |
81 | Email address {0} is already in use!
82 |
83 |
84 | Your passwords do not match.
85 |
86 |
87 | Your password needs to be at least {0} characters long.
88 |
89 |
90 | Your password must be at most {0} characters long.
91 |
92 |
93 |
94 | Your password needs to contain at least {0} lowercase letter.
95 |
96 |
97 | Your password needs to contain at least {0} lowercase letters.
98 |
99 |
100 |
101 |
102 | Your password needs to contain at least {0} uppercase letter.
103 |
104 |
105 | Your password needs to contain at least {0} uppercase letters.
106 |
107 |
108 |
109 |
110 | Your password needs to contain at least {0} number.
111 |
112 |
113 | Your password needs to contain at least {0} numbers.
114 |
115 |
116 |
117 |
118 | Your password needs to contain at least {0} special character.
119 |
120 |
121 | Your password needs to contain at least {0} special characters.
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/Classes/Controller/LoginController.php:
--------------------------------------------------------------------------------
1 | loginFailedTitle === 'i18n'
59 | ? $this->translator->translateById(
60 | 'authFailedMessage.title',
61 | [],
62 | null,
63 | null,
64 | 'Main',
65 | 'Sandstorm.UserManagement'
66 | )
67 | : $this->loginFailedTitle;
68 | }
69 |
70 | /**
71 | * @var string
72 | * @Flow\InjectConfiguration(path="authFailedMessage.body")
73 | */
74 | protected $loginFailedBody;
75 |
76 | /**
77 | * @return string
78 | */
79 | protected function getLoginFailedBody()
80 | {
81 | return $this->loginFailedBody === 'i18n'
82 | ? $this->translator->translateById(
83 | 'authFailedMessage.body',
84 | [],
85 | null,
86 | null,
87 | 'Main',
88 | 'Sandstorm.UserManagement'
89 | )
90 | : $this->loginFailedBody;
91 | }
92 |
93 | /**
94 | * SkipCsrfProtection is needed here because we will have errors otherwise if we render multiple
95 | * plugins on the same page
96 | *
97 | * @return void
98 | * @Flow\SkipCsrfProtection
99 | */
100 | public function loginAction()
101 | {
102 | $this->view->assign('account', $this->securityContext->getAccount());
103 | $this->view->assign('node', $this->request->getInternalArgument('__node'));
104 | }
105 |
106 | /**
107 | * Is called after a request has been authenticated.
108 | *
109 | * @param \Neos\Flow\Mvc\ActionRequest $originalRequest The request that was intercepted by the security framework, NULL if there was none
110 | * @throws \Neos\Flow\Exception
111 | * @return string
112 | */
113 | protected function onAuthenticationSuccess(ActionRequest $originalRequest = null)
114 | {
115 | $this->emitAuthenticationSuccess($this->controllerContext, $originalRequest);
116 |
117 | $result = $this->redirectTargetService->onAuthenticationSuccess($this->controllerContext, $originalRequest);
118 | if (is_string($result)) {
119 | $this->redirectToUri($result);
120 | } elseif ($result instanceof ActionRequest) {
121 | $this->redirectToRequest($result);
122 | }
123 |
124 | if ($result === null) {
125 | $this->view->assign('account', $this->securityContext->getAccount());
126 | } else {
127 | throw new Exception('RedirectTargetServiceInterface::onAuthenticationSuccess must return either null, an URL string or an ActionRequest object, but was: ' .
128 | gettype($result) . ' - ' . get_class($result), 1464164500);
129 | }
130 | }
131 |
132 | /**
133 | * Is called if authentication failed.
134 | *
135 | * Override this method in your login controller to take any
136 | * custom action for this event. Most likely you would want
137 | * to redirect to some action showing the login form again.
138 | *
139 | * @param \Neos\Flow\Security\Exception\AuthenticationRequiredException $exception The exception thrown while the authentication process
140 | * @return void
141 | */
142 | protected function onAuthenticationFailure(AuthenticationRequiredException $exception = null)
143 | {
144 | $this->emitAuthenticationFailure($this->controllerContext, $exception);
145 | $this->addFlashMessage($this->getLoginFailedBody(), $this->getLoginFailedTitle(),Message::SEVERITY_ERROR, [], ($exception === null ? 1347016771 : $exception->getCode()));
146 | }
147 |
148 | /**
149 | * Logs all active tokens out.
150 | */
151 | public function logoutAction()
152 | {
153 | parent::logoutAction();
154 |
155 | $this->emitLogout($this->controllerContext);
156 |
157 | $result = $this->redirectTargetService->onLogout($this->controllerContext);
158 |
159 | if (is_string($result)) {
160 | $this->redirectToUriAndShutdown($result);
161 | } elseif ($result instanceof ActionRequest) {
162 | $this->redirectToRequest($result);
163 | } else if ($result === null) {
164 | // Default: redirect to login
165 | $this->redirect('login');
166 | } else {
167 | throw new Exception('RedirectTargetServiceInterface::onLogout must return either null, an URL string or an ActionRequest object, but was: ' .
168 | gettype($result) . ' - ' . get_class($result), 1464164500);
169 | }
170 | }
171 |
172 | /**
173 | * Disable the default error flash message
174 | *
175 | * @return boolean
176 | */
177 | protected function getErrorFlashMessage()
178 | {
179 | return false;
180 | }
181 |
182 | /**
183 | * @param ControllerContext $controllerContext
184 | * @param ActionRequest $originalRequest
185 | * @Flow\Signal
186 | */
187 | protected function emitAuthenticationSuccess(ControllerContext $controllerContext, ActionRequest $originalRequest = null)
188 | {
189 | }
190 |
191 | /**
192 | * @param ControllerContext $controllerContext
193 | * @param AuthenticationRequiredException $exception
194 | * @Flow\Signal
195 | */
196 | protected function emitAuthenticationFailure(ControllerContext $controllerContext, AuthenticationRequiredException $exception = null)
197 | {
198 | }
199 |
200 | /**
201 | * @param ControllerContext $controllerContext
202 | * @Flow\Signal
203 | */
204 | protected function emitLogout(ControllerContext $controllerContext)
205 | {
206 | }
207 |
208 | /**
209 | * @param string $result
210 | */
211 | protected function redirectToUriAndShutdown(string $result)
212 | {
213 | $escapedUri = htmlentities($result, ENT_QUOTES, 'utf-8');
214 | $this->redirectToUri($this->uriFactory->createUri((string)$escapedUri));
215 | $this->bootstrap->shutdown(Bootstrap::RUNLEVEL_RUNTIME);
216 | exit();
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/de/Main.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Email address
7 | E-mail-Adresse
8 |
9 |
10 | Login
11 | Sich anmelden
12 |
13 |
14 | To the login
15 | Zum Login
16 |
17 |
18 | Password
19 | Passwort
20 |
21 |
22 | Your password
23 | Ihr Passwort
24 |
25 |
26 | Password forgotten
27 | Passwort vergessen
28 |
29 |
30 | Logout
31 | Ausloggen
32 |
33 |
34 | You are logged in as {0}.
35 | Sie sind eingeloggt als {0}.
36 |
37 |
38 | First name
39 | Vorname
40 |
41 |
42 | Last name
43 | Nachname
44 |
45 |
46 | Confirm password
47 | Passwort bestätigen
48 |
49 |
50 | Repeat password
51 | Passwort wiederholen
52 |
53 |
54 | Salutation
55 | Anrede
56 |
57 |
58 | Ms.
59 | Frau
60 |
61 |
62 | Mr.
63 | Herr
64 |
65 |
66 | Register
67 | Registrieren
68 |
69 |
70 | We have sent an email to confirm the registration to %s . Please follow the instructions in the email to confirm the registration.
71 | Wir haben eine E-Mail zur Bestätigung der Registrierung an %s gesendet. Bitte folgen Sie der Anleitung in der E-Mail, um die Registrierung zu bestätigen.
72 |
73 |
74 | The activation link was not valid.
75 | Der Aktivierungslink war nicht gültig.
76 |
77 |
78 | Please register again.
79 | Bitte registrieren Sie sich erneut.
80 |
81 |
82 | The activation link has expired.
83 | Der Aktivierungslink ist nicht mehr gültig.
84 |
85 |
86 | Your account is has been activated. You can now log in to our website.
87 | Ihr Konto ist aktiviert worden. Sie können sich jetzt auf unserer Website einloggen.
88 |
89 |
90 | Unable to login.
91 | Login nicht möglich
92 |
93 |
94 | You have entered invalid access data. Please try again.
95 | Sie haben ungültige Zugangsdaten eingegeben. Bitte versuchen Sie es noch einmal.
96 |
97 |
98 | Please confirm your account
99 | Bitte bestätigen Sie Ihr Konto
100 |
101 |
102 | Password reset
103 | Passwort zurücksetzen
104 |
105 |
106 | Email address {0} is already in use!
107 | Die E-Mail-Adresse {0} wird bereits verwendet!
108 |
109 |
110 | Your passwords do not match.
111 | Die Passwörter stimmen nicht überein.
112 |
113 |
114 | Your password needs to be at least {0} characters long.
115 | Das Passwort muss mindestens {0} Zeichen lang sein.
116 |
117 |
118 | Your password must be at most {0} characters long.
119 | Das Passwort darf höchstens {0} Zeichen lang sein.
120 |
121 |
122 |
123 | Your password needs to contain at least {0} lowercase letter.
124 | Das Passwort muss mindestens {0} Kleinbuchstaben enthalten.
125 |
126 |
127 | Your password needs to contain at least {0} lowercase letters.
128 | Das Passwort muss mindestens {0} Kleinbuchstaben enthalten.
129 |
130 |
131 |
132 |
133 | Your password needs to contain at least {0} uppercase letter.
134 | Das Passwort muss mindestens {0} Großbuchstaben enthalten.
135 |
136 |
137 | Your password needs to contain at least {0} uppercase letters.
138 | Das Passwort muss mindestens {0} Großbuchstaben enthalten.
139 |
140 |
141 |
142 |
143 | Your password needs to contain at least {0} number.
144 | Das Passwort muss mindestens {0} Zahl enthalten.
145 |
146 |
147 | Your password needs to contain at least {0} numbers.
148 | Das Passwort muss mindestens {0} Zahlen enthalten.
149 |
150 |
151 |
152 |
153 | Your password needs to contain at least {0} special character.
154 | Das Passwort muss mindestens {0} Sonderzeichen (z.B. %!=) enthalten.
155 |
156 |
157 | Your password needs to contain at least {0} special characters.
158 | Das Passwort muss mindestens {0} Sonderzeichen (z.B. %!=) enthalten.
159 |
160 |
161 |
162 |
163 |
164 |
--------------------------------------------------------------------------------
/Resources/Private/Translations/fr/Main.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Email address
7 | Adresse email
8 |
9 |
10 | Login
11 | Se connecter
12 |
13 |
14 | To the login
15 | Vers le login
16 |
17 |
18 | Password
19 | Mot de passe
20 |
21 |
22 | Your password
23 | Votre mot de passe
24 |
25 |
26 | Password forgotten
27 | Mot de passe oublié
28 |
29 |
30 | Logout
31 | Se déconnecter
32 |
33 |
34 | You are logged in as {0}.
35 | Vous êtes connecté en tant que {0}.
36 |
37 |
38 | First name
39 | Prénom
40 |
41 |
42 | Last name
43 | Nom de famille
44 |
45 |
46 | Confirm password
47 | Confirmation du mot de passe
48 |
49 |
50 | Repeat password
51 | Répéter le mot de passe
52 |
53 |
54 | Salutation
55 | Salutation
56 |
57 |
58 | Ms.
59 | Madame
60 |
61 |
62 | Mr.
63 | Monsieur
64 |
65 |
66 | Register
67 | S'inscrire
68 |
69 |
70 | We have sent an email to confirm the registration to {0}. Please follow the instructions in the email to confirm the registration.
71 | Nous avons envoyé un email à {0} pour confirmer l'enregistrement. Veuillez suivre les instructions contenues dans l'e-mail pour confirmer l'inscription.
72 |
73 |
74 | The activation link was not valid.
75 | Le lien d'activation n'était pas valide.
76 |
77 |
78 | Please register again.
79 | Veuillez vous réinscrire.
80 |
81 |
82 | The activation link has expired.
83 | Le lien d'activation a expiré.
84 |
85 |
86 | Your account is has been activated. You can now log in to our website.
87 | Votre compte a été activé. Vous pouvez maintenant vous connecter à notre site web.
88 |
89 |
90 | Unable to login.
91 | Impossible de se connecter.
92 |
93 |
94 | You have entered invalid access data. Please try again.
95 | Vous avez saisi des données d'accès invalides. Veuillez réessayer.
96 |
97 |
98 | Please confirm your account
99 | Veuillez confirmer votre compte
100 |
101 |
102 | Password reset
103 | Réinitialisation du mot de passe
104 |
105 |
106 | Email address {0} is already in use!
107 | L'adresse e-mail {0} est déjà utilisée !
108 |
109 |
110 | Your passwords do not match.
111 | Vos mots de passe ne correspondent pas.
112 |
113 |
114 | Your password needs to be at least {0} characters long.
115 | Votre mot de passe doit comporter au moins {0} caractères.
116 |
117 |
118 | Your password must be at most {0} characters long.
119 | Votre mot de passe doit comporter au maximum {0} caractères.
120 |
121 |
122 |
123 | Your password needs to contain at least {0} lowercase letter.
124 | Votre mot de passe doit contenir au moins {0} lettre minuscule.
125 |
126 |
127 | Your password needs to contain at least {0} lowercase letters.
128 | Votre mot de passe doit contenir au moins {0} lettres minuscules.
129 |
130 |
131 |
132 |
133 | Your password needs to contain at least {0} uppercase letter.
134 | Votre mot de passe doit contenir au moins {0} lettre majuscule.
135 |
136 |
137 | Your password needs to contain at least {0} uppercase letters.
138 | Votre mot de passe doit contenir au moins {0} lettres majuscules.
139 |
140 |
141 |
142 |
143 | Your password needs to contain at least {0} number.
144 | Votre mot de passe doit contenir au moins {0} chiffre.
145 |
146 |
147 | Your password needs to contain at least {0} numbers.
148 | Votre mot de passe doit contenir au moins {0} chiffres.
149 |
150 |
151 |
152 |
153 | Your password needs to contain at least {0} special character.
154 | Votre mot de passe doit contenir au moins {0} caractère spécial.
155 |
156 |
157 | Your password needs to contain at least {0} special characters.
158 | Votre mot de passe doit contenir au moins {0} caractères spéciaux.
159 |
160 |
161 |
162 |
163 |
164 |
--------------------------------------------------------------------------------
/Classes/Command/SandstormUserCommandController.php:
--------------------------------------------------------------------------------
1 | 0) {
97 | $attributesSplitBySeparator = explode(';', $additionalAttributes);
98 | array_map(function ($singleAttribute) use (&$attributes) {
99 | $splitAttribute = explode(':', $singleAttribute);
100 | $attributes[$splitAttribute[0]] = $splitAttribute[1];
101 | }, $attributesSplitBySeparator);
102 | }
103 |
104 | $passwordDto = new PasswordDto();
105 | $passwordDto->setPassword($password);
106 | $passwordDto->setPasswordConfirmation($password);
107 | $registrationFlow = new RegistrationFlow();
108 | $registrationFlow->setPasswordDto($passwordDto);
109 | $registrationFlow->setEmail($username);
110 | $registrationFlow->setAttributes($attributes);
111 |
112 | // Remove existing registration flows
113 | $alreadyExistingFlows = $this->registrationFlowRepository->findByEmail($registrationFlow->getEmail());
114 | if (count($alreadyExistingFlows) > 0) {
115 | foreach ($alreadyExistingFlows as $alreadyExistingFlow) {
116 | $this->registrationFlowRepository->remove($alreadyExistingFlow);
117 | }
118 | }
119 | $registrationFlow->storeEncryptedPassword();
120 |
121 | // Store the RF and persist so the activate command will find it
122 | $this->registrationFlowRepository->add($registrationFlow);
123 | $this->persistenceManager->persistAll();
124 |
125 | // Directly activate the account
126 | $this->activateRegistrationCommand($username);
127 |
128 | $this->outputLine('Added the User "%s" with password "%s" .', [$username, $password]);
129 | }
130 |
131 | /**
132 | * @param string $username The username identifying a pending registration flow.
133 | */
134 | public function activateRegistrationCommand($username)
135 | {
136 | /* @var $registrationFlow \Sandstorm\UserManagement\Domain\Model\RegistrationFlow */
137 | $registrationFlow = $this->registrationFlowRepository->findOneByEmail($username);
138 |
139 | if ($registrationFlow === null) {
140 | $this->outputLine('The user ' . $username . ' doesn\'t have a non-activated account.');
141 | $this->quit(1);
142 | }
143 |
144 | $this->userCreationService->createUserAndAccount($registrationFlow);
145 | $this->registrationFlowRepository->remove($registrationFlow);
146 | $this->outputLine('The user ' . $username . ' was activated.');
147 | }
148 |
149 |
150 | /**
151 | * Set a new password for the given user
152 | *
153 | * @param string $username user to modify
154 | * @param string $password new password
155 | * @param string $authenticationProvider Name of the authentication provider to use for finding the user. Default: "Sandstorm.UserManagement:Login".
156 | * @return void
157 | */
158 | public function setPasswordCommand($username, $password, $authenticationProvider = 'Sandstorm.UserManagement:Login')
159 | {
160 | // If we're in Neos context, we simply forward the command to the Neos command controller.
161 | if ($this->shouldUseNeosService()) {
162 | $cliRequest = new Request($this->request);
163 | $cliRequest->setControllerObjectName(UserCommandController::class);
164 | $cliRequest->setControllerCommandName('setPassword');
165 | $cliRequest->setArguments([
166 | 'username' => $username,
167 | 'password' => $password,
168 | 'authenticationProvider' => $authenticationProvider
169 | ]);
170 | $cliResponse = new Response($this->response);
171 | $this->dispatcher->dispatch($cliRequest, $cliResponse);
172 | $this->quit(0);
173 | }
174 |
175 | // Otherwise, we use our own logic.
176 | $account = $this->accountRepository->findByAccountIdentifierAndAuthenticationProviderName($username,
177 | $authenticationProvider);
178 |
179 | if ($account === null) {
180 | $this->outputLine('The user ' . $username . ' could not be found with auth provider ' .
181 | $authenticationProvider . ' .');
182 | $this->quit(1);
183 | }
184 |
185 | $encrypted = $this->hashService->hashPassword($password);
186 | $account->setCredentialsSource($encrypted);
187 | $this->accountRepository->update($account);
188 | $this->outputLine('Password for user ' . $username . ' changed.');
189 | }
190 |
191 | /**
192 | * Removes a user and his account.
193 | *
194 | * @param string $username user to remove
195 | * @return void
196 | */
197 | public function removeCommand($username)
198 | {
199 | /** @var User $user */
200 | $user = $this->userRepository->findOneByEmail($username);
201 | if ($user === null) {
202 | $this->outputLine('The user ' . $username . ' could not be found.');
203 | $this->quit(1);
204 | }
205 |
206 | $this->userRepository->remove($user);
207 | $this->accountRepository->remove($user->getAccount());
208 | $this->outputLine('Removed the user ' . $username . ' .');
209 | }
210 |
211 | /**
212 | * Lists all available accounts.
213 | */
214 | public function listAccountsCommand()
215 | {
216 | /** @var Account[] $accounts */
217 | $accounts = $this->accountRepository->findAll()->toArray();
218 | usort($accounts, function ($a, $b) {
219 | /** @var Account $a */
220 | /** @var Account $b */
221 | return ($a->getAccountIdentifier() > $b->getAccountIdentifier());
222 | });
223 |
224 | $tableRows = [];
225 | $headerRow = ['Identifier', 'Authentication Provider', 'Role(s)'];
226 |
227 | foreach ($accounts as $account) {
228 | $tableRows[] = [
229 | $account->getAccountIdentifier(),
230 | $account->getAuthenticationProviderName(),
231 | implode(' ,', $account->getRoles())
232 | ];
233 | }
234 |
235 | $this->output->outputTable($tableRows, $headerRow);
236 | $this->outputLine(sprintf(' %s accounts total. ', count($accounts)));
237 | }
238 |
239 | /**
240 | * Lists all available users.
241 | */
242 | public function listUsersCommand()
243 | {
244 | // If we're in Neos context, we pass on the command.
245 | if ($this->shouldUseNeosService()) {
246 | $cliRequest = new Request($this->request);
247 | $cliRequest->setControllerObjectName(UserCommandController::class);
248 | $cliRequest->setControllerCommandName('list');
249 | $cliResponse = new Response($this->response);
250 | $this->dispatcher->dispatch($cliRequest, $cliResponse);
251 |
252 | return;
253 | }
254 | /** @var User[] $users */
255 | $users = $this->userRepository->findAll()->toArray();
256 | usort($users, function ($a, $b) {
257 | /** @var User $a */
258 | /** @var User $b */
259 | return ($a->getEmail() > $b->getEmail());
260 | });
261 |
262 | $tableRows = [];
263 | $headerRow = ['Email', 'Name', 'Role(s)'];
264 |
265 | foreach ($users as $user) {
266 | $tableRows[] = [$user->getEmail(), $user->getFullName(), implode(' ,', $user->getAccount()->getRoles())];
267 | }
268 |
269 | $this->output->outputTable($tableRows, $headerRow);
270 | $this->outputLine(sprintf(' %s users total. ', count($users)));
271 | }
272 |
273 | /**
274 | * Add a role to a given account
275 | *
276 | * @param string $accountIdentifier
277 | * @param string $role
278 | * @throws \Neos\Flow\Persistence\Exception\IllegalObjectTypeException
279 | */
280 | public function addRoleCommand($accountIdentifier, $role)
281 | {
282 | /** @var Account $account */
283 | $account = $this->accountRepository->findOneByAccountIdentifier($accountIdentifier);
284 | if ($account === null) {
285 | $this->outputLine(sprintf('Account %s not found! Exiting.', $accountIdentifier));
286 | $this->quit(1);
287 | }
288 |
289 | $account->addRole(new Role($role));
290 | $this->accountRepository->update($account);
291 | $this->outputLine(sprintf(' Account %s now has roles %s.', $accountIdentifier, implode(', ', $account->getRoles())));
292 | }
293 |
294 | /**
295 | * Remove a role from an accont
296 | *
297 | * @param string $accountIdentifier
298 | * @param string $role
299 | * @throws \Neos\Flow\Persistence\Exception\IllegalObjectTypeException
300 | */
301 | public function removeRoleCommand($accountIdentifier, $role)
302 | {
303 | /** @var Account $account */
304 | $account = $this->accountRepository->findOneByAccountIdentifier($accountIdentifier);
305 | if ($account === null) {
306 | $this->outputLine(sprintf('Account %s not found! Exiting.', $accountIdentifier));
307 | $this->quit(1);
308 | }
309 |
310 | $account->removeRole(new Role($role));
311 | $this->accountRepository->update($account);
312 | $this->outputLine(sprintf(' Account %s now has roles %s.', $accountIdentifier, implode(', ', $account->getRoles())));
313 | }
314 |
315 | /**
316 | * We check if we're in the Neos context by checking if we're using the Neos user creation service.
317 | *
318 | * @return boolean
319 | */
320 | protected function shouldUseNeosService()
321 | {
322 | // The userCreationService is a DependencyProxy instance here, we can get the class name from it
323 | return get_class($this->userCreationService) === NeosUserCreationService::class;
324 | }
325 | }
326 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sandstorm.UserManagement Neos / Flow Package
2 |
3 | # 0. Features
4 | This package works in Neos CMS and Flow and provides the following functionality:
5 |
6 | * Registration of (frontend) users via a registration form
7 | * Sending out an e-mail for account confirmation
8 | * Login of registered (frontend) users via a login form
9 | * "Forgotten password" with password reset e-mail
10 |
11 | # 1. Compatibility and Maintenance
12 | Sandstorm.UserManagement is currently being maintained for the following versions:
13 |
14 | | Neos / Flow Version | Sandstorm.UserManagement Version | Branch | Maintained |
15 | |----------------------------|----------------------------------|--------|------------|
16 | | Neos 5.x-8.x, Flow 6.x-8.x | 7.x | master | Yes |
17 | | Neos 4.x, Flow 5.x | 6.x | 6.0 | Yes |
18 | | Neos 3.x, Flow 4.x | 5.x | 5.0 | Bugfixes |
19 | | Neos 2.3 LTS, Flow 3.3 LTS | 3.x | 3.0 | No |
20 | | Neos 2.2, Flow 3.2 | 1.x | - | No |
21 |
22 | ## Breaking changes in Version 5.x
23 | ### Configuration Changes
24 | Since I've removed the direct dependency to swiftmailer in favor of the Sandstorm/TemplateMailer package
25 | (which provides css inlining), the EmailService in this package was removed. This means that you will need
26 | to change some of your config options, because they are now set in the Sandstorm.TemplateMailer config path
27 | instead of inside the Sandstom.UserManagement path. Please refer to the [Sandstorm/TemplateMailer Documentation](https://github.com/sandstorm/TemplateMailer)
28 | for instructions on how to set the following configurations:
29 |
30 | * senderAddress
31 | * senderName
32 | * templatePackage
33 |
34 | Hint: to override the sender address for this package, you will need the following setting:
35 | ```YAML
36 | Sandstorm:
37 | TemplateMailer:
38 | senderAddresses:
39 | sandstorm_usermanagement_sender_email: # You need to use this exact key to override the UserManagement defaults
40 | name: Your-App
41 | address: yoursenderemail@yourapp.de
42 | ```
43 |
44 | ### Changes to Email Templates
45 | In the registration email templates, two variables are no longer available by default:
46 | * "applicationName" (filled with configured email senderAddress)
47 | * "email" (filled with the email address the mail is sent to)
48 | However, in the registration email, "registrationFlow" is now available, which gives access to the email as well to all
49 | other information the user has entered during the registration process (as long as it is stored in the RegistrationFlow object).
50 |
51 | To overwrite the existing Activation- and PasswordReset templates, do the following:
52 | * Add `ActivationToken.html`, `ActivationToken.txt`, `ResetPasswordToken.html` and `ResetPasswordToken.txt` to `/Resources/Private/EmailTemplates/`
53 | * Fill in the necessary information (you can use the [sandstormUserManagement templates](https://github.com/sandstorm/UserManagement/tree/master/Resources/Private/EmailTemplates) as a point of reference
54 | * Add your package to your TemplateMailer-Configuration https://github.com/sandstorm/TemplateMailer#configuring-template-source-packages
55 |
56 | # 2. Configuration
57 |
58 | ## Setup
59 | There are the basic config steps:
60 | 1. Run `./flow doctrine:migrate` after you add this package to install its model. The package automatically exposes its routes
61 | via auto-inclusion in the package settings. Attention: Any routes defined in the global `Routes.yaml` are loaded before this package's
62 | routes, so they may be overriden. This is especially true for the default Flow subroutes, so make sure you have removed those from your global `Routes.yaml`.
63 | If you can't remove them, just include the subroutes for this package manually before the Flow subroutes.
64 |
65 | 2. Require this package in your own package's `composer.json`. This will inform Flow that it needs to load UserManagement before
66 | your packages, which allows you to override config and will make sure authorizations work correctly. Keep in mind that you have to add this into all
67 | packages that use features from user management - very important if your site is split into multiple packages or plugins.
68 | Here's an example:
69 | ```
70 | {
71 | "description": "Your Site Package",
72 | "type": "neos-site", (or "neos-package" if you're using Flow only or building a Plugin)
73 | "require": {
74 | "neos/neos": "*",
75 | "sandstorm/usermanagement": "*"
76 | }
77 | ...more settings here...
78 | }
79 | ```
80 |
81 | 3. Run `./flow neos.flow:package:rescan` to regenerate to order in which all your packages are loaded.
82 |
83 | 4. Add and adapt the configuration settings below to your config (make sure to not miss the special Neos settings).
84 |
85 | ## Basic configuration options
86 | These are the basic configuration options for e-mails, timeouts etc. You will usually want to adapt these to your application.
87 | ```
88 | Sandstorm:
89 | UserManagement:
90 | # Validity timespan for the activation token for newly registered users.
91 | activationTokenTimeout: '2 days'
92 | # Validity timespan for the token used to reset passwords.
93 | resetPasswordTokenTimeout: '4 hours'
94 | # The message that appears if a user could not be logged in.
95 | authFailedMessage:
96 | title: 'Login nicht möglich'
97 | body: 'Sie haben ungültige Zugangsdaten eingegeben. Bitte versuchen Sie es noch einmal.'
98 | # Email settings
99 | email:
100 | # Subject line for the account confirmation email
101 | subjectActivation: 'Please confirm your account'
102 | # Subject line for the password reset email
103 | subjectResetPassword: 'Password reset'
104 | # An array of roles which are assigned to users after they activate their account.
105 | rolesForNewUsers: []
106 | ```
107 |
108 | ### I18N
109 | It is possible to use i18n for the messages configured in the settings. Simply by setting the values to 'i18n'.
110 | ```
111 | Sandstorm:
112 | UserManagement:
113 | authFailedMessage:
114 | title: 'i18n'
115 | body: 'i18n'
116 | email:
117 | subjectActivation: 'i18n'
118 | subjectResetPassword: 'i18n'
119 | ```
120 |
121 | ## Additional Settings for usage in Neos
122 | You should switch the implementation of the Redirect and User Creation Services to the Neos services. Add this to your `Objects.yaml`:
123 | ```
124 | # Use the Neos services
125 | Sandstorm\UserManagement\Domain\Service\RedirectTargetServiceInterface:
126 | className: 'Sandstorm\UserManagement\Domain\Service\Neos\NeosRedirectTargetService'
127 | Sandstorm\UserManagement\Domain\Service\UserCreationServiceInterface:
128 | className: 'Sandstorm\UserManagement\Domain\Service\Neos\NeosUserCreationService'
129 | ```
130 |
131 | Be aware that the `NeosUserCreationService` requires a non-empty firstName and lastName to be present in the `RegistrationFlow` attributes
132 | as it's in the templates of this package.
133 |
134 | ### Neos 3.0 and higher
135 |
136 | Add the following to your package's (or the global) `Settings.yaml`. This creates a separate authentication provider so Neos can
137 | distinguish between frontend and backend logins.
138 |
139 | ```
140 | Neos:
141 | Flow:
142 | security:
143 | authentication:
144 | providers:
145 | 'Neos.Neos:Backend':
146 | requestPatterns:
147 | Sandstorm.UserManagement:NeosBackend:
148 | pattern: Sandstorm\UserManagement\Security\NeosRequestPattern
149 | patternOptions:
150 | 'area': 'backend'
151 | 'Sandstorm.UserManagement:Login':
152 | provider: PersistedUsernamePasswordProvider
153 | requestPatterns:
154 | Sandstorm.UserManagement:NeosFrontend:
155 | pattern: Sandstorm\UserManagement\Security\NeosRequestPattern
156 | patternOptions:
157 | 'area': 'frontend'
158 |
159 | ```
160 |
161 | ### Neos 2.3 (Flow 3.3)
162 |
163 | Before Neos 3.0, the `Neos.Neos:Backend` authentication provider was called `Typo3BackendProvider`. Replace `Neos.Neos:Backend`
164 | with `Typo3BackendProvider` in the config above.
165 |
166 | # 3. Usage
167 |
168 | ## CLI Commands
169 | ### Creating users
170 | The package exposes a command to create users. You can run
171 |
172 | `./flow sandstormuser:create test@example.com password --additionalAttributes="firstName:Max;lastName:Mustermann"`
173 |
174 | to create a user. This will create a Neos user if you're using the package in Neos. You can assign
175 | roles to the new user in the Neos backend afterwards.
176 |
177 | ### Confirming user registration
178 | It is possible to confirm a registrationflow and trigger user creation by running
179 |
180 | `./flow sandstormuser:activateregistration test@example.com`
181 |
182 | ### Resetting passwords
183 | Since 1.1.2, it is possible to reset passwords for users created with this package.
184 |
185 | `./flow sandstormuser:setpassword test@example.com password`
186 |
187 | If the package detects that the NeosUserCreationService is used, it forwards the command to the
188 | Neos `UserCommandController->setPasswordCommand()`. Otherwise, our oackage's own logic is used.
189 |
190 | The Authentication Provider can be passed in as an optional argument to reset passwords for users created
191 | with a different provider that the default UserManagement one (`Sandstorm.UserManagement:Login`):
192 |
193 | `./flow sandstormuser:setpassword test@example.com password --authenticationProvider=Typo3BackendProvider`
194 |
195 | ## Redirect after login/logout
196 | ### Via configuration
197 | To define where users should be redirected after they log in or out, you can set some config options:
198 | ```
199 | Sandstorm:
200 | UserManagement:
201 | redirect:
202 | # To activate redirection, make these settings:
203 | afterLogin:
204 | action: 'action'
205 | controller: 'Controller'
206 | package: 'Your.Package'
207 | afterLogout:
208 | action: 'action'
209 | controller: 'Controller'
210 | package: 'Your.Package'
211 | ```
212 |
213 | ### Via node properties
214 | When using the package within Neos, you have another possibility: you can set properties on the LoginForm node type.
215 | The pages you link here will be shown after users log in or out. Please note that when a login/logout form is displayed
216 | on a restricted page: in that case you MUST set a redirect target, otherwise you will receive an error message on logout.
217 | If the redirection is configured via Settings.yaml, they will take precedence over the configuration at the node.
218 | You can, of course, set these properties from TypoScript also if you have a login/logout form directly in you template:
219 | ```
220 | loginform = Sandstorm.UserManagement:LoginForm {
221 | // This should be set, or there will be problems when you have multiple plugins on a page
222 | argumentNamespace = 'login'
223 | // Redirect to the parent page automatically after logout
224 | redirectAfterLogout = ${q(documentNode).parent().get(0)}
225 | }
226 | ```
227 |
228 | ### Via custom RedirectTargetService
229 | If redirecting to a specific controller method is still not enough for you, you can simply roll your own implementation of the
230 | `RedirectTargetServiceInterface`. Just add the implementation within your own package and add the following lines to your `Objects.yaml`.
231 | Mind the package loading order, you package should require sandstorm/usermanagement in its composer.json.
232 | ```
233 | Sandstorm\UserManagement\Domain\Service\RedirectTargetServiceInterface:
234 | className: 'Your\Package\Domain\Service\YourCustomRedirectTargetService'
235 | ```
236 |
237 | ## Checking for a logged-in user in your templates
238 | There is a ViewHelper available that allows you to check if somebody is logged into the frontend. Here's an example:
239 |
240 | ```
241 | {namespace um=Sandstorm\UserManagement\ViewHelpers}
242 |
243 |
244 |
245 | You are currently logged in.
246 |
247 |
248 | You are not logged in!
249 |
250 |
251 |
252 | ```
253 |
254 | If you have configured a different Authentication Provider than the default one, the viewhelper has an `authenticationProviderName`
255 | argument to which you can pass the name of the Auth Provider you are using.
256 |
257 | # Extending the package
258 |
259 | ## Changing / overriding templates
260 | You can change any template via the default method using `Views.yaml`. Please see
261 | http://flowframework.readthedocs.io/en/stable/TheDefinitiveGuide/PartIII/ModelViewController.html#configuring-views-through-views-yaml.
262 | Here's an example how to plug your own login template:
263 |
264 | ```YAML
265 |
266 | -
267 | requestFilter: 'mainRequest.isPackage("Neos.Neos") && isPackage("Sandstorm.UserManagement") && isController("Login") && isAction("login")'
268 | options:
269 | templatePathAndFilename: 'resource://Your.Package/Private/Templates/Login/Login.html'
270 | partialRootPaths: ['resource://Your.Package/Private/Partials']
271 | layoutRootPaths: ['resource://Your.Package/Private/Layouts']
272 | ```
273 |
274 | ## Overriding e-mail templates
275 | As documented in the configuration options above, overriding e-mail templates is easy:
276 | * Copy the `EmailTemplates` folder from the UserManagement's `Resources/Private` folder into your
277 | own package and modify the templates to your heart's desire.
278 | * Add your own package to the templatePackages config, as described in [Sandstorm/TemplateMailer Documentation](https://github.com/sandstorm/TemplateMailer).
279 |
280 | ## Changing the User model
281 | You might want to add additional information to the user model. This can be done by extending
282 | the User model delivered with this package and adding properties as you like. You will then
283 | need to switch out the implementation of `UserCreationServiceInterface` to get control over
284 | the creation process. This can be done via `Objects.yaml`:
285 | ```YAML
286 | Sandstorm\UserManagement\Domain\Service\UserCreationServiceInterface:
287 | className: 'Your\Package\Domain\Service\YourCustomUserCreationService'
288 | ```
289 |
290 | ## Hooking into the login/logout process
291 | The UserManagement package emits three signals during the login and logout process, into which you can hook
292 | using Flows [Signals and Slots](http://flowframework.readthedocs.io/en/stable/TheDefinitiveGuide/PartIII/SignalsAndSlots.html)
293 | mechanism. You could for example use this to set additional cookies when a user logs in, e.g. to enable JWT authentication
294 | with another service. Here is an example of using all three, you could copy this into your own `Package.php` file:
295 | ```PHP
296 | public function boot(Bootstrap $bootstrap) {
297 | $dispatcher = $bootstrap->getSignalSlotDispatcher();
298 | $dispatcher->connect(
299 | \Sandstorm\UserManagement\Controller\LoginController::class, 'authenticationSuccess',
300 | \Your\Package\Domain\Service\ExampleService::class, 'onAuthenticationSuccess'
301 | );
302 | $dispatcher->connect(
303 | \Sandstorm\UserManagement\Controller\LoginController::class, 'authenticationFailure',
304 | \Your\Package\Domain\Service\ExampleService::class, 'onAuthenticationFailure'
305 | );
306 | $dispatcher->connect(
307 | \Sandstorm\UserManagement\Controller\LoginController::class, 'logout',
308 | \Your\Package\Domain\Service\ExampleService::class, 'onLogout'
309 | );
310 | }
311 | ```
312 |
313 | Your example service could then look like this:
314 | ```PHP
315 | namespace Your\Package\Domain\Service;
316 |
317 | use Neos\Flow\Mvc\ActionRequest;
318 | use Neos\Flow\Mvc\Controller\ControllerContext;
319 | use Neos\Flow\Security\Exception\AuthenticationRequiredException;
320 |
321 | class ExampleService
322 | {
323 | public function onAuthenticationSuccess(ControllerContext $controllerContext, ActionRequest $originalRequest = null)
324 | {
325 | // Do custom stuff here
326 | }
327 |
328 | public function onAuthenticationFailure(ControllerContext $controllerContext, AuthenticationRequiredException $exception = null)
329 | {
330 | // Do custom stuff here
331 | }
332 |
333 | public function onLogout(ControllerContext $controllerContext)
334 | {
335 | // Do custom stuff here
336 | }
337 | }
338 | ```
339 |
340 | ## Changing the Registration Flow and validation logic
341 | The `RegistrationFlow` class is the representation of a user signing up for your application.
342 | It has a few default properties and can be extended with arbitrary additional data via its `attributes` property.
343 |
344 | ### Adding custom fields to the Registration Flow
345 | Exchange the registration template as described above and add a field:
346 | ```HTML
347 |
348 | ```
349 | This will add the field, but of course you might also want to validate it.
350 |
351 | ### Extending the Registration Flow validation logic
352 | The UserManagement package has a hook for you to implement your custom registration flow validation logic. It is
353 | called directly from the domain model validator of the package. All you need to to is create an implementation of
354 | `Sandstorm\UserManagement\Domain\Service\RegistrationFlowValidationServiceInterface` in your own package. It could
355 | look like this:
356 | ```PHP
357 | class RegistrationFlowValidationService implements RegistrationFlowValidationServiceInterface {
358 | /**
359 | * @param RegistrationFlow $registrationFlow
360 | * @param RegistrationFlowValidator $validator
361 | * @return void
362 | */
363 | public function validateRegistrationFlow(RegistrationFlow $registrationFlow, RegistrationFlowValidator $validator) {
364 | // This is an example of your own custom validation logic.
365 | if ($registrationFlow->getAttributes()['agb'] !== '1') {
366 | $validator->getResult()->forProperty('attributes.terms')->addError(new \Neos\Flow\Validation\Error('You need to accept the terms and conditions.'));
367 | }
368 | }
369 | }
370 | ```
371 |
372 | # 4. Running Tests
373 | Run all tests with:
374 | `./bin/phpunit -c ./Build/BuildEssentials/PhpUnit/UnitTests.xml Packages/Application/Sandstorm.UserManagement/Tests/Unit`
375 |
376 | # 5. Known issues
377 |
378 | Feel free to submit issues/PRs :)
379 |
380 | # 6. TODOs
381 |
382 | * More Tests.
383 |
384 | # 7. FAQ
385 |
386 | * *What happens if the user did not receive the registration email?*
387 | Just tell the user to register again. In this case, previous unfinished registrations are discarded.
388 |
389 | # 8. License
390 | MIT.
391 | https://opensource.org/licenses/MIT
392 |
--------------------------------------------------------------------------------