KEYCLOAK-272 Improved user credential management, including option to send user password reset email from admin console
This commit is contained in:
parent
457853aa28
commit
722f7c8840
8 changed files with 184 additions and 76 deletions
|
@ -227,7 +227,7 @@ module.controller('UserDetailCtrl', function($scope, realm, user, User, $locatio
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('UserCredentialsCtrl', function($scope, realm, user, User, UserCredentials, Notifications) {
|
module.controller('UserCredentialsCtrl', function($scope, realm, user, User, UserCredentials, Notifications, Dialog) {
|
||||||
console.log('UserCredentialsCtrl');
|
console.log('UserCredentialsCtrl');
|
||||||
|
|
||||||
$scope.realm = realm;
|
$scope.realm = realm;
|
||||||
|
@ -239,56 +239,45 @@ module.controller('UserCredentialsCtrl', function($scope, realm, user, User, Use
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.resetPassword = function() {
|
$scope.resetPassword = function() {
|
||||||
|
|
||||||
if ($scope.pwdChange) {
|
if ($scope.pwdChange) {
|
||||||
if ($scope.password != $scope.confirmPassword) {
|
if ($scope.password != $scope.confirmPassword) {
|
||||||
Notifications.error("Password and confirmation does not match.");
|
Notifications.error("Password and confirmation does not match.");
|
||||||
$scope.password = "";
|
|
||||||
$scope.confirmPassword = "";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$scope.user.hasOwnProperty('requiredActions')){
|
|
||||||
$scope.user.requiredActions = [];
|
|
||||||
}
|
|
||||||
if ($scope.user.requiredActions.indexOf("UPDATE_PASSWORD") < 0){
|
|
||||||
$scope.user.requiredActions.push("UPDATE_PASSWORD");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var credentials = [ { type : "password", value : $scope.password } ];
|
Dialog.confirm('Reset password', 'Are you sure you want to reset the users password?', function() {
|
||||||
|
UserCredentials.resetPassword({ realm: realm.realm, userId: user.username }, { type : "password", value : $scope.password }, function() {
|
||||||
User.update({
|
Notifications.success("The password has been reset");
|
||||||
realm: realm.realm,
|
$scope.password = null;
|
||||||
userId: $scope.user.username
|
$scope.confirmPassword = null;
|
||||||
}, $scope.user, function () {
|
|
||||||
|
|
||||||
$scope.isTotp = $scope.user.totp;
|
|
||||||
|
|
||||||
if ($scope.pwdChange){
|
|
||||||
UserCredentials.update({
|
|
||||||
realm: realm.realm,
|
|
||||||
userId: $scope.user.username
|
|
||||||
}, credentials, function () {
|
|
||||||
Notifications.success("The password has been reset. The user is required to change his password on" +
|
|
||||||
" the next login.");
|
|
||||||
$scope.password = "";
|
|
||||||
$scope.confirmPassword = "";
|
|
||||||
$scope.pwdChange = false;
|
|
||||||
$scope.isTotp = user.totp;
|
|
||||||
$scope.userChange = false;
|
|
||||||
}, function() {
|
}, function() {
|
||||||
Notifications.error("Error while resetting user password. Be aware that the update password required action" +
|
Notifications.error("Failed to reset user password");
|
||||||
" was already set.");
|
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
Notifications.success("User settings was updated.");
|
|
||||||
$scope.isTotp = user.totp;
|
|
||||||
$scope.userChange = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}, function() {
|
}, function() {
|
||||||
Notifications.error("Error while updating user settings.");
|
$scope.password = null;
|
||||||
|
$scope.confirmPassword = null;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.removeTotp = function() {
|
||||||
|
Dialog.confirm('Remove totp', 'Are you sure you want to remove the users totp configuration?', function() {
|
||||||
|
UserCredentials.removeTotp({ realm: realm.realm, userId: user.username }, { }, function() {
|
||||||
|
Notifications.success("The users totp configuration has been removed");
|
||||||
|
$scope.user.totp = false;
|
||||||
|
}, function() {
|
||||||
|
Notifications.error("Failed to remove the users totp configuration");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$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.username }, { }, function() {
|
||||||
|
Notifications.success("Password reset email sent to user");
|
||||||
|
}, function() {
|
||||||
|
Notifications.error("Failed to send password reset mail to user");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,28 @@ module.service('Dialog', function($dialog) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialog.confirm = function(title, message, success, cancel) {
|
||||||
|
var title = title;
|
||||||
|
var msg = '<span class="primary">' + message + '"</span>' +
|
||||||
|
'<span>This action can\'t be undone.</span>';
|
||||||
|
var btns = [ {
|
||||||
|
result : 'cancel',
|
||||||
|
label : 'Cancel'
|
||||||
|
}, {
|
||||||
|
result : 'ok',
|
||||||
|
label : title,
|
||||||
|
cssClass : 'destructive'
|
||||||
|
} ];
|
||||||
|
|
||||||
|
$dialog.messageBox(title, msg, btns).open().then(function(result) {
|
||||||
|
if (result == "ok") {
|
||||||
|
success();
|
||||||
|
} else {
|
||||||
|
cancel && cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return dialog
|
return dialog
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -136,15 +158,36 @@ module.factory('User', function($resource) {
|
||||||
});
|
});
|
||||||
|
|
||||||
module.factory('UserCredentials', function($resource) {
|
module.factory('UserCredentials', function($resource) {
|
||||||
return $resource('/auth/rest/admin/realms/:realm/users/:userId/credentials', {
|
var credentials = {};
|
||||||
|
|
||||||
|
credentials.resetPassword = $resource('/auth/rest/admin/realms/:realm/users/:userId/reset-password', {
|
||||||
realm : '@realm',
|
realm : '@realm',
|
||||||
userId : '@userId'
|
userId : '@userId'
|
||||||
}, {
|
}, {
|
||||||
update : {
|
update : {
|
||||||
method : 'PUT',
|
method : 'PUT'
|
||||||
isArray : true
|
|
||||||
}
|
}
|
||||||
});
|
}).update;
|
||||||
|
|
||||||
|
credentials.removeTotp = $resource('/auth/rest/admin/realms/:realm/users/:userId/remove-totp', {
|
||||||
|
realm : '@realm',
|
||||||
|
userId : '@userId'
|
||||||
|
}, {
|
||||||
|
update : {
|
||||||
|
method : 'PUT'
|
||||||
|
}
|
||||||
|
}).update;
|
||||||
|
|
||||||
|
credentials.resetPasswordEmail = $resource('/auth/rest/admin/realms/:realm/users/:userId/reset-password-email', {
|
||||||
|
realm : '@realm',
|
||||||
|
userId : '@userId'
|
||||||
|
}, {
|
||||||
|
update : {
|
||||||
|
method : 'PUT'
|
||||||
|
}
|
||||||
|
}).update;
|
||||||
|
|
||||||
|
return credentials;
|
||||||
});
|
});
|
||||||
|
|
||||||
module.factory('RealmRoleMapping', function($resource) {
|
module.factory('RealmRoleMapping', function($resource) {
|
||||||
|
|
|
@ -18,32 +18,30 @@
|
||||||
|
|
||||||
<form name="userForm" novalidate>
|
<form name="userForm" novalidate>
|
||||||
<fieldset class="border-top">
|
<fieldset class="border-top">
|
||||||
<legend uncollapsed><span class="text">Reset Password</span></legend>
|
<legend uncollapsed><span class="text">Credential Management</span></legend>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password">New Password</label>
|
<label for="password">Reset password</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="password" id="password" name="password" data-ng-model="password" autofocus
|
<input type="password" id="password" name="password" data-ng-model="password" placeholder="Temporary password" required>
|
||||||
required>
|
<input type="password" id="confirmPassword" name="confirmPassword" data-ng-model="confirmPassword" placeholder="Password confirmation" required>
|
||||||
|
<button type="submit" data-ng-click="resetPassword()" class="destructive" data-ng-show="password">Reset Password</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label class="two-lines" for="password">New Password Confirmation</label>
|
<div class="form-group" data-ng-show="user.email">
|
||||||
|
<label for="password">Reset password email</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="password" id="confirmPassword" name="confirmPassword"
|
<button type="submit" data-ng-click="resetPasswordEmail()" class="destructive">Send Email</button>
|
||||||
data-ng-model="confirmPassword" required>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group clearfix block" >
|
|
||||||
<label for="userTotp" class="control-label">TOTP Enabled</label>
|
<div class="form-group" data-ng-show="user.totp">
|
||||||
<input ng-model="user.totp" name="userTotp" class="kokosak" ng-disabled="!isTotp" id="userTotp" onoffswitch/>
|
<label for="password">Remove totp</label>
|
||||||
|
<div class="controls" data-ng-show="user.totp">
|
||||||
|
<button type="submit" data-ng-click="removeTotp()" class="destructive">Remove TOTP</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div class="form-actions">
|
|
||||||
<button type="submit" data-ng-click="resetPassword()" class="primary" data-ng-show="userChange && !pwdChange">Save</button>
|
|
||||||
<button type="submit" data-ng-click="resetPassword()" class="primary" data-ng-show="!userChange && pwdChange">Reset Password</button>
|
|
||||||
<button type="submit" data-ng-click="resetPassword()" class="primary" data-ng-show="userChange && pwdChange">Save and Reset Password</button>
|
|
||||||
<button type="submit" kc-reset data-ng-show="userChange || pwdChange">Clear changes</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -192,7 +192,7 @@ public class AdminService {
|
||||||
logger.warn("not a Realm admin");
|
logger.warn("not a Realm admin");
|
||||||
throw new NotAuthorizedException("Bearer");
|
throw new NotAuthorizedException("Bearer");
|
||||||
}
|
}
|
||||||
RealmsAdminResource adminResource = new RealmsAdminResource(admin);
|
RealmsAdminResource adminResource = new RealmsAdminResource(admin, tokenManager);
|
||||||
resourceContext.initResource(adminResource);
|
resourceContext.initResource(adminResource);
|
||||||
return adminResource;
|
return adminResource;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.services.managers.ModelToRepresentation;
|
import org.keycloak.services.managers.ModelToRepresentation;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
|
import org.keycloak.services.managers.TokenManager;
|
||||||
|
|
||||||
import javax.ws.rs.*;
|
import javax.ws.rs.*;
|
||||||
import javax.ws.rs.container.ResourceContext;
|
import javax.ws.rs.container.ResourceContext;
|
||||||
|
@ -21,6 +22,7 @@ public class RealmAdminResource extends RoleContainerResource {
|
||||||
protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
|
protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
|
||||||
protected UserModel admin;
|
protected UserModel admin;
|
||||||
protected RealmModel realm;
|
protected RealmModel realm;
|
||||||
|
private TokenManager tokenManager;
|
||||||
|
|
||||||
@Context
|
@Context
|
||||||
protected ResourceContext resourceContext;
|
protected ResourceContext resourceContext;
|
||||||
|
@ -28,10 +30,11 @@ public class RealmAdminResource extends RoleContainerResource {
|
||||||
@Context
|
@Context
|
||||||
protected KeycloakSession session;
|
protected KeycloakSession session;
|
||||||
|
|
||||||
public RealmAdminResource(UserModel admin, RealmModel realm) {
|
public RealmAdminResource(UserModel admin, RealmModel realm, TokenManager tokenManager) {
|
||||||
super(realm, realm);
|
super(realm, realm);
|
||||||
this.admin = admin;
|
this.admin = admin;
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
|
this.tokenManager = tokenManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("applications")
|
@Path("applications")
|
||||||
|
@ -72,7 +75,7 @@ public class RealmAdminResource extends RoleContainerResource {
|
||||||
|
|
||||||
@Path("users")
|
@Path("users")
|
||||||
public UsersResource users() {
|
public UsersResource users() {
|
||||||
UsersResource users = new UsersResource(realm);
|
UsersResource users = new UsersResource(realm, tokenManager);
|
||||||
resourceContext.initResource(users);
|
resourceContext.initResource(users);
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.services.managers.ModelToRepresentation;
|
import org.keycloak.services.managers.ModelToRepresentation;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
|
import org.keycloak.services.managers.TokenManager;
|
||||||
import org.keycloak.services.resources.flows.Flows;
|
import org.keycloak.services.resources.flows.Flows;
|
||||||
|
|
||||||
import javax.ws.rs.*;
|
import javax.ws.rs.*;
|
||||||
|
@ -35,9 +36,11 @@ import java.util.Map;
|
||||||
public class RealmsAdminResource {
|
public class RealmsAdminResource {
|
||||||
protected static final Logger logger = Logger.getLogger(RealmsAdminResource.class);
|
protected static final Logger logger = Logger.getLogger(RealmsAdminResource.class);
|
||||||
protected UserModel admin;
|
protected UserModel admin;
|
||||||
|
protected TokenManager tokenManager;
|
||||||
|
|
||||||
public RealmsAdminResource(UserModel admin) {
|
public RealmsAdminResource(UserModel admin, TokenManager tokenManager) {
|
||||||
this.admin = admin;
|
this.admin = admin;
|
||||||
|
this.tokenManager = tokenManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final CacheControl noCache = new CacheControl();
|
public static final CacheControl noCache = new CacheControl();
|
||||||
|
@ -110,7 +113,7 @@ public class RealmsAdminResource {
|
||||||
RealmModel realm = realmManager.getRealmByName(name);
|
RealmModel realm = realmManager.getRealmByName(name);
|
||||||
if (realm == null) throw new NotFoundException("{realm} = " + name);
|
if (realm == null) throw new NotFoundException("{realm} = " + name);
|
||||||
|
|
||||||
RealmAdminResource adminResource = new RealmAdminResource(admin, realm);
|
RealmAdminResource adminResource = new RealmAdminResource(admin, realm, tokenManager);
|
||||||
resourceContext.initResource(adminResource);
|
resourceContext.initResource(adminResource);
|
||||||
return adminResource;
|
return adminResource;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,16 @@ import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.*;
|
import org.keycloak.representations.idm.*;
|
||||||
|
import org.keycloak.services.email.EmailException;
|
||||||
|
import org.keycloak.services.email.EmailSender;
|
||||||
|
import org.keycloak.services.managers.AccessCodeEntry;
|
||||||
import org.keycloak.services.managers.ModelToRepresentation;
|
import org.keycloak.services.managers.ModelToRepresentation;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
|
import org.keycloak.services.managers.TokenManager;
|
||||||
import org.keycloak.services.resources.flows.Flows;
|
import org.keycloak.services.resources.flows.Flows;
|
||||||
|
import org.keycloak.services.resources.flows.Urls;
|
||||||
|
|
||||||
|
import javax.ws.rs.BadRequestException;
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
@ -25,12 +31,15 @@ import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.ServerErrorException;
|
||||||
import javax.ws.rs.container.ResourceContext;
|
import javax.ws.rs.container.ResourceContext;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -44,10 +53,16 @@ public class UsersResource {
|
||||||
|
|
||||||
protected RealmModel realm;
|
protected RealmModel realm;
|
||||||
|
|
||||||
public UsersResource(RealmModel realm) {
|
private TokenManager tokenManager;
|
||||||
|
|
||||||
|
public UsersResource(RealmModel realm, TokenManager tokenManager) {
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
|
this.tokenManager = tokenManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected UriInfo uriInfo;
|
||||||
|
|
||||||
@Context
|
@Context
|
||||||
protected ResourceContext resourceContext;
|
protected ResourceContext resourceContext;
|
||||||
|
|
||||||
|
@ -373,21 +388,72 @@ public class UsersResource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("{username}/credentials")
|
@Path("{username}/reset-password")
|
||||||
@PUT
|
@PUT
|
||||||
@Consumes("application/json")
|
@Consumes("application/json")
|
||||||
public void updateCredentials(@PathParam("username") String username, List<CredentialRepresentation> credentials) {
|
public void resetPassword(@PathParam("username") String username, CredentialRepresentation pass) {
|
||||||
UserModel user = realm.getUser(username);
|
UserModel user = realm.getUser(username);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
if (credentials == null) return;
|
if (pass == null || pass.getValue() == null || !CredentialRepresentation.PASSWORD.equals(pass.getType())) {
|
||||||
|
throw new BadRequestException();
|
||||||
|
}
|
||||||
|
|
||||||
for (CredentialRepresentation rep : credentials) {
|
UserCredentialModel cred = RealmManager.fromRepresentation(pass);
|
||||||
UserCredentialModel cred = RealmManager.fromRepresentation(rep);
|
|
||||||
realm.updateCredential(user, cred);
|
realm.updateCredential(user, cred);
|
||||||
}
|
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("{username}/remove-totp")
|
||||||
|
@PUT
|
||||||
|
@Consumes("application/json")
|
||||||
|
public void removeTotp(@PathParam("username") String username) {
|
||||||
|
UserModel user = realm.getUser(username);
|
||||||
|
if (user == null) {
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
user.setTotp(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("{username}/reset-password-email")
|
||||||
|
@PUT
|
||||||
|
@Consumes("application/json")
|
||||||
|
public Response resetPasswordEmail(@PathParam("username") String username) {
|
||||||
|
UserModel user = realm.getUser(username);
|
||||||
|
if (user == null) {
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.getEmail() == null) {
|
||||||
|
return Flows.errors().error("User email missing", Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
String redirect = Urls.accountBase(uriInfo.getBaseUri()).path("/").build(realm.getName()).toString();
|
||||||
|
String clientId = Constants.ACCOUNT_APPLICATION;
|
||||||
|
String state = null;
|
||||||
|
String scope = null;
|
||||||
|
|
||||||
|
UserModel client = realm.getUser(clientId);
|
||||||
|
if (client == null || !client.isEnabled()) {
|
||||||
|
return Flows.errors().error("Account management not enabled", Response.Status.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<UserModel.RequiredAction> requiredActions = new HashSet<UserModel.RequiredAction>(user.getRequiredActions());
|
||||||
|
requiredActions.add(UserModel.RequiredAction.UPDATE_PASSWORD);
|
||||||
|
|
||||||
|
AccessCodeEntry accessCode = tokenManager.createAccessCode(scope, state, redirect, realm, client, user);
|
||||||
|
accessCode.setRequiredActions(requiredActions);
|
||||||
|
accessCode.setExpiration(System.currentTimeMillis() / 1000 + realm.getAccessCodeLifespanUserAction());
|
||||||
|
|
||||||
|
try {
|
||||||
|
new EmailSender(realm.getSmtpConfig()).sendPasswordReset(user, realm, accessCode, uriInfo);
|
||||||
|
return Response.ok().build();
|
||||||
|
} catch (EmailException e) {
|
||||||
|
logger.error("Failed to send password reset email", e);
|
||||||
|
return Flows.errors().error("Failed to send email", Response.Status.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,4 +16,10 @@ public class ErrorFlows {
|
||||||
return Response.status(Response.Status.CONFLICT).entity(error).type(MediaType.APPLICATION_JSON).build();
|
return Response.status(Response.Status.CONFLICT).entity(error).type(MediaType.APPLICATION_JSON).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Response error(String message, Response.Status status) {
|
||||||
|
ErrorRepresentation error = new ErrorRepresentation();
|
||||||
|
error.setErrorMessage(message);
|
||||||
|
return Response.status(status).entity(error).type(MediaType.APPLICATION_JSON).build();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue