changeset 199:0fe5182f3b1b

Add realms authorization implementation This patch adds the realms authorization system using Keycloak. The jvm-gc service is updated to follow the realms authorization specification. See the review thread and the link below for more information. http://icedtea.classpath.org/pipermail/thermostat/2017-June/023560.html Reviewed-by: jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-June/023674.html Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-July/023985.html
author Jie Kang <jkang@redhat.com>
date Mon, 17 Jul 2017 11:22:45 -0400
parents f89ae85f9f14
children 4d6991d2839d
files common/core/pom.xml common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/Action.java common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RealmAuthorizer.java common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/Role.java common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RoleFactory.java common/core/src/test/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RealmAuthorizerTest.java common/core/src/test/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RoleFactoryTest.java common/core/src/test/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RoleTest.java common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/executor/MongoExecutor.java common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/filters/MongoRequestFilters.java common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/keycloak/KeycloakFields.java common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/response/MongoResponseBuilder.java common/mongodb/src/test/java/com/redhat/thermostat/gateway/common/mongodb/filters/MongoRequestFiltersTest.java common/mongodb/src/test/java/com/redhat/thermostat/gateway/common/mongodb/response/MongoResponseBuilderTest.java server/pom.xml server/src/main/java/com/redhat/thermostat/gateway/server/auth/keycloak/KeycloakRequestFilter.java server/src/main/java/com/redhat/thermostat/gateway/server/services/WebArchiveCoreService.java server/src/test/java/com/redhat/thermostat/gateway/server/auth/keycloak/KeycloakRequestFilterTest.java server/src/test/java/com/redhat/thermostat/gateway/server/services/WebArchiveCoreServiceTest.java services/jvm-gc/pom.xml services/jvm-gc/src/main/java/com/redhat/thermostat/service/jvm/gc/JvmGcHttpHandler.java services/jvm-gc/src/main/resources/jvm-gc-swagger.yaml services/jvm-memory/src/main/java/com/redhat/thermostat/service/jvm/memory/JvmMemoryHttpHandler.java services/system-memory/src/main/java/com/redhat/thermostat/service/system/memory/mongo/MongoStorageHandler.java tests/integration-tests/src/test/java/com/redhat/thermostat/gateway/service/jvm/gc/JvmGcServiceIntegrationTest.java tests/test-utils/src/main/java/com/redhat/thermostat/gateway/tests/utils/MongodTestUtil.java
diffstat 26 files changed, 1468 insertions(+), 50 deletions(-) [+]
line wrap: on
line diff
--- a/common/core/pom.xml	Mon Jul 17 11:42:24 2017 +0200
+++ b/common/core/pom.xml	Mon Jul 17 11:22:45 2017 -0400
@@ -67,6 +67,13 @@
             <version>${javax-rs-api.version}</version>
         </dependency>
 
+        <!-- Keycloak dependencies -->
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${keycloak.version}</version>
+        </dependency>
+
         <!-- test scoped deps -->
         <dependency>
             <groupId>junit</groupId>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/Action.java	Mon Jul 17 11:22:45 2017 -0400
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.gateway.common.core.auth.keycloak;
+
+public class Action {
+    public static final String READ = "r";
+    public static final String WRITE = "w";
+    public static final String UPDATE = "u";
+    public static final String DELETE = "d";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RealmAuthorizer.java	Mon Jul 17 11:22:45 2017 -0400
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.gateway.common.core.auth.keycloak;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.keycloak.KeycloakSecurityContext;
+
+public class RealmAuthorizer {
+
+    public static final String REALMS_HEADER = "X-Thermostat-Realms";
+
+    private final Set<Role> clientRoles;
+    private final RoleFactory roleFactory = new RoleFactory();
+
+    public RealmAuthorizer(HttpServletRequest httpServletRequest) throws ServletException {
+        Set<Role> roles = buildClientRoles(httpServletRequest);
+        this.clientRoles = Collections.unmodifiableSet(roles);
+    }
+
+    public boolean readable() {
+        return checkActionExists(Action.READ);
+    }
+
+    public boolean writable() {
+        return checkActionExists(Action.WRITE);
+    }
+
+    public boolean updatable() {
+        return checkActionExists(Action.UPDATE);
+    }
+
+    public boolean deletable() {
+        return checkActionExists(Action.DELETE);
+    }
+
+    public boolean checkActionExists(String action) {
+        for (Role role : clientRoles) {
+            if (role.getActions().contains(action)) {
+                return  true;
+            }
+        }
+        return false;
+    }
+
+    public Set<String> getReadableRealms() {
+        return getRealmsWithAction(Action.READ);
+    }
+
+    public Set<String> getWritableRealms() {
+        return getRealmsWithAction(Action.WRITE);
+    }
+    public Set<String> getUpdatableRealms() {
+        return getRealmsWithAction(Action.UPDATE);
+    }
+    public Set<String> getDeletableRealms() {
+        return getRealmsWithAction(Action.DELETE);
+    }
+
+    public Set<String> getRealmsWithAction(String action) {
+        Set<String> realms = new HashSet<>();
+        for (Role role : clientRoles) {
+            if (role.getActions().contains(action)) {
+                realms.add(role.getRealm());
+            }
+        }
+        return Collections.unmodifiableSet(realms);
+    }
+
+    protected Set<Role> getAllRoles() {
+        return clientRoles;
+    }
+
+    private Set<Role> buildClientRoles(HttpServletRequest httpServletRequest) throws ServletException {
+        Set<Role> keycloakRoles = buildKeycloakRoles(httpServletRequest);
+
+        String realmsHeader = httpServletRequest.getHeader(REALMS_HEADER);
+        if (realmsHeader != null) {
+            return buildClientPreferredRoles(keycloakRoles, realmsHeader);
+        }
+
+        return keycloakRoles;
+    }
+
+    /**
+     * @return the set of roles from the Keycloak security token
+     */
+    private Set<Role> buildKeycloakRoles(HttpServletRequest httpServletRequest) {
+        Set<Role> keycloakRoles = new HashSet<>();
+
+        KeycloakSecurityContext keycloakSecurityContext = (KeycloakSecurityContext) httpServletRequest
+                .getAttribute(KeycloakSecurityContext.class.getName());
+
+        for (String role : keycloakSecurityContext.getToken().getRealmAccess().getRoles()) {
+            if (roleFactory.isValidRole(role)) {
+                keycloakRoles.add(roleFactory.buildRole(role));
+            }
+        }
+
+        return keycloakRoles;
+    }
+
+    /**
+     * Builds a set of roles based on a clients preferred set, provided in a comma separated realms header string
+     * @param trustedRoles : The trusted set of roles that the client has
+     * @param realmsHeader : The REALMS_HEADER value as a string
+     * @return The set of roles that the client has selected
+     * @throws ServletException If realms header contains realms the client does not have or no valid realms
+     */
+    private Set<Role> buildClientPreferredRoles(Set<Role> trustedRoles, String realmsHeader) throws ServletException {
+        realmsHeader = realmsHeader.replaceAll("\\s+", "");
+        Set<String> preferredRealms = new HashSet<>(Arrays.asList(realmsHeader.split(",")));
+        Set<Role> selectedRoles = new HashSet<>();
+
+        for (String preferredRealm : preferredRealms) {
+            boolean found = false;
+            for (Role role : trustedRoles) {
+                if (role.getRealm().equals(preferredRealm)) {
+                    selectedRoles.add(role);
+                    found = true;
+                }
+            }
+            if (!found) {
+                throw new ServletException("Not authorized to access preferred realms.");
+            }
+        }
+
+        if (selectedRoles.size() > 0) {
+            return selectedRoles;
+        } else {
+            throw new ServletException("No realms selected");
+        }
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/Role.java	Mon Jul 17 11:22:45 2017 -0400
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.gateway.common.core.auth.keycloak;
+
+public class Role {
+
+    public static final String ROLE_DELIMITER = "-";
+    public static final String[] RESTRICTED_CHARACTERS = new String[]{","};
+
+    private final String actions;
+    private final String realm;
+
+    public Role(String actions, String realm) {
+        this.actions = actions;
+        this.realm = realm;
+    }
+
+    public String getActions() {
+        return this.actions;
+    }
+
+    public String getRealm() {
+        return this.realm;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RoleFactory.java	Mon Jul 17 11:22:45 2017 -0400
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.gateway.common.core.auth.keycloak;
+
+public class RoleFactory {
+
+    public boolean isValidRole(String role) {
+        if (!role.contains(Role.ROLE_DELIMITER)) {
+            return false;
+        }
+        for (String restrictedCharacter : Role.RESTRICTED_CHARACTERS) {
+            if (role.contains(restrictedCharacter)) {
+                return false;
+            }
+        }
+
+        int index = role.indexOf(Role.ROLE_DELIMITER);
+
+        // Make sure there are characters before and after the role delimiter
+        return index > 0 && index < role.length() - 1;
+    }
+
+    public Role buildRole(String role) {
+
+        int index = role.indexOf(Role.ROLE_DELIMITER);
+        String actions = role.substring(0, index);
+        String realm = role.substring(index + 1);
+
+        return new Role(actions, realm);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/test/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RealmAuthorizerTest.java	Mon Jul 17 11:22:45 2017 -0400
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.gateway.common.core.auth.keycloak;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.representations.AccessToken;
+
+public class RealmAuthorizerTest {
+
+    HttpServletRequest request;
+    AccessToken.Access access;
+
+    @Before
+    public void setup() {
+        request = mock(HttpServletRequest.class);
+        KeycloakSecurityContext keycloakSecurityContext = mock(KeycloakSecurityContext.class);
+        when(request.getAttribute(eq(KeycloakSecurityContext.class.getName()))).thenReturn(keycloakSecurityContext);
+
+        AccessToken accessToken = mock(AccessToken.class);
+        when(keycloakSecurityContext.getToken()).thenReturn(accessToken);
+
+        access = mock(AccessToken.Access.class);
+        when(accessToken.getRealmAccess()).thenReturn(access);
+    }
+
+    @Test
+    public void testBuildSingleRealm() throws ServletException {
+        String[] roles = new String[]{"a-realm"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+
+        Set<String> realms = realmAuthorizer.getRealmsWithAction("a");
+        assertTrue(realms.contains("realm"));
+    }
+
+    @Test
+    public void testBuildMultipleRealms() throws ServletException {
+        String[] roles = new String[]{"a-realm", "b-another"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+
+        Set<String> realms = realmAuthorizer.getRealmsWithAction("a");
+        assertTrue(realms.contains("realm"));
+        assertFalse(realms.contains("another"));
+
+        realms = realmAuthorizer.getRealmsWithAction("b");
+        assertTrue(realms.contains("another"));
+        assertFalse(realms.contains("realm"));
+    }
+
+    @Test
+    public void testBuildMultipleRealmsSameAction() throws ServletException {
+        String[] roles = new String[]{"r-realm", "w-realm"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+
+
+        Set<String> realms = realmAuthorizer.getRealmsWithAction("r");
+        assertTrue(realms.contains("realm"));
+
+        realms = realmAuthorizer.getRealmsWithAction("w");
+        assertTrue(realms.contains("realm"));
+    }
+
+    @Test
+    public void testBuildRoleWithoutRealm() throws ServletException {
+        String[] roles = new String[]{"a-"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+        Set<Role> realms = realmAuthorizer.getAllRoles();
+        assertTrue(realms.isEmpty());
+    }
+
+    @Test
+    public void testBuildInvalidRoleWithoutAction() throws ServletException {
+        String[] roles = new String[]{"-realm"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+        Set<Role> realms = realmAuthorizer.getAllRoles();
+        assertTrue(realms.isEmpty());
+    }
+
+
+    private void setupRealms() {
+        String[] roles = new String[]{"r-read", "w-write", "d-delete", "u-update"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+    }
+
+    @Test
+    public void testReadable() throws ServletException {
+        setupRealms();
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+        assertTrue(realmAuthorizer.readable());
+
+        Set<String> realms = realmAuthorizer.getReadableRealms();
+        assertEquals(1, realms.size());
+        assertTrue(realms.contains("read"));
+    }
+
+    @Test
+    public void testWritable() throws ServletException {
+        setupRealms();
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+        assertTrue(realmAuthorizer.writable());
+
+        Set<String> realms = realmAuthorizer.getWritableRealms();
+        assertEquals(1, realms.size());
+        assertTrue(realms.contains("write"));
+    }
+
+    @Test
+    public void testUpdatable() throws ServletException {
+        setupRealms();
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+        assertTrue(realmAuthorizer.updatable());
+
+        Set<String> realms = realmAuthorizer.getUpdatableRealms();
+        assertEquals(1, realms.size());
+        assertTrue(realms.contains("update"));
+    }
+
+    @Test
+    public void testDeletable() throws ServletException {
+        setupRealms();
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+        assertTrue(realmAuthorizer.deletable());
+
+        Set<String> realms = realmAuthorizer.getDeletableRealms();
+        assertEquals(1, realms.size());
+        assertTrue(realms.contains("delete"));
+    }
+
+    @Test
+    public void testNotReadable() throws ServletException {
+        String[] roles = new String[]{"w-write", "d-delete", "u-update"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+        assertFalse(realmAuthorizer.readable());
+
+        Set<String> realms = realmAuthorizer.getReadableRealms();
+        assertEquals(0, realms.size());
+    }
+
+    @Test
+    public void testNotWritable() throws ServletException {
+        String[] roles = new String[]{"r-read", "d-delete", "u-update"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+        assertFalse(realmAuthorizer.writable());
+
+        Set<String> realms = realmAuthorizer.getWritableRealms();
+        assertEquals(0, realms.size());
+    }
+
+    @Test
+    public void testNotUpdatable() throws ServletException {
+        String[] roles = new String[]{"w-write", "d-delete", "r-read"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+        assertFalse(realmAuthorizer.updatable());
+
+        Set<String> realms = realmAuthorizer.getUpdatableRealms();
+        assertEquals(0, realms.size());
+    }
+
+    @Test
+    public void testNotDeletable() throws ServletException {
+        String[] roles = new String[]{"w-write", "r-read", "u-update"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+        assertFalse(realmAuthorizer.deletable());
+
+        Set<String> realms = realmAuthorizer.getDeletableRealms();
+        assertEquals(0, realms.size());
+    }
+
+    @Test
+    public void testRealmsHeaderSubset() throws ServletException {
+        String[] roles = new String[]{"w-write", "r-read", "u-update"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+
+        when(request.getHeader(eq("X-Thermostat-Realms"))).thenReturn("read,update");
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+        assertEquals(1, realmAuthorizer.getReadableRealms().size());
+        assertEquals(1, realmAuthorizer.getUpdatableRealms().size());
+
+        assertEquals(0, realmAuthorizer.getWritableRealms().size());
+        assertEquals(0, realmAuthorizer.getDeletableRealms().size());
+    }
+
+    @Test (expected = ServletException.class)
+    public void testRealmsHeaderSuperset() throws ServletException {
+        String[] roles = new String[]{"r-read,","u-update"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+
+        when(request.getHeader(eq("X-Thermostat-Realms"))).thenReturn("read,update,other");
+
+        new RealmAuthorizer(request);
+    }
+
+    @Test
+    public void testRealmsHeaderWhitespace() throws ServletException {
+        String[] roles = new String[]{"w-write", "r-read", "u-update"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+
+        when(request.getHeader(eq("X-Thermostat-Realms"))).thenReturn("  read,  update , write");
+
+        RealmAuthorizer realmAuthorizer = new RealmAuthorizer(request);
+        assertEquals(1, realmAuthorizer.getReadableRealms().size());
+        assertEquals(1, realmAuthorizer.getUpdatableRealms().size());
+        assertEquals(1, realmAuthorizer.getWritableRealms().size());
+
+        assertEquals(0, realmAuthorizer.getDeletableRealms().size());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/test/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RoleFactoryTest.java	Mon Jul 17 11:22:45 2017 -0400
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.gateway.common.core.auth.keycloak;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class RoleFactoryTest {
+
+    private RoleFactory roleFactory;
+
+    @Before
+    public void setup() {
+        roleFactory = new RoleFactory();
+    }
+
+    @Test
+    public void testValidRole() {
+        String role = "a-role";
+        assertTrue(roleFactory.isValidRole(role));
+
+        Role r = roleFactory.buildRole(role);
+        verifyRole(r, "a", "role");
+    }
+
+    @Test
+    public void testValidRoleWithActions() {
+        String role = "rwd-role";
+
+        assertTrue(roleFactory.isValidRole(role));
+
+        Role r = roleFactory.buildRole(role);
+        verifyRole(r, "rwd", "role");
+    }
+
+    @Test
+    public void testNoActionRole() {
+        String role = "-role";
+        assertFalse(roleFactory.isValidRole(role));
+    }
+
+    @Test
+    public void testNoRealmRole() {
+        String role = "a-";
+        assertFalse(roleFactory.isValidRole(role));
+    }
+
+    @Test
+    public void testHyphenRealm() {
+        String role = "a-realm-with-hyphens";
+        assertTrue(roleFactory.isValidRole(role));
+
+        Role r = roleFactory.buildRole(role);
+        verifyRole(r, "a", "realm-with-hyphens");
+    }
+
+    @Test
+    public void testRealmWithCommaIsInvalid() {
+        String role = "a-invalid,realm";
+        assertFalse(roleFactory.isValidRole(role));
+    }
+
+    private void verifyRole(Role role, String expectedActions, String expectedRole) {
+        assertEquals(expectedActions, role.getActions());
+        assertEquals(expectedRole, role.getRealm());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/test/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RoleTest.java	Mon Jul 17 11:22:45 2017 -0400
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.gateway.common.core.auth.keycloak;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class RoleTest {
+
+    @Test
+    public void testSimpleRole() {
+        Role r = new Role("a", "realm");
+
+        verifyRole(r, "a", "realm");
+    }
+
+    @Test
+    public void testMultipleActionsRole() {
+        Role r = new Role("rw", "realm-1.2-3");
+
+        verifyRole(r, "rw", "realm-1.2-3");
+    }
+
+    private void verifyRole(Role role, String expectedActions, String expectedRole) {
+        assertEquals(expectedActions, role.getActions());
+        assertEquals(expectedRole, role.getRealm());
+    }
+}
--- a/common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/executor/MongoExecutor.java	Mon Jul 17 11:42:24 2017 +0200
+++ b/common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/executor/MongoExecutor.java	Mon Jul 17 11:22:45 2017 -0400
@@ -40,9 +40,11 @@
 import static com.mongodb.client.model.Projections.fields;
 import static com.mongodb.client.model.Projections.include;
 
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 import org.bson.Document;
 import org.bson.conversions.Bson;
@@ -54,27 +56,27 @@
 import com.mongodb.util.JSON;
 import com.redhat.thermostat.gateway.common.mongodb.filters.MongoRequestFilters;
 import com.redhat.thermostat.gateway.common.mongodb.filters.MongoSortFilters;
+import com.redhat.thermostat.gateway.common.mongodb.keycloak.KeycloakFields;
 
 public class MongoExecutor {
-    public MongoDataResultContainer execGetRequest(MongoCollection<Document> collection, Integer limit,
-                                                   Integer offset, String sort, String queries, String projections) {
-        return execGetRequest(collection, limit, offset, sort, buildQueries(queries), projections);
+    public MongoDataResultContainer execGetRequest(MongoCollection<Document> collection, Integer limit, Integer offset,
+                                                   String sort, String queries, String projections,
+                                                   Set<String> realms) throws IOException {
+        return execGetRequest(collection, limit, offset, sort, buildClientQueries(queries), projections, realms);
     }
 
-    public MongoDataResultContainer execGetRequest(MongoCollection<Document> collection, Integer limit,
-                                                   Integer offset, String sort, List<String> queries, String projections) {
+    public MongoDataResultContainer execGetRequest(MongoCollection<Document> collection, Integer limit, Integer offset,
+                                                   String sort, List<String> queries, String projections,
+                                                   Set<String> realms) {
         FindIterable<Document> documents = collection.find();
         MongoDataResultContainer queryDataContainer = new MongoDataResultContainer();
 
-        if (queries != null && !queries.isEmpty()) {
-            final Bson query = MongoRequestFilters.buildQueriesFilter(queries);
-            documents = documents.filter(query);
-            queryDataContainer.setGetReqCount(collection.count(query));
-            queryDataContainer.setRemainingNumQueryDocuments((int) (collection.count(query) - (limit + offset)));
-        } else {
-            queryDataContainer.setGetReqCount(collection.count());
-            queryDataContainer.setRemainingNumQueryDocuments((int) (collection.count() - (limit + offset)));
-        }
+        Bson query = MongoRequestFilters.buildQuery(queries, realms);
+        documents = documents.filter(query);
+
+        long count = collection.count(query);
+        queryDataContainer.setGetReqCount(count);
+        queryDataContainer.setRemainingNumQueryDocuments((int) (count - (limit + offset)));
 
         if (projections != null) {
             List<String> projectionsList = Arrays.asList(projections.split(","));
@@ -90,23 +92,21 @@
         return queryDataContainer;
     }
 
-    public MongoDataResultContainer execPutRequest(MongoCollection<Document> collection, String body, String queries) {
-        return execPutRequest(collection, body, buildQueries(queries));
+    public MongoDataResultContainer execPutRequest(MongoCollection<Document> collection, String body,
+                                                   String queries, Set<String> realms) throws IOException {
+        return execPutRequest(collection, body, buildClientQueries(queries), realms);
     }
 
-    public MongoDataResultContainer execPutRequest(MongoCollection<Document> collection, String body, List<String> queries) {
+    public MongoDataResultContainer execPutRequest(MongoCollection<Document> collection, String body,
+                                                   List<String> queries, Set<String> realms) {
         Document inputDocument = Document.parse(body);
         MongoDataResultContainer metaDataContainer = new MongoDataResultContainer();
 
         Document setDocument = inputDocument.get("set", Document.class);
+        setDocument.remove(KeycloakFields.REALMS_KEY);
         final Bson fields = new Document("$set", setDocument);
 
-        Bson bsonQueries;
-        if (queries != null && !queries.isEmpty()) {
-            bsonQueries = MongoRequestFilters.buildQueriesFilter(queries);
-        } else {
-            bsonQueries = new Document();
-        }
+        final Bson bsonQueries = MongoRequestFilters.buildQuery(queries, realms);
 
         collection.updateMany(bsonQueries, fields);
 
@@ -116,17 +116,18 @@
     }
 
 
-    public MongoDataResultContainer execDeleteRequest(MongoCollection<Document> collection, String queries) {
-        return execDeleteRequest(collection, buildQueries(queries));
+    public MongoDataResultContainer execDeleteRequest(MongoCollection<Document> collection, String queries,
+                                                      Set<String> realms) throws IOException {
+        return execDeleteRequest(collection, buildClientQueries(queries), realms);
     }
 
-    public MongoDataResultContainer execDeleteRequest(MongoCollection<Document> collection, List<String> queries) {
+    public MongoDataResultContainer execDeleteRequest(MongoCollection<Document> collection, List<String> queries,
+                                                      Set<String> realms) {
         MongoDataResultContainer metaDataContainer = new MongoDataResultContainer();
-        if (queries != null && !queries.isEmpty()) {
-            Bson bsonQueries = MongoRequestFilters.buildQueriesFilter(queries);
+        if (queries != null && !queries.isEmpty() || realms != null && !realms.isEmpty()) {
+            Bson bsonQueries = MongoRequestFilters.buildQuery(queries, realms);
+            metaDataContainer.setDeleteReqMatches(collection.count(bsonQueries));
             collection.deleteMany(bsonQueries);
-
-            metaDataContainer.setDeleteReqMatches(collection.count(bsonQueries));
         } else {
             metaDataContainer.setDeleteReqMatches(collection.count());
             collection.drop();
@@ -135,11 +136,21 @@
         return metaDataContainer;
     }
 
-    public MongoDataResultContainer execPostRequest(MongoCollection<DBObject> collection, String body) {
+    public MongoDataResultContainer execPostRequest(MongoCollection<DBObject> collection, String body,
+                                                    Set<String> realms) {
         MongoDataResultContainer metaDataContainer = new MongoDataResultContainer();
 
         if (body.length() > 0) {
             List<DBObject> inputList = (List<DBObject>) JSON.parse(body);
+
+            for (DBObject object : inputList) {
+                object.removeField(KeycloakFields.REALMS_KEY);
+                if (realms != null && !realms.isEmpty())  {
+                    object.put(KeycloakFields.REALMS_KEY, realms);
+                }
+
+            }
+
             collection.insertMany(inputList);
         }
 
@@ -147,9 +158,16 @@
     }
 
 
-    private List<String> buildQueries(String queries) {
+    private List<String> buildClientQueries(String queries) throws IOException {
         if (queries != null) {
-            return Arrays.asList(queries.split(","));
+            List<String> queriesList = Arrays.asList(queries.split(","));
+            for (String query : queriesList) {
+                if (query.startsWith(KeycloakFields.REALMS_KEY)) {
+                    throw new IOException("Cannot query realms property");
+                }
+            }
+
+            return queriesList;
         } else {
             return Collections.emptyList();
         }
--- a/common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/filters/MongoRequestFilters.java	Mon Jul 17 11:42:24 2017 +0200
+++ b/common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/filters/MongoRequestFilters.java	Mon Jul 17 11:22:45 2017 -0400
@@ -36,6 +36,7 @@
 
 package com.redhat.thermostat.gateway.common.mongodb.filters;
 
+import static com.mongodb.client.model.Filters.all;
 import static com.mongodb.client.model.Filters.and;
 import static com.mongodb.client.model.Filters.eq;
 import static com.mongodb.client.model.Filters.gt;
@@ -43,15 +44,20 @@
 import static com.mongodb.client.model.Filters.lt;
 import static com.mongodb.client.model.Filters.lte;
 import static com.mongodb.client.model.Filters.ne;
+import static com.mongodb.client.model.Filters.size;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.bson.Document;
 import org.bson.conversions.Bson;
 import org.bson.json.JsonParseException;
 
+import com.redhat.thermostat.gateway.common.mongodb.keycloak.KeycloakFields;
+
 public class MongoRequestFilters {
     public static Bson buildQueriesFilter(List<String> queries) {
         List<Bson> filters = new ArrayList<>();
@@ -101,4 +107,31 @@
         }
         return and(filters);
     }
+
+    public static Bson buildRealmsFilter(Set<String> realms) {
+        return and(all(KeycloakFields.REALMS_KEY, realms), size(KeycloakFields.REALMS_KEY, realms.size()));
+    }
+
+    public static Bson buildQuery(List<String> clientQueries, Set<String> realms) {
+        Bson query = null;
+
+        if (clientQueries != null && !clientQueries.isEmpty()) {
+            query = MongoRequestFilters.buildQueriesFilter(clientQueries);
+        }
+
+        if (realms != null && realms.size() > 0) {
+            Bson realmsQuery = MongoRequestFilters.buildRealmsFilter(realms);
+            if (query != null) {
+                query = and(query, realmsQuery);
+            } else {
+                query = realmsQuery;
+            }
+        }
+
+        if (query == null) {
+            query = new Document();
+        }
+
+        return query;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/keycloak/KeycloakFields.java	Mon Jul 17 11:22:45 2017 -0400
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.gateway.common.mongodb.keycloak;
+
+public class KeycloakFields {
+    public static final String REALMS_KEY = "realms";
+}
--- a/common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/response/MongoResponseBuilder.java	Mon Jul 17 11:42:24 2017 +0200
+++ b/common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/response/MongoResponseBuilder.java	Mon Jul 17 11:22:45 2017 -0400
@@ -44,6 +44,7 @@
 import com.google.gson.GsonBuilder;
 import com.mongodb.Block;
 import com.mongodb.client.FindIterable;
+import com.redhat.thermostat.gateway.common.mongodb.keycloak.KeycloakFields;
 
 /*
  *  Builds the appropriate response after executing the request's MongoDB Query.
@@ -67,6 +68,9 @@
             documents.forEach(new Block<Document>() {
                 @Override
                 public void apply(Document document) {
+                    if (document.containsKey(KeycloakFields.REALMS_KEY)) {
+                        document.remove(KeycloakFields.REALMS_KEY);
+                    }
                     queryDocuments.add(document);
                 }
             });
--- a/common/mongodb/src/test/java/com/redhat/thermostat/gateway/common/mongodb/filters/MongoRequestFiltersTest.java	Mon Jul 17 11:42:24 2017 +0200
+++ b/common/mongodb/src/test/java/com/redhat/thermostat/gateway/common/mongodb/filters/MongoRequestFiltersTest.java	Mon Jul 17 11:22:45 2017 -0400
@@ -37,12 +37,17 @@
 package com.redhat.thermostat.gateway.common.mongodb.filters;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
+import org.bson.BsonArray;
 import org.bson.BsonDocument;
+import org.bson.BsonString;
 import org.bson.conversions.Bson;
 import org.bson.json.JsonParseException;
 import org.junit.Test;
@@ -164,4 +169,25 @@
     public void testInvalidWithSymbols() {
         MongoRequestFilters.buildQueriesFilter(Collections.singletonList("a="));
     }
+
+    @Test
+    public void testBuildRealmsFilter() {
+        Set<String> realms = new HashSet<>();
+        realms.add("one");
+        realms.add("two");
+
+        Bson filter = MongoRequestFilters.buildRealmsFilter(realms);
+        BsonDocument bsonDocument = filter.toBsonDocument(BsonDocument.class, MongoClient.getDefaultCodecRegistry());
+
+        BsonDocument realmsDocument = bsonDocument.getDocument("realms");
+        assertTrue(realmsDocument.containsKey("$all"));
+        assertTrue(realmsDocument.containsKey("$size"));
+
+        assertEquals(2, realmsDocument.get("$size").asInt32().getValue());
+
+        BsonArray allDocument = realmsDocument.getArray("$all");
+        assertEquals(2, allDocument.size());
+        assertTrue(allDocument.contains(new BsonString("one")));
+        assertTrue(allDocument.contains(new BsonString("two")));
+    }
 }
--- a/common/mongodb/src/test/java/com/redhat/thermostat/gateway/common/mongodb/response/MongoResponseBuilderTest.java	Mon Jul 17 11:42:24 2017 +0200
+++ b/common/mongodb/src/test/java/com/redhat/thermostat/gateway/common/mongodb/response/MongoResponseBuilderTest.java	Mon Jul 17 11:22:45 2017 -0400
@@ -89,6 +89,21 @@
         assertEquals(expected, output);
     }
 
+    @Test
+    public void testRealmsKeyRemoved() {
+        Document d1 = Document.parse("{\"hello\" : \"blob\", \"realms\" : [\"a\",\"b\"]}");
+        Document d2 = Document.parse("{\"a\" : {\"blob\" : [\"hi\"]}, \"realms\" : [\"c\",\"d\"]}");
+        final List<Document> list = new ArrayList<>();
+        list.add(d1);
+        list.add(d2);
+
+        FindIterable<Document> iterable = new TestFindIterable<>(list);
+
+        String output = mongoResponseBuilder.queryDocuments(iterable).build();
+        String expected = "{\"response\":[{\"hello\":\"blob\"},{\"a\":{\"blob\":[\"hi\"]}}]}";
+        assertEquals(expected, output);
+    }
+
     private class TestFindIterable<T> implements FindIterable<T> {
 
         private final List<T> list;
--- a/server/pom.xml	Mon Jul 17 11:42:24 2017 +0200
+++ b/server/pom.xml	Mon Jul 17 11:22:45 2017 -0400
@@ -93,6 +93,11 @@
             <version>${keycloak.version}</version>
         </dependency>
         <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${keycloak.version}</version>
+        </dependency>
+        <dependency>
             <groupId>com.google.code.gson</groupId>
             <artifactId>gson</artifactId>
             <version>${google-gson.version}</version>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/main/java/com/redhat/thermostat/gateway/server/auth/keycloak/KeycloakRequestFilter.java	Mon Jul 17 11:22:45 2017 -0400
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.gateway.server.auth.keycloak;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.redhat.thermostat.gateway.common.core.auth.keycloak.RealmAuthorizer;
+
+public class KeycloakRequestFilter implements Filter {
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+        // Do nothing
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        try {
+            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+            RealmAuthorizer realmAuthorizer = new RealmAuthorizer(httpServletRequest);
+
+            httpServletRequest.setAttribute(RealmAuthorizer.class.getName(), realmAuthorizer);
+
+            chain.doFilter(request, response);
+        } catch (ServletException e) {
+            HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+            httpServletResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid realms header");
+        }
+    }
+
+    @Override
+    public void destroy() {
+        // Do nothing
+    }
+}
--- a/server/src/main/java/com/redhat/thermostat/gateway/server/services/WebArchiveCoreService.java	Mon Jul 17 11:42:24 2017 +0200
+++ b/server/src/main/java/com/redhat/thermostat/gateway/server/services/WebArchiveCoreService.java	Mon Jul 17 11:22:45 2017 -0400
@@ -37,10 +37,12 @@
 package com.redhat.thermostat.gateway.server.services;
 
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 
+import javax.servlet.DispatcherType;
 import javax.servlet.ServletException;
 
 import org.eclipse.jetty.security.ConstraintMapping;
@@ -60,6 +62,7 @@
 import com.redhat.thermostat.gateway.server.auth.basic.BasicUserStore;
 import com.redhat.thermostat.gateway.server.auth.keycloak.KeycloakConfiguration;
 import com.redhat.thermostat.gateway.server.auth.keycloak.KeycloakConfigurationFactory;
+import com.redhat.thermostat.gateway.server.auth.keycloak.KeycloakRequestFilter;
 
 class WebArchiveCoreService implements CoreService {
 
@@ -120,6 +123,9 @@
         webAppContext.setInitParameter("org.keycloak.json.adapterConfig", keycloakConfig);
         webAppContext.setSecurityHandler(securityHandler);
         webAppContext.addSystemClass("org.keycloak.");
+        webAppContext.addSystemClass("com.redhat.thermostat.gateway.common.core.auth.keycloak.");
+
+        webAppContext.addFilter(KeycloakRequestFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
     }
 
     private void setupBasicAuthForContext(WebAppContext webAppContext) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/test/java/com/redhat/thermostat/gateway/server/auth/keycloak/KeycloakRequestFilterTest.java	Mon Jul 17 11:22:45 2017 -0400
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.gateway.server.auth.keycloak;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.representations.AccessToken;
+import org.mockito.ArgumentMatchers;
+
+import com.redhat.thermostat.gateway.common.core.auth.keycloak.RealmAuthorizer;
+
+public class KeycloakRequestFilterTest {
+
+
+    HttpServletRequest request;
+    AccessToken.Access access;
+
+    @Before
+    public void setup() {
+        request = mock(HttpServletRequest.class);
+        KeycloakSecurityContext keycloakSecurityContext = mock(KeycloakSecurityContext.class);
+        when(request.getAttribute(eq(KeycloakSecurityContext.class.getName()))).thenReturn(keycloakSecurityContext);
+
+        AccessToken accessToken = mock(AccessToken.class);
+        when(keycloakSecurityContext.getToken()).thenReturn(accessToken);
+
+        access = mock(AccessToken.Access.class);
+        when(accessToken.getRealmAccess()).thenReturn(access);
+    }
+
+    @Test
+    public void verifyRealmsAuthorizerSet() throws IOException, ServletException {
+        String[] roles = new String[]{"a-realm"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+
+        KeycloakRequestFilter keycloakRequestFilter = new KeycloakRequestFilter();
+
+        HttpServletResponse httpServletResponse = mock(HttpServletResponse.class);
+        FilterChain filterChain = mock(FilterChain.class);
+
+        keycloakRequestFilter.doFilter(request, httpServletResponse, filterChain);
+
+        verify(request, times(1)).setAttribute(eq(RealmAuthorizer.class.getName()), ArgumentMatchers.any(RealmAuthorizer.class));
+
+        verify(filterChain, times(1)).doFilter(eq(request), eq(httpServletResponse));
+    }
+
+    @Test
+    public void verifyBadRequestSent() throws IOException, ServletException {
+        String[] roles = new String[]{"a-realm"};
+        when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles)));
+
+        when(request.getHeader(eq(RealmAuthorizer.REALMS_HEADER))).thenReturn("blob");
+
+        KeycloakRequestFilter keycloakRequestFilter = new KeycloakRequestFilter();
+
+        HttpServletResponse httpServletResponse = mock(HttpServletResponse.class);
+        FilterChain filterChain = mock(FilterChain.class);
+
+        keycloakRequestFilter.doFilter(request, httpServletResponse, filterChain);
+
+        verify(httpServletResponse, times(1)).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), eq("Invalid realms header"));
+
+        verify(request, times(0)).setAttribute(eq(RealmAuthorizer.class.getName()), ArgumentMatchers.any(RealmAuthorizer.class));
+
+        verify(filterChain, times(0)).doFilter(eq(request), eq(httpServletResponse));
+    }
+}
--- a/server/src/test/java/com/redhat/thermostat/gateway/server/services/WebArchiveCoreServiceTest.java	Mon Jul 17 11:42:24 2017 +0200
+++ b/server/src/test/java/com/redhat/thermostat/gateway/server/services/WebArchiveCoreServiceTest.java	Mon Jul 17 11:22:45 2017 -0400
@@ -50,6 +50,7 @@
 import org.eclipse.jetty.security.SecurityHandler;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.UserIdentity;
+import org.eclipse.jetty.servlet.FilterHolder;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.webapp.WebAppContext;
 import org.junit.Test;
@@ -58,6 +59,7 @@
 import com.redhat.thermostat.gateway.common.core.config.Configuration;
 import com.redhat.thermostat.gateway.common.core.config.ServiceConfiguration;
 import com.redhat.thermostat.gateway.server.auth.basic.BasicLoginService;
+import com.redhat.thermostat.gateway.server.auth.keycloak.KeycloakRequestFilter;
 
 public class WebArchiveCoreServiceTest {
 
@@ -115,6 +117,11 @@
         assertTrue(securityHandler instanceof ConstraintSecurityHandler);
         assertTrue(securityHandler.getAuthenticator() instanceof KeycloakJettyAuthenticator);
         assertEquals(securityHandler.getRealmName(), "thermostat");
+
+        FilterHolder[] filters = webAppContext.getServletHandler().getFilters();
+
+        assertEquals(1, filters.length);
+        assertEquals(KeycloakRequestFilter.class, filters[0].getHeldClass());
     }
 
 
--- a/services/jvm-gc/pom.xml	Mon Jul 17 11:42:24 2017 +0200
+++ b/services/jvm-gc/pom.xml	Mon Jul 17 11:22:45 2017 -0400
@@ -74,6 +74,11 @@
     <!-- Thermostat Web Gateway Dependencies -->
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-web-gateway-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-web-gateway-common-mongodb</artifactId>
       <version>${project.version}</version>
     </dependency>
--- a/services/jvm-gc/src/main/java/com/redhat/thermostat/service/jvm/gc/JvmGcHttpHandler.java	Mon Jul 17 11:42:24 2017 +0200
+++ b/services/jvm-gc/src/main/java/com/redhat/thermostat/service/jvm/gc/JvmGcHttpHandler.java	Mon Jul 17 11:22:45 2017 -0400
@@ -51,6 +51,7 @@
 import javax.ws.rs.core.Response;
 
 import com.mongodb.DBObject;
+import com.redhat.thermostat.gateway.common.core.auth.keycloak.RealmAuthorizer;
 import com.redhat.thermostat.gateway.common.mongodb.ThermostatMongoStorage;
 import com.redhat.thermostat.gateway.common.mongodb.executor.MongoDataResultContainer;
 import com.redhat.thermostat.gateway.common.mongodb.executor.MongoExecutor;
@@ -73,12 +74,25 @@
                              @QueryParam("q") String queries,
                              @QueryParam("p") String projections,
                              @QueryParam("m") @DefaultValue("false") Boolean metadata,
-                             @Context HttpServletRequest requestInfo,
+                             @Context HttpServletRequest httpServletRequest,
                              @Context ServletContext context) {
         try {
+            RealmAuthorizer realmAuthorizer = (RealmAuthorizer) httpServletRequest.getAttribute(RealmAuthorizer.class.getName());
             ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE);
-            MongoDataResultContainer execResult = mongoExecutor.execGetRequest(
-                    storage.getDatabase().getCollection(collectionName), limit, offset, sort, queries, projections);
+
+            MongoDataResultContainer execResult;
+
+            if (realmAuthorizer != null) {
+                if (realmAuthorizer.readable()) {
+                    execResult = mongoExecutor.execGetRequest(
+                            storage.getDatabase().getCollection(collectionName), limit, offset, sort, queries, projections, realmAuthorizer.getReadableRealms());
+                } else {
+                    return Response.status(Response.Status.FORBIDDEN).build();
+                }
+            } else {
+                execResult = mongoExecutor.execGetRequest(
+                        storage.getDatabase().getCollection(collectionName), limit, offset, sort, queries, projections, null);
+            }
 
             MongoResponseBuilder.Builder response = new MongoResponseBuilder.Builder();
             response.queryDocuments(execResult.getQueryDataResult());
@@ -86,7 +100,7 @@
             if (metadata) {
                 MongoMetaDataResponseBuilder.MetaBuilder metaDataResponse = new MongoMetaDataResponseBuilder.MetaBuilder();
                 MongoMetaDataGenerator metaDataGenerator = new MongoMetaDataGenerator(limit, offset, sort, queries,
-                        projections, requestInfo, execResult);
+                        projections, httpServletRequest, execResult);
 
                 metaDataGenerator.setDocAndPayloadCount(metaDataResponse);
                 metaDataGenerator.setPrev(metaDataResponse);
@@ -96,7 +110,7 @@
             }
             return Response.status(Response.Status.OK).entity(response.build()).build();
         } catch (Exception e) {
-            return Response.status(Response.Status.BAD_REQUEST).entity(e.getStackTrace()).build();
+            return Response.status(Response.Status.BAD_REQUEST).build();
         }
     }
 
@@ -106,10 +120,22 @@
     public Response putJvmGc(String body,
                              @QueryParam("q") String queries,
                              @QueryParam("m") @DefaultValue("false") String metadata,
-                             @Context ServletContext context) {
+                             @Context ServletContext context,
+                             @Context HttpServletRequest httpServletRequest) {
         try {
+            RealmAuthorizer realmAuthorizer = (RealmAuthorizer) httpServletRequest.getAttribute(RealmAuthorizer.class.getName());
             ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE);
-            mongoExecutor.execPutRequest(storage.getDatabase().getCollection(collectionName), body, queries);
+
+            if (realmAuthorizer != null) {
+                if (realmAuthorizer.updatable()) {
+                    mongoExecutor.execPutRequest(storage.getDatabase().getCollection(collectionName), body, queries, realmAuthorizer.getUpdatableRealms());
+                } else {
+                    return Response.status(Response.Status.FORBIDDEN).build();
+                }
+            } else {
+                mongoExecutor.execPutRequest(storage.getDatabase().getCollection(collectionName), body, queries, null);
+            }
+
             return Response.status(Response.Status.OK).build();
         } catch (Exception e) {
             return Response.status(Response.Status.BAD_REQUEST).build();
@@ -121,10 +147,21 @@
     @Produces({ "application/json", "text/html; charset=utf-8" })
     public Response postJvmGc(String body,
                               @QueryParam("m") @DefaultValue("false") String metadata,
-                              @Context ServletContext context) {
+                              @Context ServletContext context,
+                              @Context HttpServletRequest httpServletRequest) {
         try {
+            RealmAuthorizer realmAuthorizer = (RealmAuthorizer) httpServletRequest.getAttribute(RealmAuthorizer.class.getName());
             ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE);
-            mongoExecutor.execPostRequest(storage.getDatabase().getCollection(collectionName, DBObject.class), body);
+
+            if (realmAuthorizer != null) {
+                if (realmAuthorizer.writable()) {
+                    mongoExecutor.execPostRequest(storage.getDatabase().getCollection(collectionName, DBObject.class), body, realmAuthorizer.getWritableRealms());
+                } else {
+                    return Response.status(Response.Status.FORBIDDEN).build();
+                }
+            } else {
+                mongoExecutor.execPostRequest(storage.getDatabase().getCollection(collectionName, DBObject.class), body, null);
+            }
             return Response.status(Response.Status.OK).build();
         } catch (Exception e) {
             return Response.status(Response.Status.BAD_REQUEST).build();
@@ -136,10 +173,22 @@
     @Produces({ "application/json", "text/html; charset=utf-8" })
     public Response deleteJvmGc(@QueryParam("q") String queries,
                                 @QueryParam("m") @DefaultValue("false") String metadata,
-                                @Context ServletContext context) {
+                                @Context ServletContext context,
+                                @Context HttpServletRequest httpServletRequest) {
         try {
+            RealmAuthorizer realmAuthorizer = (RealmAuthorizer) httpServletRequest.getAttribute(RealmAuthorizer.class.getName());
             ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE);
-            mongoExecutor.execDeleteRequest(storage.getDatabase().getCollection(collectionName), queries);
+
+            if (realmAuthorizer != null) {
+                if (realmAuthorizer.deletable()) {
+                    mongoExecutor.execDeleteRequest(storage.getDatabase().getCollection(collectionName), queries, realmAuthorizer.getDeletableRealms());
+                } else {
+                    return Response.status(Response.Status.FORBIDDEN).build();
+                }
+            } else {
+                mongoExecutor.execDeleteRequest(storage.getDatabase().getCollection(collectionName), queries, null);
+            }
+
             return Response.status(Response.Status.OK).build();
         } catch (Exception e) {
             return Response.status(Response.Status.BAD_REQUEST).build();
--- a/services/jvm-gc/src/main/resources/jvm-gc-swagger.yaml	Mon Jul 17 11:42:24 2017 +0200
+++ b/services/jvm-gc/src/main/resources/jvm-gc-swagger.yaml	Mon Jul 17 11:22:45 2017 -0400
@@ -13,6 +13,8 @@
 basePath: /jvm-gc/0.0.2
 paths:
   /:
+    parameters:
+      - $ref: '#/parameters/thermostat-realms'
     get:
       description: Get jvm gc information.
       parameters:
@@ -167,4 +169,9 @@
     name: m
     type: boolean
     in: query
-    description: "Metadata flag. If set to 'true', the subsequent request response will return metadata information. If set to 'false', such metadata information will be omitted."
\ No newline at end of file
+    description: "Metadata flag. If set to 'true', the subsequent request response will return metadata information. If set to 'false', such metadata information will be omitted."
+  thermostat-realms:
+    name: X-Thermostat-Realms
+    type: string
+    in: header
+    description: "Realms Header used to specify a subset of roles to use for Keycloak authorization. Attempts to specify realms that the client does not have, or no valid realms at all will result in a 400 Bad Request response. Expects a comma separated list of realms Example 'X-Thermostat-Realms: realm-one, realm-two'"
\ No newline at end of file
--- a/services/jvm-memory/src/main/java/com/redhat/thermostat/service/jvm/memory/JvmMemoryHttpHandler.java	Mon Jul 17 11:42:24 2017 +0200
+++ b/services/jvm-memory/src/main/java/com/redhat/thermostat/service/jvm/memory/JvmMemoryHttpHandler.java	Mon Jul 17 11:22:45 2017 -0400
@@ -80,7 +80,7 @@
             Integer offset = offsetParam.getValue();
             ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE);
             MongoDataResultContainer execResult = mongoExecutor.execGetRequest(
-                    storage.getDatabase().getCollection(collectionName), limit, offset, sort, queries, projections);
+                    storage.getDatabase().getCollection(collectionName), limit, offset, sort, queries, projections, null);
 
             MongoResponseBuilder.Builder response = new MongoResponseBuilder.Builder();
             response.queryDocuments(execResult.getQueryDataResult());
@@ -112,7 +112,7 @@
         try {
             ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE);
 
-            MongoDataResultContainer putExec = mongoExecutor.execPutRequest(storage.getDatabase().getCollection(collectionName), body, queries);
+            mongoExecutor.execPutRequest(storage.getDatabase().getCollection(collectionName), body, queries, null);
 
             return Response.status(Response.Status.OK).build();
         } catch (Exception e) {
@@ -129,7 +129,7 @@
         try {
             ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE);
 
-            mongoExecutor.execPostRequest(storage.getDatabase().getCollection(collectionName, DBObject.class), body);
+            mongoExecutor.execPostRequest(storage.getDatabase().getCollection(collectionName, DBObject.class), body, null);
             return Response.status(Response.Status.OK).build();
         } catch (Exception e) {
             return Response.status(Response.Status.BAD_REQUEST).build();
@@ -145,7 +145,7 @@
         try {
             ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE);
 
-            MongoDataResultContainer delExec = mongoExecutor.execDeleteRequest(storage.getDatabase().getCollection(collectionName), queries);
+            MongoDataResultContainer delExec = mongoExecutor.execDeleteRequest(storage.getDatabase().getCollection(collectionName), queries, null);
 
             return Response.status(Response.Status.OK).build();
         } catch (Exception e) {
--- a/services/system-memory/src/main/java/com/redhat/thermostat/service/system/memory/mongo/MongoStorageHandler.java	Mon Jul 17 11:42:24 2017 +0200
+++ b/services/system-memory/src/main/java/com/redhat/thermostat/service/system/memory/mongo/MongoStorageHandler.java	Mon Jul 17 11:22:45 2017 -0400
@@ -48,7 +48,6 @@
 import org.bson.Document;
 import org.bson.conversions.Bson;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
--- a/tests/integration-tests/src/test/java/com/redhat/thermostat/gateway/service/jvm/gc/JvmGcServiceIntegrationTest.java	Mon Jul 17 11:42:24 2017 +0200
+++ b/tests/integration-tests/src/test/java/com/redhat/thermostat/gateway/service/jvm/gc/JvmGcServiceIntegrationTest.java	Mon Jul 17 11:22:45 2017 -0400
@@ -38,15 +38,26 @@
 package com.redhat.thermostat.gateway.service.jvm.gc;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
 
+import java.lang.reflect.Type;
+import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
+import org.bson.Document;
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.eclipse.jetty.client.util.StringContentProvider;
 import org.eclipse.jetty.http.HttpMethod;
 import org.junit.Test;
 
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+import com.mongodb.Block;
+import com.mongodb.client.FindIterable;
+import com.mongodb.client.MongoCollection;
 import com.redhat.thermostat.gateway.tests.integration.MongoIntegrationTest;
 
 public class JvmGcServiceIntegrationTest extends MongoIntegrationTest {
@@ -327,4 +338,195 @@
         makeHttpMethodRequest(HttpMethod.POST,"", data,"application/json","", 200);
         makeHttpGetRequest(gcUrl +"?q=b==test1&m=true&o=2&l=3", expectedResponse, 200);
     }
+
+    @Test
+    public void testUpdateDoesNotAffectRealms() throws InterruptedException, TimeoutException, ExecutionException {
+        MongoCollection<Document> collection = mongodTestUtil.getCollection(serviceName);
+        String data = "[{\"item\":1,\"realms\":[\"a\",\"b\"]}," +
+                "{\"item\":2,\"realms\":[\"a\",\"b\"]}]";
+        final Gson gson = new GsonBuilder().create();
+        final Type listType = new TypeToken<List<Document>>() {}.getType();
+        List<Document> insertDocuments = gson.fromJson(data, listType);
+
+        collection.insertMany(insertDocuments);
+
+        String updateString = "{\"set\" : {\"realms\" : 1, \"a\" : 2}}";
+        makeHttpMethodRequest(HttpMethod.PUT,"", updateString,"application/json","", 200);
+
+        FindIterable<Document> documents = collection.find();
+        documents.forEach(new Block<Document>() {
+            @Override
+            public void apply(Document document) {
+                assertEquals("[\"a\",\"b\"]", gson.toJson(document.get("realms"), listType));
+            }
+        });
+    }
+
+    @Test
+    public void testUpdateOnlyRealmsDoesNotAffectRealms() throws InterruptedException, TimeoutException, ExecutionException {
+        MongoCollection<Document> collection = mongodTestUtil.getCollection(serviceName);
+        String data = "[{\"item\":1,\"realms\":[\"a\",\"b\"]}," +
+                "{\"item\":2,\"realms\":[\"a\",\"b\"]}]";
+        final Gson gson = new GsonBuilder().create();
+        final Type listType = new TypeToken<List<Document>>() {}.getType();
+        List<Document> insertDocuments = gson.fromJson(data, listType);
+
+        collection.insertMany(insertDocuments);
+
+        String updateString = "{\"set\" : {\"realms\" : 1}}";
+        makeHttpMethodRequest(HttpMethod.PUT,"", updateString,"application/json","", 400);
+
+        FindIterable<Document> documents = collection.find();
+        documents.forEach(new Block<Document>() {
+            @Override
+            public void apply(Document document) {
+                assertEquals("[\"a\",\"b\"]", gson.toJson(document.get("realms"), listType));
+            }
+        });
+    }
+
+    @Test
+    public void testUpdateRealmsQueryMatchIsNotUsed() throws InterruptedException, TimeoutException, ExecutionException {
+        MongoCollection<Document> collection = mongodTestUtil.getCollection(serviceName);
+        String data = "[{\"item\":1,\"realms\":[\"a\"]}," +
+                "{\"item\":2,\"realms\":[\"b\"]}]";
+        final Gson gson = new GsonBuilder().create();
+        final Type listType = new TypeToken<List<Document>>() {}.getType();
+        List<Document> insertDocuments = gson.fromJson(data, listType);
+
+        collection.insertMany(insertDocuments);
+
+        String updateString = "{\"set\" : {\"item\" : 5}}";
+        StringContentProvider stringContentProvider = new StringContentProvider(updateString, "UTF-8");
+        ContentResponse response = client.newRequest(gcUrl).param("q", "realms==[\"a\"]")
+                .method(HttpMethod.PUT).content(stringContentProvider, "application/json")
+                .send();
+        assertEquals(400, response.getStatus());
+
+        FindIterable<Document> documents = collection.find();
+        documents.forEach(new Block<Document>() {
+            @Override
+            public void apply(Document document) {
+                if (document.get("item", Double.class).equals(5)) {
+                    fail();
+                }
+            }
+        });
+    }
+
+    @Test
+    public void testUpdateMultipleRealmsDoesNotAffectRealms() throws InterruptedException, TimeoutException, ExecutionException {
+        MongoCollection<Document> collection = mongodTestUtil.getCollection(serviceName);
+        String data = "[{\"item\":1,\"realms\":[\"a\",\"b\"]}," +
+                "{\"item\":2,\"realms\":[\"a\",\"b\"]}]";
+        final Gson gson = new GsonBuilder().create();
+        final Type listType = new TypeToken<List<Document>>() {}.getType();
+        List<Document> insertDocuments = gson.fromJson(data, listType);
+
+        collection.insertMany(insertDocuments);
+
+        String updateString = "{\"set\" : {\"realms\" : 1, \"realms\" : 2}}";
+        makeHttpMethodRequest(HttpMethod.PUT,"", updateString,"application/json","", 400);
+
+        FindIterable<Document> documents = collection.find();
+        documents.forEach(new Block<Document>() {
+            @Override
+            public void apply(Document document) {
+                assertEquals("[\"a\",\"b\"]", gson.toJson(document.get("realms"), listType));
+            }
+        });
+    }
+
+    @Test
+    public void testGetCannotSeeRealms() throws InterruptedException, ExecutionException, TimeoutException {
+        MongoCollection<Document> collection = mongodTestUtil.getCollection(serviceName);
+        String data = "[{\"item\":1,\"realms\":[\"a\",\"b\"]}," +
+                "{\"item\":2,\"realms\":[\"a\",\"b\"]}]";
+        final Gson gson = new GsonBuilder().create();
+        final Type listType = new TypeToken<List<Document>>() {}.getType();
+        List<Document> insertDocuments = gson.fromJson(data, listType);
+
+        collection.insertMany(insertDocuments);
+
+        String expected = "{\"response\":[{\"item\":1.0}]}";
+
+        makeHttpGetRequest(gcUrl,expected, 200);
+    }
+
+    @Test
+    public void testGetProjectionCannotSeeRealms() throws InterruptedException, ExecutionException, TimeoutException {
+        MongoCollection<Document> collection = mongodTestUtil.getCollection(serviceName);
+        String data = "[{\"item\":1,\"realms\":[\"a\",\"b\"]}," +
+                "{\"item\":2,\"realms\":[\"a\",\"b\"]}]";
+        final Gson gson = new GsonBuilder().create();
+        final Type listType = new TypeToken<List<Document>>() {}.getType();
+        List<Document> insertDocuments = gson.fromJson(data, listType);
+
+        collection.insertMany(insertDocuments);
+
+        String expected = "{\"response\":[{\"item\":1.0}]}";
+
+        makeHttpGetRequest(gcUrl + "?p=realms,item",expected, 200);
+    }
+
+    @Test
+    public void testGetQueryCannotMatchRealms() throws InterruptedException, ExecutionException, TimeoutException {
+        MongoCollection<Document> collection = mongodTestUtil.getCollection(serviceName);
+        String data = "[{\"item\":1,\"realms\":[\"a\"]}," +
+                "{\"item\":2,\"realms\":[\"b\"]}]";
+        final Gson gson = new GsonBuilder().create();
+        final Type listType = new TypeToken<List<Document>>() {}.getType();
+        List<Document> insertDocuments = gson.fromJson(data, listType);
+
+        collection.insertMany(insertDocuments);
+
+        ContentResponse response = client.newRequest(gcUrl)
+                .param("q", "realms==[\"a\"]").method(HttpMethod.GET).send();
+
+        assertEquals(400, response.getStatus());
+    }
+
+
+    @Test
+    public void testPostCannotAddRealms() throws InterruptedException, ExecutionException, TimeoutException {
+        String data = "[{\"item\":1,\"realms\":[\"a\",\"b\"]}," +
+                "{\"item\":2,\"realms\":[\"a\",\"b\"]}]";
+
+        makeHttpMethodRequest(HttpMethod.POST, "", data, "application/json", "", 200);
+
+        MongoCollection<Document> collection = mongodTestUtil.getCollection(serviceName);
+
+        FindIterable<Document> documents = collection.find();
+        documents.forEach(new Block<Document>() {
+            @Override
+            public void apply(Document document) {
+                assertNull(document.get("realms"));
+            }
+        });
+    }
+
+    @Test
+    public void testDeleteCannotQueryRealms() throws InterruptedException, TimeoutException, ExecutionException {
+        MongoCollection<Document> collection = mongodTestUtil.getCollection(serviceName);
+        String data = "[{\"item\":1,\"realms\":[\"a\",\"b\"]}," +
+                "{\"item\":2,\"realms\":[\"a\",\"b\"]}]";
+        final Gson gson = new GsonBuilder().create();
+        final Type listType = new TypeToken<List<Document>>() {}.getType();
+        List<Document> insertDocuments = gson.fromJson(data, listType);
+
+        collection.insertMany(insertDocuments);
+
+        ContentResponse response = client.newRequest(gcUrl)
+                .param("q", "realms==[\"a\",\"b\"]").method(HttpMethod.DELETE)
+                .send();
+        assertEquals(400, response.getStatus());
+
+        FindIterable<Document> documents = collection.find();
+        documents.forEach(new Block<Document>() {
+            @Override
+            public void apply(Document document) {
+                assertEquals("[\"a\",\"b\"]", gson.toJson(document.get("realms"), listType));
+            }
+        });
+    }
 }
--- a/tests/test-utils/src/main/java/com/redhat/thermostat/gateway/tests/utils/MongodTestUtil.java	Mon Jul 17 11:42:24 2017 +0200
+++ b/tests/test-utils/src/main/java/com/redhat/thermostat/gateway/tests/utils/MongodTestUtil.java	Mon Jul 17 11:22:45 2017 -0400
@@ -43,6 +43,7 @@
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
 
+import com.mongodb.client.MongoCollection;
 import com.redhat.thermostat.gateway.common.util.OS;
 import org.bson.Document;
 
@@ -97,6 +98,11 @@
         mongoClient.getDatabase(databaseName).getCollection(collectionName).drop();
     }
 
+
+    public MongoCollection<Document> getCollection(String collectionName) {
+        return mongoClient.getDatabase(databaseName).getCollection(collectionName);
+    }
+
     private void finish() throws IOException {
         Files.walkFileTree(tempDbDir, new SimpleFileVisitor<Path>() {
             @Override
@@ -161,4 +167,5 @@
     public boolean isConnectedToDatabase() {
         return connectedToDatabase;
     }
+
 }
\ No newline at end of file