keycloak-aplcache
Changes
testsuite/src/test/resources/testrealm.json 18(+16 -2)
Details
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
index db12ace..a15d053 100755
--- a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
@@ -13,7 +13,7 @@ public class UserRepresentation {
protected String self; // link
protected String username;
- protected String status;
+ protected boolean enabled;
protected String firstName;
protected String lastName;
protected String email;
@@ -61,6 +61,14 @@ public class UserRepresentation {
this.username = username;
}
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
public Map<String, String> getAttributes() {
return attributes;
}
@@ -92,14 +100,6 @@ public class UserRepresentation {
return this;
}
- public String getStatus() {
- return status;
- }
-
- public void setStatus(String status) {
- this.status = status;
- }
-
public List<String> getRequiredActions() {
return requiredActions;
}
diff --git a/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json b/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json
index 177b45f..284a4be 100755
--- a/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json
+++ b/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json
@@ -18,7 +18,7 @@
"users" : [
{
"username" : "bburke@redhat.com",
- "status": "ENABLED",
+ "enabled": true,
"attributes" : {
"email" : "bburke@redhat.com"
},
@@ -29,7 +29,7 @@
},
{
"username" : "third-party",
- "status": "ENABLED",
+ "enabled": true,
"credentials" : [
{ "type" : "password",
"value" : "password" }
diff --git a/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java b/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
index b735cd3..ce80497 100755
--- a/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
+++ b/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
@@ -3,11 +3,13 @@ package org.keycloak.services.managers;
import org.keycloak.representations.SkeletonKeyToken;
import org.keycloak.services.models.RoleModel;
import org.keycloak.services.models.UserModel;
+import org.keycloak.services.models.UserModel.RequiredAction;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import java.util.UUID;
/**
@@ -23,6 +25,7 @@ public class AccessCodeEntry {
protected long expiration;
protected SkeletonKeyToken token;
protected UserModel user;
+ protected Set<RequiredAction> requiredActions;
protected UserModel client;
protected List<RoleModel> realmRolesRequested = new ArrayList<RoleModel>();
MultivaluedMap<String, RoleModel> resourceRolesRequested = new MultivaluedHashMap<String, RoleModel>();
@@ -75,6 +78,14 @@ public class AccessCodeEntry {
this.user = user;
}
+ public Set<RequiredAction> getRequiredActions() {
+ return requiredActions;
+ }
+
+ public void setRequiredActions(Set<RequiredAction> requiredActions) {
+ this.requiredActions = requiredActions;
+ }
+
public List<RoleModel> getRealmRolesRequested() {
return realmRolesRequested;
}
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 2874ef1..a0c10e9 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -12,8 +12,6 @@ import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RequiredCredentialModel;
import org.keycloak.services.models.UserModel;
-import org.keycloak.services.models.UserModel.RequiredAction;
-import org.keycloak.services.models.UserModel.Status;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.resources.SaasService;
@@ -205,7 +203,7 @@ public class AuthenticationManager {
return AuthenticationStatus.INVALID_USER;
}
- if (!user.isEnabled() && user.getStatus() == Status.DISABLED) {
+ if (!user.isEnabled()) {
logger.info("Account is disabled, contact admin.");
return AuthenticationStatus.ACCOUNT_DISABLED;
}
@@ -249,7 +247,7 @@ public class AuthenticationManager {
}
}
- if (user.getStatus() == Status.ACTIONS_REQUIRED) {
+ if (!user.getRequiredActions().isEmpty()) {
return AuthenticationStatus.ACTIONS_REQUIRED;
} else {
return AuthenticationStatus.SUCCESS;
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 438ac68..f584843 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -210,7 +210,7 @@ public class RealmManager {
public UserModel createUser(RealmModel newRealm, UserRepresentation userRep) {
UserModel user = newRealm.addUser(userRep.getUsername());
- user.setStatus(UserModel.Status.valueOf(userRep.getStatus()));
+ user.setEnabled(userRep.isEnabled());
user.setEmail(userRep.getEmail());
if (userRep.getAttributes() != null) {
for (Map.Entry<String, String> entry : userRep.getAttributes().entrySet()) {
diff --git a/services/src/main/java/org/keycloak/services/managers/UserManager.java b/services/src/main/java/org/keycloak/services/managers/UserManager.java
index b486cfe..49cd813 100755
--- a/services/src/main/java/org/keycloak/services/managers/UserManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/UserManager.java
@@ -19,7 +19,7 @@ public class UserManager {
rep.setEmail(user.getEmail());
rep.setLastName(user.getLastName());
rep.setFirstName(user.getFirstName());
- rep.setStatus(user.getStatus().name());
+ rep.setEnabled(user.isEnabled());
rep.setUsername(user.getLoginName());
for (Map.Entry<String, String> entry : user.getAttributes().entrySet()) {
rep.attribute(entry.getKey(), entry.getValue());
@@ -29,7 +29,7 @@ public class UserManager {
public UserModel createUser(RealmModel newRealm, UserRepresentation userRep) {
UserModel user = newRealm.addUser(userRep.getUsername());
- user.setStatus(UserModel.Status.valueOf(userRep.getStatus()));
+ user.setEnabled(userRep.isEnabled());
user.setEmail(userRep.getEmail());
user.setFirstName(userRep.getFirstName());
user.setLastName(userRep.getLastName());
@@ -56,7 +56,7 @@ public class UserManager {
* @param userRep
*/
public void updateUserAsAdmin(UserModel user, UserRepresentation userRep) {
- user.setStatus(UserModel.Status.valueOf(userRep.getStatus()));
+ user.setEnabled(userRep.isEnabled());
user.setEmail(userRep.getEmail());
user.setFirstName(userRep.getFirstName());
user.setLastName(userRep.getLastName());
diff --git a/services/src/main/java/org/keycloak/services/models/picketlink/UserAdapter.java b/services/src/main/java/org/keycloak/services/models/picketlink/UserAdapter.java
index 6ff4a96..169a4e2 100755
--- a/services/src/main/java/org/keycloak/services/models/picketlink/UserAdapter.java
+++ b/services/src/main/java/org/keycloak/services/models/picketlink/UserAdapter.java
@@ -3,8 +3,9 @@ package org.keycloak.services.models.picketlink;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import org.keycloak.services.models.UserModel;
import org.keycloak.services.models.utils.ArrayUtils;
@@ -20,7 +21,6 @@ public class UserAdapter implements UserModel {
private static final String EMAIL_VERIFIED_ATTR = "emailVerified";
private static final String KEYCLOAK_TOTP_ATTR = "totpEnabled";
private static final String REQUIRED_ACTIONS_ATTR = "requiredActions";
- private static final String STATUS_ATTR = "status";
protected User user;
protected IdentityManager idm;
@@ -44,23 +44,9 @@ public class UserAdapter implements UserModel {
return user.isEnabled();
}
- public UserModel.Status getStatus() {
- Attribute<UserModel.Status> a = user.getAttribute(STATUS_ATTR);
- if (a != null) {
- return a.getValue();
- } else {
- return user.isEnabled() ? UserModel.Status.ENABLED : UserModel.Status.DISABLED;
- }
- }
-
@Override
- public void setStatus(UserModel.Status status) {
- user.setAttribute(new Attribute<UserModel.Status>(STATUS_ATTR, status));
- if (status == UserModel.Status.DISABLED) {
- user.setEnabled(false);
- } else {
- user.setEnabled(true);
- }
+ public void setEnabled(boolean enabled) {
+ user.setEnabled(enabled);
idm.update(user);
}
@@ -131,7 +117,7 @@ public class UserAdapter implements UserModel {
@Override
public Map<String, String> getAttributes() {
Map<String, String> attributes = new HashMap<String, String>();
- for (Attribute attribute : user.getAttributes()) {
+ for (Attribute<?> attribute : user.getAttributes()) {
if (attribute.getValue() != null) attributes.put(attribute.getName(), attribute.getValue().toString());
}
return attributes;
@@ -152,12 +138,16 @@ public class UserAdapter implements UserModel {
}
@Override
- public List<RequiredAction> getRequiredActions() {
+ public Set<RequiredAction> getRequiredActions() {
RequiredAction[] actions = getRequiredActionsArray();
if (actions == null) {
- return null;
+ return Collections.emptySet();
} else {
- return Collections.unmodifiableList(Arrays.asList(actions));
+ Set<RequiredAction> s = new HashSet<RequiredAction>();
+ for (RequiredAction a : actions) {
+ s.add(a);
+ }
+ return Collections.unmodifiableSet(s);
}
}
@@ -167,7 +157,9 @@ public class UserAdapter implements UserModel {
if (actions == null) {
actions = new RequiredAction[] { action };
} else {
- actions = ArrayUtils.add(actions, action);
+ if (Arrays.binarySearch(actions, action) < 0) {
+ actions = ArrayUtils.add(actions, action);
+ }
}
Attribute<RequiredAction[]> a = new Attribute<RequiredAction[]>(REQUIRED_ACTIONS_ATTR, actions);
diff --git a/services/src/main/java/org/keycloak/services/models/UserModel.java b/services/src/main/java/org/keycloak/services/models/UserModel.java
index bab26e0..9bd370a 100755
--- a/services/src/main/java/org/keycloak/services/models/UserModel.java
+++ b/services/src/main/java/org/keycloak/services/models/UserModel.java
@@ -1,7 +1,7 @@
package org.keycloak.services.models;
-import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -19,9 +19,7 @@ public interface UserModel {
boolean isTotp();
- Status getStatus();
-
- void setStatus(Status status);
+ void setEnabled(boolean enabled);
void setAttribute(String name, String value);
@@ -31,7 +29,7 @@ public interface UserModel {
Map<String, String> getAttributes();
- List<RequiredAction> getRequiredActions();
+ Set<RequiredAction> getRequiredActions();
void addRequiredAction(RequiredAction action);
@@ -55,10 +53,6 @@ public interface UserModel {
void setTotp(boolean totp);
- public static enum Status {
- ENABLED, DISABLED, ACTIONS_REQUIRED
- }
-
public static enum RequiredAction {
VERIFY_EMAIL, UPDATE_PROFILE, CONFIGURE_TOTP, RESET_PASSWORD
}
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 3213fe3..da8aac6 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -21,6 +21,9 @@
*/
package org.keycloak.services.resources;
+import java.util.HashSet;
+import java.util.Set;
+
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
@@ -98,49 +101,44 @@ public class AccountService {
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response processAccountUpdate(final MultivaluedMap<String, String> formData) {
- UserModel user = getUser(RequiredAction.UPDATE_PROFILE);
- if (user != null) {
- user.setFirstName(formData.getFirst("firstName"));
- user.setLastName(formData.getFirst("lastName"));
- user.setEmail(formData.getFirst("email"));
-
- Response response = redirectOauth();
- if (response != null) {
- return response;
- } else {
- return Flows.forms(realm, request, uriInfo).setUser(user).forwardToAccount();
- }
- } else {
+ AccessCodeEntry accessCodeEntry = getAccessCodeEntry(RequiredAction.UPDATE_PROFILE);
+ UserModel user = accessCodeEntry != null ? getUserFromAccessCode(accessCodeEntry) : getUserFromAuthManager();
+ if (user == null) {
return Response.status(Status.FORBIDDEN).build();
}
- }
- private UserModel getUser(RequiredAction action) {
- if (uriInfo.getQueryParameters().containsKey(FormFlows.CODE)) {
- AccessCodeEntry accessCodeEntry = getAccessCodeEntry(uriInfo.getQueryParameters().getFirst(FormFlows.CODE));
- if (accessCodeEntry == null) {
- return null;
- }
+ user.setFirstName(formData.getFirst("firstName"));
+ user.setLastName(formData.getFirst("lastName"));
+ user.setEmail(formData.getFirst("email"));
- String loginName = accessCodeEntry.getUser().getLoginName();
- UserModel user = realm.getUser(loginName);
- if (!user.getRequiredActions().contains(action)) {
- return null;
- }
- if (!accessCodeEntry.getUser().getRequiredActions().contains(action)) {
- return null;
- }
- return user;
+ user.removeRequiredAction(UserModel.RequiredAction.UPDATE_PROFILE);
+ if (accessCodeEntry != null) {
+ accessCodeEntry.getRequiredActions().remove(UserModel.RequiredAction.UPDATE_PROFILE);
+ }
+
+ Response response = redirectOauth(accessCodeEntry);
+ if (response != null) {
+ return response;
} else {
- return getUserFromAuthManager();
+ return Flows.forms(realm, request, uriInfo).setUser(user).forwardToAccount();
}
}
+ private UserModel getUserFromAccessCode(AccessCodeEntry accessCodeEntry) {
+ String loginName = accessCodeEntry.getUser().getLoginName();
+ return realm.getUser(loginName);
+ }
+
private UserModel getUserFromAuthManager() {
return authManager.authenticateIdentityCookie(realm, uriInfo, headers);
}
- private AccessCodeEntry getAccessCodeEntry(String code) {
+ private AccessCodeEntry getAccessCodeEntry(RequiredAction requiredAction) {
+ String code = uriInfo.getQueryParameters().getFirst(FormFlows.CODE);
+ if (code == null) {
+ return null;
+ }
+
JWSInput input = new JWSInput(code, providers);
boolean verifiedCode = false;
try {
@@ -163,6 +161,11 @@ public class AccountService {
return null;
}
+ if (accessCodeEntry.getRequiredActions() == null
+ || !accessCodeEntry.getRequiredActions().contains(requiredAction)) {
+ return null;
+ }
+
return accessCodeEntry;
}
@@ -170,72 +173,80 @@ public class AccountService {
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response processTotpUpdate(final MultivaluedMap<String, String> formData) {
- UserModel user = getUser(RequiredAction.CONFIGURE_TOTP);
- if (user != null) {
- FormFlows forms = Flows.forms(realm, request, uriInfo);
+ AccessCodeEntry accessCodeEntry = getAccessCodeEntry(RequiredAction.CONFIGURE_TOTP);
+ UserModel user = accessCodeEntry != null ? getUserFromAccessCode(accessCodeEntry) : getUserFromAuthManager();
+ if (user == null) {
+ return Response.status(Status.FORBIDDEN).build();
+ }
- String totp = formData.getFirst("totp");
- String totpSecret = formData.getFirst("totpSecret");
+ FormFlows forms = Flows.forms(realm, request, uriInfo);
- String error = null;
+ String totp = formData.getFirst("totp");
+ String totpSecret = formData.getFirst("totpSecret");
- if (Validation.isEmpty(totp)) {
- error = Messages.MISSING_TOTP;
- } else if (!new TimeBasedOTP().validate(totp, totpSecret.getBytes())) {
- error = Messages.INVALID_TOTP;
- }
+ String error = null;
- if (error != null) {
- return forms.setError(error).setUser(user).forwardToTotp();
- }
+ if (Validation.isEmpty(totp)) {
+ error = Messages.MISSING_TOTP;
+ } else if (!new TimeBasedOTP().validate(totp, totpSecret.getBytes())) {
+ error = Messages.INVALID_TOTP;
+ }
- UserCredentialModel credentials = new UserCredentialModel();
- credentials.setType(CredentialRepresentation.TOTP);
- credentials.setValue(formData.getFirst("totpSecret"));
- realm.updateCredential(user, credentials);
+ if (error != null) {
+ return forms.setError(error).setUser(user).forwardToTotp();
+ }
- user.removeRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
+ UserCredentialModel credentials = new UserCredentialModel();
+ credentials.setType(CredentialRepresentation.TOTP);
+ credentials.setValue(formData.getFirst("totpSecret"));
+ realm.updateCredential(user, credentials);
- user.setTotp(true);
+ user.removeRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
+ if (accessCodeEntry != null) {
+ accessCodeEntry.getRequiredActions().remove(UserModel.RequiredAction.CONFIGURE_TOTP);
+ }
- Response response = redirectOauth();
- if (response != null) {
- return response;
- } else {
- return Flows.forms(realm, request, uriInfo).setUser(user).forwardToTotp();
- }
+ user.setTotp(true);
+
+ Response response = redirectOauth(accessCodeEntry);
+ if (response != null) {
+ return response;
} else {
- return Response.status(Status.FORBIDDEN).build();
+ return Flows.forms(realm, request, uriInfo).setUser(user).forwardToTotp();
}
}
@Path("email-verify")
@GET
- public Response processEmailVerification(@QueryParam("code") String code) {
- AccessCodeEntry accessCodeEntry = getAccessCodeEntry(code);
- String loginName = accessCodeEntry.getUser().getLoginName();
- UserModel user = realm.getUser(loginName);
- if (user != null) {
- user.setEmailVerified(true);
- user.removeRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL);
-
- Response response = redirectOauth();
- if (response != null) {
- return response;
- } else {
- return Flows.forms(realm, request, uriInfo).setUser(user).forwardToVerifyEmail();
- }
- } else {
+ public Response processEmailVerification() {
+ AccessCodeEntry accessCodeEntry = getAccessCodeEntry(RequiredAction.VERIFY_EMAIL);
+ UserModel user = accessCodeEntry != null ? getUserFromAccessCode(accessCodeEntry) : null;
+ if (user == null) {
return Response.status(Status.FORBIDDEN).build();
}
+
+ user.setEmailVerified(true);
+ user.removeRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL);
+ if (accessCodeEntry != null) {
+ accessCodeEntry.getRequiredActions().remove(UserModel.RequiredAction.VERIFY_EMAIL);
+ }
+
+ Response response = redirectOauth(accessCodeEntry);
+ if (response != null) {
+ return response;
+ } else {
+ return Flows.forms(realm, request, uriInfo).setUser(user).forwardToVerifyEmail();
+ }
}
- private Response redirectOauth() {
+ private Response redirectOauth(AccessCodeEntry accessCodeEntry) {
+ if (accessCodeEntry == null) {
+ return null;
+ }
String redirect = uriInfo.getQueryParameters().getFirst("redirect_uri");
if (redirect != null) {
- AccessCodeEntry accessCode = getAccessCodeEntry(uriInfo.getQueryParameters().getFirst(FormFlows.CODE));
String state = uriInfo.getQueryParameters().getFirst("state");
- return Flows.oauth(realm, request, uriInfo, authManager, tokenManager).redirectAccessCode(accessCode, state,
+ return Flows.oauth(realm, request, uriInfo, authManager, tokenManager).redirectAccessCode(accessCodeEntry, state,
redirect);
} else {
return null;
@@ -246,50 +257,53 @@ public class AccountService {
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response processPasswordUpdate(final MultivaluedMap<String, String> formData) {
- UserModel user = getUser(RequiredAction.RESET_PASSWORD);
- if (user != null) {
- FormFlows forms = Flows.forms(realm, request, uriInfo).setUser(user);
+ AccessCodeEntry accessCodeEntry = getAccessCodeEntry(RequiredAction.RESET_PASSWORD);
+ UserModel user = accessCodeEntry != null ? getUserFromAccessCode(accessCodeEntry) : getUserFromAuthManager();
+ if (user == null) {
+ return Response.status(Status.FORBIDDEN).build();
+ }
- String password = formData.getFirst("password");
- String passwordNew = formData.getFirst("password-new");
- String passwordConfirm = formData.getFirst("password-confirm");
+ FormFlows forms = Flows.forms(realm, request, uriInfo).setUser(user);
- String error = null;
+ String password = formData.getFirst("password");
+ String passwordNew = formData.getFirst("password-new");
+ String passwordConfirm = formData.getFirst("password-confirm");
- if (Validation.isEmpty(passwordNew)) {
- error = Messages.MISSING_PASSWORD;
- } else if (!passwordNew.equals(passwordConfirm)) {
- error = Messages.INVALID_PASSWORD_CONFIRM;
- }
+ String error = null;
- if (user.getRequiredActions() == null || !user.getRequiredActions().contains(RequiredAction.RESET_PASSWORD)) {
- if (Validation.isEmpty(password)) {
- error = Messages.MISSING_PASSWORD;
- } else if (!realm.validatePassword(user, password)) {
- error = Messages.INVALID_PASSWORD_EXISTING;
- }
- }
+ if (Validation.isEmpty(passwordNew)) {
+ error = Messages.MISSING_PASSWORD;
+ } else if (!passwordNew.equals(passwordConfirm)) {
+ error = Messages.INVALID_PASSWORD_CONFIRM;
+ }
- if (error != null) {
- return forms.setError(error).forwardToPassword();
+ if (accessCodeEntry == null) {
+ if (Validation.isEmpty(password)) {
+ error = Messages.MISSING_PASSWORD;
+ } else if (!realm.validatePassword(user, password)) {
+ error = Messages.INVALID_PASSWORD_EXISTING;
}
+ }
- UserCredentialModel credentials = new UserCredentialModel();
- credentials.setType(CredentialRepresentation.PASSWORD);
- credentials.setValue(passwordNew);
+ if (error != null) {
+ return forms.setError(error).forwardToPassword();
+ }
- realm.updateCredential(user, credentials);
+ UserCredentialModel credentials = new UserCredentialModel();
+ credentials.setType(CredentialRepresentation.PASSWORD);
+ credentials.setValue(passwordNew);
- user.removeRequiredAction(RequiredAction.RESET_PASSWORD);
- user.setStatus(UserModel.Status.ENABLED);
+ realm.updateCredential(user, credentials);
- authManager.expireIdentityCookie(realm, uriInfo);
- new ResourceAdminManager().singleLogOut(realm, user.getLoginName());
-
- return Flows.forms(realm, request, uriInfo).forwardToLogin();
- } else {
- return Response.status(Status.FORBIDDEN).build();
+ user.removeRequiredAction(RequiredAction.RESET_PASSWORD);
+ if (accessCodeEntry != null) {
+ accessCodeEntry.getRequiredActions().remove(UserModel.RequiredAction.RESET_PASSWORD);
}
+
+ authManager.expireIdentityCookie(realm, uriInfo);
+ new ResourceAdminManager().singleLogOut(realm, user.getLoginName());
+
+ return Flows.forms(realm, request, uriInfo).forwardToLogin();
}
@Path("")
@@ -328,7 +342,14 @@ public class AccountService {
@Path("password")
@GET
public Response passwordPage() {
- UserModel user = getUser(RequiredAction.RESET_PASSWORD);
+ UserModel user = getUserFromAuthManager();
+
+ // TODO Remove when we have a separate login-reset-password page
+ if (user == null) {
+ AccessCodeEntry accessCodeEntry = getAccessCodeEntry(RequiredAction.RESET_PASSWORD);
+ user = accessCodeEntry != null ? getUserFromAccessCode(accessCodeEntry) : null;
+ }
+
if (user != null) {
return Flows.forms(realm, request, uriInfo).setUser(user).forwardToPassword();
} else {
@@ -361,10 +382,12 @@ public class AccountService {
// String username = formData.getFirst("username");
UserModel user = realm.getUser(username);
- user.addRequiredAction(RequiredAction.RESET_PASSWORD);
- user.setStatus(UserModel.Status.ACTIONS_REQUIRED);
+
+ Set<RequiredAction> requiredActions = new HashSet<RequiredAction>(user.getRequiredActions());
+ requiredActions.add(RequiredAction.RESET_PASSWORD);
AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user);
+ accessCode.setRequiredActions(requiredActions);
accessCode.setExpiration(System.currentTimeMillis() / 1000 + realm.getAccessCodeLifespanUserAction());
if (user.getEmail() == null) {
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
index a71e8ac..acd86cb 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
@@ -21,6 +21,10 @@
*/
package org.keycloak.services.resources.flows;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.services.managers.AccessCodeEntry;
@@ -30,6 +34,7 @@ import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RoleModel;
import org.keycloak.services.models.UserModel;
+import org.keycloak.services.models.UserModel.RequiredAction;
import org.keycloak.services.resources.TokenService;
import javax.ws.rs.core.Response;
@@ -88,16 +93,19 @@ public class OAuthFlows {
log.info("processAccessCode: go to oauth page?: "
+ (!isResource && (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested()
.size() > 0)));
- if (!isResource
- && (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested().size() > 0)) {
+
+ Set<RequiredAction> requiredActions = user.getRequiredActions();
+ if (!requiredActions.isEmpty()) {
+ accessCode.setRequiredActions(new HashSet<UserModel.RequiredAction>(requiredActions));
accessCode.setExpiration(System.currentTimeMillis() / 1000 + realm.getAccessCodeLifespanUserAction());
- return oauthGrantPage(accessCode, client);
+ return Flows.forms(realm, request, uriInfo).setCode(accessCode.getCode()).setUser(user)
+ .forwardToAction(user.getRequiredActions().iterator().next());
}
- if (user.getRequiredActions() != null) {
+ if (!isResource
+ && (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested().size() > 0)) {
accessCode.setExpiration(System.currentTimeMillis() / 1000 + realm.getAccessCodeLifespanUserAction());
- return Flows.forms(realm, request, uriInfo).setCode(accessCode.getCode()).setUser(user)
- .forwardToAction(user.getRequiredActions().get(0));
+ return oauthGrantPage(accessCode, client);
}
if (redirect != null) {
diff --git a/services/src/main/java/org/keycloak/services/resources/SaasService.java b/services/src/main/java/org/keycloak/services/resources/SaasService.java
index 12156c3..4a64674 100755
--- a/services/src/main/java/org/keycloak/services/resources/SaasService.java
+++ b/services/src/main/java/org/keycloak/services/resources/SaasService.java
@@ -229,7 +229,7 @@ public class SaasService {
return Flows.forms(realm, request, uriInfo).setError(Messages.ACCOUNT_DISABLED).setFormData(formData)
.forwardToLogin();
case ACTIONS_REQUIRED:
- return Flows.forms(realm, request, uriInfo).forwardToAction(user.getRequiredActions().get(0));
+ return Flows.forms(realm, request, uriInfo).forwardToAction(user.getRequiredActions().iterator().next());
default:
return Flows.forms(realm, request, uriInfo).setError(Messages.INVALID_USER).setFormData(formData)
.forwardToLogin();
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index 2df6a7c..44b1d92 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -20,7 +20,6 @@ import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.models.*;
import org.keycloak.services.models.UserModel.RequiredAction;
-import org.keycloak.services.models.UserModel.Status;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.OAuthFlows;
import org.keycloak.services.validation.Validation;
@@ -218,7 +217,6 @@ public class TokenService {
for (RequiredCredentialModel c : realm.getRequiredCredentials()) {
if (c.getType().equals(CredentialRepresentation.TOTP) && !user.isTotp()) {
user.addRequiredAction(RequiredAction.CONFIGURE_TOTP);
- user.setStatus(Status.ACTIONS_REQUIRED);
logger.info("User is required to configure totp");
}
}
@@ -227,7 +225,6 @@ public class TokenService {
private void isEmailVerificationRequired(UserModel user) {
if (realm.isVerifyEmail() && !user.isEmailVerified()) {
user.addRequiredAction(RequiredAction.VERIFY_EMAIL);
- user.setStatus(Status.ACTIONS_REQUIRED);
logger.info("User is required to verify email");
}
}
@@ -417,6 +414,12 @@ public class TokenService {
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
.build();
}
+ if (accessCode.getRequiredActions() != null && !accessCode.getRequiredActions().isEmpty()) {
+ Map<String, String> res = new HashMap<String, String>();
+ res.put("error", "invalid_grant");
+ res.put("error_description", "Actions required");
+ return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
+ }
if (!client.getLoginName().equals(accessCode.getClient().getLoginName())) {
Map<String, String> res = new HashMap<String, String>();
res.put("error", "invalid_grant");
diff --git a/services/src/test/java/org/keycloak/services/managers/AuthenticationManagerTest.java b/services/src/test/java/org/keycloak/services/managers/AuthenticationManagerTest.java
index ad3149c..76870e0 100644
--- a/services/src/test/java/org/keycloak/services/managers/AuthenticationManagerTest.java
+++ b/services/src/test/java/org/keycloak/services/managers/AuthenticationManagerTest.java
@@ -17,7 +17,6 @@ import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.UserCredentialModel;
import org.keycloak.services.models.UserModel;
import org.keycloak.services.models.UserModel.RequiredAction;
-import org.keycloak.services.models.UserModel.Status;
import org.keycloak.services.resources.KeycloakApplication;
import org.picketlink.idm.credential.util.TimeBasedOTP;
@@ -66,7 +65,6 @@ public class AuthenticationManagerTest {
public void authFormRequiredAction() {
realm.addRequiredCredential(CredentialRepresentation.TOTP);
user.addRequiredAction(RequiredAction.CONFIGURE_TOTP);
- user.setStatus(Status.ACTIONS_REQUIRED);
AuthenticationStatus status = am.authenticateForm(realm, user, formData);
Assert.assertEquals(AuthenticationStatus.ACTIONS_REQUIRED, status);
@@ -74,21 +72,13 @@ public class AuthenticationManagerTest {
@Test
public void authFormUserDisabled() {
- user.setStatus(Status.DISABLED);
+ user.setEnabled(false);
AuthenticationStatus status = am.authenticateForm(realm, user, formData);
Assert.assertEquals(AuthenticationStatus.ACCOUNT_DISABLED, status);
}
@Test
- public void authFormUserRequiredActions() {
- user.setStatus(Status.ACTIONS_REQUIRED);
-
- AuthenticationStatus status = am.authenticateForm(realm, user, formData);
- Assert.assertEquals(AuthenticationStatus.ACTIONS_REQUIRED, status);
- }
-
- @Test
public void authFormWithTotp() {
realm.addRequiredCredential(CredentialRepresentation.TOTP);
diff --git a/services/src/test/java/org/keycloak/test/AdapterTest.java b/services/src/test/java/org/keycloak/test/AdapterTest.java
index 015caac..581c1e8 100755
--- a/services/src/test/java/org/keycloak/test/AdapterTest.java
+++ b/services/src/test/java/org/keycloak/test/AdapterTest.java
@@ -15,11 +15,13 @@ import org.keycloak.services.models.RequiredCredentialModel;
import org.keycloak.services.models.RoleModel;
import org.keycloak.services.models.UserModel;
import org.keycloak.services.models.UserCredentialModel;
+import org.keycloak.services.models.UserModel.RequiredAction;
import org.keycloak.services.resources.KeycloakApplication;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
@@ -265,53 +267,41 @@ public class AdapterTest {
}
@Test
- public void testUserStatus() throws Exception {
+ public void testUserRequiredActions() throws Exception {
test1CreateRealm();
UserModel user = realmModel.addUser("bburke");
- Assert.assertTrue(user.isEnabled());
- Assert.assertEquals(UserModel.Status.ENABLED, user.getStatus());
-
- user.setStatus(UserModel.Status.DISABLED);
- user = realmModel.getUser("bburke");
- Assert.assertFalse(user.isEnabled());
- Assert.assertEquals(UserModel.Status.DISABLED, user.getStatus());
+ Assert.assertTrue(user.getRequiredActions().isEmpty());
- user.setStatus(UserModel.Status.ACTIONS_REQUIRED);
+ user.addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
user = realmModel.getUser("bburke");
- Assert.assertTrue(user.isEnabled());
- Assert.assertEquals(UserModel.Status.ACTIONS_REQUIRED, user.getStatus());
- }
-
- @Test
- public void testUserRequiredActions() throws Exception {
- test1CreateRealm();
-
- UserModel user = realmModel.addUser("bburke");
-
- Assert.assertNull(user.getRequiredActions());
+ Assert.assertEquals(1, user.getRequiredActions().size());
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.CONFIGURE_TOTP));
user.addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
user = realmModel.getUser("bburke");
- Assert.assertEquals(Arrays.asList(UserModel.RequiredAction.CONFIGURE_TOTP), user.getRequiredActions());
+ Assert.assertEquals(1, user.getRequiredActions().size());
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.CONFIGURE_TOTP));
user.addRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL);
user = realmModel.getUser("bburke");
- Assert.assertEquals(Arrays.asList(UserModel.RequiredAction.CONFIGURE_TOTP, UserModel.RequiredAction.VERIFY_EMAIL),
- user.getRequiredActions());
+ Assert.assertEquals(2, user.getRequiredActions().size());
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.CONFIGURE_TOTP));
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL));
user.removeRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
user = realmModel.getUser("bburke");
- Assert.assertEquals(Arrays.asList(UserModel.RequiredAction.VERIFY_EMAIL), user.getRequiredActions());
+ Assert.assertEquals(1, user.getRequiredActions().size());
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL));
user.removeRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL);
user = realmModel.getUser("bburke");
- Assert.assertNull(user.getRequiredActions());
+ Assert.assertTrue(user.getRequiredActions().isEmpty());
}
}
diff --git a/services/src/test/resources/testrealm.json b/services/src/test/resources/testrealm.json
index 2549914..cfe5215 100755
--- a/services/src/test/resources/testrealm.json
+++ b/services/src/test/resources/testrealm.json
@@ -12,7 +12,7 @@
"users": [
{
"username": "wburke",
- "status": "ENABLED",
+ "enabled": true,
"attributes": {
"email": "bburke@redhat.com"
},
@@ -25,7 +25,7 @@
},
{
"username": "loginclient",
- "status": "ENABLED",
+ "enabled": true,
"credentials": [
{
"type": "password",
@@ -35,7 +35,7 @@
},
{
"username": "admin",
- "status": "ENABLED",
+ "enabled": true,
"credentials": [
{
"type": "password",
@@ -45,7 +45,7 @@
},
{
"username": "oauthclient",
- "status": "ENABLED",
+ "enabled": true,
"credentials": [
{
"type": "password",
@@ -55,7 +55,7 @@
},
{
"username": "mySocialUser",
- "status": "ENABLED"
+ "enabled": true
}
],
"roleMappings": [
diff --git a/services/src/test/resources/testrealm-demo.json b/services/src/test/resources/testrealm-demo.json
index 8ef37fe..92d23ea 100755
--- a/services/src/test/resources/testrealm-demo.json
+++ b/services/src/test/resources/testrealm-demo.json
@@ -15,7 +15,7 @@
"users" : [
{
"username" : "bburke@redhat.com",
- "status": "ENABLED",
+ "enabled": true,
"attributes" : {
"email" : "bburke@redhat.com"
},
@@ -26,7 +26,7 @@
},
{
"username" : "third-party",
- "status": "ENABLED",
+ "enabled": true,
"credentials" : [
{ "type" : "Password",
"value" : "password" }
diff --git a/testsuite/src/test/java/org/keycloak/testsuite/RegisterTest.java b/testsuite/src/test/java/org/keycloak/testsuite/RegisterTest.java
index 26683e1..1417262 100644
--- a/testsuite/src/test/java/org/keycloak/testsuite/RegisterTest.java
+++ b/testsuite/src/test/java/org/keycloak/testsuite/RegisterTest.java
@@ -39,7 +39,7 @@ public class RegisterTest extends AbstractDroneTest {
Assert.assertTrue(registerPage.isCurrent());
- registerPage.register("name", "email", "username", null, null);
+ registerPage.register("name", "email", "registerExistingUser", null, null);
Assert.assertTrue(registerPage.isCurrent());
Assert.assertEquals("Please specify password", registerPage.getError());
@@ -52,7 +52,7 @@ public class RegisterTest extends AbstractDroneTest {
Assert.assertTrue(registerPage.isCurrent());
- registerPage.register("name", "email", "bburke@redhat.com", "password", "invalid");
+ registerPage.register("name", "email", "registerUserInvalidPasswordConfirm", "password", "invalid");
Assert.assertTrue(registerPage.isCurrent());
Assert.assertEquals("Password confirmation doesn't match", registerPage.getError());
@@ -65,7 +65,7 @@ public class RegisterTest extends AbstractDroneTest {
Assert.assertTrue(registerPage.isCurrent());
- registerPage.register("name", "email", "username", null, null);
+ registerPage.register("name", "email", "registerUserMissingPassword", null, null);
Assert.assertTrue(registerPage.isCurrent());
Assert.assertEquals("Please specify password", registerPage.getError());
@@ -91,7 +91,7 @@ public class RegisterTest extends AbstractDroneTest {
Assert.assertTrue(registerPage.isCurrent());
- registerPage.register("name", "email", "username", "password", "password");
+ registerPage.register("name", "email", "registerUserSuccess", "password", "password");
Assert.assertTrue(appPage.isCurrent());
}
diff --git a/testsuite/src/test/java/org/keycloak/testsuite/ResetPasswordTest.java b/testsuite/src/test/java/org/keycloak/testsuite/ResetPasswordTest.java
index e90a1d9..22b0ab7 100644
--- a/testsuite/src/test/java/org/keycloak/testsuite/ResetPasswordTest.java
+++ b/testsuite/src/test/java/org/keycloak/testsuite/ResetPasswordTest.java
@@ -91,4 +91,24 @@ public class ResetPasswordTest extends AbstractDroneTest {
Assert.assertEquals("bburke@redhat.com", appPage.getUser());
}
+ @Test
+ public void tempPassword() {
+ appPage.open();
+
+ Assert.assertTrue(loginPage.isCurrent());
+
+ loginPage.login("reset@pass.com", "temp-password");
+
+ Assert.assertTrue(changePasswordPage.isCurrent());
+
+ changePasswordPage.changePassword("new-password", "new-password");
+
+ Assert.assertTrue(loginPage.isCurrent());
+
+ loginPage.login("reset@pass.com", "new-password");
+
+ Assert.assertTrue(appPage.isCurrent());
+ Assert.assertEquals("reset@pass.com", appPage.getUser());
+ }
+
}
testsuite/src/test/resources/testrealm.json 18(+16 -2)
diff --git a/testsuite/src/test/resources/testrealm.json b/testsuite/src/test/resources/testrealm.json
index 3161c1f..0ea2810 100755
--- a/testsuite/src/test/resources/testrealm.json
+++ b/testsuite/src/test/resources/testrealm.json
@@ -17,7 +17,7 @@
"users" : [
{
"username" : "bburke@redhat.com",
- "status": "ENABLED",
+ "enabled": true,
"email" : "bburke@redhat.com",
"credentials" : [
{ "type" : "password",
@@ -25,8 +25,18 @@
]
},
{
+ "username" : "reset@pass.com",
+ "enabled": true,
+ "requiredActions" : [ "RESET_PASSWORD" ],
+ "email" : "reset@pass.com",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "temp-password" }
+ ]
+ },
+ {
"username" : "third-party",
- "status": "ENABLED",
+ "enabled": true,
"credentials" : [
{ "type" : "password",
"value" : "password" }
@@ -49,6 +59,10 @@
"roles": ["user"]
},
{
+ "username": "reset@pass.com",
+ "roles": ["user"]
+ },
+ {
"username": "third-party",
"roles": ["KEYCLOAK_IDENTITY_REQUESTER"]
}
diff --git a/testsuite/src/test/resources/testrealm-email.json b/testsuite/src/test/resources/testrealm-email.json
index 49340fd..79e1a94 100755
--- a/testsuite/src/test/resources/testrealm-email.json
+++ b/testsuite/src/test/resources/testrealm-email.json
@@ -17,7 +17,7 @@
"users" : [
{
"username" : "bburke@redhat.com",
- "status": "ENABLED",
+ "enabled": true,
"email" : "bburke@redhat.com",
"credentials" : [
{ "type" : "password",
@@ -26,7 +26,7 @@
},
{
"username" : "third-party",
- "status": "ENABLED",
+ "enabled": true,
"credentials" : [
{ "type" : "password",
"value" : "password" }
diff --git a/testsuite/src/test/resources/testrealm-totp.json b/testsuite/src/test/resources/testrealm-totp.json
index 014339f..d73f805 100755
--- a/testsuite/src/test/resources/testrealm-totp.json
+++ b/testsuite/src/test/resources/testrealm-totp.json
@@ -16,7 +16,7 @@
"users" : [
{
"username" : "bburke@redhat.com",
- "status": "ENABLED",
+ "enabled": true,
"email" : "bburke@redhat.com",
"credentials" : [
{ "type" : "password",
@@ -25,7 +25,7 @@
},
{
"username" : "third-party",
- "status": "ENABLED",
+ "enabled": true,
"credentials" : [
{ "type" : "password",
"value" : "password" }