KEYCLOAK-15395. Removed totp/remove (DELETE) and credentials/password (GET, POST) endpoints.
This commit is contained in:
parent
af2f18449b
commit
2572b1464b
3 changed files with 2 additions and 213 deletions
|
@ -9,11 +9,8 @@ import org.keycloak.credential.CredentialModel;
|
|||
import org.keycloak.credential.CredentialProvider;
|
||||
import org.keycloak.credential.CredentialTypeMetadata;
|
||||
import org.keycloak.credential.CredentialTypeMetadataContext;
|
||||
import org.keycloak.credential.PasswordCredentialProvider;
|
||||
import org.keycloak.credential.PasswordCredentialProviderFactory;
|
||||
import org.keycloak.credential.UserCredentialStoreManager;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
|
@ -28,7 +25,6 @@ import javax.ws.rs.Consumes;
|
|||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
|
@ -57,14 +53,12 @@ public class AccountCredentialResource {
|
|||
|
||||
|
||||
private final KeycloakSession session;
|
||||
private final EventBuilder event;
|
||||
private final UserModel user;
|
||||
private final RealmModel realm;
|
||||
private Auth auth;
|
||||
|
||||
public AccountCredentialResource(KeycloakSession session, EventBuilder event, UserModel user, Auth auth) {
|
||||
public AccountCredentialResource(KeycloakSession session, UserModel user, Auth auth) {
|
||||
this.session = session;
|
||||
this.event = event;
|
||||
this.user = user;
|
||||
this.auth = auth;
|
||||
realm = session.getContext().getRealm();
|
||||
|
@ -341,116 +335,4 @@ public class AccountCredentialResource {
|
|||
// session.userCredentialManager().moveCredentialTo(realm, user, credentialId, newPreviousCredentialId);
|
||||
// }
|
||||
|
||||
@GET
|
||||
@Path("password")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public PasswordDetails passwordDetails() throws IOException {
|
||||
auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE);
|
||||
|
||||
PasswordCredentialProvider passwordProvider = (PasswordCredentialProvider) session.getProvider(CredentialProvider.class, PasswordCredentialProviderFactory.PROVIDER_ID);
|
||||
CredentialModel password = passwordProvider.getPassword(realm, user);
|
||||
|
||||
PasswordDetails details = new PasswordDetails();
|
||||
if (password != null) {
|
||||
details.setRegistered(true);
|
||||
Long createdDate = password.getCreatedDate();
|
||||
if (createdDate != null) {
|
||||
details.setLastUpdate(createdDate);
|
||||
}
|
||||
} else {
|
||||
details.setRegistered(false);
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("password")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Response passwordUpdate(PasswordUpdate update) {
|
||||
auth.require(AccountRoles.MANAGE_ACCOUNT);
|
||||
|
||||
event.event(EventType.UPDATE_PASSWORD);
|
||||
|
||||
UserCredentialModel cred = UserCredentialModel.password(update.getCurrentPassword());
|
||||
if (!session.userCredentialManager().isValid(realm, user, cred)) {
|
||||
event.error(org.keycloak.events.Errors.INVALID_USER_CREDENTIALS);
|
||||
return ErrorResponse.error(Messages.INVALID_PASSWORD_EXISTING, Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (update.getNewPassword() == null) {
|
||||
return ErrorResponse.error(Messages.INVALID_PASSWORD_EXISTING, Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
String confirmation = update.getConfirmation();
|
||||
if ((confirmation != null) && !update.getNewPassword().equals(confirmation)) {
|
||||
return ErrorResponse.error(Messages.NOTMATCH_PASSWORD, Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
try {
|
||||
session.userCredentialManager().updateCredential(realm, user, UserCredentialModel.password(update.getNewPassword(), false));
|
||||
} catch (ModelException e) {
|
||||
return ErrorResponse.error(e.getMessage(), e.getParameters(), Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
event.client(auth.getClient()).user(auth.getUser()).success();
|
||||
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
public static class PasswordDetails {
|
||||
|
||||
private boolean registered;
|
||||
private long lastUpdate;
|
||||
|
||||
public boolean isRegistered() {
|
||||
return registered;
|
||||
}
|
||||
|
||||
public void setRegistered(boolean registered) {
|
||||
this.registered = registered;
|
||||
}
|
||||
|
||||
public long getLastUpdate() {
|
||||
return lastUpdate;
|
||||
}
|
||||
|
||||
public void setLastUpdate(long lastUpdate) {
|
||||
this.lastUpdate = lastUpdate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class PasswordUpdate {
|
||||
|
||||
private String currentPassword;
|
||||
private String newPassword;
|
||||
private String confirmation;
|
||||
|
||||
public String getCurrentPassword() {
|
||||
return currentPassword;
|
||||
}
|
||||
|
||||
public void setCurrentPassword(String currentPassword) {
|
||||
this.currentPassword = currentPassword;
|
||||
}
|
||||
|
||||
public String getNewPassword() {
|
||||
return newPassword;
|
||||
}
|
||||
|
||||
public void setNewPassword(String newPassword) {
|
||||
this.newPassword = newPassword;
|
||||
}
|
||||
|
||||
public String getConfirmation() {
|
||||
return confirmation;
|
||||
}
|
||||
|
||||
public void setConfirmation(String confirmation) {
|
||||
this.confirmation = confirmation;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -63,7 +63,6 @@ import javax.ws.rs.core.HttpHeaders;
|
|||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
|
@ -75,7 +74,6 @@ import java.util.Set;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.theme.Theme;
|
||||
|
||||
/**
|
||||
|
@ -265,7 +263,7 @@ public class AccountRestService {
|
|||
@Path("/credentials")
|
||||
public AccountCredentialResource credentials() {
|
||||
checkAccountApiEnabled();
|
||||
return new AccountCredentialResource(session, event, user, auth);
|
||||
return new AccountCredentialResource(session, user, auth);
|
||||
}
|
||||
|
||||
@Path("/resources")
|
||||
|
@ -396,17 +394,6 @@ public class AccountRestService {
|
|||
return upsert(clientId, consent);
|
||||
}
|
||||
|
||||
@Path("/totp/remove")
|
||||
@DELETE
|
||||
public Response removeTOTP() {
|
||||
auth.require(AccountRoles.MANAGE_ACCOUNT);
|
||||
|
||||
session.userCredentialManager().disableCredentialType(realm, user, CredentialModel.OTP);
|
||||
event.event(EventType.REMOVE_TOTP).client(auth.getClient()).user(auth.getUser()).success();
|
||||
|
||||
return Cors.add(request, Response.noContent()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or updates the consent of the given, requested consent for
|
||||
* the client with the given client id. Returns the appropriate REST response.
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package org.keycloak.testsuite.account;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
|
@ -31,7 +30,6 @@ import org.keycloak.common.Profile;
|
|||
import org.keycloak.common.enums.AccountRestApiVersion;
|
||||
import org.keycloak.common.util.ObjectUtil;
|
||||
import org.keycloak.credential.CredentialTypeMetadata;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.credential.OTPCredentialModel;
|
||||
|
@ -54,7 +52,6 @@ import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
|||
import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.account.AccountCredentialResource;
|
||||
import org.keycloak.services.resources.account.AccountCredentialResource.PasswordUpdate;
|
||||
import org.keycloak.services.util.ResolveRelative;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.admin.authentication.AbstractAuthenticationTest;
|
||||
|
@ -259,21 +256,6 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
assertEquals(403, SimpleHttp.doPost(getAccountUrl(null), httpClient).auth(viewToken.getToken()).json(new UserRepresentation()).asStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProfilePreviewPermissions() throws IOException {
|
||||
TokenUtil noaccessToken = new TokenUtil("no-account-access", "password");
|
||||
TokenUtil viewToken = new TokenUtil("view-account-access", "password");
|
||||
|
||||
// Read password details with no access
|
||||
assertEquals(403, SimpleHttp.doGet(getAccountUrl("credentials/password"), httpClient).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||
|
||||
// Update password with no access
|
||||
assertEquals(403, SimpleHttp.doPost(getAccountUrl("credentials/password"), httpClient).auth(noaccessToken.getToken()).json(new PasswordUpdate()).asStatus());
|
||||
|
||||
// Update password with read only
|
||||
assertEquals(403, SimpleHttp.doPost(getAccountUrl("credentials/password"), httpClient).auth(viewToken.getToken()).json(new PasswordUpdate()).asStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateProfilePermissions() throws IOException {
|
||||
TokenUtil noaccessToken = new TokenUtil("no-account-access", "password");
|
||||
|
@ -285,68 +267,6 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
assertEquals(200, status);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPasswordDetails() throws IOException {
|
||||
getPasswordDetails();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostPasswordUpdate() throws IOException {
|
||||
//Get the time of lastUpdate
|
||||
AccountCredentialResource.PasswordDetails initialDetails = getPasswordDetails();
|
||||
|
||||
// ignore login event
|
||||
events.poll();
|
||||
|
||||
//Change the password
|
||||
updatePassword("password", "Str0ng3rP4ssw0rd", 204);
|
||||
|
||||
//Get the new value for lastUpdate
|
||||
AccountCredentialResource.PasswordDetails updatedDetails = getPasswordDetails();
|
||||
assertTrue(initialDetails.getLastUpdate() < updatedDetails.getLastUpdate());
|
||||
Assert.assertEquals(EventType.UPDATE_PASSWORD.name(), events.poll().getType());
|
||||
|
||||
//Try to change password again; should fail as current password is incorrect
|
||||
updatePassword("password", "Str0ng3rP4ssw0rd", 400);
|
||||
|
||||
//Verify that lastUpdate hasn't changed
|
||||
AccountCredentialResource.PasswordDetails finalDetails = getPasswordDetails();
|
||||
assertEquals(updatedDetails.getLastUpdate(), finalDetails.getLastUpdate());
|
||||
|
||||
//Change the password back
|
||||
updatePassword("Str0ng3rP4ssw0rd", "password", 204);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPasswordConfirmation() throws IOException {
|
||||
updatePassword("password", "Str0ng3rP4ssw0rd", "confirmationDoesNotMatch", 400);
|
||||
|
||||
updatePassword("password", "Str0ng3rP4ssw0rd", "Str0ng3rP4ssw0rd", 204);
|
||||
|
||||
//Change the password back
|
||||
updatePassword("Str0ng3rP4ssw0rd", "password", 204);
|
||||
}
|
||||
|
||||
private AccountCredentialResource.PasswordDetails getPasswordDetails() throws IOException {
|
||||
AccountCredentialResource.PasswordDetails details = SimpleHttp.doGet(getAccountUrl("credentials/password"), httpClient).auth(tokenUtil.getToken()).asJson(new TypeReference<AccountCredentialResource.PasswordDetails>() {});
|
||||
assertTrue(details.isRegistered());
|
||||
assertNotNull(details.getLastUpdate());
|
||||
return details;
|
||||
}
|
||||
|
||||
private void updatePassword(String currentPass, String newPass, int expectedStatus) throws IOException {
|
||||
updatePassword(currentPass, newPass, null, expectedStatus);
|
||||
}
|
||||
|
||||
private void updatePassword(String currentPass, String newPass, String confirmation, int expectedStatus) throws IOException {
|
||||
AccountCredentialResource.PasswordUpdate passwordUpdate = new AccountCredentialResource.PasswordUpdate();
|
||||
passwordUpdate.setCurrentPassword(currentPass);
|
||||
passwordUpdate.setNewPassword(newPass);
|
||||
passwordUpdate.setConfirmation(confirmation);
|
||||
int status = SimpleHttp.doPost(getAccountUrl("credentials/password"), httpClient).auth(tokenUtil.getToken()).json(passwordUpdate).asStatus();
|
||||
assertEquals(expectedStatus, status);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCredentialsGet() throws IOException {
|
||||
configureBrowserFlowWithWebAuthnAuthenticator("browser-webauthn");
|
||||
|
|
Loading…
Reference in a new issue