KEYCLOAK-272 Improved user credential management, including option to send user password reset email from admin console
This commit is contained in:
1 parent
457853aa28
commit
722f7c8840
8 files changed
+184
-76
No files matched your search
@@ -192,7 +192,7 @@ public class AdminService {
|
||||
logger.warn("not a Realm admin");
|
||||
throw new NotAuthorizedException("Bearer");
|
||||
}
|
||||
RealmsAdminResource adminResource = new RealmsAdminResource(admin);
|
||||
RealmsAdminResource adminResource = new RealmsAdminResource(admin, tokenManager);
|
||||
resourceContext.initResource(adminResource);
|
||||
return adminResource;
|
||||
}
|
||||
|
||||
+5
-2
@@ -8,6 +8,7 @@ import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.services.managers.ModelToRepresentation;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.managers.TokenManager;
|
||||
|
||||
import javax.ws.rs.*;
|
||||
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 UserModel admin;
|
||||
protected RealmModel realm;
|
||||
private TokenManager tokenManager;
|
||||
|
||||
@Context
|
||||
protected ResourceContext resourceContext;
|
||||
@@ -28,10 +30,11 @@ public class RealmAdminResource extends RoleContainerResource {
|
||||
@Context
|
||||
protected KeycloakSession session;
|
||||
|
||||
public RealmAdminResource(UserModel admin, RealmModel realm) {
|
||||
public RealmAdminResource(UserModel admin, RealmModel realm, TokenManager tokenManager) {
|
||||
super(realm, realm);
|
||||
this.admin = admin;
|
||||
this.realm = realm;
|
||||
this.tokenManager = tokenManager;
|
||||
}
|
||||
|
||||
@Path("applications")
|
||||
@@ -72,7 +75,7 @@ public class RealmAdminResource extends RoleContainerResource {
|
||||
|
||||
@Path("users")
|
||||
public UsersResource users() {
|
||||
UsersResource users = new UsersResource(realm);
|
||||
UsersResource users = new UsersResource(realm, tokenManager);
|
||||
resourceContext.initResource(users);
|
||||
return users;
|
||||
}
|
||||
|
||||
+5
-2
@@ -11,6 +11,7 @@ import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.services.managers.ModelToRepresentation;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.managers.TokenManager;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
|
||||
import javax.ws.rs.*;
|
||||
@@ -35,9 +36,11 @@ import java.util.Map;
|
||||
public class RealmsAdminResource {
|
||||
protected static final Logger logger = Logger.getLogger(RealmsAdminResource.class);
|
||||
protected UserModel admin;
|
||||
protected TokenManager tokenManager;
|
||||
|
||||
public RealmsAdminResource(UserModel admin) {
|
||||
public RealmsAdminResource(UserModel admin, TokenManager tokenManager) {
|
||||
this.admin = admin;
|
||||
this.tokenManager = tokenManager;
|
||||
}
|
||||
|
||||
public static final CacheControl noCache = new CacheControl();
|
||||
@@ -110,7 +113,7 @@ public class RealmsAdminResource {
|
||||
RealmModel realm = realmManager.getRealmByName(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);
|
||||
return adminResource;
|
||||
}
|
||||
|
||||
@@ -10,10 +10,16 @@ import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
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.RealmManager;
|
||||
import org.keycloak.services.managers.TokenManager;
|
||||
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.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
@@ -25,12 +31,15 @@ import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.ServerErrorException;
|
||||
import javax.ws.rs.container.ResourceContext;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -44,10 +53,16 @@ public class UsersResource {
|
||||
|
||||
protected RealmModel realm;
|
||||
|
||||
public UsersResource(RealmModel realm) {
|
||||
private TokenManager tokenManager;
|
||||
|
||||
public UsersResource(RealmModel realm, TokenManager tokenManager) {
|
||||
this.realm = realm;
|
||||
this.tokenManager = tokenManager;
|
||||
}
|
||||
|
||||
@Context
|
||||
protected UriInfo uriInfo;
|
||||
|
||||
@Context
|
||||
protected ResourceContext resourceContext;
|
||||
|
||||
@@ -373,21 +388,72 @@ public class UsersResource {
|
||||
}
|
||||
}
|
||||
|
||||
@Path("{username}/credentials")
|
||||
@Path("{username}/reset-password")
|
||||
@PUT
|
||||
@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);
|
||||
if (user == null) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
if (credentials == null) return;
|
||||
|
||||
for (CredentialRepresentation rep : credentials) {
|
||||
UserCredentialModel cred = RealmManager.fromRepresentation(rep);
|
||||
realm.updateCredential(user, cred);
|
||||
if (pass == null || pass.getValue() == null || !CredentialRepresentation.PASSWORD.equals(pass.getType())) {
|
||||
throw new BadRequestException();
|
||||
}
|
||||
|
||||
UserCredentialModel cred = RealmManager.fromRepresentation(pass);
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user