refactor recover password
This commit is contained in:
parent
41275b0e91
commit
22ebb81650
22 changed files with 121 additions and 86 deletions
|
@ -68,7 +68,8 @@ public enum EventType {
|
|||
IDENTITY_PROVIDER_ACCCOUNT_LINKING_ERROR(false),
|
||||
IMPERSONATE(true),
|
||||
CUSTOM_REQUIRED_ACTION(true),
|
||||
CUSTOM_REQUIRED_ACTION_ERROR(true);
|
||||
CUSTOM_REQUIRED_ACTION_ERROR(true),
|
||||
EXECUTE_ACTIONS(true);
|
||||
|
||||
private boolean saveByDefault;
|
||||
|
||||
|
|
|
@ -304,7 +304,7 @@ module.controller('UserTabCtrl', function($scope, $location, Dialog, Notificatio
|
|||
};
|
||||
});
|
||||
|
||||
module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser, User, UserFederationInstances, UserImpersonation, RequiredActions, $location, Dialog, Notifications) {
|
||||
module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser, User, UserExecuteActionsEmail, UserFederationInstances, UserImpersonation, RequiredActions, $location, Dialog, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.create = !user.id;
|
||||
$scope.editUsername = $scope.create || $scope.realm.editUsernameAllowed;
|
||||
|
@ -389,6 +389,21 @@ module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser
|
|||
}
|
||||
}, true);
|
||||
|
||||
$scope.sendExecuteActionsEmail = function() {
|
||||
if ($scope.changed) {
|
||||
Dialog.message("Cannot send email", "You must save your current changes before you can send an email");
|
||||
return;
|
||||
}
|
||||
Dialog.confirm('Send Email', 'Are you sure you want to send email to user?', function() {
|
||||
UserExecuteActionsEmail.update({ realm: realm.realm, userId: user.id }, { }, function() {
|
||||
Notifications.success("Email sent to user");
|
||||
}, function() {
|
||||
Notifications.error("Failed to send email to user");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.save = function() {
|
||||
convertAttributeValuesToLists();
|
||||
|
||||
|
@ -513,15 +528,6 @@ module.controller('UserCredentialsCtrl', function($scope, realm, user, User, Use
|
|||
});
|
||||
};
|
||||
|
||||
$scope.resetPasswordEmail = function() {
|
||||
Dialog.confirm('Reset password email', 'Are you sure you want to send password reset email to user?', function() {
|
||||
UserCredentials.resetPasswordEmail({ realm: realm.realm, userId: user.id }, { }, function() {
|
||||
Notifications.success("Password reset email sent to user");
|
||||
}, function() {
|
||||
Notifications.error("Failed to send password reset mail to user");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$watch('user', function() {
|
||||
if (!angular.equals($scope.user, user)) {
|
||||
|
|
|
@ -5,7 +5,7 @@ var module = angular.module('keycloak.services', [ 'ngResource', 'ngRoute' ]);
|
|||
module.service('Dialog', function($modal) {
|
||||
var dialog = {};
|
||||
|
||||
var openDialog = function(title, message, btns) {
|
||||
var openDialog = function(title, message, btns, template) {
|
||||
var controller = function($scope, $modalInstance, title, message, btns) {
|
||||
$scope.title = title;
|
||||
$scope.message = message;
|
||||
|
@ -20,7 +20,7 @@ module.service('Dialog', function($modal) {
|
|||
};
|
||||
|
||||
return $modal.open({
|
||||
templateUrl: resourceUrl + '/templates/kc-modal.html',
|
||||
templateUrl: resourceUrl + template,
|
||||
controller: controller,
|
||||
resolve: {
|
||||
title: function() {
|
||||
|
@ -56,7 +56,7 @@ module.service('Dialog', function($modal) {
|
|||
}
|
||||
}
|
||||
|
||||
openDialog(title, msg, btns).then(success);
|
||||
openDialog(title, msg, btns, '/templates/kc-modal.html').then(success);
|
||||
}
|
||||
|
||||
dialog.confirmGenerateKeys = function(name, type, success) {
|
||||
|
@ -73,7 +73,7 @@ module.service('Dialog', function($modal) {
|
|||
}
|
||||
}
|
||||
|
||||
openDialog(title, msg, btns).then(success);
|
||||
openDialog(title, msg, btns, '/templates/kc-modal.html').then(success);
|
||||
}
|
||||
|
||||
dialog.confirm = function(title, message, success, cancel) {
|
||||
|
@ -88,10 +88,21 @@ module.service('Dialog', function($modal) {
|
|||
}
|
||||
}
|
||||
|
||||
openDialog(title, message, btns).then(success, cancel);
|
||||
openDialog(title, message, btns, '/templates/kc-modal.html').then(success, cancel);
|
||||
}
|
||||
|
||||
return dialog
|
||||
dialog.message = function(title, message, success, cancel) {
|
||||
var btns = {
|
||||
ok: {
|
||||
label: "Ok",
|
||||
cssClass: 'btn btn-default'
|
||||
}
|
||||
}
|
||||
|
||||
openDialog(title, message, btns, '/templates/kc-modal-message.html').then(success, cancel);
|
||||
}
|
||||
|
||||
return dialog
|
||||
});
|
||||
|
||||
module.service('CopyDialog', function($modal) {
|
||||
|
@ -427,16 +438,18 @@ module.factory('UserCredentials', function($resource) {
|
|||
}
|
||||
}).update;
|
||||
|
||||
credentials.resetPasswordEmail = $resource(authUrl + '/admin/realms/:realm/users/:userId/reset-password-email', {
|
||||
return credentials;
|
||||
});
|
||||
|
||||
module.factory('UserExecuteActionsEmail', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/users/:userId/execute-actions-email', {
|
||||
realm : '@realm',
|
||||
userId : '@userId'
|
||||
}, {
|
||||
update : {
|
||||
method : 'PUT'
|
||||
}
|
||||
}).update;
|
||||
|
||||
return credentials;
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('RealmRoleMapping', function($resource) {
|
||||
|
|
|
@ -37,14 +37,7 @@
|
|||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="border-top" data-ng-show="user.email || user.totp">
|
||||
<div class="form-group" data-ng-show="user.email">
|
||||
<label class="col-md-2 control-label" for="password">Reset password email</label>
|
||||
<div class="col-sm-5">
|
||||
<button class="btn btn-danger" type="submit" data-ng-click="resetPasswordEmail()" tooltip="Send an email to user with a link to reset their password" tooltip-placement="right">Send Email</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<fieldset class="border-top" data-ng-show="user.totp">
|
||||
<div class="form-group" data-ng-show="user.totp">
|
||||
<label class="col-md-2 control-label">Remove totp</label>
|
||||
<div class="col-sm-5" data-ng-show="user.totp">
|
||||
|
|
|
@ -99,6 +99,15 @@
|
|||
</div>
|
||||
<kc-tooltip>Require an action when the user logs in. 'Verify email' sends an email to the user to verify their email address. 'Update profile' requires user to enter in new personal information. 'Update password' requires user to enter in a new password. 'Configure TOTP' requires setup of a mobile password generator.</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group clearfix" data-ng-show="!create && user.email">
|
||||
<label class="col-md-2 control-label" for="reqActionsEmail">Required Actions Email</label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<button id="reqActionsEmail" class="btn btn-default" data-ng-click="sendExecuteActionsEmail()">Send Email</button>
|
||||
</div>
|
||||
<kc-tooltip>Sends an email to user with an embedded link. Clicking on link will allow the user to execute all their required actions. They will not have to login prior to this. For example, set the required action to update password, click this button, and the user will be able to change their password without logging in.</kc-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="form-group clearfix" data-ng-if="realm.internationalizationEnabled">
|
||||
<label class="col-md-2 control-label" for="locale">Locale</label>
|
||||
<div class="col-md-6">
|
||||
|
@ -113,10 +122,10 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-md-2 control-label" for="reqActions">Impersonate user</label>
|
||||
<label class="col-md-2 control-label" for="impersonate">Impersonate user</label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<button data-ng-show="access.impersonation" class="btn btn-default" data-ng-click="impersonate()">Impersonate</button>
|
||||
<button id="impersonate" data-ng-show="access.impersonation" class="btn btn-default" data-ng-click="impersonate()">Impersonate</button>
|
||||
</div>
|
||||
<kc-tooltip>Login as this user. If user is in same realm as you, your current login session will be logged out before you are logged in as this user.</kc-tooltip>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="cancel()">
|
||||
<span class="pficon pficon-close"></span>
|
||||
</button>
|
||||
<h4 class="modal-title">{{title}}</h4>
|
||||
</div>
|
||||
<div class="modal-body">{{message}}</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-ng-class="btns.ok.cssClass" ng-click="ok()">{{btns.ok.label}}</button>
|
||||
</div>
|
|
@ -1,5 +0,0 @@
|
|||
<html>
|
||||
<body>
|
||||
${msg("changePasswordBodyHtml",link, linkExpiration, realmName)}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
${msg("executeActionsBodyHtml",link, linkExpiration, realmName)}
|
||||
</body>
|
||||
</html>
|
|
@ -1,10 +1,10 @@
|
|||
emailVerificationSubject=E-Mail verifizieren
|
||||
passwordResetSubject=Passwort zur\u00FCckzusetzen
|
||||
passwordResetBody=Someone just requested to change your {2} account''s password. If this was you, click on the link below to set a new password.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your password, just ignore this message and nothing will be changed.
|
||||
passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s password. If this was you, click on the link below to set a new password.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your password, just ignore this message and nothing will be changed.</p>
|
||||
changePasswordSubject=Change password
|
||||
changePasswordBody=Your adminstrator has just requested that you change your {2} account''s password. Click on the link below to set a new password\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you don''t want to reset your password, just ignore this message and nothing will be changed.
|
||||
changePasswordBodyHtml=<p>Your adminstrator has just requested that you change your {2} account''s password. Click on the link below to set a new password</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your password, just ignore this message and nothing will be changed.</p>
|
||||
passwordResetBody=Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your credentials, just ignore this message and nothing will be changed.
|
||||
passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your credentials, just ignore this message and nothing will be changed.</p>
|
||||
executeActionsSubject=Update Your Account
|
||||
executeActionsBody=Your adminstrator has just requested that you update your {2} account. Click on the link below to start this process.\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you are unaware that your admin has requested this, just ignore this message and nothing will be changed.
|
||||
executeActionsBodyHtml=<p>Your adminstrator has just requested that you update your {2} account. Click on the link below to start this process.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you are unaware that your admin has requested this, just ignore this message and nothing will be changed.</p>
|
||||
emailVerificationBody=Jemand hat ein {2} Konto mit dieser E-Mail Adresse erstellt. Fall das Sie waren, dann klicken Sie auf den Link um die E-Mail Adresse zu verifizieren.\n\n{0}\n\nDieser Link wird in {1} Minuten ablaufen.\n\nFalls Sie dieses Konto nicht erstellt haben, dann k\u00F6nnen sie diese Nachricht ignorieren.
|
||||
emailVerificationBodyHtml=<p>Jemand hat ein {2} Konto mit dieser E-Mail Adresse erstellt. Fall das Sie waren, dann klicken Sie auf den Link um die E-Mail Adresse zu verifizieren.</p><p><a href="{0}">{0}</a></p><p>Dieser Link wird in {1} Minuten ablaufen.</p><p>Falls Sie dieses Konto nicht erstellt haben, dann k\u00F6nnen sie diese Nachricht ignorieren.</p>
|
||||
eventLoginErrorSubject=Fehlgeschlagene Anmeldung
|
||||
|
|
|
@ -2,11 +2,11 @@ emailVerificationSubject=Verify email
|
|||
emailVerificationBody=Someone has created a {2} account with this email address. If this was you, click the link below to verify your email address\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you didn''t create this account, just ignore this message.
|
||||
emailVerificationBodyHtml=<p>Someone has created a {2} account with this email address. If this was you, click the link below to verify your email address</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you didn''t create this account, just ignore this message.</p>
|
||||
passwordResetSubject=Reset password
|
||||
passwordResetBody=Someone just requested to change your {2} account''s password. If this was you, click on the link below to set a new password.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your password, just ignore this message and nothing will be changed.
|
||||
passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s password. If this was you, click on the link below to set a new password.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your password, just ignore this message and nothing will be changed.</p>
|
||||
changePasswordSubject=Change password
|
||||
changePasswordBody=Your adminstrator has just requested that you change your {2} account''s password. Click on the link below to set a new password\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you don''t want to reset your password, just ignore this message and nothing will be changed.
|
||||
changePasswordBodyHtml=<p>Your adminstrator has just requested that you change your {2} account''s password. Click on the link below to set a new password</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your password, just ignore this message and nothing will be changed.</p>
|
||||
passwordResetBody=Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your credentials, just ignore this message and nothing will be changed.
|
||||
passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your credentials, just ignore this message and nothing will be changed.</p>
|
||||
executeActionsSubject=Update Your Account
|
||||
executeActionsBody=Your adminstrator has just requested that you update your {2} account. Click on the link below to start this process.\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you are unaware that your admin has requested this, just ignore this message and nothing will be changed.
|
||||
executeActionsBodyHtml=<p>Your adminstrator has just requested that you update your {2} account. Click on the link below to start this process.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you are unaware that your admin has requested this, just ignore this message and nothing will be changed.</p>
|
||||
eventLoginErrorSubject=Login error
|
||||
eventLoginErrorBody=A failed login attempt was detected to your account on {0} from {1}. If this was not you, please contact an admin.
|
||||
eventLoginErrorBodyHtml=<p>A failed login attempt was detected to your account on {0} from {1}. If this was not you, please contact an admin.</p>
|
||||
|
|
|
@ -2,11 +2,11 @@ emailVerificationSubject=Verifica\u00E7\u00E3o de e-mail
|
|||
emailVerificationBody=Algu\u00E9m criou uma conta {2} com este endere\u00E7o de e-mail. Se foi voc\u00EA, clique no link abaixo para verificar o seu endere\u00E7o de email\n\n{0}\n\nEste link ir\u00E1 expirar dentro de {1} minutos.\n\nSe n\u00E3o foi voc\u00EA que criou esta conta, basta ignorar esta mensagem.
|
||||
emailVerificationBodyHtml=<p>Algu\u00E9m criou uma conta {2} com este endere\u00E7o de e-mail. Se foi voc\u00EA, clique no link abaixo para verificar o seu endere\u00E7o de email</p><p><a href="{0}">{0}</a></p><p>Este link ir\u00E1 expirar dentro de {1} minutos.</p><p>Se n\u00E3o foi voc\u00EA que criou esta conta, basta ignorar esta mensagem.</p>
|
||||
passwordResetSubject=Redefini\u00E7\u00E3o de senha
|
||||
passwordResetBody=Someone just requested to change your {2} account''s password. If this was you, click on the link below to set a new password.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your password, just ignore this message and nothing will be changed.
|
||||
passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s password. If this was you, click on the link below to set a new password.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your password, just ignore this message and nothing will be changed.</p>
|
||||
changePasswordSubject=Change password
|
||||
changePasswordBody=Your adminstrator has just requested that you change your {2} account''s password. Click on the link below to set a new password\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you don''t want to reset your password, just ignore this message and nothing will be changed.
|
||||
changePasswordBodyHtml=<p>Your adminstrator has just requested that you change your {2} account''s password. Click on the link below to set a new password</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your password, just ignore this message and nothing will be changed.</p>
|
||||
passwordResetBody=Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your credentials, just ignore this message and nothing will be changed.
|
||||
passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your credentials, just ignore this message and nothing will be changed.</p>
|
||||
executeActionsSubject=Update Your Account
|
||||
executeActionsBody=Your adminstrator has just requested that you update your {2} account. Click on the link below to start this process.\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you are unaware that your admin has requested this, just ignore this message and nothing will be changed.
|
||||
executeActionsBodyHtml=<p>Your adminstrator has just requested that you update your {2} account. Click on the link below to start this process.</p><p><a href="{0}">{0}</a></p><p>This link will expire within {1} minutes.</p><p>If you are unaware that your admin has requested this, just ignore this message and nothing will be changed.</p>
|
||||
eventLoginErrorSubject=Erro de login
|
||||
eventLoginErrorBody=Uma tentativa de login mal sucedida para a sua conta foi detectada em {0} de {1}. Se n\u00E3o foi voc\u00EA, por favor, entre em contato com um administrador.
|
||||
eventLoginErrorBodyHtml=<p>Uma tentativa de login mal sucedida para a sua conta foi detectada em {0} de {1}. Se n\u00E3o foi voc\u00EA, por favor, entre em contato com um administrador.</p>
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
${msg("changePasswordBody",link, linkExpiration, realmName)}
|
|
@ -0,0 +1 @@
|
|||
${msg("executeActionsBody",link, linkExpiration, realmName)}
|
|
@ -19,7 +19,6 @@ public interface EmailProvider extends Provider {
|
|||
/**
|
||||
* Reset password sent from forgot password link on login
|
||||
*
|
||||
* @param code
|
||||
* @param link
|
||||
* @param expirationInMinutes
|
||||
* @throws EmailException
|
||||
|
@ -33,7 +32,7 @@ public interface EmailProvider extends Provider {
|
|||
* @param expirationInMinutes
|
||||
* @throws EmailException
|
||||
*/
|
||||
public void sendChangePassword(String link, long expirationInMinutes) throws EmailException;
|
||||
public void sendExecuteActions(String link, long expirationInMinutes) throws EmailException;
|
||||
|
||||
public void sendVerifyEmail(String link, long expirationInMinutes) throws EmailException;
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void sendChangePassword(String link, long expirationInMinutes) throws EmailException {
|
||||
public void sendExecuteActions(String link, long expirationInMinutes) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
attributes.put("link", link);
|
||||
attributes.put("linkExpiration", expirationInMinutes);
|
||||
|
@ -90,7 +90,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
|
|||
String realmName = realm.getName().substring(0, 1).toUpperCase() + realm.getName().substring(1);
|
||||
attributes.put("realmName", realmName);
|
||||
|
||||
send("changePasswordSubject", "changePassword.ftl", attributes);
|
||||
send("executeActionsSubject", "executeActions.ftl", attributes);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -47,12 +47,12 @@ public interface UserResource {
|
|||
public void resetPassword(CredentialRepresentation credentialRepresentation);
|
||||
|
||||
@PUT
|
||||
@Path("reset-password-email")
|
||||
public void resetPasswordEmail();
|
||||
@Path("execute-actions-email")
|
||||
public void executeActionsEmail();
|
||||
|
||||
@PUT
|
||||
@Path("reset-password-email")
|
||||
public void resetPasswordEmail(@QueryParam("client_id") String clientId);
|
||||
@Path("execute-actions-email")
|
||||
public void executeActionsEmail(@QueryParam("client_id") String clientId);
|
||||
|
||||
@PUT
|
||||
@Path("send-verify-email")
|
||||
|
|
|
@ -77,11 +77,12 @@ public interface ClientSessionModel {
|
|||
UPDATE_PROFILE,
|
||||
CONFIGURE_TOTP,
|
||||
UPDATE_PASSWORD,
|
||||
RECOVER_PASSWORD,
|
||||
RECOVER_PASSWORD, // deprecated
|
||||
AUTHENTICATE,
|
||||
SOCIAL_CALLBACK,
|
||||
LOGGED_OUT,
|
||||
RESET_CREDENTIALS
|
||||
RESET_CREDENTIALS,
|
||||
EXECUTE_ACTIONS
|
||||
}
|
||||
|
||||
public enum ExecutionStatus {
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.keycloak.authentication.RequiredActionProvider;
|
|||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
|
@ -35,6 +36,13 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
|
|||
}
|
||||
@Override
|
||||
public void requiredActionChallenge(RequiredActionContext context) {
|
||||
// if this is EXECUTE_ACTIONS we know that the email sent is valid so we can verify it automatically
|
||||
if (context.getClientSession().getNote(ClientSessionModel.Action.EXECUTE_ACTIONS.name()) != null) {
|
||||
context.getUser().setEmailVerified(true);
|
||||
context.success();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Validation.isBlank(context.getUser().getEmail())) {
|
||||
context.ignore();
|
||||
return;
|
||||
|
|
|
@ -156,8 +156,8 @@ public class Urls {
|
|||
return loginResetCredentialsBuilder(baseUri).build(realmId);
|
||||
}
|
||||
|
||||
public static UriBuilder recoverPasswordBuilder(URI baseUri) {
|
||||
return loginActionsBase(baseUri).path(LoginActionsService.class, "recoverPassword");
|
||||
public static UriBuilder executeActionsBuilder(URI baseUri) {
|
||||
return loginActionsBase(baseUri).path(LoginActionsService.class, "executeActions");
|
||||
}
|
||||
|
||||
public static UriBuilder loginResetCredentialsBuilder(URI baseUri) {
|
||||
|
|
|
@ -274,18 +274,13 @@ public class LoginActionsService {
|
|||
@QueryParam("execution") String execution) {
|
||||
event.event(EventType.LOGIN);
|
||||
Checks checks = new Checks();
|
||||
if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
|
||||
if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name())) {
|
||||
return checks.response;
|
||||
}
|
||||
event.detail(Details.CODE_ID, code);
|
||||
ClientSessionCode clientSessionCode = checks.clientCode;
|
||||
ClientSessionModel clientSession = clientSessionCode.getClientSession();
|
||||
|
||||
if (clientSession.getAction().equals(ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
|
||||
TokenManager.dettachClientSession(session.sessions(), realm, clientSession);
|
||||
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
}
|
||||
|
||||
return processAuthentication(execution, clientSession, null);
|
||||
}
|
||||
|
||||
|
@ -568,20 +563,21 @@ public class LoginActionsService {
|
|||
* @param key
|
||||
* @return
|
||||
*/
|
||||
@Path("recover-password")
|
||||
@Path("execute-actions")
|
||||
@GET
|
||||
public Response recoverPassword(@QueryParam("key") String key) {
|
||||
event.event(EventType.RESET_PASSWORD);
|
||||
public Response executeActions(@QueryParam("key") String key) {
|
||||
event.event(EventType.EXECUTE_ACTIONS);
|
||||
if (key != null) {
|
||||
Checks checks = new Checks();
|
||||
if (!checks.verifyCode(key, ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
|
||||
if (!checks.verifyCode(key, ClientSessionModel.Action.EXECUTE_ACTIONS.name())) {
|
||||
return checks.response;
|
||||
}
|
||||
ClientSessionModel clientSession = checks.clientCode.getClientSession();
|
||||
clientSession.setNote("END_AFTER_REQUIRED_ACTIONS", "true");
|
||||
clientSession.setNote(ClientSessionModel.Action.EXECUTE_ACTIONS.name(), "true");
|
||||
return AuthenticationManager.nextActionAfterAuthentication(session, clientSession.getUserSession(), clientSession, clientConnection, request, uriInfo, event);
|
||||
} else {
|
||||
event.error(Errors.RESET_CREDENTIAL_DISABLED);
|
||||
event.error(Errors.INVALID_CODE);
|
||||
return ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -834,10 +834,10 @@ public class UsersResource {
|
|||
* @param clientId client id
|
||||
* @return
|
||||
*/
|
||||
@Path("{id}/reset-password-email")
|
||||
@Path("{id}/execute-actions-email")
|
||||
@PUT
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Response resetPasswordEmail(@PathParam("id") String id, @QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri, @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId) {
|
||||
public Response executeActionsEmail(@PathParam("id") String id, @QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri, @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId) {
|
||||
auth.requireManage();
|
||||
|
||||
UserModel user = session.users().getUserById(id, realm);
|
||||
|
@ -851,17 +851,16 @@ public class UsersResource {
|
|||
|
||||
ClientSessionModel clientSession = createClientSession(user, redirectUri, clientId);
|
||||
ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
|
||||
accessCode.setAction(ClientSessionModel.Action.RECOVER_PASSWORD.name());
|
||||
accessCode.setAction(ClientSessionModel.Action.EXECUTE_ACTIONS.name());
|
||||
|
||||
try {
|
||||
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
|
||||
UriBuilder builder = Urls.recoverPasswordBuilder(uriInfo.getBaseUri());
|
||||
UriBuilder builder = Urls.executeActionsBuilder(uriInfo.getBaseUri());
|
||||
builder.queryParam("key", accessCode.getCode());
|
||||
|
||||
String link = builder.build(realm.getName()).toString();
|
||||
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
|
||||
|
||||
this.session.getProvider(EmailProvider.class).setRealm(realm).setUser(user).sendChangePassword(link, expiration);
|
||||
this.session.getProvider(EmailProvider.class).setRealm(realm).setUser(user).sendExecuteActions(link, expiration);
|
||||
|
||||
//audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getCodeId()).success();
|
||||
|
||||
|
@ -869,8 +868,8 @@ public class UsersResource {
|
|||
|
||||
return Response.ok().build();
|
||||
} catch (EmailException e) {
|
||||
logger.error("Failed to send password reset email", e);
|
||||
return ErrorResponse.error("Failed to send email", Response.Status.INTERNAL_SERVER_ERROR);
|
||||
logger.error("Failed to send execute actions email", e);
|
||||
return ErrorResponse.error("Failed to send execute actions email", Response.Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -361,7 +361,7 @@ public class UserTest extends AbstractClientTest {
|
|||
UserResource user = realm.users().get(id);
|
||||
|
||||
try {
|
||||
user.resetPasswordEmail();
|
||||
user.executeActionsEmail();
|
||||
fail("Expected failure");
|
||||
} catch (ClientErrorException e) {
|
||||
assertEquals(400, e.getResponse().getStatus());
|
||||
|
@ -374,7 +374,7 @@ public class UserTest extends AbstractClientTest {
|
|||
userRep.setEmail("user1@localhost");
|
||||
userRep.setEnabled(false);
|
||||
user.update(userRep);
|
||||
user.resetPasswordEmail();
|
||||
user.executeActionsEmail();
|
||||
fail("Expected failure");
|
||||
} catch (ClientErrorException e) {
|
||||
assertEquals(400, e.getResponse().getStatus());
|
||||
|
@ -385,7 +385,7 @@ public class UserTest extends AbstractClientTest {
|
|||
try {
|
||||
userRep.setEnabled(true);
|
||||
user.update(userRep);
|
||||
user.resetPasswordEmail("invalidClientId");
|
||||
user.executeActionsEmail("invalidClientId");
|
||||
fail("Expected failure");
|
||||
} catch (ClientErrorException e) {
|
||||
assertEquals(400, e.getResponse().getStatus());
|
||||
|
|
Loading…
Reference in a new issue