KEYCLOAK-8495: Account REST Svc doesn't require acct roles
This commit is contained in:
parent
a7f57c7e23
commit
3ed77825a2
3 changed files with 49 additions and 2 deletions
|
@ -19,7 +19,9 @@ import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
import org.keycloak.models.AccountRoles;
|
||||||
import org.keycloak.models.ModelException;
|
import org.keycloak.models.ModelException;
|
||||||
|
import org.keycloak.services.managers.Auth;
|
||||||
import org.keycloak.services.messages.Messages;
|
import org.keycloak.services.messages.Messages;
|
||||||
|
|
||||||
public class AccountCredentialResource {
|
public class AccountCredentialResource {
|
||||||
|
@ -28,11 +30,13 @@ public class AccountCredentialResource {
|
||||||
private final EventBuilder event;
|
private final EventBuilder event;
|
||||||
private final UserModel user;
|
private final UserModel user;
|
||||||
private final RealmModel realm;
|
private final RealmModel realm;
|
||||||
|
private Auth auth;
|
||||||
|
|
||||||
public AccountCredentialResource(KeycloakSession session, EventBuilder event, UserModel user) {
|
public AccountCredentialResource(KeycloakSession session, EventBuilder event, UserModel user, Auth auth) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.event = event;
|
this.event = event;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
this.auth = auth;
|
||||||
realm = session.getContext().getRealm();
|
realm = session.getContext().getRealm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +44,8 @@ public class AccountCredentialResource {
|
||||||
@Path("password")
|
@Path("password")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public PasswordDetails passwordDetails() {
|
public PasswordDetails passwordDetails() {
|
||||||
|
auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE);
|
||||||
|
|
||||||
PasswordCredentialProvider passwordProvider = (PasswordCredentialProvider) session.getProvider(CredentialProvider.class, PasswordCredentialProviderFactory.PROVIDER_ID);
|
PasswordCredentialProvider passwordProvider = (PasswordCredentialProvider) session.getProvider(CredentialProvider.class, PasswordCredentialProviderFactory.PROVIDER_ID);
|
||||||
CredentialModel password = passwordProvider.getPassword(realm, user);
|
CredentialModel password = passwordProvider.getPassword(realm, user);
|
||||||
|
|
||||||
|
@ -58,6 +64,8 @@ public class AccountCredentialResource {
|
||||||
@Path("password")
|
@Path("password")
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
public Response passwordUpdate(PasswordUpdate update) {
|
public Response passwordUpdate(PasswordUpdate update) {
|
||||||
|
auth.require(AccountRoles.MANAGE_ACCOUNT);
|
||||||
|
|
||||||
event.event(EventType.UPDATE_PASSWORD);
|
event.event(EventType.UPDATE_PASSWORD);
|
||||||
|
|
||||||
UserCredentialModel cred = UserCredentialModel.password(update.getCurrentPassword());
|
UserCredentialModel cred = UserCredentialModel.password(update.getCurrentPassword());
|
||||||
|
|
|
@ -207,6 +207,8 @@ public class AccountRestService {
|
||||||
@NoCache
|
@NoCache
|
||||||
public Response sessions() {
|
public Response sessions() {
|
||||||
checkAccountApiEnabled();
|
checkAccountApiEnabled();
|
||||||
|
auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE);
|
||||||
|
|
||||||
List<SessionRepresentation> reps = new LinkedList<>();
|
List<SessionRepresentation> reps = new LinkedList<>();
|
||||||
|
|
||||||
List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, user);
|
List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, user);
|
||||||
|
@ -245,6 +247,8 @@ public class AccountRestService {
|
||||||
@NoCache
|
@NoCache
|
||||||
public Response sessionsLogout(@QueryParam("current") boolean removeCurrent) {
|
public Response sessionsLogout(@QueryParam("current") boolean removeCurrent) {
|
||||||
checkAccountApiEnabled();
|
checkAccountApiEnabled();
|
||||||
|
auth.require(AccountRoles.MANAGE_ACCOUNT);
|
||||||
|
|
||||||
UserSessionModel userSession = auth.getSession();
|
UserSessionModel userSession = auth.getSession();
|
||||||
|
|
||||||
List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
|
List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
|
||||||
|
@ -269,6 +273,8 @@ public class AccountRestService {
|
||||||
@NoCache
|
@NoCache
|
||||||
public Response sessionLogout(@QueryParam("id") String id) {
|
public Response sessionLogout(@QueryParam("id") String id) {
|
||||||
checkAccountApiEnabled();
|
checkAccountApiEnabled();
|
||||||
|
auth.require(AccountRoles.MANAGE_ACCOUNT);
|
||||||
|
|
||||||
UserSessionModel userSession = session.sessions().getUserSession(realm, id);
|
UserSessionModel userSession = session.sessions().getUserSession(realm, id);
|
||||||
if (userSession != null && userSession.getUser().equals(user)) {
|
if (userSession != null && userSession.getUser().equals(user)) {
|
||||||
AuthenticationManager.backchannelLogout(session, userSession, true);
|
AuthenticationManager.backchannelLogout(session, userSession, true);
|
||||||
|
@ -279,7 +285,7 @@ public class AccountRestService {
|
||||||
@Path("/credentials")
|
@Path("/credentials")
|
||||||
public AccountCredentialResource credentials() {
|
public AccountCredentialResource credentials() {
|
||||||
checkAccountApiEnabled();
|
checkAccountApiEnabled();
|
||||||
return new AccountCredentialResource(session, event, user);
|
return new AccountCredentialResource(session, event, user, auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Federated identities
|
// TODO Federated identities
|
||||||
|
|
|
@ -45,6 +45,7 @@ import static org.junit.Assert.*;
|
||||||
import org.keycloak.services.messages.Messages;
|
import org.keycloak.services.messages.Messages;
|
||||||
|
|
||||||
import static org.keycloak.common.Profile.Feature.ACCOUNT_API;
|
import static org.keycloak.common.Profile.Feature.ACCOUNT_API;
|
||||||
|
import org.keycloak.services.resources.account.AccountCredentialResource.PasswordUpdate;
|
||||||
import static org.keycloak.testsuite.ProfileAssume.assumeFeatureEnabled;
|
import static org.keycloak.testsuite.ProfileAssume.assumeFeatureEnabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -220,6 +221,38 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
||||||
// Update with read only
|
// Update with read only
|
||||||
assertEquals(403, SimpleHttp.doPost(getAccountUrl(null), client).auth(viewToken.getToken()).json(new UserRepresentation()).asStatus());
|
assertEquals(403, SimpleHttp.doPost(getAccountUrl(null), client).auth(viewToken.getToken()).json(new UserRepresentation()).asStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProfilePreviewPermissions() throws IOException {
|
||||||
|
assumeFeatureEnabled(ACCOUNT_API);
|
||||||
|
|
||||||
|
TokenUtil noaccessToken = new TokenUtil("no-account-access", "password");
|
||||||
|
TokenUtil viewToken = new TokenUtil("view-account-access", "password");
|
||||||
|
|
||||||
|
// Read sessions with no access
|
||||||
|
assertEquals(403, SimpleHttp.doGet(getAccountUrl("sessions"), client).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||||
|
|
||||||
|
// Delete all sessions with no access
|
||||||
|
assertEquals(403, SimpleHttp.doDelete(getAccountUrl("sessions"), client).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||||
|
|
||||||
|
// Delete all sessions with read only
|
||||||
|
assertEquals(403, SimpleHttp.doDelete(getAccountUrl("sessions"), client).header("Accept", "application/json").auth(viewToken.getToken()).asStatus());
|
||||||
|
|
||||||
|
// Delete single session with no access
|
||||||
|
assertEquals(403, SimpleHttp.doDelete(getAccountUrl("session?id=bogusId"), client).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||||
|
|
||||||
|
// Delete single session with read only
|
||||||
|
assertEquals(403, SimpleHttp.doDelete(getAccountUrl("session?id=bogusId"), client).header("Accept", "application/json").auth(viewToken.getToken()).asStatus());
|
||||||
|
|
||||||
|
// Read password details with no access
|
||||||
|
assertEquals(403, SimpleHttp.doGet(getAccountUrl("credentials/password"), client).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||||
|
|
||||||
|
// Update password with no access
|
||||||
|
assertEquals(403, SimpleHttp.doPost(getAccountUrl("credentials/password"), client).auth(noaccessToken.getToken()).json(new PasswordUpdate()).asStatus());
|
||||||
|
|
||||||
|
// Update password with read only
|
||||||
|
assertEquals(403, SimpleHttp.doPost(getAccountUrl("credentials/password"), client).auth(viewToken.getToken()).json(new PasswordUpdate()).asStatus());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateProfilePermissions() throws IOException {
|
public void testUpdateProfilePermissions() throws IOException {
|
||||||
|
|
Loading…
Reference in a new issue