\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
-
-#: Auth/SocialAuthenticate.php:456
-msgid "Provider cannot be empty"
-msgstr ""
-
-#: Controller/SocialAccountsController.php:52
-msgid "Account validated successfully"
-msgstr ""
-
-#: Controller/SocialAccountsController.php:54
-msgid "Account could not be validated"
-msgstr ""
-
-#: Controller/SocialAccountsController.php:57
-msgid "Invalid token and/or social account"
-msgstr ""
-
-#: Controller/SocialAccountsController.php:59;87
-msgid "Social Account already active"
-msgstr ""
-
-#: Controller/SocialAccountsController.php:61
-msgid "Social Account could not be validated"
-msgstr ""
-
-#: Controller/SocialAccountsController.php:80
-msgid "Email sent successfully"
-msgstr ""
-
-#: Controller/SocialAccountsController.php:82
-msgid "Email could not be sent"
-msgstr ""
-
-#: Controller/SocialAccountsController.php:85
-msgid "Invalid account"
-msgstr ""
-
-#: Controller/SocialAccountsController.php:89
-msgid "Email could not be resent"
-msgstr ""
-
-#: Controller/Component/RememberMeComponent.php:68
-msgid "Invalid app salt, app salt must be at least 256 bits (32 bytes) long"
-msgstr ""
-
-#: Controller/Component/UsersAuthComponent.php:204
-msgid "You can't enable email validation workflow if use_email is false"
-msgstr ""
-
-#: Controller/Traits/LinkSocialTrait.php:54
-msgid "Could not associate account, please try again."
-msgstr ""
-
-#: Controller/Traits/LinkSocialTrait.php:77
-msgid "Social account was associated."
-msgstr ""
-
-#: Controller/Traits/LoginTrait.php:104
-msgid "Issues trying to log in with your social account"
-msgstr ""
-
-#: Controller/Traits/LoginTrait.php:109
-#: Template/Users/social_email.ctp:16
-msgid "Please enter your email"
-msgstr ""
-
-#: Controller/Traits/LoginTrait.php:120
-msgid "Your user has not been validated yet. Please check your inbox for instructions"
-msgstr ""
-
-#: Controller/Traits/LoginTrait.php:125
-msgid "Your social account has not been validated yet. Please check your inbox for instructions"
-msgstr ""
-
-#: Controller/Traits/LoginTrait.php:180
-#: Controller/Traits/RegisterTrait.php:82
-msgid "Invalid reCaptcha"
-msgstr ""
-
-#: Controller/Traits/LoginTrait.php:191
-msgid "You are already logged in"
-msgstr ""
-
-#: Controller/Traits/LoginTrait.php:212
-msgid "Please enable Google Authenticator first."
-msgstr ""
-
-#: Controller/Traits/LoginTrait.php:287
-msgid "Verification code is invalid. Try again"
-msgstr ""
-
-#: Controller/Traits/LoginTrait.php:340
-msgid "Username or password is incorrect"
-msgstr ""
-
-#: Controller/Traits/LoginTrait.php:363
-msgid "You've successfully logged out"
-msgstr ""
-
-#: Controller/Traits/PasswordManagementTrait.php:49;82
-#: Controller/Traits/ProfileTrait.php:50
-msgid "User was not found"
-msgstr ""
-
-#: Controller/Traits/PasswordManagementTrait.php:70;78;86
-msgid "Password could not be changed"
-msgstr ""
-
-#: Controller/Traits/PasswordManagementTrait.php:74
-msgid "Password has been changed successfully"
-msgstr ""
-
-#: Controller/Traits/PasswordManagementTrait.php:84
-msgid "{0}"
-msgstr ""
-
-#: Controller/Traits/PasswordManagementTrait.php:127
-msgid "Please check your email to continue with password reset process"
-msgstr ""
-
-#: Controller/Traits/PasswordManagementTrait.php:130
-#: Shell/UsersShell.php:247
-msgid "The password token could not be generated. Please try again"
-msgstr ""
-
-#: Controller/Traits/PasswordManagementTrait.php:136
-#: Controller/Traits/UserValidationTrait.php:107
-msgid "User {0} was not found"
-msgstr ""
-
-#: Controller/Traits/PasswordManagementTrait.php:138
-msgid "The user is not active"
-msgstr ""
-
-#: Controller/Traits/PasswordManagementTrait.php:140
-#: Controller/Traits/UserValidationTrait.php:102;111
-msgid "Token could not be reset"
-msgstr ""
-
-#: Controller/Traits/PasswordManagementTrait.php:164
-msgid "Google Authenticator token was successfully reset"
-msgstr ""
-
-#: Controller/Traits/ProfileTrait.php:54
-msgid "Not authorized, please login first"
-msgstr ""
-
-#: Controller/Traits/RegisterTrait.php:43
-msgid "You must log out to register a new user account"
-msgstr ""
-
-#: Controller/Traits/RegisterTrait.php:89
-msgid "The user could not be saved"
-msgstr ""
-
-#: Controller/Traits/RegisterTrait.php:123
-msgid "You have registered successfully, please log in"
-msgstr ""
-
-#: Controller/Traits/RegisterTrait.php:125
-msgid "Please validate your account before log in"
-msgstr ""
-
-#: Controller/Traits/SimpleCrudTrait.php:77;107
-msgid "The {0} has been saved"
-msgstr ""
-
-#: Controller/Traits/SimpleCrudTrait.php:81;111
-msgid "The {0} could not be saved"
-msgstr ""
-
-#: Controller/Traits/SimpleCrudTrait.php:131
-msgid "The {0} has been deleted"
-msgstr ""
-
-#: Controller/Traits/SimpleCrudTrait.php:133
-msgid "The {0} could not be deleted"
-msgstr ""
-
-#: Controller/Traits/SocialTrait.php:40
-msgid "The reCaptcha could not be validated"
-msgstr ""
-
-#: Controller/Traits/UserValidationTrait.php:43
-msgid "User account validated successfully"
-msgstr ""
-
-#: Controller/Traits/UserValidationTrait.php:45
-msgid "User account could not be validated"
-msgstr ""
-
-#: Controller/Traits/UserValidationTrait.php:48
-msgid "User already active"
-msgstr ""
-
-#: Controller/Traits/UserValidationTrait.php:54
-msgid "Reset password token was validated successfully"
-msgstr ""
-
-#: Controller/Traits/UserValidationTrait.php:62
-msgid "Reset password token could not be validated"
-msgstr ""
-
-#: Controller/Traits/UserValidationTrait.php:66
-msgid "Invalid validation type"
-msgstr ""
-
-#: Controller/Traits/UserValidationTrait.php:69
-msgid "Invalid token or user account already validated"
-msgstr ""
-
-#: Controller/Traits/UserValidationTrait.php:71
-msgid "Token already expired"
-msgstr ""
-
-#: Controller/Traits/UserValidationTrait.php:97
-msgid "Token has been reset successfully. Please check your email."
-msgstr ""
-
-#: Controller/Traits/UserValidationTrait.php:109
-msgid "User {0} is already active"
-msgstr ""
-
-#: Mailer/UsersMailer.php:34
-msgid "Your account validation link"
-msgstr ""
-
-#: Mailer/UsersMailer.php:52
-msgid "{0}Your reset password link"
-msgstr ""
-
-#: Mailer/UsersMailer.php:75
-msgid "{0}Your social account validation link"
-msgstr ""
-
-#: Model/Behavior/AuthFinderBehavior.php:49
-msgid "Missing 'username' in options data"
-msgstr ""
-
-#: Model/Behavior/LinkSocialBehavior.php:53
-msgid "Social account already associated to another user"
-msgstr ""
-
-#: Model/Behavior/PasswordBehavior.php:45
-msgid "Reference cannot be null"
-msgstr ""
-
-#: Model/Behavior/PasswordBehavior.php:50
-msgid "Token expiration cannot be empty"
-msgstr ""
-
-#: Model/Behavior/PasswordBehavior.php:56;117
-msgid "User not found"
-msgstr ""
-
-#: Model/Behavior/PasswordBehavior.php:60
-#: Model/Behavior/RegisterBehavior.php:112;205
-msgid "User account already validated"
-msgstr ""
-
-#: Model/Behavior/PasswordBehavior.php:67
-msgid "User not active"
-msgstr ""
-
-#: Model/Behavior/PasswordBehavior.php:122
-msgid "The current password does not match"
-msgstr ""
-
-#: Model/Behavior/PasswordBehavior.php:125
-msgid "You cannot use the current password as the new one"
-msgstr ""
-
-#: Model/Behavior/RegisterBehavior.php:90
-msgid "User not found for the given token and email."
-msgstr ""
-
-#: Model/Behavior/RegisterBehavior.php:93
-msgid "Token has already expired user with no token"
-msgstr ""
-
-#: Model/Behavior/SocialAccountBehavior.php:103;130
-msgid "Account already validated"
-msgstr ""
-
-#: Model/Behavior/SocialAccountBehavior.php:106;133
-msgid "Account not found for the given token and email."
-msgstr ""
-
-#: Model/Behavior/SocialBehavior.php:82
-msgid "Unable to login user with reference {0}"
-msgstr ""
-
-#: Model/Behavior/SocialBehavior.php:121
-msgid "Email not present"
-msgstr ""
-
-#: Model/Table/UsersTable.php:81
-msgid "Your password does not match your confirm password. Please try again"
-msgstr ""
-
-#: Model/Table/UsersTable.php:173
-msgid "Username already exists"
-msgstr ""
-
-#: Model/Table/UsersTable.php:179
-msgid "Email already exists"
-msgstr ""
-
-#: Shell/UsersShell.php:58
-msgid "Utilities for CakeDC Users Plugin"
-msgstr ""
-
-#: Shell/UsersShell.php:60
-msgid "Activate an specific user"
-msgstr ""
-
-#: Shell/UsersShell.php:63
-msgid "Add a new superadmin user for testing purposes"
-msgstr ""
-
-#: Shell/UsersShell.php:66
-msgid "Add a new user"
-msgstr ""
-
-#: Shell/UsersShell.php:69
-msgid "Change the role for an specific user"
-msgstr ""
-
-#: Shell/UsersShell.php:72
-msgid "Deactivate an specific user"
-msgstr ""
-
-#: Shell/UsersShell.php:75
-msgid "Delete an specific user"
-msgstr ""
-
-#: Shell/UsersShell.php:78
-msgid "Reset the password via email"
-msgstr ""
-
-#: Shell/UsersShell.php:81
-msgid "Reset the password for all users"
-msgstr ""
-
-#: Shell/UsersShell.php:84
-msgid "Reset the password for an specific user"
-msgstr ""
-
-#: Shell/UsersShell.php:133;159
-msgid "Please enter a password."
-msgstr ""
-
-#: Shell/UsersShell.php:137
-msgid "Password changed for all users"
-msgstr ""
-
-#: Shell/UsersShell.php:138;166
-msgid "New password: {0}"
-msgstr ""
-
-#: Shell/UsersShell.php:156;184;262;359
-msgid "Please enter a username."
-msgstr ""
-
-#: Shell/UsersShell.php:165
-msgid "Password changed for user: {0}"
-msgstr ""
-
-#: Shell/UsersShell.php:187
-msgid "Please enter a role."
-msgstr ""
-
-#: Shell/UsersShell.php:193
-msgid "Role changed for user: {0}"
-msgstr ""
-
-#: Shell/UsersShell.php:194
-msgid "New role: {0}"
-msgstr ""
-
-#: Shell/UsersShell.php:209
-msgid "User was activated: {0}"
-msgstr ""
-
-#: Shell/UsersShell.php:224
-msgid "User was de-activated: {0}"
-msgstr ""
-
-#: Shell/UsersShell.php:236
-msgid "Please enter a username or email."
-msgstr ""
-
-#: Shell/UsersShell.php:244
-msgid "Please ask the user to check the email to continue with password reset process"
-msgstr ""
-
-#: Shell/UsersShell.php:308
-msgid "Superuser added:"
-msgstr ""
-
-#: Shell/UsersShell.php:310
-msgid "User added:"
-msgstr ""
-
-#: Shell/UsersShell.php:312
-msgid "Id: {0}"
-msgstr ""
-
-#: Shell/UsersShell.php:313
-msgid "Username: {0}"
-msgstr ""
-
-#: Shell/UsersShell.php:314
-msgid "Email: {0}"
-msgstr ""
-
-#: Shell/UsersShell.php:315
-msgid "Role: {0}"
-msgstr ""
-
-#: Shell/UsersShell.php:316
-msgid "Password: {0}"
-msgstr ""
-
-#: Shell/UsersShell.php:318
-msgid "User could not be added:"
-msgstr ""
-
-#: Shell/UsersShell.php:321
-msgid "Field: {0} Error: {1}"
-msgstr ""
-
-#: Shell/UsersShell.php:337
-msgid "The user was not found."
-msgstr ""
-
-#: Shell/UsersShell.php:367
-msgid "The user {0} was not deleted. Please try again"
-msgstr ""
-
-#: Shell/UsersShell.php:369
-msgid "The user {0} was deleted successfully"
-msgstr ""
-
-#: Template/Email/html/reset_password.ctp:21
-#: Template/Email/html/social_account_validation.ctp:14
-#: Template/Email/html/validation.ctp:21
-#: Template/Email/text/reset_password.ctp:20
-#: Template/Email/text/social_account_validation.ctp:22
-#: Template/Email/text/validation.ctp:20
-msgid "Hi {0}"
-msgstr ""
-
-#: Template/Email/html/reset_password.ctp:24
-msgid "Reset your password here"
-msgstr ""
-
-#: Template/Email/html/reset_password.ctp:27
-#: Template/Email/html/social_account_validation.ctp:32
-#: Template/Email/html/validation.ctp:27
-msgid "If the link is not correctly displayed, please copy the following address in your web browser {0}"
-msgstr ""
-
-#: Template/Email/html/reset_password.ctp:34
-#: Template/Email/html/social_account_validation.ctp:39
-#: Template/Email/html/validation.ctp:34
-#: Template/Email/text/reset_password.ctp:28
-#: Template/Email/text/social_account_validation.ctp:30
-#: Template/Email/text/validation.ctp:28
-msgid "Thank you"
-msgstr ""
-
-#: Template/Email/html/social_account_validation.ctp:18
-msgid "Activate your social login here"
-msgstr ""
-
-#: Template/Email/html/validation.ctp:24
-msgid "Activate your account here"
-msgstr ""
-
-#: Template/Email/text/reset_password.ctp:22
-#: Template/Email/text/validation.ctp:22
-msgid "Please copy the following address in your web browser {0}"
-msgstr ""
-
-#: Template/Email/text/social_account_validation.ctp:24
-msgid "Please copy the following address in your web browser to activate your social login {0}"
-msgstr ""
-
-#: Template/Users/add.ctp:13
-#: Template/Users/edit.ctp:16
-#: Template/Users/index.ctp:13;26
-#: Template/Users/view.ctp:15
-msgid "Actions"
-msgstr ""
-
-#: Template/Users/add.ctp:15
-#: Template/Users/edit.ctp:27
-#: Template/Users/view.ctp:23
-msgid "List Users"
-msgstr ""
-
-#: Template/Users/add.ctp:21
-#: Template/Users/register.ctp:17
-msgid "Add User"
-msgstr ""
-
-#: Template/Users/add.ctp:23
-#: Template/Users/edit.ctp:35
-#: Template/Users/index.ctp:22
-#: Template/Users/profile.ctp:30
-#: Template/Users/register.ctp:19
-#: Template/Users/view.ctp:33
-msgid "Username"
-msgstr ""
-
-#: Template/Users/add.ctp:24
-#: Template/Users/edit.ctp:36
-#: Template/Users/index.ctp:23
-#: Template/Users/profile.ctp:32
-#: Template/Users/register.ctp:20
-#: Template/Users/view.ctp:35
-msgid "Email"
-msgstr ""
-
-#: Template/Users/add.ctp:25
-#: Template/Users/register.ctp:21
-msgid "Password"
-msgstr ""
-
-#: Template/Users/add.ctp:26
-#: Template/Users/edit.ctp:37
-#: Template/Users/index.ctp:24
-#: Template/Users/register.ctp:26
-msgid "First name"
-msgstr ""
-
-#: Template/Users/add.ctp:27
-#: Template/Users/edit.ctp:38
-#: Template/Users/index.ctp:25
-#: Template/Users/register.ctp:27
-msgid "Last name"
-msgstr ""
-
-#: Template/Users/add.ctp:30
-#: Template/Users/edit.ctp:53
-#: Template/Users/view.ctp:49;74
-msgid "Active"
-msgstr ""
-
-#: Template/Users/add.ctp:34
-#: Template/Users/change_password.ctp:25
-#: Template/Users/edit.ctp:57
-#: Template/Users/register.ctp:36
-#: Template/Users/request_reset_password.ctp:8
-#: Template/Users/resend_token_validation.ctp:20
-#: Template/Users/social_email.ctp:19
-msgid "Submit"
-msgstr ""
-
-#: Template/Users/change_password.ctp:5
-msgid "Please enter the new password"
-msgstr ""
-
-#: Template/Users/change_password.ctp:10
-msgid "Current password"
-msgstr ""
-
-#: Template/Users/change_password.ctp:16
-msgid "New password"
-msgstr ""
-
-#: Template/Users/change_password.ctp:21
-#: Template/Users/register.ctp:24
-msgid "Confirm password"
-msgstr ""
-
-#: Template/Users/edit.ctp:21
-#: Template/Users/index.ctp:40
-msgid "Delete"
-msgstr ""
-
-#: Template/Users/edit.ctp:23
-#: Template/Users/index.ctp:40
-#: Template/Users/view.ctp:21
-msgid "Are you sure you want to delete # {0}?"
-msgstr ""
-
-#: Template/Users/edit.ctp:33
-#: Template/Users/view.ctp:17
-msgid "Edit User"
-msgstr ""
-
-#: Template/Users/edit.ctp:39
-#: Template/Users/view.ctp:43
-msgid "Token"
-msgstr ""
-
-#: Template/Users/edit.ctp:41
-msgid "Token expires"
-msgstr ""
-
-#: Template/Users/edit.ctp:44
-msgid "API token"
-msgstr ""
-
-#: Template/Users/edit.ctp:47
-msgid "Activation date"
-msgstr ""
-
-#: Template/Users/edit.ctp:50
-msgid "TOS date"
-msgstr ""
-
-#: Template/Users/edit.ctp:63
-msgid "Reset Google Authenticator Token"
-msgstr ""
-
-#: Template/Users/edit.ctp:69
-msgid "Are you sure you want to reset token for user \"{0}\"?"
-msgstr ""
-
-#: Template/Users/index.ctp:15
-msgid "New {0}"
-msgstr ""
-
-#: Template/Users/index.ctp:37
-msgid "View"
-msgstr ""
-
-#: Template/Users/index.ctp:38
-msgid "Change password"
-msgstr ""
-
-#: Template/Users/index.ctp:39
-msgid "Edit"
-msgstr ""
-
-#: Template/Users/index.ctp:49
-msgid "previous"
-msgstr ""
-
-#: Template/Users/index.ctp:51
-msgid "next"
-msgstr ""
-
-#: Template/Users/login.ctp:19
-msgid "Please enter your username and password"
-msgstr ""
-
-#: Template/Users/login.ctp:29
-msgid "Remember me"
-msgstr ""
-
-#: Template/Users/login.ctp:37
-msgid "Register"
-msgstr ""
-
-#: Template/Users/login.ctp:43
-msgid "Reset Password"
-msgstr ""
-
-#: Template/Users/login.ctp:48
-msgid "Login"
-msgstr ""
-
-#: Template/Users/profile.ctp:21
-#: View/Helper/UserHelper.php:54
-msgid "{0} {1}"
-msgstr ""
-
-#: Template/Users/profile.ctp:27
-msgid "Change Password"
-msgstr ""
-
-#: Template/Users/profile.ctp:38
-#: Template/Users/view.ctp:68
-msgid "Social Accounts"
-msgstr ""
-
-#: Template/Users/profile.ctp:42
-#: Template/Users/view.ctp:73
-msgid "Avatar"
-msgstr ""
-
-#: Template/Users/profile.ctp:43
-#: Template/Users/view.ctp:72
-msgid "Provider"
-msgstr ""
-
-#: Template/Users/profile.ctp:44
-msgid "Link"
-msgstr ""
-
-#: Template/Users/profile.ctp:51
-msgid "Link to {0}"
-msgstr ""
-
-#: Template/Users/register.ctp:29
-msgid "Accept TOS conditions?"
-msgstr ""
-
-#: Template/Users/request_reset_password.ctp:5
-msgid "Please enter your email to reset your password"
-msgstr ""
-
-#: Template/Users/resend_token_validation.ctp:15
-msgid "Resend Validation email"
-msgstr ""
-
-#: Template/Users/resend_token_validation.ctp:17
-msgid "Email or username"
-msgstr ""
-
-#: Template/Users/verify.ctp:13
-msgid "Verification Code"
-msgstr ""
-
-#: Template/Users/verify.ctp:15
-msgid " Verify"
-msgstr ""
-
-#: Template/Users/view.ctp:19
-msgid "Delete User"
-msgstr ""
-
-#: Template/Users/view.ctp:24
-msgid "New User"
-msgstr ""
-
-#: Template/Users/view.ctp:31
-msgid "Id"
-msgstr ""
-
-#: Template/Users/view.ctp:37
-msgid "First Name"
-msgstr ""
-
-#: Template/Users/view.ctp:39
-msgid "Last Name"
-msgstr ""
-
-#: Template/Users/view.ctp:41
-msgid "Role"
-msgstr ""
-
-#: Template/Users/view.ctp:45
-msgid "Api Token"
-msgstr ""
-
-#: Template/Users/view.ctp:53
-msgid "Token Expires"
-msgstr ""
-
-#: Template/Users/view.ctp:55
-msgid "Activation Date"
-msgstr ""
-
-#: Template/Users/view.ctp:57
-msgid "Tos Date"
-msgstr ""
-
-#: Template/Users/view.ctp:59;75
-msgid "Created"
-msgstr ""
-
-#: Template/Users/view.ctp:61;76
-msgid "Modified"
-msgstr ""
-
-#: View/Helper/UserHelper.php:45
-msgid "Sign in with"
-msgstr ""
-
-#: View/Helper/UserHelper.php:48
-msgid "fa fa-{0}"
-msgstr ""
-
-#: View/Helper/UserHelper.php:57
-msgid "btn btn-social btn-{0} "
-msgstr ""
-
-#: View/Helper/UserHelper.php:106
-msgid "Logout"
-msgstr ""
-
-#: View/Helper/UserHelper.php:123
-msgid "Welcome, {0}"
-msgstr ""
-
-#: View/Helper/UserHelper.php:146
-msgid "reCaptcha is not configured! Please configure Users.reCaptcha.key"
-msgstr ""
-
-#: View/Helper/UserHelper.php:205
-msgid "btn btn-social btn-{0}"
-msgstr ""
-
-#: View/Helper/UserHelper.php:211
-msgid "Connected with {0}"
-msgstr ""
-
-#: View/Helper/UserHelper.php:216
-msgid "Connect with {0}"
-msgstr ""
-
diff --git a/src/Locale/es/Users.mo b/src/Locale/es/Users.mo
deleted file mode 100644
index 50edf1e2b..000000000
Binary files a/src/Locale/es/Users.mo and /dev/null differ
diff --git a/src/Locale/fr_FR/Users.mo b/src/Locale/fr_FR/Users.mo
deleted file mode 100644
index fc623371d..000000000
Binary files a/src/Locale/fr_FR/Users.mo and /dev/null differ
diff --git a/src/Locale/hu_HU/Users.mo b/src/Locale/hu_HU/Users.mo
deleted file mode 100644
index ce8e248ce..000000000
Binary files a/src/Locale/hu_HU/Users.mo and /dev/null differ
diff --git a/src/Locale/it_IT/Users.mo b/src/Locale/it_IT/Users.mo
deleted file mode 100644
index f242989f4..000000000
Binary files a/src/Locale/it_IT/Users.mo and /dev/null differ
diff --git a/src/Locale/pl/Users.mo b/src/Locale/pl/Users.mo
deleted file mode 100644
index c5a1c88a3..000000000
Binary files a/src/Locale/pl/Users.mo and /dev/null differ
diff --git a/src/Locale/pt_BR/Users.mo b/src/Locale/pt_BR/Users.mo
deleted file mode 100644
index 5cbc087b8..000000000
Binary files a/src/Locale/pt_BR/Users.mo and /dev/null differ
diff --git a/src/Locale/sv/Users.mo b/src/Locale/sv/Users.mo
deleted file mode 100644
index 746e0c282..000000000
Binary files a/src/Locale/sv/Users.mo and /dev/null differ
diff --git a/src/Locale/tr_TR/tr_TR.mo b/src/Locale/tr_TR/tr_TR.mo
deleted file mode 100644
index c65ebec0d..000000000
Binary files a/src/Locale/tr_TR/tr_TR.mo and /dev/null differ
diff --git a/src/Mailer/UsersMailer.php b/src/Mailer/UsersMailer.php
index 2e021884d..2e4070e96 100644
--- a/src/Mailer/UsersMailer.php
+++ b/src/Mailer/UsersMailer.php
@@ -1,81 +1,115 @@
setHidden(['password', 'token_expires', 'api_token']);
- $subject = __d('CakeDC/Users', 'Your account validation link');
+ $subject = __d('cake_d_c/users', 'Your account validation link');
+ $viewVars = [
+ 'activationUrl' => UsersUrl::actionUrl('validateEmail', [
+ '_full' => true,
+ $user['token'],
+ ]),
+ ] + $user->toArray();
+
$this
->setTo($user['email'])
- ->setSubject($firstName . $subject)
- ->setViewVars($user->toArray())
- ->setTemplate('CakeDC/Users.validation');
+ ->setSubject($firstName . $subject)
+ ->setEmailFormat(Message::MESSAGE_BOTH)
+ ->setViewVars($viewVars);
+
+ $this->viewBuilder()
+ ->setTemplate('CakeDC/Users.validation');
}
/**
* Send the reset password email to the user
*
- * @param EntityInterface $user User entity
- *
+ * @param \Cake\Datasource\EntityInterface $user User entity
* @return void
*/
protected function resetPassword(EntityInterface $user)
{
- $firstName = isset($user['first_name'])? $user['first_name'] . ', ' : '';
- $subject = __d('CakeDC/Users', '{0}Your reset password link', $firstName);
+ $firstName = isset($user['first_name']) ? $user['first_name'] . ', ' : '';
+ $subject = __d('cake_d_c/users', '{0}Your reset password link', $firstName);
// un-hide the token to be able to send it in the email content
$user->setHidden(['password', 'token_expires', 'api_token']);
+ $viewVars = [
+ 'activationUrl' => UsersUrl::actionUrl('resetPassword', [
+ '_full' => true,
+ $user['token'],
+ ]),
+ ] + $user->toArray();
+
$this
->setTo($user['email'])
->setSubject($subject)
- ->setViewVars($user->toArray())
+ ->setEmailFormat(Message::MESSAGE_BOTH)
+ ->setViewVars($viewVars);
+ $this
+ ->viewBuilder()
->setTemplate('CakeDC/Users.resetPassword');
}
/**
* Send account validation email to the user
*
- * @param EntityInterface $user User entity
- * @param EntityInterface $socialAccount SocialAccount entity
- *
+ * @param \Cake\Datasource\EntityInterface $user User entity
+ * @param \Cake\Datasource\EntityInterface $socialAccount SocialAccount entity
* @return void
*/
protected function socialAccountValidation(EntityInterface $user, EntityInterface $socialAccount)
{
- $firstName = isset($user['first_name'])? $user['first_name'] . ', ' : '';
+ $firstName = isset($user['first_name']) ? $user['first_name'] . ', ' : '';
// note: we control the space after the username in the previous line
- $subject = __d('CakeDC/Users', '{0}Your social account validation link', $firstName);
+ $subject = __d('cake_d_c/users', '{0}Your social account validation link', $firstName);
+ $activationUrl = [
+ '_full' => true,
+ 'prefix' => false,
+ 'plugin' => 'CakeDC/Users',
+ 'controller' => 'SocialAccounts',
+ 'action' => 'validateAccount',
+ $socialAccount['provider'] ?? null,
+ $socialAccount['reference'] ?? null,
+ $socialAccount['token'] ?? null,
+ ];
$this
->setTo($user['email'])
->setSubject($subject)
- ->setViewVars(compact('user', 'socialAccount'))
+ ->setEmailFormat(Message::MESSAGE_BOTH)
+ ->setViewVars(['user' => $user, 'socialAccount' => $socialAccount, 'activationUrl' => $activationUrl]);
+ $this
+ ->viewBuilder()
->setTemplate('CakeDC/Users.socialAccountValidation');
}
}
diff --git a/src/Middleware/SocialAuthMiddleware.php b/src/Middleware/SocialAuthMiddleware.php
new file mode 100644
index 000000000..00ca65130
--- /dev/null
+++ b/src/Middleware/SocialAuthMiddleware.php
@@ -0,0 +1,133 @@
+getPrevious() !== null ? get_class($exception->getPrevious()) : self::class;
+ $response = new Response();
+ if ($baseClassName === MissingEmailException::class) {
+ $this->setErrorMessage($request, __d('cake_d_c/users', 'Please enter your email'));
+
+ $request->getSession()->write(
+ Configure::read('Users.Key.Session.social'),
+ $exception->getAttributes()['rawData']
+ );
+
+ return $this->responseWithActionLocation($response, 'socialEmail');
+ }
+
+ $this->setErrorMessage($request, __d('cake_d_c/users', 'Could not identify your account, please try again'));
+
+ return $this->responseWithActionLocation($response, 'login');
+ }
+
+ /**
+ * Set request error message
+ *
+ * @param \Cake\Http\ServerRequest $request the request with session attribute
+ * @param string $message the message
+ * @return void
+ */
+ private function setErrorMessage(ServerRequest $request, $message)
+ {
+ $messages = (array)$request->getSession()->read('Flash.flash');
+ $messages[] = [
+ 'key' => 'flash',
+ 'element' => 'Flash/error',
+ 'params' => [],
+ 'message' => $message,
+ ];
+ $request->getSession()->write('Flash.flash', $messages);
+ }
+
+ /**
+ * Set location header to response using the string action
+ *
+ * @param \Cake\Http\Response $response to set location header
+ * @param string $action action at users controller
+ * @return \Psr\Http\Message\ResponseInterface
+ */
+ protected function responseWithActionLocation(Response $response, $action)
+ {
+ $url = Router::url(UsersUrl::actionUrl($action));
+
+ return $response->withLocation($url);
+ }
+
+ /**
+ * Go to next handling SocialAuthenticationException
+ *
+ * @param \Cake\Http\ServerRequest $request The request
+ * @param \Psr\Http\Server\RequestHandlerInterface $handler The request handler.
+ * @return \Psr\Http\Message\ResponseInterface
+ */
+ protected function goNext(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
+ {
+ try {
+ return $handler->handle($request);
+ } catch (SocialAuthenticationException $exception) {
+ return $this->onAuthenticationException($request, $exception);
+ }
+ }
+
+ /**
+ * Callable implementation for the middleware stack.
+ *
+ * @param \Psr\Http\Message\ServerRequestInterface $request The request.
+ * @param \Psr\Http\Server\RequestHandlerInterface $handler The request handler.
+ * @return \Psr\Http\Message\ResponseInterface A response.
+ */
+ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
+ {
+ if (!(new UsersUrl())->checkActionOnRequest('socialLogin', $request)) {
+ return $handler->handle($request);
+ }
+
+ $service = (new ServiceFactory())->createFromRequest($request);
+ if (!$service->isGetUserStep($request)) {
+ return (new Response())
+ ->withLocation($service->getAuthorizationUrl($request));
+ }
+ $request = $request->withAttribute(SocialAuthenticator::SOCIAL_SERVICE_ATTRIBUTE, $service);
+
+ return $this->goNext($request, $handler);
+ }
+}
diff --git a/src/Middleware/SocialEmailMiddleware.php b/src/Middleware/SocialEmailMiddleware.php
new file mode 100644
index 000000000..8f2d2d60b
--- /dev/null
+++ b/src/Middleware/SocialEmailMiddleware.php
@@ -0,0 +1,43 @@
+getAttribute('session');
+ if (!(new UsersUrl())->checkActionOnRequest('socialEmail', $request)) {
+ $session->delete(Configure::read('Users.Key.Session.social'));
+
+ return $handler->handle($request);
+ }
+
+ if (!$session->check(Configure::read('Users.Key.Session.social'))) {
+ throw new NotFoundException();
+ }
+
+ return $this->goNext($request, $handler);
+ }
+}
diff --git a/src/Middleware/UnauthorizedHandler/DefaultRedirectHandler.php b/src/Middleware/UnauthorizedHandler/DefaultRedirectHandler.php
new file mode 100644
index 000000000..709a3fe43
--- /dev/null
+++ b/src/Middleware/UnauthorizedHandler/DefaultRedirectHandler.php
@@ -0,0 +1,118 @@
+ [
+ 'MissingIdentityException' => MissingIdentityException::class,
+ 'ForbiddenException' => ForbiddenException::class,
+ ],
+ 'url' => [
+ 'plugin' => 'CakeDC/Users',
+ 'controller' => 'Users',
+ 'action' => 'login',
+ ],
+ 'queryParam' => 'redirect',
+ 'statusCode' => 302,
+ 'flash' => [],
+ ];
+
+ /**
+ * @inheritDoc
+ */
+ public function handle(Exception $exception, ServerRequestInterface $request, array $options = []): ResponseInterface
+ {
+ $options += $this->defaultOptions;
+ $response = parent::handle($exception, $request, $options);
+ $session = $request->getAttribute('session');
+ if ($session instanceof Session) {
+ $this->addFlashMessage($session, $options);
+ }
+
+ return $response;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function getUrl(ServerRequestInterface $request, array $options): string
+ {
+ $url = $options['url'];
+ if (is_callable($url)) {
+ return $url($request, $options);
+ }
+
+ if ($request->getAttribute('identity') && $request instanceof ServerRequest) {
+ return $request->referer() ?? '/';
+ }
+
+ if ($options['queryParam'] !== null) {
+ $url['?'][$options['queryParam']] = (string)$request->getUri();
+ }
+
+ return Router::url($url);
+ }
+
+ /**
+ * Add a flash message informing location is not authorized.
+ *
+ * @param \Cake\Http\Session $session The CakePHP session.
+ * @param array $options Defined options.
+ * @return void
+ */
+ protected function addFlashMessage(Session $session, $options): void
+ {
+ $messages = (array)$session->read('Flash.flash');
+ $messages[] = $this->createFlashMessage($options);
+ $session->write('Flash.flash', $messages);
+ }
+
+ /**
+ * Create a flash message data.
+ *
+ * @param array $options Handler options
+ * @return array
+ */
+ protected function createFlashMessage($options): array
+ {
+ $message = (array)($options['flash'] ?? []);
+
+ return $message + [
+ 'message' => __d('cake_d_c/users', 'You are not authorized to access that location.'),
+ 'key' => 'flash',
+ 'element' => 'flash/error',
+ 'params' => [],
+ ];
+ }
+}
diff --git a/src/Model/Behavior/AuthFinderBehavior.php b/src/Model/Behavior/AuthFinderBehavior.php
index ef51d1fc9..3dc19fe2b 100644
--- a/src/Model/Behavior/AuthFinderBehavior.php
+++ b/src/Model/Behavior/AuthFinderBehavior.php
@@ -1,11 +1,13 @@
where([$this->_table->aliasField('active') => 1]);
@@ -37,21 +37,21 @@ public function findActive(Query $query, array $options = [])
/**
* Custom finder to log in users
*
- * @param Query $query Query object to modify
+ * @param \Cake\ORM\Query $query Query object to modify
* @param array $options Query options
- * @return Query
+ * @return \Cake\ORM\Query
* @throws \BadMethodCallException
*/
public function findAuth(Query $query, array $options = [])
{
- $identifier = Hash::get($options, 'username');
+ $identifier = $options['username'] ?? null;
if (empty($identifier)) {
- throw new \BadMethodCallException(__d('CakeDC/Users', 'Missing \'username\' in options data'));
+ throw new \BadMethodCallException(__d('cake_d_c/users', 'Missing \'username\' in options data'));
}
$where = $query->clause('where') ?: [];
$query
->where(function ($exp) use ($identifier, $where) {
- $or = $exp->or_([$this->_table->aliasField('email') => $identifier]);
+ $or = $exp->or([$this->_table->aliasField('email') => $identifier]);
return $or->add($where);
}, [], true)
diff --git a/src/Model/Behavior/BaseTokenBehavior.php b/src/Model/Behavior/BaseTokenBehavior.php
index 1e6207159..fbbe3cc89 100644
--- a/src/Model/Behavior/BaseTokenBehavior.php
+++ b/src/Model/Behavior/BaseTokenBehavior.php
@@ -1,18 +1,20 @@
updateToken($tokenExpiration);
} else {
$user['active'] = true;
- $user['activation_date'] = new Time();
+ $user['activation_date'] = new FrozenTime();
}
return $user;
@@ -45,15 +47,14 @@ protected function _updateActive(EntityInterface $user, $validateEmail, $tokenEx
/**
* Remove user token for validation
*
- * @param EntityInterface $user user object.
- * @return EntityInterface
+ * @param \Cake\Datasource\EntityInterface $user user object.
+ * @return \Cake\Datasource\EntityInterface
*/
protected function _removeValidationToken(EntityInterface $user)
{
- $user->token = null;
- $user->token_expires = null;
- $result = $this->_table->save($user);
+ $user['token'] = null;
+ $user['token_expires'] = null;
- return $result;
+ return $this->_table->save($user);
}
}
diff --git a/src/Model/Behavior/LinkSocialBehavior.php b/src/Model/Behavior/LinkSocialBehavior.php
index 68f48608c..14096acea 100644
--- a/src/Model/Behavior/LinkSocialBehavior.php
+++ b/src/Model/Behavior/LinkSocialBehavior.php
@@ -1,27 +1,27 @@
_table->SocialAccounts->getAlias();
$socialAccount = $this->_table->SocialAccounts->find()
->where([
$alias . '.reference' => $reference,
- $alias . '.provider' => Hash::get($data, 'provider')
+ $alias . '.provider' => $data['provider'] ?? null,
])->first();
if ($socialAccount && $user->id !== $socialAccount->user_id) {
$user->setErrors([
'social_accounts' => [
- '_existsIn' => __d('CakeDC/Users', 'Social account already associated to another user')
- ]
+ '_existsIn' => __d('cake_d_c/users', 'Social account already associated to another user'),
+ ],
]);
return $user;
@@ -63,16 +62,15 @@ public function linkSocialAccount(EntityInterface $user, $data)
/**
* Create or update a new social account linking to the user.
*
- * @param EntityInterface $user User to link.
+ * @param \Cake\Datasource\EntityInterface $user User to link.
* @param array $data Social account information.
- * @param EntityInterface $socialAccount to update or create.
- *
- * @return EntityInterface
+ * @param \Cake\Datasource\EntityInterface $socialAccount to update or create.
+ * @return \Cake\Datasource\EntityInterface
*/
protected function createOrUpdateSocialAccount(EntityInterface $user, $data, $socialAccount)
{
if (!$socialAccount) {
- $socialAccount = $this->_table->SocialAccounts->newEntity();
+ $socialAccount = $this->_table->SocialAccounts->newEntity([]);
}
$data['user_id'] = $user->id;
@@ -105,36 +103,37 @@ protected function createOrUpdateSocialAccount(EntityInterface $user, $data, $so
/**
* Populate the social account
*
- * @param EntityInterface $socialAccount to populate.
+ * @param \Cake\Datasource\EntityInterface $socialAccount to populate.
* @param array $data Social account information.
- *
- * @return EntityInterface
+ * @return \Cake\Datasource\EntityInterface
*/
protected function populateSocialAccount($socialAccount, $data)
{
$accountData = $socialAccount->toArray();
- $accountData['username'] = Hash::get($data, 'username');
- $accountData['reference'] = Hash::get($data, 'id');
- $accountData['avatar'] = Hash::get($data, 'avatar');
- $accountData['link'] = Hash::get($data, 'link');
- $accountData['avatar'] = str_replace('normal', 'square', $accountData['avatar']);
- $accountData['description'] = Hash::get($data, 'bio');
- $accountData['token'] = Hash::get($data, 'credentials.token');
- $accountData['token_secret'] = Hash::get($data, 'credentials.secret');
- $accountData['user_id'] = Hash::get($data, 'user_id');
+ $accountData['username'] = $data['username'] ?? null;
+ $accountData['reference'] = $data['id'] ?? null;
+ $accountData['avatar'] = $data['avatar'] ?? null;
+ $accountData['link'] = $data['link'] ?? null;
+ if ($accountData['avatar'] ?? null) {
+ $accountData['avatar'] = str_replace('normal', 'square', $accountData['avatar']);
+ }
+ $accountData['description'] = $data['bio'] ?? null;
+ $accountData['token'] = $data['credentials']['token'] ?? null;
+ $accountData['token_secret'] = $data['credentials']['secret'] ?? null;
+ $accountData['user_id'] = $data['user_id'] ?? null;
$accountData['token_expires'] = null;
- $expires = Hash::get($data, 'credentials.expires');
+ $expires = $data['credentials']['expires'] ?? null;
if (!empty($expires)) {
- $expiresTime = new Time();
+ $expiresTime = new FrozenTime();
$accountData['token_expires'] = $expiresTime->setTimestamp($expires)->format('Y-m-d H:i:s');
}
- $accountData['data'] = serialize(Hash::get($data, 'raw'));
+ $accountData['data'] = serialize($data['raw'] ?? null);
$accountData['active'] = true;
$socialAccount = $this->_table->SocialAccounts->patchEntity($socialAccount, $accountData);
//ensure provider is present in Entity
- $socialAccount['provider'] = Hash::get($data, 'provider');
+ $socialAccount['provider'] = $data['provider'] ?? null;
return $socialAccount;
}
diff --git a/src/Model/Behavior/PasswordBehavior.php b/src/Model/Behavior/PasswordBehavior.php
index 852ca9a7f..e307d15a0 100644
--- a/src/Model/Behavior/PasswordBehavior.php
+++ b/src/Model/Behavior/PasswordBehavior.php
@@ -1,25 +1,26 @@
_getUser($reference);
if (empty($user)) {
- throw new UserNotFoundException(__d('CakeDC/Users', "User not found"));
+ throw new UserNotFoundException(__d('cake_d_c/users', 'User not found'));
}
- if (Hash::get($options, 'checkActive')) {
+ if ($options['checkActive'] ?? false) {
if ($user->active) {
- throw new UserAlreadyActiveException(__d('CakeDC/Users', "User account already validated"));
+ throw new UserAlreadyActiveException(__d('cake_d_c/users', 'User account already validated'));
}
$user->active = false;
$user->activation_date = null;
}
- if (Hash::get($options, 'ensureActive')) {
- if (!$user['active']) {
- throw new UserNotActiveException(__d('CakeDC/Users', "User not active"));
- }
+ if (($options['ensureActive'] ?? false) && !$user['active']) {
+ throw new UserNotActiveException(__d('cake_d_c/users', 'User not active'));
}
$user->updateToken($expiration);
$saveResult = $this->_table->save($user);
- if (Hash::get($options, 'sendEmail')) {
- $this->sendResetPasswordEmail($user);
+ if ($options['sendEmail'] ?? false) {
+ switch ($options['type'] ?? null) {
+ case 'email':
+ $this->_sendValidationEmail($user);
+ break;
+ case 'password':
+ $this->_sendResetPasswordEmail($user);
+ break;
+ }
}
return $saveResult;
@@ -79,16 +84,30 @@ public function resetToken($reference, array $options = [])
/**
* Send the reset password related email link
*
- * @param EntityInterface $user user
+ * @param \Cake\Datasource\EntityInterface $user user
* @return void
*/
- protected function sendResetPasswordEmail($user)
+ protected function _sendResetPasswordEmail($user)
{
$this
->getMailer(Configure::read('Users.Email.mailerClass') ?: 'CakeDC/Users.Users')
->send('resetPassword', [$user]);
}
+ /**
+ * Wrapper for mailer
+ *
+ * @param \Cake\Datasource\EntityInterface $user user
+ * @return void
+ */
+ protected function _sendValidationEmail($user)
+ {
+ $mailer = Configure::read('Users.Email.mailerClass') ?: 'CakeDC/Users.Users';
+ $this
+ ->getMailer($mailer)
+ ->send('validation', [$user]);
+ }
+
/**
* Get the user by email or username
*
@@ -103,27 +122,27 @@ protected function _getUser($reference)
/**
* Change password method
*
- * @param EntityInterface $user user data.
- * @throws WrongPasswordException
+ * @param \Cake\Datasource\EntityInterface $user user data.
+ * @throws \CakeDC\Users\Exception\WrongPasswordException
* @return mixed
*/
public function changePassword(EntityInterface $user)
{
try {
$currentUser = $this->_table->get($user->id, [
- 'contain' => []
+ 'contain' => [],
]);
} catch (RecordNotFoundException $e) {
- throw new UserNotFoundException(__d('CakeDC/Users', "User not found"));
+ throw new UserNotFoundException(__d('cake_d_c/users', 'User not found'));
}
if (!empty($user->current_password)) {
if (!$user->checkPassword($user->current_password, $currentUser->password)) {
- throw new WrongPasswordException(__d('CakeDC/Users', 'The current password does not match'));
+ throw new WrongPasswordException(__d('cake_d_c/users', 'The current password does not match'));
}
if ($user->current_password === $user->password_confirm) {
throw new WrongPasswordException(__d(
- 'CakeDC/Users',
+ 'cake_d_c/users',
'You cannot use the current password as the new one'
));
}
diff --git a/src/Model/Behavior/RegisterBehavior.php b/src/Model/Behavior/RegisterBehavior.php
index fc3243a2f..b2fb4fb10 100644
--- a/src/Model/Behavior/RegisterBehavior.php
+++ b/src/Model/Behavior/RegisterBehavior.php
@@ -1,25 +1,25 @@
validateEmail = (bool)Configure::read('Users.Email.validate');
@@ -44,19 +53,27 @@ public function initialize(array $config)
/**
* Registers an user.
*
- * @param EntityInterface $user User information
+ * @param \Cake\Datasource\EntityInterface $user User information
* @param array $data User information
* @param array $options ['tokenExpiration]
- * @return bool|EntityInterface
+ * @return bool|\Cake\Datasource\EntityInterface
*/
public function register($user, $data, $options)
{
- $validateEmail = Hash::get($options, 'validate_email');
- $tokenExpiration = Hash::get($options, 'token_expiration');
+ $validateEmail = $options['validate_email'] ?? null;
+ $tokenExpiration = $options['token_expiration'] ?? null;
+ $validator = $options['validator'] ?? null;
+ if (is_string($validator)) {
+ $validate = $validator;
+ } else {
+ $this->_table->setValidator('current', $validator ?: $this->getRegisterValidators($options));
+ $validate = 'current';
+ }
+
$user = $this->_table->patchEntity(
$user,
$data,
- ['validate' => Hash::get($options, 'validator') ?: $this->getRegisterValidators($options)]
+ ['validate' => $validate]
);
$user['role'] = Configure::read('Users.Registration.defaultRole') ?: 'user';
$user->validated = false;
@@ -76,23 +93,23 @@ public function register($user, $data, $options)
*
* @param string $token toke to be validated.
* @param null $callback function that will be returned.
- * @throws TokenExpiredException when token has expired.
- * @throws UserNotFoundException when user isn't found.
- * @return EntityInterface $user
+ * @throws \CakeDC\Users\Exception\TokenExpiredException when token has expired.
+ * @throws \CakeDC\Users\Exception\UserNotFoundException when user isn't found.
+ * @return \Cake\Datasource\EntityInterface $user
*/
public function validate($token, $callback = null)
{
- $user = $this->_table->find()
+ $user = $token ? $this->_table->find()
->select(['token_expires', 'id', 'active', 'token'])
->where(['token' => $token])
- ->first();
+ ->first() : null;
if (empty($user)) {
- throw new UserNotFoundException(__d('CakeDC/Users', "User not found for the given token and email."));
+ throw new UserNotFoundException(__d('cake_d_c/users', 'User not found for the given token and email.'));
}
if ($user->tokenExpired()) {
- throw new TokenExpiredException(__d('CakeDC/Users', "Token has already expired user with no token"));
+ throw new TokenExpiredException(__d('cake_d_c/users', 'Token has already expired user with no token'));
}
- if (!method_exists($this, $callback)) {
+ if (!method_exists($this, (string)$callback)) {
return $user;
}
@@ -102,32 +119,31 @@ public function validate($token, $callback = null)
/**
* Activates an user
*
- * @param EntityInterface $user user object.
+ * @param \Cake\Datasource\EntityInterface $user user object.
* @return mixed User entity or bool false if the user could not be activated
- * @throws UserAlreadyActiveException
+ * @throws \CakeDC\Users\Exception\UserAlreadyActiveException
*/
public function activateUser(EntityInterface $user)
{
if ($user->active) {
- throw new UserAlreadyActiveException(__d('CakeDC/Users', "User account already validated"));
+ throw new UserAlreadyActiveException(__d('cake_d_c/users', 'User account already validated'));
}
$user->activation_date = new \DateTime();
$user->token_expires = null;
$user->active = true;
- $result = $this->_table->save($user);
- return $result;
+ return $this->_table->save($user);
}
/**
* buildValidator
*
- * @param Event $event event
- * @param Validator $validator validator
+ * @param \Cake\Event\Event $event event
+ * @param \Cake\Validation\Validator $validator validator
* @param string $name name
- * @return Validator
+ * @return \Cake\Validation\Validator
*/
- public function buildValidator(Event $event, Validator $validator, $name)
+ public function buildValidator(\Cake\Event\EventInterface $event, Validator $validator, $name)
{
if ($name === 'default') {
return $this->_emailValidator($validator, $this->validateEmail);
@@ -139,16 +155,16 @@ public function buildValidator(Event $event, Validator $validator, $name)
/**
* Email validator
*
- * @param Validator $validator Validator instance.
+ * @param \Cake\Validation\Validator $validator Validator instance.
* @param bool $validateEmail true when email needs to be required
- * @return Validator
+ * @return \Cake\Validation\Validator
*/
protected function _emailValidator(Validator $validator, $validateEmail)
{
$this->validateEmail = $validateEmail;
$validator
->add('email', 'valid', ['rule' => 'email'])
- ->notEmpty('email', __d('Users', 'This field is required'), function ($context) {
+ ->notBlank('email', __d('cake_d_c/users', 'This field is required'), function ($context) {
return $this->validateEmail;
});
@@ -158,14 +174,14 @@ protected function _emailValidator(Validator $validator, $validateEmail)
/**
* Tos validator
*
- * @param Validator $validator Validator instance.
- * @return Validator
+ * @param \Cake\Validation\Validator $validator Validator instance.
+ * @return \Cake\Validation\Validator
*/
protected function _tosValidator(Validator $validator)
{
$validator
->requirePresence('tos', 'create')
- ->notEmpty('tos');
+ ->notBlank('tos');
return $validator;
}
@@ -174,12 +190,12 @@ protected function _tosValidator(Validator $validator)
* Returns the list of validators
*
* @param array $options Array of options ['validate_email' => true/false, 'use_tos' => true/false]
- * @return Validator
+ * @return \Cake\Validation\Validator
*/
public function getRegisterValidators($options)
{
- $validateEmail = Hash::get($options, 'validate_email');
- $useTos = Hash::get($options, 'use_tos');
+ $validateEmail = $options['validate_email'] ?? null;
+ $useTos = $options['use_tos'] ?? null;
$validator = $this->_table->validationDefault(new Validator());
$validator = $this->_table->validationRegister($validator);
@@ -194,31 +210,10 @@ public function getRegisterValidators($options)
return $validator;
}
- /**
- * @param EntityInterface $user User information
- * @param array $options ['tokenExpiration]
- * @return bool|EntityInterface
- */
- public function resendValidationEmail($user, $options)
- {
- if ($user->active) {
- throw new UserAlreadyActiveException(__d('CakeDC/Users', "User account already validated"));
- }
-
- $tokenExpiration = Hash::get($options, 'token_expiration');
- $user = $this->_updateActive($user, true, $tokenExpiration);
- $userSaved = $this->_table->saveOrFail($user);
- if ($userSaved) {
- $this->_sendValidationEmail($userSaved);
- }
-
- return $userSaved;
- }
-
/**
* Wrapper for mailer
*
- * @param EntityInterface $user user
+ * @param \Cake\Datasource\EntityInterface $user user
* @return void
*/
protected function _sendValidationEmail($user)
diff --git a/src/Model/Behavior/SocialAccountBehavior.php b/src/Model/Behavior/SocialAccountBehavior.php
index 1ab405ab3..10408a060 100644
--- a/src/Model/Behavior/SocialAccountBehavior.php
+++ b/src/Model/Behavior/SocialAccountBehavior.php
@@ -1,30 +1,29 @@
_table->belongsTo('Users', [
- 'foreignKey' => 'user_id',
- 'joinType' => 'INNER',
- 'className' => Configure::read('Users.table')
- ]);
+ $this->_table->belongsTo('Users')->setForeignKey('user_id')->setJoinType('INNER')->setClassName(Configure::read('Users.table'));
}
/**
* After save callback
*
- * @param Event $event event
- * @param Entity $entity entity
+ * @param \Cake\Event\EventInterface $event event
+ * @param \Cake\Datasource\EntityInterface $entity entity
* @param \ArrayObject $options options
* @return mixed
*/
- public function afterSave(Event $event, Entity $entity, $options)
+ public function afterSave(EventInterface $event, EntityInterface $entity, ArrayObject $options)
{
- if ($entity->active) {
+ if ($entity->get('active')) {
return true;
}
- $user = $this->_table->Users->find()->where(['Users.id' => $entity->user_id, 'Users.active' => true])->first();
+ $user = $this->_table->getAssociation('Users')->find()
+ ->where(['Users.id' => $entity->get('user_id'), 'Users.active' => true])
+ ->first();
if (empty($user)) {
return true;
}
@@ -70,9 +67,9 @@ public function afterSave(Event $event, Entity $entity, $options)
/**
* Send social validation email to the user
*
- * @param EntityInterface $socialAccount social account
- * @param EntityInterface $user user
- * @return void
+ * @param \Cake\Datasource\EntityInterface $socialAccount social account
+ * @param \Cake\Datasource\EntityInterface $user user
+ * @return array
*/
protected function sendSocialValidationEmail(EntityInterface $socialAccount, EntityInterface $user)
{
@@ -87,9 +84,9 @@ protected function sendSocialValidationEmail(EntityInterface $socialAccount, Ent
* @param string $provider provider
* @param string $reference reference
* @param string $token token
- * @throws RecordNotFoundException
- * @throws AccountAlreadyActiveException
- * @return User
+ * @throws \Cake\Datasource\Exception\RecordNotFoundException
+ * @throws \CakeDC\Users\Exception\AccountAlreadyActiveException
+ * @return \CakeDC\Users\Model\Entity\User
*/
public function validateAccount($provider, $reference, $token)
{
@@ -100,10 +97,12 @@ public function validateAccount($provider, $reference, $token)
if (!empty($socialAccount) && $socialAccount->token === $token) {
if ($socialAccount->active) {
- throw new AccountAlreadyActiveException(__d('CakeDC/Users', "Account already validated"));
+ throw new AccountAlreadyActiveException(__d('cake_d_c/users', 'Account already validated'));
}
} else {
- throw new RecordNotFoundException(__d('CakeDC/Users', "Account not found for the given token and email."));
+ throw new RecordNotFoundException(
+ __d('cake_d_c/users', 'Account not found for the given token and email.')
+ );
}
return $this->_activateAccount($socialAccount);
@@ -114,9 +113,9 @@ public function validateAccount($provider, $reference, $token)
*
* @param string $provider provider
* @param string $reference reference
- * @throws RecordNotFoundException
- * @throws AccountAlreadyActiveException
- * @return User
+ * @throws \Cake\Datasource\Exception\RecordNotFoundException
+ * @throws \CakeDC\Users\Exception\AccountAlreadyActiveException
+ * @return \CakeDC\Users\Model\Entity\User
*/
public function resendValidation($provider, $reference)
{
@@ -127,10 +126,14 @@ public function resendValidation($provider, $reference)
if (!empty($socialAccount)) {
if ($socialAccount->active) {
- throw new AccountAlreadyActiveException(__d('CakeDC/Users', "Account already validated"));
+ throw new AccountAlreadyActiveException(
+ __d('cake_d_c/users', 'Account already validated')
+ );
}
} else {
- throw new RecordNotFoundException(__d('CakeDC/Users', "Account not found for the given token and email."));
+ throw new RecordNotFoundException(
+ __d('cake_d_c/users', 'Account not found for the given token and email.')
+ );
}
return $this->sendSocialValidationEmail($socialAccount, $socialAccount->user);
@@ -139,14 +142,13 @@ public function resendValidation($provider, $reference)
/**
* Activates an account
*
- * @param SocialAccount $socialAccount social account
- * @return EntityInterface
+ * @param \CakeDC\Users\Model\Entity\SocialAccount $socialAccount social account
+ * @return \Cake\Datasource\EntityInterface
*/
protected function _activateAccount($socialAccount)
{
$socialAccount->active = true;
- $result = $this->_table->save($socialAccount);
- return $result;
+ return $this->_table->save($socialAccount);
}
}
diff --git a/src/Model/Behavior/SocialBehavior.php b/src/Model/Behavior/SocialBehavior.php
index 7fdd2c0f0..7911ed802 100644
--- a/src/Model/Behavior/SocialBehavior.php
+++ b/src/Model/Behavior/SocialBehavior.php
@@ -1,30 +1,32 @@
_username = $config['username'];
@@ -58,18 +60,18 @@ public function initialize(array $config)
*
* @param array $data Array social login.
* @param array $options Array option data.
- * @throws InvalidArgumentException
- * @throws UserNotActiveException
- * @throws AccountNotActiveException
- * @return bool|EntityInterface|mixed
+ * @throws \InvalidArgumentException
+ * @throws \CakeDC\Users\Exception\UserNotActiveException
+ * @throws \CakeDC\Users\Exception\AccountNotActiveException
+ * @return bool|\Cake\Datasource\EntityInterface|mixed
*/
public function socialLogin(array $data, array $options)
{
- $reference = Hash::get($data, 'id');
+ $reference = $data['id'] ?? null;
$existingAccount = $this->_table->SocialAccounts->find()
->where([
'SocialAccounts.reference' => $reference,
- 'SocialAccounts.provider' => Hash::get($data, 'provider')
+ 'SocialAccounts.provider' => $data['provider'] ?? null,
])
->contain(['Users'])
->first();
@@ -79,22 +81,35 @@ public function socialLogin(array $data, array $options)
$existingAccount = $user->social_accounts[0];
} else {
//@todo: what if we don't have a social account after createSocialUser?
- throw new InvalidArgumentException(__d('CakeDC/Users', 'Unable to login user with reference {0}', $reference));
+ throw new InvalidArgumentException(
+ __d('cake_d_c/users', 'Unable to login user with reference {0}', $reference)
+ );
}
} else {
$user = $existingAccount->user;
+ $accountData = $this->extractAccountData($data);
+ $this->_table->SocialAccounts->patchEntity($existingAccount, $accountData);
+ $this->_table->SocialAccounts->save($existingAccount);
+ $event = $this->dispatchEvent(Plugin::EVENT_SOCIAL_LOGIN_EXISTING_ACCOUNT, [
+ 'userEntity' => $user,
+ 'data' => $data,
+ ]);
+
+ if ($event->getResult() instanceof EntityInterface) {
+ $user = $this->_table->save($event->getResult());
+ }
}
if (!empty($existingAccount)) {
if (!$existingAccount->active) {
throw new AccountNotActiveException([
$existingAccount->provider,
- $existingAccount->reference
+ $existingAccount->reference,
]);
}
if (!$user->active) {
throw new UserNotActiveException([
$existingAccount->provider,
- $existingAccount->$user
+ $existingAccount->$user,
]);
}
}
@@ -107,37 +122,36 @@ public function socialLogin(array $data, array $options)
*
* @param array $data Array social user.
* @param array $options Array option data.
- * @throws MissingEmailException
- * @return bool|EntityInterface|mixed result of the save operation
+ * @throws \CakeDC\Users\Exception\MissingEmailException
+ * @return bool|\Cake\Datasource\EntityInterface|mixed result of the save operation
*/
protected function _createSocialUser($data, $options = [])
{
- $useEmail = Hash::get($options, 'use_email');
- $validateEmail = Hash::get($options, 'validate_email');
- $tokenExpiration = Hash::get($options, 'token_expiration');
+ $useEmail = $options['use_email'] ?? null;
+ $validateEmail = $options['validate_email'] ?? null;
+ $tokenExpiration = $options['token_expiration'] ?? null;
$existingUser = null;
- $email = Hash::get($data, 'email');
+ $email = $data['email'] ?? null;
if ($useEmail && empty($email)) {
- throw new MissingEmailException(__d('CakeDC/Users', 'Email not present'));
+ throw new MissingEmailException(__d('cake_d_c/users', 'Email not present'));
} else {
- $existingUser = $this->_table->find()
- ->where([$this->_table->aliasField('email') => $email])
- ->first();
+ $existingUser = $this->_table->find('existingForSocialLogin', ['email' => $email])->first();
}
$user = $this->_populateUser($data, $existingUser, $useEmail, $validateEmail, $tokenExpiration);
- $event = $this->dispatchEvent(UsersAuthComponent::EVENT_BEFORE_SOCIAL_LOGIN_USER_CREATE, [
+ $event = $this->dispatchEvent(Plugin::EVENT_BEFORE_SOCIAL_LOGIN_USER_CREATE, [
'userEntity' => $user,
+ 'data' => $data,
]);
- if ($event->result instanceof EntityInterface) {
- $user = $event->result;
+ $result = $event->getResult();
+ if ($result instanceof EntityInterface) {
+ $user = $result;
}
$this->_table->isValidateEmail = $validateEmail;
- $result = $this->_table->save($user);
- return $result;
+ return $this->_table->save($user);
}
/**
@@ -145,76 +159,61 @@ protected function _createSocialUser($data, $options = [])
* data to create a new one
*
* @param array $data Array social login.
- * @param EntityInterface $existingUser user data.
+ * @param \Cake\Datasource\EntityInterface $existingUser user data.
* @param string $useEmail email to use.
* @param string $validateEmail email to validate.
* @param string $tokenExpiration token_expires data.
- * @return EntityInterface
+ * @return \Cake\Datasource\EntityInterface
* @todo refactor
*/
protected function _populateUser($data, $existingUser, $useEmail, $validateEmail, $tokenExpiration)
{
- $accountData['username'] = Hash::get($data, 'username');
- $accountData['reference'] = Hash::get($data, 'id');
- $accountData['avatar'] = Hash::get($data, 'avatar');
- $accountData['link'] = Hash::get($data, 'link');
-
- $accountData['avatar'] = str_replace('normal', 'square', $accountData['avatar']);
- $accountData['description'] = Hash::get($data, 'bio');
- $accountData['token'] = Hash::get($data, 'credentials.token');
- $accountData['token_secret'] = Hash::get($data, 'credentials.secret');
- $expires = Hash::get($data, 'credentials.expires');
- if (!empty($expires)) {
- $expiresTime = new DateTime();
- $accountData['token_expires'] = $expiresTime->setTimestamp($expires)->format('Y-m-d H:i:s');
- } else {
- $accountData['token_expires'] = null;
- }
- $accountData['data'] = serialize(Hash::get($data, 'raw'));
+ $userData = [];
+ $accountData = $this->extractAccountData($data);
$accountData['active'] = true;
- $dataValidated = Hash::get($data, 'validated');
+ $dataValidated = $data['validated'] ?? null;
if (empty($existingUser)) {
- $firstName = Hash::get($data, 'first_name');
- $lastName = Hash::get($data, 'last_name');
+ $firstName = $data['first_name'] ?? null;
+ $lastName = $data['last_name'] ?? null;
if (!empty($firstName) && !empty($lastName)) {
$userData['first_name'] = $firstName;
$userData['last_name'] = $lastName;
} else {
- $name = explode(' ', Hash::get($data, 'full_name'));
+ $name = explode(' ', $data['full_name'] ?? '');
$userData['first_name'] = Hash::get($name, 0);
array_shift($name);
$userData['last_name'] = implode(' ', $name);
}
- $userData['username'] = Hash::get($data, 'username');
- $username = Hash::get($userData, 'username');
+ $userData['username'] = $data['username'] ?? null;
+ $username = $userData['username'] ?? null;
if (empty($username)) {
- $dataEmail = Hash::get($data, 'email');
+ $dataEmail = $data['email'] ?? null;
if (!empty($dataEmail)) {
$email = explode('@', $dataEmail);
$userData['username'] = Hash::get($email, 0);
} else {
- $firstName = Hash::get($userData, 'first_name');
- $lastName = Hash::get($userData, 'last_name');
+ $firstName = $userData['first_name'] ?? null;
+ $lastName = $userData['last_name'] ?? null;
$userData['username'] = strtolower($firstName . $lastName);
- $userData['username'] = preg_replace('/[^A-Za-z0-9]/i', '', Hash::get($userData, 'username'));
+ $userData['username'] = preg_replace('/[^A-Za-z0-9]/i', '', $userData['username'] ?? null);
}
}
- $userData['username'] = $this->generateUniqueUsername(Hash::get($userData, 'username'));
+ $userData['username'] = $this->generateUniqueUsername($userData['username'] ?? null);
if ($useEmail) {
- $userData['email'] = Hash::get($data, 'email');
+ $userData['email'] = $data['email'] ?? null;
if (empty($dataValidated)) {
$accountData['active'] = false;
}
}
$userData['password'] = $this->randomString();
- $userData['avatar'] = Hash::get($data, 'avatar');
+ $userData['avatar'] = $data['avatar'] ?? null;
$userData['validated'] = !empty($dataValidated);
- $userData['tos_date'] = date("Y-m-d H:i:s");
- $userData['gender'] = Hash::get($data, 'gender');
+ $userData['tos_date'] = date('Y-m-d H:i:s');
+ $userData['gender'] = $data['gender'] ?? null;
$userData['social_accounts'][] = $accountData;
$user = $this->_table->newEntity($userData);
@@ -227,8 +226,9 @@ protected function _populateUser($data, $existingUser, $useEmail, $validateEmail
}
$socialAccount = $this->_table->SocialAccounts->newEntity($accountData);
//ensure provider is present in Entity
- $socialAccount['provider'] = Hash::get($data, 'provider');
+ $socialAccount['provider'] = $data['provider'] ?? null;
$user['social_accounts'] = [$socialAccount];
+ $user['role'] = Configure::read('Users.Registration.defaultRole') ?: 'user';
return $user;
}
@@ -247,7 +247,7 @@ public function generateUniqueUsername($username)
->where([$this->_table->aliasField($this->_username) => $username])
->count();
if ($existingUsername > 0) {
- $username = $username . $i;
+ $username .= $i;
$i++;
continue;
}
@@ -256,4 +256,51 @@ public function generateUniqueUsername($username)
return $username;
}
+
+ /**
+ * Prepare a query to retrieve existing entity for social login
+ *
+ * @param \Cake\ORM\Query $query The base query.
+ * @param array $options Find options with email key.
+ * @return \Cake\ORM\Query
+ */
+ public function findExistingForSocialLogin(\Cake\ORM\Query $query, array $options)
+ {
+ return $query->where([
+ $this->_table->aliasField('email') => $options['email'],
+ ]);
+ }
+
+ /**
+ * Extract the account data to insert/update
+ *
+ * @param array $data Social data.
+ * @throws \Exception
+ * @return array
+ */
+ protected function extractAccountData(array $data)
+ {
+ $accountData = [];
+ $accountData['username'] = $data['username'] ?? null;
+ $accountData['reference'] = $data['id'] ?? null;
+ $accountData['avatar'] = $data['avatar'] ?? null;
+ $accountData['link'] = $data['link'] ?? null;
+
+ if ($accountData['avatar'] ?? null) {
+ $accountData['avatar'] = str_replace('normal', 'square', $accountData['avatar']);
+ }
+ $accountData['description'] = $data['bio'] ?? null;
+ $accountData['token'] = $data['credentials']['token'] ?? null;
+ $accountData['token_secret'] = $data['credentials']['secret'] ?? null;
+ $expires = $data['credentials']['expires'] ?? null;
+ if (!empty($expires)) {
+ $expiresTime = new DateTime();
+ $accountData['token_expires'] = $expiresTime->setTimestamp($expires)->format('Y-m-d H:i:s');
+ } else {
+ $accountData['token_expires'] = null;
+ }
+ $accountData['data'] = serialize($data['raw'] ?? null);
+
+ return $accountData;
+ }
}
diff --git a/src/Model/Entity/SocialAccount.php b/src/Model/Entity/SocialAccount.php
index 9c6f421ec..c8429655f 100644
--- a/src/Model/Entity/SocialAccount.php
+++ b/src/Model/Entity/SocialAccount.php
@@ -1,11 +1,13 @@
set('tos_date', Time::now());
+ if ((bool)$tos) {
+ $this->set('tos_date', FrozenTime::now());
}
return $tos;
@@ -88,7 +99,7 @@ public function hashPassword($password)
{
$PasswordHasher = $this->getPasswordHasher();
- return $PasswordHasher->hash($password);
+ return $PasswordHasher->hash((string)$password);
}
/**
@@ -100,10 +111,10 @@ public function getPasswordHasher()
{
$passwordHasher = Configure::read('Users.passwordHasher');
if (!class_exists($passwordHasher)) {
- $passwordHasher = '\Cake\Auth\DefaultPasswordHasher';
+ $passwordHasher = \Cake\Auth\DefaultPasswordHasher::class;
}
- return new $passwordHasher;
+ return new $passwordHasher();
}
/**
@@ -131,7 +142,7 @@ public function tokenExpired()
return true;
}
- return new Time($this->token_expires) < Time::now();
+ return new FrozenTime($this->token_expires) < FrozenTime::now();
}
/**
@@ -142,21 +153,40 @@ public function tokenExpired()
protected function _getAvatar()
{
$avatar = null;
- if (!empty($this->_properties['social_accounts'][0])) {
- $avatar = $this->_properties['social_accounts'][0]['avatar'];
+ if (isset($this->social_accounts[0])) {
+ $avatar = $this->social_accounts[0]['avatar'];
}
return $avatar;
}
+ /**
+ * Return the u2f_registration inside additional_data
+ *
+ * @return object|null
+ */
+ protected function _getU2fRegistration()
+ {
+ if (is_string($this->additional_data)) {
+ $this->additional_data = json_decode($this->additional_data, true);
+ }
+ if (!isset($this->additional_data['u2f_registration'])) {
+ return null;
+ }
+ $object = (object)$this->additional_data['u2f_registration'];
+
+ return $object->keyHandle !== null ? $object : null;
+ }
+
/**
* Generate token_expires and token in a user
+ *
* @param int $tokenExpiration seconds to expire the token from Now
* @return void
*/
public function updateToken($tokenExpiration = 0)
{
- $expiration = new Time('now');
+ $expiration = new FrozenTime('now');
$this->token_expires = $expiration->addSeconds($tokenExpiration);
$this->token = bin2hex(Security::randomBytes(16));
}
diff --git a/src/Model/Table/SocialAccountsTable.php b/src/Model/Table/SocialAccountsTable.php
index 1363dd399..1d1578c19 100644
--- a/src/Model/Table/SocialAccountsTable.php
+++ b/src/Model/Table/SocialAccountsTable.php
@@ -1,17 +1,18 @@
add('id', 'valid', ['rule' => 'uuid'])
- ->allowEmpty('id', 'create');
+ ->allowEmptyString('id', null, 'create');
$validator
->requirePresence('provider', 'create')
- ->notEmpty('provider');
+ ->notEmptyString('provider');
$validator
- ->allowEmpty('username');
+ ->allowEmptyString('username');
$validator
->requirePresence('reference', 'create')
- ->notEmpty('reference');
+ ->notEmptyString('reference');
$validator
->requirePresence('link', 'create')
- ->notEmpty('reference');
+ ->notEmptyString('reference');
$validator
- ->allowEmpty('avatar');
+ ->allowEmptyString('avatar');
$validator
- ->allowEmpty('description');
+ ->allowEmptyString('description');
$validator
->requirePresence('token', 'create')
- ->notEmpty('token');
+ ->notEmptyString('token');
$validator
- ->allowEmpty('token_secret');
+ ->allowEmptyString('token_secret');
$validator
->add('token_expires', 'valid', ['rule' => 'datetime'])
- ->allowEmpty('token_expires');
+ ->allowEmptyString('token_expires');
$validator
->add('active', 'valid', ['rule' => 'boolean'])
->requirePresence('active', 'create')
- ->notEmpty('active');
+ ->notBlank('active');
$validator
->requirePresence('data', 'create')
- ->notEmpty('data');
+ ->notBlank('data');
return $validator;
}
@@ -108,10 +110,10 @@ public function validationDefault(Validator $validator)
* Returns a rules checker object that will be used for validating
* application integrity.
*
- * @param RulesChecker $rules The rules object to be modified.
- * @return RulesChecker
+ * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
+ * @return \Cake\ORM\RulesChecker
*/
- public function buildRules(RulesChecker $rules)
+ public function buildRules(RulesChecker $rules): RulesChecker
{
$rules->add($rules->existsIn(['user_id'], 'Users'));
@@ -121,13 +123,13 @@ public function buildRules(RulesChecker $rules)
/**
* Finder for active social accounts
*
- * @param Query $query query
+ * @param \Cake\ORM\Query $query query
* @return \Cake\ORM\Query
*/
public function findActive(Query $query)
{
return $query->where([
- $this->aliasField('active') => true
+ $this->aliasField('active') => true,
]);
}
}
diff --git a/src/Model/Table/UsersTable.php b/src/Model/Table/UsersTable.php
index d8e1919f7..4e6954348 100644
--- a/src/Model/Table/UsersTable.php
+++ b/src/Model/Table/UsersTable.php
@@ -1,33 +1,48 @@
setColumnType('additional_data', 'json');
+
+ return parent::_initializeSchema($schema);
+ }
+
/**
* Initialize method
*
* @param array $config The configuration for the Table.
* @return void
*/
- public function initialize(array $config)
+ public function initialize(array $config): void
{
parent::initialize($config);
@@ -55,31 +83,32 @@ public function initialize(array $config)
$this->addBehavior('CakeDC/Users.Social');
$this->addBehavior('CakeDC/Users.LinkSocial');
$this->addBehavior('CakeDC/Users.AuthFinder');
- $this->hasMany('SocialAccounts', [
- 'foreignKey' => 'user_id',
- 'className' => 'CakeDC/Users.SocialAccounts'
- ]);
+ $this->hasMany('SocialAccounts')->setForeignKey('user_id')->setClassName('CakeDC/Users.SocialAccounts');
}
/**
* Adds some rules for password confirm
- * @param Validator $validator Cake validator object.
- * @return Validator
+ *
+ * @param \Cake\Validation\Validator $validator Cake validator object.
+ * @return \Cake\Validation\Validator
*/
public function validationPasswordConfirm(Validator $validator)
{
$validator
->requirePresence('password_confirm', 'create')
- ->notEmpty('password_confirm');
+ ->notBlank('password_confirm');
$validator
->requirePresence('password', 'create')
- ->notEmpty('password')
+ ->notBlank('password')
->add('password', [
'password_confirm_check' => [
'rule' => ['compareWith', 'password_confirm'],
- 'message' => __d('CakeDC/Users', 'Your password does not match your confirm password. Please try again'),
- 'allowEmpty' => false
+ 'message' => __d(
+ 'cake_d_c/users',
+ 'Your password does not match your confirm password. Please try again'
+ ),
+ 'allowEmpty' => false,
]]);
return $validator;
@@ -88,13 +117,13 @@ public function validationPasswordConfirm(Validator $validator)
/**
* Adds rules for current password
*
- * @param Validator $validator Cake validator object.
- * @return Validator
+ * @param \Cake\Validation\Validator $validator Cake validator object.
+ * @return \Cake\Validation\Validator
*/
public function validationCurrentPassword(Validator $validator)
{
$validator
- ->notEmpty('current_password');
+ ->notBlank('current_password');
return $validator;
}
@@ -102,81 +131,80 @@ public function validationCurrentPassword(Validator $validator)
/**
* Default validation rules.
*
- * @param Validator $validator Validator instance.
- * @return Validator
+ * @param \Cake\Validation\Validator $validator Validator instance.
+ * @return \Cake\Validation\Validator
*/
- public function validationDefault(Validator $validator)
+ public function validationDefault(Validator $validator): Validator
{
$validator
- ->allowEmpty('id', 'create');
+ ->allowEmptyString('id', null, 'create');
$validator
->requirePresence('username', 'create')
- ->notEmpty('username');
+ ->notEmptyString('username');
$validator
->requirePresence('password', 'create')
- ->notEmpty('password');
+ ->notEmptyString('password');
$validator
- ->allowEmpty('first_name');
+ ->allowEmptyString('first_name');
$validator
- ->allowEmpty('last_name');
+ ->allowEmptyString('last_name');
$validator
- ->allowEmpty('token');
+ ->allowEmptyString('token');
$validator
->add('token_expires', 'valid', ['rule' => 'datetime'])
- ->allowEmpty('token_expires');
+ ->allowEmptyDateTime('token_expires');
$validator
- ->allowEmpty('api_token');
+ ->allowEmptyString('api_token');
$validator
->add('activation_date', 'valid', ['rule' => 'datetime'])
- ->allowEmpty('activation_date');
+ ->allowEmptyDateTime('activation_date');
$validator
->add('tos_date', 'valid', ['rule' => 'datetime'])
- ->allowEmpty('tos_date');
+ ->allowEmptyDateTime('tos_date');
return $validator;
}
/**
* Wrapper for all validation rules for register
- * @param Validator $validator Cake validator object.
*
- * @return Validator
+ * @param \Cake\Validation\Validator $validator Cake validator object.
+ * @return \Cake\Validation\Validator
*/
public function validationRegister(Validator $validator)
{
$validator = $this->validationDefault($validator);
- $validator = $this->validationPasswordConfirm($validator);
- return $validator;
+ return $this->validationPasswordConfirm($validator);
}
/**
* Returns a rules checker object that will be used for validating
* application integrity.
*
- * @param RulesChecker $rules The rules object to be modified.
- * @return RulesChecker
+ * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
+ * @return \Cake\ORM\RulesChecker
*/
- public function buildRules(RulesChecker $rules)
+ public function buildRules(RulesChecker $rules): RulesChecker
{
$rules->add($rules->isUnique(['username']), '_isUnique', [
'errorField' => 'username',
- 'message' => __d('CakeDC/Users', 'Username already exists')
+ 'message' => __d('cake_d_c/users', 'Username already exists'),
]);
if ($this->isValidateEmail) {
$rules->add($rules->isUnique(['email']), '_isUnique', [
'errorField' => 'email',
- 'message' => __d('CakeDC/Users', 'Email already exists')
+ 'message' => __d('cake_d_c/users', 'Email already exists'),
]);
}
diff --git a/src/Plugin.php b/src/Plugin.php
new file mode 100644
index 000000000..b39c99565
--- /dev/null
+++ b/src/Plugin.php
@@ -0,0 +1,59 @@
+getLoader('Users.middlewareQueueLoader');
+
+ return $loader(
+ $middlewareQueue,
+ new AuthenticationServiceProvider(),
+ new AuthorizationServiceProvider()
+ );
+ }
+}
diff --git a/src/Provider/AuthenticationServiceProvider.php b/src/Provider/AuthenticationServiceProvider.php
new file mode 100644
index 000000000..1c4dcc3e4
--- /dev/null
+++ b/src/Provider/AuthenticationServiceProvider.php
@@ -0,0 +1,39 @@
+loadService($request, $key);
+ }
+}
diff --git a/src/Provider/AuthorizationServiceProvider.php b/src/Provider/AuthorizationServiceProvider.php
new file mode 100644
index 000000000..8c727f41a
--- /dev/null
+++ b/src/Provider/AuthorizationServiceProvider.php
@@ -0,0 +1,39 @@
+loadService($request, $key);
+ }
+}
diff --git a/src/Provider/ServiceProviderLoaderTrait.php b/src/Provider/ServiceProviderLoaderTrait.php
new file mode 100644
index 000000000..ccfc0183c
--- /dev/null
+++ b/src/Provider/ServiceProviderLoaderTrait.php
@@ -0,0 +1,55 @@
+getLoader($loaderKey);
+
+ return $serviceLoader($request);
+ }
+
+ /**
+ * Get the loader callable
+ *
+ * @param string $loaderKey loader configuration key
+ * @return callable
+ */
+ protected function getLoader($loaderKey)
+ {
+ $serviceLoader = Configure::read($loaderKey);
+ if (is_string($serviceLoader)) {
+ $serviceLoader = new $serviceLoader();
+ }
+
+ return $serviceLoader;
+ }
+}
diff --git a/src/Shell/UsersShell.php b/src/Shell/UsersShell.php
index 292dc71b3..bd38e2e44 100644
--- a/src/Shell/UsersShell.php
+++ b/src/Shell/UsersShell.php
@@ -1,32 +1,32 @@
Users = $this->loadModel(Configure::read('Users.table'));
- }
-
- /**
- *
- * @return ConsoleOptionParser
- */
- public function getOptionParser()
+ public function getOptionParser(): ConsoleOptionParser
{
$parser = parent::getOptionParser();
- $parser->setDescription(__d('CakeDC/Users', 'Utilities for CakeDC Users Plugin'))
+ $parser->setDescription(__d('cake_d_c/users', 'Utilities for CakeDC Users Plugin'))
->addSubcommand('activateUser', [
- 'help' => __d('CakeDC/Users', 'Activate an specific user')
+ 'help' => __d('cake_d_c/users', 'Activate an specific user'),
])
->addSubcommand('addSuperuser', [
- 'help' => __d('CakeDC/Users', 'Add a new superadmin user for testing purposes')
+ 'help' => __d('cake_d_c/users', 'Add a new superadmin user for testing purposes'),
])
->addSubcommand('addUser', [
- 'help' => __d('CakeDC/Users', 'Add a new user')
+ 'help' => __d('cake_d_c/users', 'Add a new user'),
])
->addSubcommand('changeRole', [
- 'help' => __d('CakeDC/Users', 'Change the role for an specific user')
+ 'help' => __d('cake_d_c/users', 'Change the role for an specific user'),
+ ])
+ ->addSubcommand('changeApiToken', [
+ 'help' => __d('cake_d_c/users', 'Change the api token for an specific user'),
])
->addSubcommand('deactivateUser', [
- 'help' => __d('CakeDC/Users', 'Deactivate an specific user')
+ 'help' => __d('cake_d_c/users', 'Deactivate an specific user'),
])
->addSubcommand('deleteUser', [
- 'help' => __d('CakeDC/Users', 'Delete an specific user')
+ 'help' => __d('cake_d_c/users', 'Delete an specific user'),
])
->addSubcommand('passwordEmail', [
- 'help' => __d('CakeDC/Users', 'Reset the password via email')
+ 'help' => __d('cake_d_c/users', 'Reset the password via email'),
])
->addSubcommand('resetAllPasswords', [
- 'help' => __d('CakeDC/Users', 'Reset the password for all users')
+ 'help' => __d('cake_d_c/users', 'Reset the password for all users'),
])
->addSubcommand('resetPassword', [
- 'help' => __d('CakeDC/Users', 'Reset the password for an specific user')
+ 'help' => __d('cake_d_c/users', 'Reset the password for an specific user'),
])
->addOptions([
'username' => ['short' => 'u', 'help' => 'The username for the new user'],
'password' => ['short' => 'p', 'help' => 'The password for the new user'],
'email' => ['short' => 'e', 'help' => 'The email for the new user'],
- 'role' => ['short' => 'r', 'help' => 'The role for the new user']
+ 'role' => ['short' => 'r', 'help' => 'The role for the new user'],
]);
return $parser;
}
+ /**
+ * initialize callback
+ *
+ * @return void
+ */
+ public function initialize(): void
+ {
+ parent::initialize();
+ $this->Users = $this->loadModel(Configure::read('Users.table'));
+ }
+
/**
* Add a new user
*
@@ -113,7 +115,7 @@ public function addSuperuser()
$this->_createUser([
'username' => 'superadmin',
'role' => 'superuser',
- 'is_superuser' => true
+ 'is_superuser' => true,
]);
}
@@ -130,12 +132,12 @@ public function resetAllPasswords()
{
$password = Hash::get($this->args, 0);
if (empty($password)) {
- $this->abort(__d('CakeDC/Users', 'Please enter a password.'));
+ $this->abort(__d('cake_d_c/users', 'Please enter a password.'));
}
$hashedPassword = $this->_generatedHashedPassword($password);
$this->Users->updateAll(['password' => $hashedPassword], ['id IS NOT NULL']);
- $this->out(__d('CakeDC/Users', 'Password changed for all users'));
- $this->out(__d('CakeDC/Users', 'New password: {0}', $password));
+ $this->out(__d('cake_d_c/users', 'Password changed for all users'));
+ $this->out(__d('cake_d_c/users', 'New password: {0}', $password));
}
/**
@@ -153,17 +155,17 @@ public function resetPassword()
$username = Hash::get($this->args, 0);
$password = Hash::get($this->args, 1);
if (empty($username)) {
- $this->abort(__d('CakeDC/Users', 'Please enter a username.'));
+ $this->abort(__d('cake_d_c/users', 'Please enter a username.'));
}
if (empty($password)) {
- $this->abort(__d('CakeDC/Users', 'Please enter a password.'));
+ $this->abort(__d('cake_d_c/users', 'Please enter a password.'));
}
$data = [
- 'password' => $password
+ 'password' => $password,
];
$this->_updateUser($username, $data);
- $this->out(__d('CakeDC/Users', 'Password changed for user: {0}', $username));
- $this->out(__d('CakeDC/Users', 'New password: {0}', $password));
+ $this->out(__d('cake_d_c/users', 'Password changed for user: {0}', $username));
+ $this->out(__d('cake_d_c/users', 'New password: {0}', $password));
}
/**
@@ -181,17 +183,51 @@ public function changeRole()
$username = Hash::get($this->args, 0);
$role = Hash::get($this->args, 1);
if (empty($username)) {
- $this->abort(__d('CakeDC/Users', 'Please enter a username.'));
+ $this->abort(__d('cake_d_c/users', 'Please enter a username.'));
}
if (empty($role)) {
- $this->abort(__d('CakeDC/Users', 'Please enter a role.'));
+ $this->abort(__d('cake_d_c/users', 'Please enter a role.'));
+ }
+ $data = [
+ 'role' => $role,
+ ];
+ $savedUser = $this->_updateUser($username, $data);
+ $this->out(__d('cake_d_c/users', 'Role changed for user: {0}', $username));
+ $this->out(__d('cake_d_c/users', 'New role: {0}', $savedUser->role));
+ }
+
+ /**
+ * Change api token for a user
+ *
+ * Arguments:
+ *
+ * - Username
+ * - Token to be set
+ *
+ * @return void
+ */
+ public function changeApiToken()
+ {
+ $username = Hash::get($this->args, 0);
+ $token = Hash::get($this->args, 1);
+ if (empty($username)) {
+ $this->abort(__d('cake_d_c/users', 'Please enter a username.'));
+ }
+ if (empty($token)) {
+ $this->abort(__d('cake_d_c/users', 'Please enter a token.'));
}
$data = [
- 'role' => $role
+ 'api_token' => $token,
];
$savedUser = $this->_updateUser($username, $data);
- $this->out(__d('CakeDC/Users', 'Role changed for user: {0}', $username));
- $this->out(__d('CakeDC/Users', 'New role: {0}', $savedUser->role));
+ if (!$savedUser) {
+ $this->err(__d('cake_d_c/users', 'User was not saved, check validation errors'));
+ }
+ /**
+ * @var \CakeDC\Users\Model\Entity\User $savedUser
+ */
+ $this->out(__d('cake_d_c/users', 'Api token changed for user: {0}', $username));
+ $this->out(__d('cake_d_c/users', 'New token: {0}', $savedUser->api_token));
}
/**
@@ -206,7 +242,7 @@ public function changeRole()
public function activateUser()
{
$user = $this->_changeUserActive(true);
- $this->out(__d('CakeDC/Users', 'User was activated: {0}', $user->username));
+ $this->out(__d('cake_d_c/users', 'User was activated: {0}', $user->username));
}
/**
@@ -221,7 +257,7 @@ public function activateUser()
public function deactivateUser()
{
$user = $this->_changeUserActive(false);
- $this->out(__d('CakeDC/Users', 'User was de-activated: {0}', $user->username));
+ $this->out(__d('cake_d_c/users', 'User was de-activated: {0}', $user->username));
}
/**
@@ -233,7 +269,7 @@ public function passwordEmail()
{
$reference = Hash::get($this->args, 0);
if (empty($reference)) {
- $this->abort(__d('CakeDC/Users', 'Please enter a username or email.'));
+ $this->abort(__d('cake_d_c/users', 'Please enter a username or email.'));
}
$resetUser = $this->Users->resetToken($reference, [
'expiration' => Configure::read('Users.Token.expiration'),
@@ -241,10 +277,16 @@ public function passwordEmail()
'sendEmail' => true,
]);
if ($resetUser) {
- $msg = __d('CakeDC/Users', 'Please ask the user to check the email to continue with password reset process');
+ $msg = __d(
+ 'cake_d_c/users',
+ 'Please ask the user to check the email to continue with password reset process'
+ );
$this->out($msg);
} else {
- $msg = __d('CakeDC/Users', 'The password token could not be generated. Please try again');
+ $msg = __d(
+ 'cake_d_c/users',
+ 'The password token could not be generated. Please try again'
+ );
$this->abort($msg);
}
}
@@ -259,10 +301,10 @@ protected function _changeUserActive($active)
{
$username = Hash::get($this->args, 0);
if (empty($username)) {
- $this->abort(__d('CakeDC/Users', 'Please enter a username.'));
+ $this->abort(__d('cake_d_c/users', 'Please enter a username.'));
}
$data = [
- 'active' => $active
+ 'active' => $active,
];
return $this->_updateUser($username, $data);
@@ -279,8 +321,8 @@ protected function _createUser($template)
if (!empty($this->params['username'])) {
$username = $this->params['username'];
} else {
- $username = !empty($template['username']) ?
- $template['username'] : $this->_generateRandomUsername();
+ $username = empty($template['username']) ?
+ $this->_generateRandomUsername() : $template['username'];
}
$password = (empty($this->params['password']) ?
@@ -303,22 +345,22 @@ protected function _createUser($template)
$userEntity->role = $role;
$savedUser = $this->Users->save($userEntity);
- if (!empty($savedUser)) {
+ if (is_object($savedUser)) {
if ($savedUser->is_superuser) {
- $this->out(__d('CakeDC/Users', 'Superuser added:'));
+ $this->out(__d('cake_d_c/users', 'Superuser added:'));
} else {
- $this->out(__d('CakeDC/Users', 'User added:'));
+ $this->out(__d('cake_d_c/users', 'User added:'));
}
- $this->out(__d('CakeDC/Users', 'Id: {0}', $savedUser->id));
- $this->out(__d('CakeDC/Users', 'Username: {0}', $savedUser->username));
- $this->out(__d('CakeDC/Users', 'Email: {0}', $savedUser->email));
- $this->out(__d('CakeDC/Users', 'Role: {0}', $savedUser->role));
- $this->out(__d('CakeDC/Users', 'Password: {0}', $password));
+ $this->out(__d('cake_d_c/users', 'Id: {0}', $savedUser->id));
+ $this->out(__d('cake_d_c/users', 'Username: {0}', $savedUser->username));
+ $this->out(__d('cake_d_c/users', 'Email: {0}', $savedUser->email));
+ $this->out(__d('cake_d_c/users', 'Role: {0}', $savedUser->role));
+ $this->out(__d('cake_d_c/users', 'Password: {0}', $password));
} else {
- $this->out(__d('CakeDC/Users', 'User could not be added:'));
+ $this->out(__d('cake_d_c/users', 'User could not be added:'));
collection($userEntity->getErrors())->each(function ($error, $field) {
- $this->out(__d('CakeDC/Users', 'Field: {0} Error: {1}', $field, implode(',', $error)));
+ $this->out(__d('cake_d_c/users', 'Field: {0} Error: {1}', $field, implode(',', $error)));
});
}
}
@@ -328,23 +370,25 @@ protected function _createUser($template)
*
* @param string $username username
* @param array $data data
- * @return bool
+ * @return \CakeDC\Users\Model\Entity\User|bool
*/
protected function _updateUser($username, $data)
{
$user = $this->Users->find()->where(['username' => $username])->first();
- if (empty($user)) {
- $this->abort(__d('CakeDC/Users', 'The user was not found.'));
+ if (!is_object($user)) {
+ $this->abort(__d('cake_d_c/users', 'The user was not found.'));
}
+ /**
+ * @var \Cake\Datasource\EntityInterface $user
+ */
$user = $this->Users->patchEntity($user, $data);
collection($data)->filter(function ($value, $field) use ($user) {
return !$user->isAccessible($field);
})->each(function ($value, $field) use (&$user) {
$user->{$field} = $value;
});
- $savedUser = $this->Users->save($user);
- return $savedUser;
+ return $this->Users->save($user);
}
/**
@@ -356,17 +400,20 @@ public function deleteUser()
{
$username = Hash::get($this->args, 0);
if (empty($username)) {
- $this->abort(__d('CakeDC/Users', 'Please enter a username.'));
+ $this->abort(__d('cake_d_c/users', 'Please enter a username.'));
}
- $user = $this->Users->find()->where(['username' => $username])->first();
+ /**
+ * @var \Cake\Datasource\EntityInterface $user
+ */
+ $user = $this->Users->find()->where(['username' => $username])->firstOrFail();
if (isset($this->Users->SocialAccounts)) {
$this->Users->SocialAccounts->deleteAll(['user_id' => $user->id]);
}
$deleteUser = $this->Users->delete($user);
if (!$deleteUser) {
- $this->abort(__d('CakeDC/Users', 'The user {0} was not deleted. Please try again', $username));
+ $this->abort(__d('cake_d_c/users', 'The user {0} was not deleted. Please try again', $username));
}
- $this->out(__d('CakeDC/Users', 'The user {0} was deleted successfully', $username));
+ $this->out(__d('cake_d_c/users', 'The user {0} was deleted successfully', $username));
}
/**
@@ -397,8 +444,9 @@ protected function _generateRandomUsername()
*/
protected function _generatedHashedPassword($password)
{
- return (new User)->hashPassword($password);
+ return (new User())->hashPassword($password);
}
+
//add filters LIKE in username and email to some tasks
// --force to ignore "you are about to do X to Y users"
}
diff --git a/src/Template/Email/html/reset_password.ctp b/src/Template/Email/html/reset_password.ctp
deleted file mode 100644
index f6055a1d4..000000000
--- a/src/Template/Email/html/reset_password.ctp
+++ /dev/null
@@ -1,35 +0,0 @@
- true,
- 'plugin' => 'CakeDC/Users',
- 'controller' => 'Users',
- 'action' => 'resetPassword',
- isset($token) ? $token : ''
-];
-?>
-
- = __d('CakeDC/Users', "Hi {0}", isset($first_name) ? $first_name : '') ?>,
-
-
- = $this->Html->link(__d('CakeDC/Users', 'Reset your password here'), $activationUrl) ?>
-
-
-= __d(
- 'CakeDC/Users',
- "If the link is not correctly displayed, please copy the following address in your web browser {0}",
- $this->Url->build($activationUrl)
-) ?>
-
-
- = __d('CakeDC/Users', 'Thank you') ?>,
-
diff --git a/src/Template/Email/html/social_account_validation.ctp b/src/Template/Email/html/social_account_validation.ctp
deleted file mode 100644
index acef3fbc5..000000000
--- a/src/Template/Email/html/social_account_validation.ctp
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
- = __d('CakeDC/Users', "Hi {0}", $user['first_name']) ?>,
-
-
- true,
- 'plugin' => 'CakeDC/Users',
- 'controller' => 'SocialAccounts',
- 'action' => 'validateAccount',
- $socialAccount['provider'],
- $socialAccount['reference'],
- $socialAccount['token'],
- ];
- echo $this->Html->link($text, $activationUrl);
- ?>
-
-
-= __d(
- 'CakeDC/Users',
- "If the link is not correctly displayed, please copy the following address in your web browser {0}",
- $this->Url->build($activationUrl)
-) ?>
-
-
- = __d('CakeDC/Users', 'Thank you') ?>,
-
diff --git a/src/Template/Email/html/validation.ctp b/src/Template/Email/html/validation.ctp
deleted file mode 100644
index 8a1840468..000000000
--- a/src/Template/Email/html/validation.ctp
+++ /dev/null
@@ -1,35 +0,0 @@
- true,
- 'plugin' => 'CakeDC/Users',
- 'controller' => 'Users',
- 'action' => 'validateEmail',
- isset($token) ? $token : ''
-];
-?>
-
- = __d('CakeDC/Users', "Hi {0}", isset($first_name) ? $first_name : '') ?>,
-
-
- = $this->Html->link(__d('CakeDC/Users', 'Activate your account here'), $activationUrl) ?>
-
-
-= __d(
- 'CakeDC/Users',
- "If the link is not correctly displayed, please copy the following address in your web browser {0}",
- $this->Url->build($activationUrl)
-) ?>
-
-
- = __d('CakeDC/Users', 'Thank you') ?>,
-
diff --git a/src/Template/Email/text/reset_password.ctp b/src/Template/Email/text/reset_password.ctp
deleted file mode 100644
index 96f001565..000000000
--- a/src/Template/Email/text/reset_password.ctp
+++ /dev/null
@@ -1,29 +0,0 @@
- true,
- 'plugin' => 'CakeDC/Users',
- 'controller' => 'Users',
- 'action' => 'resetPassword',
- isset($token) ? $token : ''
-];
-?>
-= __d('CakeDC/Users', "Hi {0}", isset($first_name) ? $first_name : '') ?>,
-
-= __d(
- 'CakeDC/Users',
- "Please copy the following address in your web browser {0}",
- $this->Url->build($activationUrl)
-) ?>
-
-= __d('CakeDC/Users', 'Thank you') ?>,
-
diff --git a/src/Template/Email/text/social_account_validation.ctp b/src/Template/Email/text/social_account_validation.ctp
deleted file mode 100644
index 87e2d6813..000000000
--- a/src/Template/Email/text/social_account_validation.ctp
+++ /dev/null
@@ -1,31 +0,0 @@
- true,
- 'plugin' => 'CakeDC/Users',
- 'controller' => 'SocialAccounts',
- 'action' => 'validateAccount',
- $socialAccount['provider'],
- $socialAccount['reference'],
- $socialAccount['token'],
-];
-?>
-= __d('CakeDC/Users', "Hi {0}", $user['first_name']) ?>,
-
-= __d(
- 'CakeDC/Users',
- "Please copy the following address in your web browser to activate your social login {0}",
- $this->Url->build($activationUrl)
-) ?>
-
-= __d('CakeDC/Users', 'Thank you') ?>,
-
diff --git a/src/Template/Email/text/validation.ctp b/src/Template/Email/text/validation.ctp
deleted file mode 100644
index ecf9d96a8..000000000
--- a/src/Template/Email/text/validation.ctp
+++ /dev/null
@@ -1,29 +0,0 @@
- true,
- 'plugin' => 'CakeDC/Users',
- 'controller' => 'Users',
- 'action' => 'validateEmail',
- isset($token) ? $token : ''
-];
-?>
-= __d('CakeDC/Users', "Hi {0}", isset($first_name) ? $first_name : '') ?>,
-
-= __d(
- 'CakeDC/Users',
- "Please copy the following address in your web browser {0}",
- $this->Url->build($activationUrl)
-) ?>
-
-= __d('CakeDC/Users', 'Thank you') ?>,
-
diff --git a/src/Template/Users/index.ctp b/src/Template/Users/index.ctp
deleted file mode 100644
index 6c9e49871..000000000
--- a/src/Template/Users/index.ctp
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
= __d('CakeDC/Users', 'Actions') ?>
-
- = $this->Html->link(__d('CakeDC/Users', 'New {0}', $tableAlias), ['action' => 'add']) ?>
-
-
-
-
-
-
- = $this->Paginator->sort('username', __d('CakeDC/Users', 'Username')) ?>
- = $this->Paginator->sort('email', __d('CakeDC/Users', 'Email')) ?>
- = $this->Paginator->sort('first_name', __d('CakeDC/Users', 'First name')) ?>
- = $this->Paginator->sort('last_name', __d('CakeDC/Users', 'Last name')) ?>
- = __d('CakeDC/Users', 'Actions') ?>
-
-
-
-
-
- = h($user->username) ?>
- = h($user->email) ?>
- = h($user->first_name) ?>
- = h($user->last_name) ?>
-
- = $this->Html->link(__d('CakeDC/Users', 'View'), ['action' => 'view', $user->id]) ?>
- = $this->Html->link(__d('CakeDC/Users', 'Change password'), ['action' => 'changePassword', $user->id]) ?>
- = $this->Html->link(__d('CakeDC/Users', 'Edit'), ['action' => 'edit', $user->id]) ?>
- = $this->Form->postLink(__d('CakeDC/Users', 'Delete'), ['action' => 'delete', $user->id], ['confirm' => __d('CakeDC/Users', 'Are you sure you want to delete # {0}?', $user->id)]) ?>
-
-
-
-
-
-
-
-
-
= $this->Paginator->counter() ?>
-
-
diff --git a/src/Template/Users/request_reset_password.ctp b/src/Template/Users/request_reset_password.ctp
deleted file mode 100644
index e406ff573..000000000
--- a/src/Template/Users/request_reset_password.ctp
+++ /dev/null
@@ -1,10 +0,0 @@
-
- = $this->Flash->render('auth') ?>
- = $this->Form->create('User') ?>
-
- = __d('CakeDC/Users', 'Please enter your email to reset your password') ?>
- = $this->Form->control('reference') ?>
-
- = $this->Form->button(__d('CakeDC/Users', 'Submit')); ?>
- = $this->Form->end() ?>
-
diff --git a/src/Template/Users/social_email.ctp b/src/Template/Users/social_email.ctp
deleted file mode 100644
index b0a272b6d..000000000
--- a/src/Template/Users/social_email.ctp
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
- = $this->Flash->render() ?>
- = $this->Form->create('User') ?>
-
- = __d('CakeDC/Users', 'Please enter your email') ?>
- = $this->Form->control('email') ?>
-
- = $this->Form->button(__d('CakeDC/Users', 'Submit')); ?>
- = $this->Form->end() ?>
-
diff --git a/src/Traits/RandomStringTrait.php b/src/Traits/RandomStringTrait.php
index 668a0c828..6caffad7b 100644
--- a/src/Traits/RandomStringTrait.php
+++ b/src/Traits/RandomStringTrait.php
@@ -1,11 +1,13 @@
$prefix, 'plugin' => $plugin, 'controller' => $controller, 'action' => $action];
+ }
+
+ /**
+ * Check if the action is the one from a request
+ *
+ * @param string $action users action
+ * @param \Cake\Http\ServerRequest $request the request
+ * @return bool
+ */
+ public static function checkActionOnRequest($action, ServerRequest $request)
+ {
+ $route = static::actionParams($action);
+ foreach ($route as $param => $value) {
+ if ($request->getParam($param, null) !== $value) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Setup needed config urls but without overwriting
+ *
+ * @return void
+ */
+ public static function setupConfigUrls()
+ {
+ $urls = self::getDefaultConfigUrls();
+ foreach ($urls as $configKey => $url) {
+ if (!Configure::check($configKey)) {
+ Configure::write($configKey, $url);
+ }
+ }
+ }
+
+ /**
+ * Get a list of default config urls using static::actionUrl method for users url.
+ *
+ * @return array
+ */
+ private static function getDefaultConfigUrls()
+ {
+ $loginAction = static::actionUrl('login');
+
+ return [
+ 'Users.Profile.route' => static::actionUrl('profile'),
+ 'OneTimePasswordAuthenticator.verifyAction' => static::actionUrl('verify'),
+ 'U2f.startAction' => static::actionUrl('u2f'),
+ 'Webauthn2fa.startAction' => static::actionUrl('webauthn2fa'),
+ 'Auth.AuthenticationComponent.loginAction' => $loginAction,
+ 'Auth.AuthenticationComponent.logoutRedirect' => $loginAction,
+ 'Auth.Authenticators.Form.loginUrl' => $loginAction,
+ 'Auth.Authenticators.Cookie.loginUrl' => $loginAction,
+ 'Auth.Authenticators.SocialPendingEmail.loginUrl' => $loginAction,
+ 'Auth.AuthorizationMiddleware.unauthorizedHandler.url' => $loginAction,
+ 'OAuth.path' => static::actionParams('socialLogin'),
+ ];
+ }
+}
diff --git a/src/View/Helper/AuthLinkHelper.php b/src/View/Helper/AuthLinkHelper.php
index b9b8e0c97..06274ae37 100644
--- a/src/View/Helper/AuthLinkHelper.php
+++ b/src/View/Helper/AuthLinkHelper.php
@@ -1,28 +1,31 @@
isAuthorized($url)) {
- return Hash::get($options, 'before') .
+ return ($options['before'] ?? '') .
parent::link($title, $url, $linkOptions) .
- Hash::get($options, 'after');
+ ($options['after'] ?? '');
}
- return false;
+ return '';
}
/**
- * Returns true if the target url is authorized for the logged in user
+ * Wrapper for FormHelper.postLink.
+ * Write the link only if user is authorized.
*
- * @param string|array|null $url url that the user is making request.
- * @return bool
+ * @param string $title Link's title
+ * @param string|array $url Link's url
+ * @param array $options Link's options
+ * @return string Link as a string.
*/
- public function isAuthorized($url = null)
+ public function postLink($title, $url = null, array $options = []): string
{
- $event = new Event(UsersAuthComponent::EVENT_IS_AUTHORIZED, $this, ['url' => $url]);
- $result = EventManager::instance()->dispatch($event);
+ return $this->isAuthorized($url)
+ ? $this->Form->postLink($title, $url, $options)
+ : '';
+ }
- return $result->result;
+ /**
+ * Get the current request
+ *
+ * @return \Cake\Http\ServerRequest
+ */
+ public function getRequest()
+ {
+ return $this->getView()->getRequest();
}
}
diff --git a/src/View/Helper/UserHelper.php b/src/View/Helper/UserHelper.php
index c1f4cb7b7..00d32bd93 100644
--- a/src/View/Helper/UserHelper.php
+++ b/src/View/Helper/UserHelper.php
@@ -1,25 +1,30 @@
Html->tag('i', '', [
'class' => 'fa fa-' . strtolower($name),
@@ -51,13 +56,17 @@ public function socialLogin($name, $options = [])
if (isset($options['title'])) {
$providerTitle = $options['title'];
} else {
- $providerTitle = Hash::get($options, 'label') . ' ' . Inflector::camelize($name);
+ $providerTitle = ($options['label'] ?? '') . ' ' . Inflector::camelize($name);
}
- $providerClass = 'btn btn-social btn-' . strtolower($name) . ((Hash::get($options, 'class')) ? ' ' . Hash::get($options, 'class') : '');
+ $providerClass = 'btn btn-social btn-' . strtolower($name);
+ $optionClass = $options['class'] ?? null;
+ if ($optionClass) {
+ $providerClass .= " $optionClass";
+ }
return $this->Html->link($icon . $providerTitle, "/auth/$name", [
- 'escape' => false, 'class' => $providerClass
+ 'escape' => false, 'class' => $providerClass,
]);
}
@@ -75,7 +84,8 @@ public function socialLoginList(array $providerOptions = [])
$outProviders = [];
$providers = Configure::read('OAuth.providers');
foreach ($providers as $provider => $options) {
- if (!empty($options['options']['redirectUri']) &&
+ if (
+ !empty($options['options']['redirectUri']) &&
!empty($options['options']['clientId']) &&
!empty($options['options']['clientSecret'])
) {
@@ -99,30 +109,40 @@ public function socialLoginList(array $providerOptions = [])
*/
public function logout($message = null, $options = [])
{
- return $this->AuthLink->link(empty($message) ? __d('CakeDC/Users', 'Logout') : $message, [
- 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'logout'
- ], $options);
+ $url = UsersUrl::actionUrl('logout');
+ $title = empty($message) ? __d('cake_d_c/users', 'Logout') : $message;
+
+ return $this->AuthLink->link($title, $url, $options);
}
/**
* Welcome display
- * @return mixed
+ *
+ * @return string|null
*/
public function welcome()
{
- $userId = $this->request->getSession()->read('Auth.User.id');
- if (empty($userId)) {
- return;
+ $identity = $this->getView()->getRequest()->getAttribute('identity');
+ if (!$identity) {
+ return null;
}
$profileUrl = Configure::read('Users.Profile.route');
- $label = __d('CakeDC/Users', 'Welcome, {0}', $this->AuthLink->link($this->request->getSession()->read('Auth.User.first_name') ?: $this->request->getSession()->read('Auth.User.username'), $profileUrl));
+ $title = $identity['first_name'] ?? null;
+ $title = $title ?: ($identity['username'] ?? null);
+ $title = is_array($title) ? '-' : (string)$title;
+ $label = __d(
+ 'cake_d_c/users',
+ 'Welcome, {0}',
+ $this->AuthLink->link($title, $profileUrl)
+ );
return $this->Html->tag('span', $label, ['class' => 'welcome']);
}
/**
* Add reCaptcha script
+ *
* @return void
*/
public function addReCaptchaScript()
@@ -134,19 +154,32 @@ public function addReCaptchaScript()
/**
* Add reCaptcha to the form
+ *
* @return mixed
*/
public function addReCaptcha()
{
if (!Configure::read('Users.reCaptcha.key')) {
- return $this->Html->tag('p', __d('CakeDC/Users', 'reCaptcha is not configured! Please configure Users.reCaptcha.key'));
+ return $this->Html->tag(
+ 'p',
+ __d(
+ 'cake_d_c/users',
+ 'reCaptcha is not configured! Please configure Users.reCaptcha.key'
+ )
+ );
}
$this->addReCaptchaScript();
- $this->Form->unlockField('g-recaptcha-response');
+ try {
+ $this->Form->unlockField('g-recaptcha-response');
+ } catch (\Exception $e) {
+ }
return $this->Html->tag('div', '', [
'class' => 'g-recaptcha',
- 'data-sitekey' => Configure::read('Users.reCaptcha.key')
+ 'data-sitekey' => Configure::read('Users.reCaptcha.key'),
+ 'data-theme' => Configure::read('Users.reCaptcha.theme') ?: 'light',
+ 'data-size' => Configure::read('Users.reCaptcha.size') ?: 'normal',
+ 'data-tabindex' => Configure::read('Users.reCaptcha.tabindex') ?: '3',
]);
}
@@ -154,7 +187,6 @@ public function addReCaptcha()
* Generate a link if the target url is authorized for the logged in user
*
* @deprecated Since 3.2.1. Use AuthLinkHelper::link() instead
- *
* @param string $title link's title.
* @param string|array|null $url url that the user is making request.
* @param array $options Array with option data.
@@ -170,49 +202,32 @@ public function link($title, $url = null, array $options = [])
return $this->AuthLink->link($title, $url, $options);
}
- /**
- * Returns true if the target url is authorized for the logged in user
- *
- * @deprecated Since 3.2.1. Use AuthLinkHelper::link() instead
- *
- * @param string|array|null $url url that the user is making request.
- * @return bool
- */
- public function isAuthorized($url = null)
- {
- trigger_error(
- 'UserHelper::isAuthorized() deprecated since 3.2.1. Use AuthLinkHelper::isAuthorized() instead',
- E_USER_DEPRECATED
- );
-
- return $this->AuthLink->isAuthorized($url);
- }
/**
* Create links for all social providers enabled social link (connect)
*
* @param string $name Provider name in lowercase
* @param array $provider Provider configuration
* @param bool $isConnected User is connected with this provider
- *
* @return string
*/
public function socialConnectLink($name, $provider, $isConnected = false)
{
- $linkClass = 'btn btn-social btn-' . strtolower($name) . ((Hash::get($provider['options'], 'class')) ? ' ' . Hash::get($provider['options'], 'class') : '');
+ $optionClass = $provider['options']['class'] ?? null;
+ $linkClass = 'btn btn-social btn-' . strtolower($name) . ($optionClass ? ' ' . $optionClass : '');
if ($isConnected) {
- $title = __d('CakeDC/Users', 'Connected with {0}', Inflector::camelize($name));
+ $title = __d('cake_d_c/users', 'Connected with {0}', Inflector::camelize($name));
return " $title ";
}
- $title = __d('CakeDC/Users', 'Connect with {0}', Inflector::camelize($name));
+ $title = __d('cake_d_c/users', 'Connect with {0}', Inflector::camelize($name));
return $this->Html->link(
" $title",
"/link-social/$name",
[
'escape' => false,
- 'class' => $linkClass
+ 'class' => $linkClass,
]
);
}
@@ -221,22 +236,25 @@ public function socialConnectLink($name, $provider, $isConnected = false)
* Create links for all social providers enabled social link (connect)
*
* @param array $socialAccounts All social accounts connected by a user.
- *
* @return string
*/
public function socialConnectLinkList($socialAccounts = [])
{
if (!Configure::read('Users.Social.login')) {
- return "";
+ return '';
}
- $html = "";
- $connectedProviders = array_map(function ($item) {
- return strtolower($item->provider);
- }, (array)$socialAccounts);
+ $html = '';
+ $connectedProviders = array_map(
+ function ($item) {
+ return strtolower($item->provider);
+ },
+ (array)$socialAccounts
+ );
$providers = Configure::read('OAuth.providers');
foreach ($providers as $name => $provider) {
- if (!empty($provider['options']['callbackLinkSocialUri']) &&
+ if (
+ !empty($provider['options']['callbackLinkSocialUri']) &&
!empty($provider['options']['linkSocialUri']) &&
!empty($provider['options']['clientId']) &&
!empty($provider['options']['clientSecret'])
diff --git a/src/Webauthn/AuthenticateAdapter.php b/src/Webauthn/AuthenticateAdapter.php
new file mode 100644
index 000000000..592c9590a
--- /dev/null
+++ b/src/Webauthn/AuthenticateAdapter.php
@@ -0,0 +1,68 @@
+getUserEntity();
+ $allowed = array_map(function (PublicKeyCredentialSource $credential) {
+ return $credential->getPublicKeyCredentialDescriptor();
+ }, $this->repository->findAllForUserEntity($userEntity));
+
+ $options = $this->server->generatePublicKeyCredentialRequestOptions(
+ PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_PREFERRED, // Default value
+ $allowed
+ );
+ $this->request->getSession()->write(
+ 'Webauthn2fa.authenticateOptions',
+ $options
+ );
+
+ return $options;
+ }
+
+ /**
+ * Verify the registration response
+ *
+ * @return \Webauthn\PublicKeyCredentialSource
+ */
+ public function verifyResponse(): \Webauthn\PublicKeyCredentialSource
+ {
+ $options = $this->request->getSession()->read('Webauthn2fa.authenticateOptions');
+
+ return $this->loadAndCheckAssertionResponse($options);
+ }
+
+ /**
+ * @param \Webauthn\PublicKeyCredentialRequestOptions $options request options
+ * @return \Webauthn\PublicKeyCredentialSource
+ */
+ protected function loadAndCheckAssertionResponse($options): PublicKeyCredentialSource
+ {
+ return $this->server->loadAndCheckAssertionResponse(
+ json_encode($this->request->getData()),
+ $options,
+ $this->getUserEntity(),
+ $this->request
+ );
+ }
+}
diff --git a/src/Webauthn/BaseAdapter.php b/src/Webauthn/BaseAdapter.php
new file mode 100644
index 000000000..4ee30b09c
--- /dev/null
+++ b/src/Webauthn/BaseAdapter.php
@@ -0,0 +1,94 @@
+request = $request;
+ $rpEntity = new PublicKeyCredentialRpEntity(
+ Configure::read('Webauthn2fa.appName'), // The application name
+ Configure::read('Webauthn2fa.id')
+ );
+ /**
+ * @var \Cake\ORM\Entity $userSession
+ */
+ $userSession = $request->getSession()->read('Webauthn2fa.User');
+ $usersTable = $usersTable ?? TableRegistry::getTableLocator()
+ ->get($userSession->getSource());
+ $this->user = $usersTable->get($userSession->id);
+ $this->repository = new UserCredentialSourceRepository(
+ $this->user,
+ $usersTable
+ );
+
+ $this->server = new Server(
+ $rpEntity,
+ $this->repository
+ );
+ }
+
+ /**
+ * @return \Webauthn\PublicKeyCredentialUserEntity
+ */
+ protected function getUserEntity(): PublicKeyCredentialUserEntity
+ {
+ $user = $this->getUser();
+
+ return new PublicKeyCredentialUserEntity(
+ $user->webauthn_username ?? $user->username,
+ (string)$user->id,
+ (string)$user->first_name
+ );
+ }
+
+ /**
+ * @return array|mixed|null
+ */
+ public function getUser()
+ {
+ return $this->user;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasCredential(): bool
+ {
+ return (bool)$this->repository->findAllForUserEntity(
+ $this->getUserEntity()
+ );
+ }
+}
diff --git a/src/Webauthn/RegisterAdapter.php b/src/Webauthn/RegisterAdapter.php
new file mode 100644
index 000000000..c28b0504a
--- /dev/null
+++ b/src/Webauthn/RegisterAdapter.php
@@ -0,0 +1,65 @@
+getUserEntity();
+ $options = $this->server->generatePublicKeyCredentialCreationOptions(
+ $userEntity,
+ PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE,
+ []
+ );
+ $this->request->getSession()->write('Webauthn2fa.registerOptions', $options);
+ $this->request->getSession()->write('Webauthn2fa.userEntity', $userEntity);
+
+ return $options;
+ }
+
+ /**
+ * Verify the registration response
+ *
+ * @return \Webauthn\PublicKeyCredentialSource
+ */
+ public function verifyResponse(): \Webauthn\PublicKeyCredentialSource
+ {
+ $options = $this->request->getSession()->read('Webauthn2fa.registerOptions');
+ $credential = $this->loadAndCheckAttestationResponse($options);
+ $this->repository->saveCredentialSource($credential);
+
+ return $credential;
+ }
+
+ /**
+ * @param \Webauthn\PublicKeyCredentialCreationOptions $options creation options
+ * @return \Webauthn\PublicKeyCredentialSource
+ */
+ protected function loadAndCheckAttestationResponse($options): \Webauthn\PublicKeyCredentialSource
+ {
+ $credential = $this->server->loadAndCheckAttestationResponse(
+ json_encode($this->request->getData()),
+ $options,
+ $this->request
+ );
+
+ return $credential;
+ }
+}
diff --git a/src/Webauthn/Repository/UserCredentialSourceRepository.php b/src/Webauthn/Repository/UserCredentialSourceRepository.php
new file mode 100644
index 000000000..ecda3cbb0
--- /dev/null
+++ b/src/Webauthn/Repository/UserCredentialSourceRepository.php
@@ -0,0 +1,77 @@
+user = $user;
+ $this->usersTable = $usersTable;
+ }
+
+ /**
+ * @param string $publicKeyCredentialId Public key credential id
+ * @return \Webauthn\PublicKeyCredentialSource|null
+ */
+ public function findOneByCredentialId(string $publicKeyCredentialId): ?PublicKeyCredentialSource
+ {
+ $encodedId = Base64Url::encode($publicKeyCredentialId);
+ $credential = $this->user['additional_data']['webauthn_credentials'][$encodedId] ?? null;
+
+ return $credential
+ ? PublicKeyCredentialSource::createFromArray($credential)
+ : null;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function findAllForUserEntity(PublicKeyCredentialUserEntity $publicKeyCredentialUserEntity): array
+ {
+ if ($publicKeyCredentialUserEntity->getId() != $this->user->id) {
+ return [];
+ }
+ $credentials = $this->user['additional_data']['webauthn_credentials'] ?? [];
+ $list = [];
+ foreach ($credentials as $credential) {
+ $list[] = PublicKeyCredentialSource::createFromArray($credential);
+ }
+
+ return $list;
+ }
+
+ /**
+ * @param \Webauthn\PublicKeyCredentialSource $publicKeyCredentialSource Public key credential source
+ */
+ public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource): void
+ {
+ $credentials = $this->user['additional_data']['webauthn_credentials'] ?? [];
+ $id = Base64Url::encode($publicKeyCredentialSource->getPublicKeyCredentialId());
+ $credentials[$id] = json_decode(json_encode($publicKeyCredentialSource), true);
+ $this->user['additional_data'] = $this->user['additional_data'] ?? [];
+ $this->user['additional_data']['webauthn_credentials'] = $credentials;
+ $this->usersTable->saveOrFail($this->user);
+ }
+}
diff --git a/src/Template/Users/add.ctp b/templates/Users/add.php
similarity index 59%
rename from src/Template/Users/add.ctp
rename to templates/Users/add.php
index 751b2f3c6..2b795d20b 100644
--- a/src/Template/Users/add.ctp
+++ b/templates/Users/add.php
@@ -1,36 +1,36 @@
-
= __d('CakeDC/Users', 'Actions') ?>
+
= __d('cake_d_c/users', 'Actions') ?>
- = $this->Html->link(__d('CakeDC/Users', 'List Users'), ['action' => 'index']) ?>
+ = $this->Html->link(__d('cake_d_c/users', 'List Users'), ['action' => 'index']) ?>
= $this->Form->create(${$tableAlias}); ?>
- = __d('CakeDC/Users', 'Add User') ?>
+ = __d('cake_d_c/users', 'Add User') ?>
Form->control('username', ['label' => __d('CakeDC/Users', 'Username')]);
- echo $this->Form->control('email', ['label' => __d('CakeDC/Users', 'Email')]);
- echo $this->Form->control('password', ['label' => __d('CakeDC/Users', 'Password')]);
- echo $this->Form->control('first_name', ['label' => __d('CakeDC/Users', 'First name')]);
- echo $this->Form->control('last_name', ['label' => __d('CakeDC/Users', 'Last name')]);
+ echo $this->Form->control('username', ['label' => __d('cake_d_c/users', 'Username')]);
+ echo $this->Form->control('email', ['label' => __d('cake_d_c/users', 'Email')]);
+ echo $this->Form->control('password', ['label' => __d('cake_d_c/users', 'Password')]);
+ echo $this->Form->control('first_name', ['label' => __d('cake_d_c/users', 'First name')]);
+ echo $this->Form->control('last_name', ['label' => __d('cake_d_c/users', 'Last name')]);
echo $this->Form->control('active', [
'type' => 'checkbox',
- 'label' => __d('CakeDC/Users', 'Active')
+ 'label' => __d('cake_d_c/users', 'Active')
]);
?>
- = $this->Form->button(__d('CakeDC/Users', 'Submit')) ?>
+ = $this->Form->button(__d('cake_d_c/users', 'Submit')) ?>
= $this->Form->end() ?>
diff --git a/src/Template/Users/change_password.ctp b/templates/Users/change_password.php
similarity index 64%
rename from src/Template/Users/change_password.ctp
rename to templates/Users/change_password.php
index 339f2fc65..7efba8f10 100644
--- a/src/Template/Users/change_password.ctp
+++ b/templates/Users/change_password.php
@@ -2,26 +2,26 @@
= $this->Flash->render('auth') ?>
= $this->Form->create($user) ?>
- = __d('CakeDC/Users', 'Please enter the new password') ?>
+ = __d('cake_d_c/users', 'Please enter the new password') ?>
= $this->Form->control('current_password', [
'type' => 'password',
'required' => true,
- 'label' => __d('CakeDC/Users', 'Current password')]);
+ 'label' => __d('cake_d_c/users', 'Current password')]);
?>
= $this->Form->control('password', [
'type' => 'password',
'required' => true,
- 'label' => __d('CakeDC/Users', 'New password')]);
+ 'label' => __d('cake_d_c/users', 'New password')]);
?>
= $this->Form->control('password_confirm', [
'type' => 'password',
'required' => true,
- 'label' => __d('CakeDC/Users', 'Confirm password')]);
+ 'label' => __d('cake_d_c/users', 'Confirm password')]);
?>
- = $this->Form->button(__d('CakeDC/Users', 'Submit')); ?>
+ = $this->Form->button(__d('cake_d_c/users', 'Submit')); ?>
= $this->Form->end() ?>
\ No newline at end of file
diff --git a/src/Template/Users/edit.ctp b/templates/Users/edit.php
similarity index 52%
rename from src/Template/Users/edit.ctp
rename to templates/Users/edit.php
index 91794b822..551076252 100644
--- a/src/Template/Users/edit.ctp
+++ b/templates/Users/edit.php
@@ -1,73 +1,74 @@
-
= __d('CakeDC/Users', 'Actions') ?>
+
= __d('cake_d_c/users', 'Actions') ?>
Form->postLink(
- __d('CakeDC/Users', 'Delete'),
+ __d('cake_d_c/users', 'Delete'),
['action' => 'delete', $Users->id],
- ['confirm' => __d('CakeDC/Users', 'Are you sure you want to delete # {0}?', $Users->id)]
+ ['confirm' => __d('cake_d_c/users', 'Are you sure you want to delete # {0}?', $Users->id)]
);
?>
- = $this->Html->link(__d('CakeDC/Users', 'List Users'), ['action' => 'index']) ?>
+ = $this->Html->link(__d('cake_d_c/users', 'List Users'), ['action' => 'index']) ?>
diff --git a/src/Template/Users/profile.ctp b/templates/Users/profile.php
similarity index 67%
rename from src/Template/Users/profile.ctp
rename to templates/Users/profile.php
index 8b59d9ee2..bd786ffc1 100644
--- a/src/Template/Users/profile.ctp
+++ b/templates/Users/profile.php
@@ -1,11 +1,11 @@
@@ -18,37 +18,37 @@
=
$this->Html->tag(
'span',
- __d('CakeDC/Users', '{0} {1}', $user->first_name, $user->last_name),
+ __d('cake_d_c/users', '{0} {1}', $user->first_name, $user->last_name),
['class' => 'full_name']
)
?>
- = $this->Html->link(__d('CakeDC/Users', 'Change Password'), ['plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'changePassword']); ?>
+ = $this->Html->link(__d('cake_d_c/users', 'Change Password'), ['plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'changePassword']); ?>
-
+
= h($user->username) ?>
-
+
= h($user->email) ?>
= $this->User->socialConnectLinkList($user->social_accounts) ?>
social_accounts)):
?>
-
+
- = __d('CakeDC/Users', 'Avatar'); ?>
- = __d('CakeDC/Users', 'Provider'); ?>
- = __d('CakeDC/Users', 'Link'); ?>
+ = __d('cake_d_c/users', 'Avatar'); ?>
+ = __d('cake_d_c/users', 'Provider'); ?>
+ = __d('cake_d_c/users', 'Link'); ?>
social_accounts as $socialAccount):
$escapedUsername = h($socialAccount->username);
- $linkText = empty($escapedUsername) ? __d('CakeDC/Users', 'Link to {0}', h($socialAccount->provider)) : h($socialAccount->username)
+ $linkText = empty($escapedUsername) ? __d('cake_d_c/users', 'Link to {0}', h($socialAccount->provider)) : h($socialAccount->username)
?>
=
@@ -59,11 +59,11 @@
= h($socialAccount->provider) ?>
=
- $this->Html->link(
+ $socialAccount->link && $socialAccount->link != '#' ? $this->Html->link(
$linkText,
$socialAccount->link,
['target' => '_blank']
- ) ?>
+ ) : '-' ?>
= $this->Form->create($user); ?>
- = __d('CakeDC/Users', 'Add User') ?>
+ = __d('cake_d_c/users', 'Add User') ?>
Form->control('username', ['label' => __d('CakeDC/Users', 'Username')]);
- echo $this->Form->control('email', ['label' => __d('CakeDC/Users', 'Email')]);
- echo $this->Form->control('password', ['label' => __d('CakeDC/Users', 'Password')]);
+ echo $this->Form->control('username', ['label' => __d('cake_d_c/users', 'Username')]);
+ echo $this->Form->control('email', ['label' => __d('cake_d_c/users', 'Email')]);
+ echo $this->Form->control('password', ['label' => __d('cake_d_c/users', 'Password')]);
echo $this->Form->control('password_confirm', [
+ 'required' => true,
'type' => 'password',
- 'label' => __d('CakeDC/Users', 'Confirm password')
+ 'label' => __d('cake_d_c/users', 'Confirm password')
]);
- echo $this->Form->control('first_name', ['label' => __d('CakeDC/Users', 'First name')]);
- echo $this->Form->control('last_name', ['label' => __d('CakeDC/Users', 'Last name')]);
+ echo $this->Form->control('first_name', ['label' => __d('cake_d_c/users', 'First name')]);
+ echo $this->Form->control('last_name', ['label' => __d('cake_d_c/users', 'Last name')]);
if (Configure::read('Users.Tos.required')) {
- echo $this->Form->control('tos', ['type' => 'checkbox', 'label' => __d('CakeDC/Users', 'Accept TOS conditions?'), 'required' => true]);
+ echo $this->Form->control('tos', ['type' => 'checkbox', 'label' => __d('cake_d_c/users', 'Accept TOS conditions?'), 'required' => true]);
}
if (Configure::read('Users.reCaptcha.registration')) {
echo $this->User->addReCaptcha();
}
?>
- = $this->Form->button(__d('CakeDC/Users', 'Submit')) ?>
+ = $this->Form->button(__d('cake_d_c/users', 'Submit')) ?>
= $this->Form->end() ?>
diff --git a/templates/Users/request_reset_password.php b/templates/Users/request_reset_password.php
new file mode 100644
index 000000000..cde49a009
--- /dev/null
+++ b/templates/Users/request_reset_password.php
@@ -0,0 +1,25 @@
+
+
+ = $this->Flash->render('auth') ?>
+ = $this->Form->create($user) ?>
+
+ = __d('cake_d_c/users', 'Please enter your email or username to reset your password') ?>
+ = $this->Form->control('reference') ?>
+
+ = $this->Form->button(__d('cake_d_c/users', 'Submit')); ?>
+ = $this->Form->end() ?>
+
diff --git a/src/Template/Users/resend_token_validation.ctp b/templates/Users/resend_token_validation.php
similarity index 61%
rename from src/Template/Users/resend_token_validation.ctp
rename to templates/Users/resend_token_validation.php
index 2d2680fbe..2023e3c18 100644
--- a/src/Template/Users/resend_token_validation.ctp
+++ b/templates/Users/resend_token_validation.php
@@ -1,22 +1,22 @@
= $this->Form->create($user); ?>
- = __d('CakeDC/Users', 'Resend Validation email') ?>
+ = __d('cake_d_c/users', 'Resend Validation email') ?>
Form->control('reference', ['label' => __d('CakeDC/Users', 'Email or username')]);
+ echo $this->Form->control('reference', ['label' => __d('cake_d_c/users', 'Email or username')]);
?>
- = $this->Form->button(__d('CakeDC/Users', 'Submit')) ?>
+ = $this->Form->button(__d('cake_d_c/users', 'Submit')) ?>
= $this->Form->end() ?>
diff --git a/templates/Users/social_email.php b/templates/Users/social_email.php
new file mode 100644
index 000000000..55c1208e9
--- /dev/null
+++ b/templates/Users/social_email.php
@@ -0,0 +1,21 @@
+
+
+ = $this->Flash->render() ?>
+ = $this->Form->create() ?>
+
+ = __d('cake_d_c/users', 'Please enter your email') ?>
+ = $this->Form->control('email', ['type' => 'email', 'required' => true]) ?>
+
+ = $this->Form->button(__d('cake_d_c/users', 'Submit')); ?>
+ = $this->Form->end() ?>
+
diff --git a/templates/Users/u2f_authenticate.php b/templates/Users/u2f_authenticate.php
new file mode 100644
index 000000000..16ce1bf01
--- /dev/null
+++ b/templates/Users/u2f_authenticate.php
@@ -0,0 +1,59 @@
+Html->script('CakeDC/Users.u2f-api.js', ['block' => true]);
+?>
+
+Html->scriptStart(['block' => true]);
+?>
+ setTimeout(function() {
+ var req = = $req ?>;
+ var appId = req[0].appId;
+ var challenge = req[0].challenge;
+
+ u2f.sign(appId, challenge, req, function(data) {
+ var targetForm = document.getElementById('u2fAuthenticateFrm');
+ var targetInput = document.getElementById('authenticateResponse');
+ if(data.errorCode && data.errorCode != 0) {
+ alert("= __d('cake_d_c/users', 'Yubico key check has failed, please try again')?>");
+
+ return;
+ }
+ targetInput.value = JSON.stringify(data);
+ targetForm.submit();
+ });
+ }, 1000);
+Html->scriptEnd();?>
diff --git a/templates/Users/u2f_register.php b/templates/Users/u2f_register.php
new file mode 100644
index 000000000..0ea8da5af
--- /dev/null
+++ b/templates/Users/u2f_register.php
@@ -0,0 +1,64 @@
+Html->script('CakeDC/Users.u2f-api.js', ['block' => true]);
+?>
+
+ $registerRequest->appId,
+ 'version' => $registerRequest->version,
+ 'challenge' => $registerRequest->challenge,
+ 'attestation' => 'direct'
+]);
+$this->Html->scriptStart(['block' => true]);
+?>
+setTimeout(function() {
+ var req = = $req ?>;
+ var appId = req.appId;
+ var registerRequests = [req];
+ u2f.register(appId, registerRequests, [], function(data) {
+ var targetForm = document.getElementById('u2fRegisterFrm');
+ var targetInput = document.getElementById('registerResponse');
+
+ if(data.errorCode && data.errorCode != 0) {
+ alert("= __d('cake_d_c/users', 'Yubico key check has failed, please try again')?>");
+
+ return;
+ }
+ targetInput.value = JSON.stringify(data);
+ targetForm.submit();
+ });
+}, 1000);
+Html->scriptEnd();?>
diff --git a/src/Template/Users/verify.ctp b/templates/Users/verify.php
similarity index 73%
rename from src/Template/Users/verify.ctp
rename to templates/Users/verify.php
index f1f7d2524..92648a029 100644
--- a/src/Template/Users/verify.ctp
+++ b/templates/Users/verify.php
@@ -10,9 +10,9 @@
- = $this->Form->control('code', ['required' => true, 'label' => __d('CakeDC/Users', 'Verification Code')]) ?>
+ = $this->Form->control('code', ['required' => true, 'label' => __d('cake_d_c/users', 'Verification Code')]) ?>
- = $this->Form->button(__d('CakeDC/Users', ' Verify'), ['class' => 'btn btn-primary']); ?>
+ = $this->Form->button(__d('cake_d_c/users', ' Verify'), ['class' => 'btn btn-primary', 'escapeTitle' => false]); ?>
= $this->Form->end() ?>
diff --git a/src/Template/Users/view.ctp b/templates/Users/view.php
similarity index 50%
rename from src/Template/Users/view.ctp
rename to templates/Users/view.php
index d2e21bf7a..9e0d28106 100644
--- a/src/Template/Users/view.ctp
+++ b/templates/Users/view.php
@@ -1,79 +1,79 @@
-
= __d('CakeDC/Users', 'Actions') ?>
+
= __d('cake_d_c/users', 'Actions') ?>
- = $this->Html->link(__d('CakeDC/Users', 'Edit User'), ['action' => 'edit', $Users->id]) ?>
+ = $this->Html->link(__d('cake_d_c/users', 'Edit User'), ['action' => 'edit', $Users->id]) ?>
= $this->Form->postLink(
- __d('CakeDC/Users', 'Delete User'),
+ __d('cake_d_c/users', 'Delete User'),
['action' => 'delete', $Users->id],
- ['confirm' => __d('CakeDC/Users', 'Are you sure you want to delete # {0}?', $Users->id)]
+ ['confirm' => __d('cake_d_c/users', 'Are you sure you want to delete # {0}?', $Users->id)]
) ?>
- = $this->Html->link(__d('CakeDC/Users', 'List Users'), ['action' => 'index']) ?>
- = $this->Html->link(__d('CakeDC/Users', 'New User'), ['action' => 'add']) ?>
+ = $this->Html->link(__d('cake_d_c/users', 'List Users'), ['action' => 'index']) ?>
+ = $this->Html->link(__d('cake_d_c/users', 'New User'), ['action' => 'add']) ?>
= h($Users->id) ?>
-
+
= h($Users->id) ?>
-
+
= h($Users->username) ?>
-
+
= h($Users->email) ?>
-
+
= h($Users->first_name) ?>
-
+
= h($Users->last_name) ?>
-
+
= h($Users->role) ?>
-
+
= h($Users->token) ?>
-
+
= h($Users->api_token) ?>
-
+
= $this->Number->format($Users->active) ?>
-
+
= h($Users->token_expires) ?>
-
+
= h($Users->activation_date) ?>
-
+
= h($Users->tos_date) ?>
-
+
= h($Users->created) ?>
-
+
= h($Users->modified) ?>