keycloak-uncached

Details

diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountCredentialResource.java b/services/src/main/java/org/keycloak/services/resources/account/AccountCredentialResource.java
index 9078987..cbd5054 100644
--- a/services/src/main/java/org/keycloak/services/resources/account/AccountCredentialResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/account/AccountCredentialResource.java
@@ -19,7 +19,9 @@ import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.Response;
+import org.keycloak.models.AccountRoles;
 import org.keycloak.models.ModelException;
+import org.keycloak.services.managers.Auth;
 import org.keycloak.services.messages.Messages;
 
 public class AccountCredentialResource {
@@ -28,11 +30,13 @@ public class AccountCredentialResource {
     private final EventBuilder event;
     private final UserModel user;
     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.event = event;
         this.user = user;
+        this.auth = auth;
         realm = session.getContext().getRealm();
     }
 
@@ -40,6 +44,8 @@ public class AccountCredentialResource {
     @Path("password")
     @Produces(MediaType.APPLICATION_JSON)
     public PasswordDetails passwordDetails() {
+        auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE);
+        
         PasswordCredentialProvider passwordProvider = (PasswordCredentialProvider) session.getProvider(CredentialProvider.class, PasswordCredentialProviderFactory.PROVIDER_ID);
         CredentialModel password = passwordProvider.getPassword(realm, user);
 
@@ -58,6 +64,8 @@ public class AccountCredentialResource {
     @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());
diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java b/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java
index 72fe538..ae298b3 100755
--- a/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java
+++ b/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java
@@ -207,6 +207,8 @@ public class AccountRestService {
     @NoCache
     public Response sessions() {
         checkAccountApiEnabled();
+        auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE);
+        
         List<SessionRepresentation> reps = new LinkedList<>();
 
         List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, user);
@@ -245,6 +247,8 @@ public class AccountRestService {
     @NoCache
     public Response sessionsLogout(@QueryParam("current") boolean removeCurrent) {
         checkAccountApiEnabled();
+        auth.require(AccountRoles.MANAGE_ACCOUNT);
+        
         UserSessionModel userSession = auth.getSession();
 
         List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
@@ -269,6 +273,8 @@ public class AccountRestService {
     @NoCache
     public Response sessionLogout(@QueryParam("id") String id) {
         checkAccountApiEnabled();
+        auth.require(AccountRoles.MANAGE_ACCOUNT);
+        
         UserSessionModel userSession = session.sessions().getUserSession(realm, id);
         if (userSession != null && userSession.getUser().equals(user)) {
             AuthenticationManager.backchannelLogout(session, userSession, true);
@@ -279,7 +285,7 @@ public class AccountRestService {
     @Path("/credentials")
     public AccountCredentialResource credentials() {
         checkAccountApiEnabled();
-        return new AccountCredentialResource(session, event, user);
+        return new AccountCredentialResource(session, event, user, auth);
     }
 
    // TODO Federated identities
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java
index 0cbda1b..7daccb0 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java
@@ -45,6 +45,7 @@ import static org.junit.Assert.*;
 import org.keycloak.services.messages.Messages;
 
 import static org.keycloak.common.Profile.Feature.ACCOUNT_API;
+import org.keycloak.services.resources.account.AccountCredentialResource.PasswordUpdate;
 import static org.keycloak.testsuite.ProfileAssume.assumeFeatureEnabled;
 
 /**
@@ -220,6 +221,38 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
         // Update with read only
         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
     public void testUpdateProfilePermissions() throws IOException {