Mercurial > hg > thermostat-ng > web-gateway
changeset 231:eb87674844fd
Add default realm authorizer
Reviewed-by: jerboaa
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-August/024600.html
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/DefaultRealmAuthorizer.java Mon Aug 21 16:44:08 2017 -0400 @@ -0,0 +1,54 @@ +/* + * 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; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class DefaultRealmAuthorizer extends RealmAuthorizer { + public static final String DEFAULT_REALM = "thermostat"; + + public DefaultRealmAuthorizer() throws InvalidRoleException { + RoleFactory roleFactory = new RoleFactory(); + Role r = roleFactory.buildRole("r,w,u,d-" + DEFAULT_REALM); + Set<Role> roles = new HashSet<>(); + roles.add(r); + + clientRoles = Collections.unmodifiableSet(roles); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/InvalidRoleException.java Mon Aug 21 16:44:08 2017 -0400 @@ -0,0 +1,43 @@ +/* + * 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; + +public class InvalidRoleException extends Exception { + public InvalidRoleException(String s) { + super(s); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/RealmAuthorizer.java Mon Aug 21 16:44:08 2017 -0400 @@ -0,0 +1,100 @@ +/* + * 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; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import com.redhat.thermostat.gateway.common.core.auth.keycloak.Action; + +public abstract class RealmAuthorizer { + + protected Set<Role> clientRoles = Collections.emptySet(); + + 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.containsAction(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.containsAction(action)) { + realms.add(role.getRealm()); + } + } + return Collections.unmodifiableSet(realms); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/Role.java Mon Aug 21 16:44:08 2017 -0400 @@ -0,0 +1,96 @@ +/* + * 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; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +public class Role { + public static final String ACTION_DELIMITER = ","; + public static final String ROLE_DELIMITER = "-"; + /** + * Array of regex that valid roles cannot match + * Role cannot contain any whitespaces + */ + public static final String[] RESTRICTED_CHARACTERS_REGEX = new String[]{".*\\s+.*"}; + + private final Set<String> actions; + private final String realm; + + public Role(Set<String> actions, String realm) { + Objects.requireNonNull(actions); + Objects.requireNonNull(realm); + this.actions = Collections.unmodifiableSet(actions); + this.realm = realm; + } + + public Set<String> getActions() { + return this.actions; + } + + public String getRealm() { + return this.realm; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Role role = (Role) o; + + if (!actions.equals(role.actions)) { + return false; + } + + return realm.equals(role.realm); + } + + public boolean containsAction(String action) { + return this.actions.contains(action); + } + + @Override + public int hashCode() { + return Objects.hash(actions, realm); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/RoleFactory.java Mon Aug 21 16:44:08 2017 -0400 @@ -0,0 +1,76 @@ +/* + * 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; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class RoleFactory { + + private boolean isValidRole(String role) { + if (!role.contains(Role.ROLE_DELIMITER)) { + return false; + } + for (String restrictedCharacter : Role.RESTRICTED_CHARACTERS_REGEX) { + if (role.matches(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) throws InvalidRoleException { + role = role.trim(); + + if (!isValidRole(role)) { + throw new InvalidRoleException("Invalid role: " + role); + } + + int index = role.indexOf(Role.ROLE_DELIMITER); + String actions = role.substring(0, index); + + String realm = role.substring(index + 1); + + Set<String> actionSet = new HashSet<>(Arrays.asList(actions.split(Role.ACTION_DELIMITER))); + return new Role(actionSet, realm); + } +}
--- a/common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/InvalidRoleException.java Mon Aug 21 08:57:27 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/* - * 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 InvalidRoleException extends Exception { - public InvalidRoleException(String s) { - super(s); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/KeycloakRealmAuthorizer.java Mon Aug 21 16:44:08 2017 -0400 @@ -0,0 +1,136 @@ +/* + * 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; + +import com.redhat.thermostat.gateway.common.core.auth.InvalidRoleException; +import com.redhat.thermostat.gateway.common.core.auth.RealmAuthorizer; +import com.redhat.thermostat.gateway.common.core.auth.Role; +import com.redhat.thermostat.gateway.common.core.auth.RoleFactory; + +public class KeycloakRealmAuthorizer extends RealmAuthorizer { + + public static final String REALMS_HEADER = "X-Thermostat-Realms"; + private static final String REALMS_HEADER_DELIMITER_REGEX = "\\s+"; + + private final RoleFactory roleFactory = new RoleFactory(); + + public KeycloakRealmAuthorizer(HttpServletRequest httpServletRequest) throws ServletException { + this.clientRoles = buildClientRoles(httpServletRequest); + } + + /** + * Package private for testing + */ + 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 Collections.unmodifiableSet(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()) { + try { + keycloakRoles.add(roleFactory.buildRole(role)); + } catch (InvalidRoleException e) { + //Do nothing + } + } + + 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.trim(); + Set<String> preferredRealms = new HashSet<>(Arrays.asList(realmsHeader.split(REALMS_HEADER_DELIMITER_REGEX))); + 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"); + } + } + + +}
--- a/common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RealmAuthorizer.java Mon Aug 21 08:57:27 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,179 +0,0 @@ -/* - * 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 static final String REALMS_HEADER_DELIMITER_REGEX = "\\s+"; - - 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.containsAction(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.containsAction(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()) { - try { - keycloakRoles.add(roleFactory.buildRole(role)); - } catch (InvalidRoleException e) { - //Do nothing - } - } - - 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.trim(); - Set<String> preferredRealms = new HashSet<>(Arrays.asList(realmsHeader.split(REALMS_HEADER_DELIMITER_REGEX))); - 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"); - } - } - - -}
--- a/common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/Role.java Mon Aug 21 08:57:27 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ -/* - * 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.Collections; -import java.util.Objects; -import java.util.Set; - -public class Role { - public static final String ACTION_DELIMITER = ","; - public static final String ROLE_DELIMITER = "-"; - /** - * Array of regex that valid roles cannot match - * Role cannot contain any whitespaces - */ - public static final String[] RESTRICTED_CHARACTERS_REGEX = new String[]{".*\\s+.*"}; - - private final Set<String> actions; - private final String realm; - - public Role(Set<String> actions, String realm) { - Objects.requireNonNull(actions); - Objects.requireNonNull(realm); - this.actions = Collections.unmodifiableSet(actions); - this.realm = realm; - } - - public Set<String> getActions() { - return this.actions; - } - - public String getRealm() { - return this.realm; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - Role role = (Role) o; - - if (!actions.equals(role.actions)) { - return false; - } - - return realm.equals(role.realm); - } - - public boolean containsAction(String action) { - return this.actions.contains(action); - } - - @Override - public int hashCode() { - return Objects.hash(actions, realm); - } -}
--- a/common/core/src/main/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RoleFactory.java Mon Aug 21 08:57:27 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -/* - * 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.HashSet; -import java.util.Set; - -public class RoleFactory { - - private boolean isValidRole(String role) { - if (!role.contains(Role.ROLE_DELIMITER)) { - return false; - } - for (String restrictedCharacter : Role.RESTRICTED_CHARACTERS_REGEX) { - if (role.matches(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) throws InvalidRoleException { - role = role.trim(); - - if (!isValidRole(role)) { - throw new InvalidRoleException("Invalid role: " + role); - } - - int index = role.indexOf(Role.ROLE_DELIMITER); - String actions = role.substring(0, index); - - String realm = role.substring(index + 1); - - Set<String> actionSet = new HashSet<>(Arrays.asList(actions.split(Role.ACTION_DELIMITER))); - return new Role(actionSet, realm); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/test/java/com/redhat/thermostat/gateway/common/core/auth/DefaultRealmAuthorizerTest.java Mon Aug 21 16:44:08 2017 -0400 @@ -0,0 +1,60 @@ +/* + * 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class DefaultRealmAuthorizerTest { + + @Test + public void testDefaultRoleExists() throws InvalidRoleException { + DefaultRealmAuthorizer realmAuthorizer = new DefaultRealmAuthorizer(); + + assertEquals(1, realmAuthorizer.clientRoles.size()); + + for (Role r : realmAuthorizer.clientRoles) { + assertEquals("thermostat", r.getRealm()); + assertTrue(r.containsAction("r")); + assertTrue(r.containsAction("w")); + assertTrue(r.containsAction("u")); + assertTrue(r.containsAction("d")); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/test/java/com/redhat/thermostat/gateway/common/core/auth/RoleFactoryTest.java Mon Aug 21 16:44:08 2017 -0400 @@ -0,0 +1,134 @@ +/* + * 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.HashSet; +import java.util.Set; + +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() throws InvalidRoleException { + String role = "a-valid,role"; + roleFactory.buildRole(role); + + Set<String> actions = new HashSet<>(); + actions.add("a"); + Role r = roleFactory.buildRole(role); + verifyRole(r, actions, "valid,role"); + } + + @Test + public void testValidRoleWithActions() throws InvalidRoleException { + String role = "r,w,d-role"; + + Role r = roleFactory.buildRole(role); + Set<String> actions = new HashSet<>(); + actions.add("r"); + actions.add("w"); + actions.add("d"); + verifyRole(r, actions, "role"); + } + + @Test(expected = InvalidRoleException.class) + public void testNoActionRole() throws InvalidRoleException { + String role = "-role"; + roleFactory.buildRole(role); + } + + @Test(expected = InvalidRoleException.class) + public void testNoRealmRole() throws InvalidRoleException { + String role = "a-"; + roleFactory.buildRole(role); + } + + @Test + public void testHyphenRealm() throws InvalidRoleException { + String role = "a-realm-with-hyphens"; + + Role r = roleFactory.buildRole(role); + Set<String> actions = new HashSet<>(); + actions.add("a"); + verifyRole(r, actions, "realm-with-hyphens"); + } + + @Test(expected = InvalidRoleException.class) + public void testRealmWithWhitespaceIsInvalid() throws InvalidRoleException { + String role = "a-invalid \trealm"; + roleFactory.buildRole(role); + } + + @Test + public void testRealmWithLeadingWhitespace() throws InvalidRoleException { + String role = " a-role"; + + Role r = roleFactory.buildRole(role); + Set<String> actions = new HashSet<>(); + actions.add("a"); + verifyRole(r, actions, "role"); + } + + @Test + public void testRealmWithTrailingWhitespace() throws InvalidRoleException { + String role = "a-role\t"; + + Role r = roleFactory.buildRole(role); + Set<String> actions = new HashSet<>(); + actions.add("a"); + verifyRole(r, actions, "role"); + } + + private void verifyRole(Role role, Set<String> expectedActions, String expectedRole) { + for (String item : expectedActions) { + assertTrue(role.containsAction(item)); + } + 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/RoleTest.java Mon Aug 21 16:44:08 2017 -0400 @@ -0,0 +1,125 @@ +/* + * 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.Test; + +import com.redhat.thermostat.gateway.common.core.auth.Role; + +public class RoleTest { + + @Test + public void testSimpleRole() { + Set<String> actions = new HashSet<>(); + actions.add("a"); + Role r = new Role(actions, "realm"); + + verifyRole(r, actions, "realm"); + } + + @Test + public void testMultipleActionsRole() { + Set<String> actions = new HashSet<>(); + actions.add("a"); + Role r = new Role(actions, "realm-1.2-3"); + + verifyRole(r, actions, "realm-1.2-3"); + } + + /* + Roles are added to set data structures which rely on the equals() + implementation to prevent duplicates + */ + @Test + public void testEquals() { + Set<String> actionsOne = new HashSet<>(); + actionsOne.add("a"); + + Role one = new Role(actionsOne, "b"); + Role two = new Role(actionsOne, "b"); + + assertEquals(one, two); + + Set<String> actionsTwo = new HashSet<>(); + actionsTwo.add("a"); + + Role three = new Role(actionsTwo, "b"); + + assertEquals(one, three); + } + + @Test + public void testNotEquals() { + Set<String> actionsOne = new HashSet<>(); + actionsOne.add("a"); + + Set<String> actionsTwo = new HashSet<>(); + actionsTwo.add("b"); + + Role one = new Role(actionsOne, "b"); + Role two = new Role(actionsOne, "c"); + Role three = new Role(actionsTwo, "b"); + + assertNotEquals(one, two); + assertNotEquals(one, three); + } + + @Test(expected = UnsupportedOperationException.class) + public void testActionsCannotBeModified() { + Set<String> actions = new HashSet<>(); + actions.add("a"); + Role r = new Role(actions, "realm-1.2-3"); + + verifyRole(r, actions, "realm-1.2-3"); + + r.getActions().add("not-allowed"); + } + + private void verifyRole(Role role, Set<String> expectedActions, String expectedRole) { + for (String item : expectedActions) { + assertTrue(role.containsAction(item)); + } + 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/KeycloakRealmAuthorizerTest.java Mon Aug 21 16:44:08 2017 -0400 @@ -0,0 +1,282 @@ +/* + * 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; + +import com.redhat.thermostat.gateway.common.core.auth.Role; + +public class KeycloakRealmAuthorizerTest { + + 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))); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(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))); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(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))); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(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))); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(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))); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(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(); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(request); + assertTrue(realmAuthorizer.readable()); + + Set<String> realms = realmAuthorizer.getReadableRealms(); + assertEquals(1, realms.size()); + assertTrue(realms.contains("read")); + } + + @Test + public void testWritable() throws ServletException { + setupRealms(); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(request); + assertTrue(realmAuthorizer.writable()); + + Set<String> realms = realmAuthorizer.getWritableRealms(); + assertEquals(1, realms.size()); + assertTrue(realms.contains("write")); + } + + @Test + public void testUpdatable() throws ServletException { + setupRealms(); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(request); + assertTrue(realmAuthorizer.updatable()); + + Set<String> realms = realmAuthorizer.getUpdatableRealms(); + assertEquals(1, realms.size()); + assertTrue(realms.contains("update")); + } + + @Test + public void testDeletable() throws ServletException { + setupRealms(); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(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))); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(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))); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(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))); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(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))); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(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"); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(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 KeycloakRealmAuthorizer(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\twrite "); + + KeycloakRealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(request); + assertEquals(1, realmAuthorizer.getReadableRealms().size()); + assertEquals(1, realmAuthorizer.getUpdatableRealms().size()); + assertEquals(1, realmAuthorizer.getWritableRealms().size()); + + assertEquals(0, realmAuthorizer.getDeletableRealms().size()); + } + +}
--- a/common/core/src/test/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RealmAuthorizerTest.java Mon Aug 21 08:57:27 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,280 +0,0 @@ -/* - * 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\twrite "); - - 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()); - } - -}
--- a/common/core/src/test/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RoleFactoryTest.java Mon Aug 21 08:57:27 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ -/* - * 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 java.util.HashSet; -import java.util.Set; - -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() throws InvalidRoleException { - String role = "a-valid,role"; - roleFactory.buildRole(role); - - Set<String> actions = new HashSet<>(); - actions.add("a"); - Role r = roleFactory.buildRole(role); - verifyRole(r, actions, "valid,role"); - } - - @Test - public void testValidRoleWithActions() throws InvalidRoleException { - String role = "r,w,d-role"; - - Role r = roleFactory.buildRole(role); - Set<String> actions = new HashSet<>(); - actions.add("r"); - actions.add("w"); - actions.add("d"); - verifyRole(r, actions, "role"); - } - - @Test(expected = InvalidRoleException.class) - public void testNoActionRole() throws InvalidRoleException { - String role = "-role"; - roleFactory.buildRole(role); - } - - @Test(expected = InvalidRoleException.class) - public void testNoRealmRole() throws InvalidRoleException { - String role = "a-"; - roleFactory.buildRole(role); - } - - @Test - public void testHyphenRealm() throws InvalidRoleException { - String role = "a-realm-with-hyphens"; - - Role r = roleFactory.buildRole(role); - Set<String> actions = new HashSet<>(); - actions.add("a"); - verifyRole(r, actions, "realm-with-hyphens"); - } - - @Test(expected = InvalidRoleException.class) - public void testRealmWithWhitespaceIsInvalid() throws InvalidRoleException { - String role = "a-invalid \trealm"; - roleFactory.buildRole(role); - } - - @Test - public void testRealmWithLeadingWhitespace() throws InvalidRoleException { - String role = " a-role"; - - Role r = roleFactory.buildRole(role); - Set<String> actions = new HashSet<>(); - actions.add("a"); - verifyRole(r, actions, "role"); - } - - @Test - public void testRealmWithTrailingWhitespace() throws InvalidRoleException { - String role = "a-role\t"; - - Role r = roleFactory.buildRole(role); - Set<String> actions = new HashSet<>(); - actions.add("a"); - verifyRole(r, actions, "role"); - } - - private void verifyRole(Role role, Set<String> expectedActions, String expectedRole) { - for (String item : expectedActions) { - assertTrue(role.containsAction(item)); - } - assertEquals(expectedRole, role.getRealm()); - } -}
--- a/common/core/src/test/java/com/redhat/thermostat/gateway/common/core/auth/keycloak/RoleTest.java Mon Aug 21 08:57:27 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,123 +0,0 @@ -/* - * 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.assertNotEquals; -import static org.junit.Assert.assertTrue; - -import java.util.HashSet; -import java.util.Set; - -import org.junit.Test; - -public class RoleTest { - - @Test - public void testSimpleRole() { - Set<String> actions = new HashSet<>(); - actions.add("a"); - Role r = new Role(actions, "realm"); - - verifyRole(r, actions, "realm"); - } - - @Test - public void testMultipleActionsRole() { - Set<String> actions = new HashSet<>(); - actions.add("a"); - Role r = new Role(actions, "realm-1.2-3"); - - verifyRole(r, actions, "realm-1.2-3"); - } - - /* - Roles are added to set data structures which rely on the equals() - implementation to prevent duplicates - */ - @Test - public void testEquals() { - Set<String> actionsOne = new HashSet<>(); - actionsOne.add("a"); - - Role one = new Role(actionsOne, "b"); - Role two = new Role(actionsOne, "b"); - - assertEquals(one, two); - - Set<String> actionsTwo = new HashSet<>(); - actionsTwo.add("a"); - - Role three = new Role(actionsTwo, "b"); - - assertEquals(one, three); - } - - @Test - public void testNotEquals() { - Set<String> actionsOne = new HashSet<>(); - actionsOne.add("a"); - - Set<String> actionsTwo = new HashSet<>(); - actionsTwo.add("b"); - - Role one = new Role(actionsOne, "b"); - Role two = new Role(actionsOne, "c"); - Role three = new Role(actionsTwo, "b"); - - assertNotEquals(one, two); - assertNotEquals(one, three); - } - - @Test(expected = UnsupportedOperationException.class) - public void testActionsCannotBeModified() { - Set<String> actions = new HashSet<>(); - actions.add("a"); - Role r = new Role(actions, "realm-1.2-3"); - - verifyRole(r, actions, "realm-1.2-3"); - - r.getActions().add("not-allowed"); - } - - private void verifyRole(Role role, Set<String> expectedActions, String expectedRole) { - for (String item : expectedActions) { - assertTrue(role.containsAction(item)); - } - assertEquals(expectedRole, role.getRealm()); - } -}
--- a/common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/servlet/MongoHttpHandlerHelper.java Mon Aug 21 08:57:27 2017 -0400 +++ b/common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/servlet/MongoHttpHandlerHelper.java Mon Aug 21 16:44:08 2017 -0400 @@ -40,7 +40,7 @@ import com.mongodb.MongoTimeoutException; import com.mongodb.MongoWriteException; -import com.redhat.thermostat.gateway.common.core.auth.keycloak.RealmAuthorizer; +import com.redhat.thermostat.gateway.common.core.auth.RealmAuthorizer; import com.redhat.thermostat.gateway.common.mongodb.ThermostatFields; import com.redhat.thermostat.gateway.common.mongodb.ThermostatMongoStorage; import com.redhat.thermostat.gateway.common.mongodb.executor.MongoDataResultContainer; @@ -60,6 +60,7 @@ import static com.redhat.thermostat.gateway.common.util.ServiceException.DATABASE_UNAVAILABLE; import static com.redhat.thermostat.gateway.common.util.ServiceException.EXPECTED_JSON_ARRAY; import static com.redhat.thermostat.gateway.common.util.ServiceException.MALFORMED_CLIENT_REQUEST; +import static com.redhat.thermostat.gateway.common.util.ServiceException.UNEXPECTED_ERROR; public class MongoHttpHandlerHelper { @@ -74,7 +75,8 @@ .add(UnsupportedOperationException.class, MALFORMED_CLIENT_REQUEST) .add(ClassCastException.class, EXPECTED_JSON_ARRAY) .add(MongoTimeoutException.class, DATABASE_UNAVAILABLE) - .add(IOException.class, CANNOT_QUERY_REALMS_PROPERTY); + .add(IOException.class, CANNOT_QUERY_REALMS_PROPERTY) + .add(NullPointerException.class, UNEXPECTED_ERROR); } /* @@ -93,36 +95,30 @@ try { boolean metadata = Boolean.valueOf(returnMetadata); RealmAuthorizer realmAuthorizer = (RealmAuthorizer) httpServletRequest.getAttribute(RealmAuthorizer.class.getName()); - ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE); - MongoDataResultContainer execResult; + if (realmAuthorizer.readable()) { + ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE); + + MongoDataResultContainer execResult = mongoExecutor.execGetRequest( + storage.getDatabase().getCollection(collectionName), limit, offset, sort, queries, includes, excludes, realmAuthorizer.getReadableRealms()); - if (realmAuthorizer != null) { - if (realmAuthorizer.readable()) { - execResult = mongoExecutor.execGetRequest( - storage.getDatabase().getCollection(collectionName), limit, offset, sort, queries, includes, excludes, realmAuthorizer.getReadableRealms()); - } else { - return Response.status(Response.Status.FORBIDDEN).build(); + MongoResponseBuilder.Builder response = new MongoResponseBuilder.Builder(); + response.addQueryDocuments(execResult.getQueryDataResult()); + if (metadata) { + MongoMetaDataResponseBuilder.MetaBuilder metaDataResponse = new MongoMetaDataResponseBuilder.MetaBuilder(); + MongoMetaDataGenerator metaDataGenerator = new MongoMetaDataGenerator(limit, offset, sort, queries, + includes, excludes, httpServletRequest, execResult); + + metaDataGenerator.setDocAndPayloadCount(metaDataResponse); + metaDataGenerator.setPrev(metaDataResponse); + metaDataGenerator.setNext(metaDataResponse); + + response.addMetaData(metaDataResponse.build()); } + return Response.status(Response.Status.OK).entity(response.build()).build(); } else { - execResult = mongoExecutor.execGetRequest( - storage.getDatabase().getCollection(collectionName), limit, offset, sort, queries, includes, excludes, null); + return Response.status(Response.Status.FORBIDDEN).build(); } - - MongoResponseBuilder.Builder response = new MongoResponseBuilder.Builder(); - response.addQueryDocuments(execResult.getQueryDataResult()); - if (metadata) { - MongoMetaDataResponseBuilder.MetaBuilder metaDataResponse = new MongoMetaDataResponseBuilder.MetaBuilder(); - MongoMetaDataGenerator metaDataGenerator = new MongoMetaDataGenerator(limit, offset, sort, queries, - includes, excludes, httpServletRequest, execResult); - - metaDataGenerator.setDocAndPayloadCount(metaDataResponse); - metaDataGenerator.setPrev(metaDataResponse); - metaDataGenerator.setNext(metaDataResponse); - - response.addMetaData(metaDataResponse.build()); - } - return Response.status(Response.Status.OK).entity(response.build()).build(); } catch (Exception e) { return exceptionHandler.generateResponseForException(e); } @@ -147,19 +143,16 @@ public Response handlePut(HttpServletRequest httpServletRequest, ServletContext context, String systemId, String jvmId, String queries, String metadata, String body) { try { RealmAuthorizer realmAuthorizer = (RealmAuthorizer) httpServletRequest.getAttribute(RealmAuthorizer.class.getName()); - ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE); + + if (realmAuthorizer.updatable()) { + ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE); - if (realmAuthorizer != null) { - if (realmAuthorizer.updatable()) { - mongoExecutor.execPutRequest(storage.getDatabase().getCollection(collectionName), body, queries, realmAuthorizer.getUpdatableRealms(), systemId, jvmId); - } else { - return Response.status(Response.Status.FORBIDDEN).build(); - } + mongoExecutor.execPutRequest(storage.getDatabase().getCollection(collectionName), body, queries, realmAuthorizer.getUpdatableRealms(), systemId, jvmId); + + return Response.status(Response.Status.OK).build(); } else { - mongoExecutor.execPutRequest(storage.getDatabase().getCollection(collectionName), body, queries, null, systemId, jvmId); + return Response.status(Response.Status.FORBIDDEN).build(); } - - return Response.status(Response.Status.OK).build(); } catch (Exception e) { return exceptionHandler.generateResponseForException(e); } @@ -180,18 +173,16 @@ public Response handlePost(HttpServletRequest httpServletRequest, ServletContext context, String systemId, String jvmId, String metadata, String body) { try { RealmAuthorizer realmAuthorizer = (RealmAuthorizer) httpServletRequest.getAttribute(RealmAuthorizer.class.getName()); - ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE); + + if (realmAuthorizer.writable()) { + ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE); - if (realmAuthorizer != null) { - if (realmAuthorizer.writable()) { - mongoExecutor.execPostRequest(storage.getDatabase().getCollection(collectionName, DBObject.class), body, realmAuthorizer.getWritableRealms(), systemId, jvmId); - } else { - return Response.status(Response.Status.FORBIDDEN).build(); - } + mongoExecutor.execPostRequest(storage.getDatabase().getCollection(collectionName, DBObject.class), body, realmAuthorizer.getWritableRealms(), systemId, jvmId); + + return Response.status(Response.Status.OK).build(); } else { - mongoExecutor.execPostRequest(storage.getDatabase().getCollection(collectionName, DBObject.class), body, null, systemId, jvmId); + return Response.status(Response.Status.FORBIDDEN).build(); } - return Response.status(Response.Status.OK).build(); } catch (Exception e) { return exceptionHandler.generateResponseForException(e); } @@ -212,18 +203,16 @@ public Response handleDelete(HttpServletRequest httpServletRequest, ServletContext context, String queries, String metadata) { try { RealmAuthorizer realmAuthorizer = (RealmAuthorizer) httpServletRequest.getAttribute(RealmAuthorizer.class.getName()); - ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE); + + if (realmAuthorizer.deletable()) { + ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE); - if (realmAuthorizer != null) { - if (realmAuthorizer.deletable()) { - mongoExecutor.execDeleteRequest(storage.getDatabase().getCollection(collectionName), queries, realmAuthorizer.getDeletableRealms()); - } else { - return Response.status(Response.Status.FORBIDDEN).build(); - } + mongoExecutor.execDeleteRequest(storage.getDatabase().getCollection(collectionName), queries, realmAuthorizer.getDeletableRealms()); + + return Response.status(Response.Status.OK).build(); } else { - mongoExecutor.execDeleteRequest(storage.getDatabase().getCollection(collectionName), queries, null); + return Response.status(Response.Status.FORBIDDEN).build(); } - return Response.status(Response.Status.OK).build(); } catch (Exception e) { return exceptionHandler.generateResponseForException(e); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/src/main/java/com/redhat/thermostat/gateway/server/auth/DefaultAuthFilter.java Mon Aug 21 16:44:08 2017 -0400 @@ -0,0 +1,81 @@ +/* + * 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; + +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 com.redhat.thermostat.gateway.common.core.auth.DefaultRealmAuthorizer; +import com.redhat.thermostat.gateway.common.core.auth.InvalidRoleException; +import com.redhat.thermostat.gateway.common.core.auth.RealmAuthorizer; + +public class DefaultAuthFilter implements Filter { + private RealmAuthorizer realmAuthorizer; + + public DefaultAuthFilter() { + try { + realmAuthorizer = new DefaultRealmAuthorizer(); + } catch (InvalidRoleException e) { + throw new IllegalStateException("Unable to create DefaultRealmAuthorizer", e); + } + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + // Do nothing + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + httpServletRequest.setAttribute(RealmAuthorizer.class.getName(), realmAuthorizer); + + chain.doFilter(request, response); + } + + @Override + public void destroy() { + // Do nothing + } +}
--- a/server/src/main/java/com/redhat/thermostat/gateway/server/auth/keycloak/KeycloakRequestFilter.java Mon Aug 21 08:57:27 2017 -0400 +++ b/server/src/main/java/com/redhat/thermostat/gateway/server/auth/keycloak/KeycloakRequestFilter.java Mon Aug 21 16:44:08 2017 -0400 @@ -47,7 +47,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import com.redhat.thermostat.gateway.common.core.auth.keycloak.RealmAuthorizer; +import com.redhat.thermostat.gateway.common.core.auth.RealmAuthorizer; +import com.redhat.thermostat.gateway.common.core.auth.keycloak.KeycloakRealmAuthorizer; public class KeycloakRequestFilter implements Filter { @Override @@ -59,7 +60,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { HttpServletRequest httpServletRequest = (HttpServletRequest) request; - RealmAuthorizer realmAuthorizer = new RealmAuthorizer(httpServletRequest); + RealmAuthorizer realmAuthorizer = new KeycloakRealmAuthorizer(httpServletRequest); httpServletRequest.setAttribute(RealmAuthorizer.class.getName(), realmAuthorizer);
--- a/server/src/main/java/com/redhat/thermostat/gateway/server/services/WebArchiveCoreService.java Mon Aug 21 08:57:27 2017 -0400 +++ b/server/src/main/java/com/redhat/thermostat/gateway/server/services/WebArchiveCoreService.java Mon Aug 21 16:44:08 2017 -0400 @@ -59,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.common.core.servlet.GlobalConstants; +import com.redhat.thermostat.gateway.server.auth.DefaultAuthFilter; import com.redhat.thermostat.gateway.server.auth.basic.BasicLoginService; import com.redhat.thermostat.gateway.server.auth.basic.BasicUserStore; import com.redhat.thermostat.gateway.server.auth.keycloak.KeycloakConfiguration; @@ -86,6 +87,8 @@ webAppContext.setAttribute(GlobalConstants.SERVICE_CONFIG_KEY, serviceConfig); webAppContext.addSystemClass(Configuration.class.getName()); + webAppContext.addSystemClass("com.redhat.thermostat.gateway.common.core.auth.RealmAuthorizer"); + initializeWebSockets(server, webAppContext); @@ -100,9 +103,15 @@ setupBasicAuthForContext(webAppContext); } else if (isSet(ServiceConfiguration.ConfigurationKey.SECURITY_KEYCLOAK)) { setupKeycloakAuthForContext(webAppContext); + } else { + setupDefaultAuthForContext(webAppContext); } } + private void setupDefaultAuthForContext(WebAppContext webAppContext) { + webAppContext.addFilter(DefaultAuthFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); + } + private void setupKeycloakAuthForContext(WebAppContext webAppContext) { String keycloakConfig = (String) serviceConfig.asMap().get(ServiceConfiguration.ConfigurationKey.KEYCLOAK_CONFIG.name()); KeycloakConfiguration keycloakConfiguration = new KeycloakConfigurationFactory().createKeycloakConfiguration(keycloakConfig); @@ -128,7 +137,6 @@ 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)); }
--- a/server/src/test/java/com/redhat/thermostat/gateway/server/auth/keycloak/KeycloakRequestFilterTest.java Mon Aug 21 08:57:27 2017 -0400 +++ b/server/src/test/java/com/redhat/thermostat/gateway/server/auth/keycloak/KeycloakRequestFilterTest.java Mon Aug 21 16:44:08 2017 -0400 @@ -57,7 +57,8 @@ import org.keycloak.representations.AccessToken; import org.mockito.ArgumentMatchers; -import com.redhat.thermostat.gateway.common.core.auth.keycloak.RealmAuthorizer; +import com.redhat.thermostat.gateway.common.core.auth.RealmAuthorizer; +import com.redhat.thermostat.gateway.common.core.auth.keycloak.KeycloakRealmAuthorizer; public class KeycloakRequestFilterTest { @@ -90,7 +91,7 @@ keycloakRequestFilter.doFilter(request, httpServletResponse, filterChain); - verify(request, times(1)).setAttribute(eq(RealmAuthorizer.class.getName()), ArgumentMatchers.any(RealmAuthorizer.class)); + verify(request, times(1)).setAttribute(eq(RealmAuthorizer.class.getName()), ArgumentMatchers.any(KeycloakRealmAuthorizer.class)); verify(filterChain, times(1)).doFilter(eq(request), eq(httpServletResponse)); } @@ -100,7 +101,7 @@ String[] roles = new String[]{"a-realm"}; when(access.getRoles()).thenReturn(new HashSet<>(Arrays.asList(roles))); - when(request.getHeader(eq(RealmAuthorizer.REALMS_HEADER))).thenReturn("blob"); + when(request.getHeader(eq(KeycloakRealmAuthorizer.REALMS_HEADER))).thenReturn("blob"); KeycloakRequestFilter keycloakRequestFilter = new KeycloakRequestFilter(); @@ -111,7 +112,7 @@ 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(request, times(0)).setAttribute(eq(KeycloakRealmAuthorizer.class.getName()), ArgumentMatchers.any(KeycloakRealmAuthorizer.class)); verify(filterChain, times(0)).doFilter(eq(request), eq(httpServletResponse)); }
--- a/services/jvms/src/main/java/com/redhat/thermostat/gateway/service/jvms/http/JvmsHttpHandler.java Mon Aug 21 08:57:27 2017 -0400 +++ b/services/jvms/src/main/java/com/redhat/thermostat/gateway/service/jvms/http/JvmsHttpHandler.java Mon Aug 21 16:44:08 2017 -0400 @@ -40,6 +40,7 @@ import static com.redhat.thermostat.gateway.common.util.ServiceException.DATABASE_UNAVAILABLE; import static com.redhat.thermostat.gateway.common.util.ServiceException.EXPECTED_JSON_ARRAY; import static com.redhat.thermostat.gateway.common.util.ServiceException.MALFORMED_CLIENT_REQUEST; +import static com.redhat.thermostat.gateway.common.util.ServiceException.UNEXPECTED_ERROR; import java.io.IOException; @@ -60,6 +61,7 @@ import com.mongodb.MongoTimeoutException; import com.mongodb.MongoWriteException; +import com.redhat.thermostat.gateway.common.core.auth.RealmAuthorizer; import com.redhat.thermostat.gateway.common.mongodb.servlet.RequestParameters; import com.redhat.thermostat.gateway.common.mongodb.ThermostatMongoStorage; import com.redhat.thermostat.gateway.common.mongodb.servlet.ServletContextConstants; @@ -81,7 +83,8 @@ .add(UnsupportedOperationException.class, MALFORMED_CLIENT_REQUEST) .add(ClassCastException.class, EXPECTED_JSON_ARRAY) .add(MongoTimeoutException.class, DATABASE_UNAVAILABLE) - .add(IOException.class, CANNOT_QUERY_REALMS_PROPERTY); + .add(IOException.class, CANNOT_QUERY_REALMS_PROPERTY) + .add(NullPointerException.class, UNEXPECTED_ERROR); } @GET @@ -145,9 +148,15 @@ @Context HttpServletRequest httpServletRequest ) { try { - ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE); - String message = mongoStorageHandler.getJvmInfo(storage.getDatabase().getCollection(collectionName), systemId, jvmId, includes, excludes); - return Response.status(Response.Status.OK).entity(message).build(); + RealmAuthorizer realmAuthorizer = (RealmAuthorizer) httpServletRequest.getAttribute(RealmAuthorizer.class.getName()); + + if (realmAuthorizer.readable()) { + ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE); + String message = mongoStorageHandler.getJvmInfo(storage.getDatabase().getCollection(collectionName), systemId, jvmId, includes, excludes); + return Response.status(Response.Status.OK).entity(message).build(); + } else { + return Response.status(Response.Status.FORBIDDEN).build(); + } } catch (Exception e) { return exceptionHandler.generateResponseForException(e); } @@ -188,9 +197,15 @@ @Context ServletContext context, @Context HttpServletRequest httpServletRequest) { try { - ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE); - mongoStorageHandler.updateTimestamps(storage.getDatabase().getCollection(collectionName), body, systemId, timeStamp); - return Response.status(Response.Status.OK).build(); + RealmAuthorizer realmAuthorizer = (RealmAuthorizer) httpServletRequest.getAttribute(RealmAuthorizer.class.getName()); + + if (realmAuthorizer.updatable()) { + ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE); + mongoStorageHandler.updateTimestamps(storage.getDatabase().getCollection(collectionName), body, systemId, timeStamp); + return Response.status(Response.Status.OK).build(); + } else { + return Response.status(Response.Status.FORBIDDEN).build(); + } } catch (Exception e) { return exceptionHandler.generateResponseForException(e); } @@ -206,11 +221,19 @@ @QueryParam(RequestParameters.METADATA) @DefaultValue("false") String metadata, @QueryParam(RequestParameters.INCLUDE) String includes, @QueryParam(RequestParameters.EXCLUDE) String excludes, - @Context ServletContext context) { + @Context ServletContext context, + @Context HttpServletRequest httpServletRequest) { try { - ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE); - String message = mongoStorageHandler.getJvmsTree(storage.getDatabase().getCollection(collectionName), aliveOnly, excludes, includes, limit, offset); - return Response.status(Response.Status.OK).entity(message).build(); + RealmAuthorizer realmAuthorizer = (RealmAuthorizer) httpServletRequest.getAttribute(RealmAuthorizer.class.getName()); + + if (realmAuthorizer.readable()) { + ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE); + + String message = mongoStorageHandler.getJvmsTree(storage.getDatabase().getCollection(collectionName), aliveOnly, excludes, includes, limit, offset); + return Response.status(Response.Status.OK).entity(message).build(); + } else { + return Response.status(Response.Status.FORBIDDEN).build(); + } } catch (Exception e) { return exceptionHandler.generateResponseForException(e); }
--- a/services/jvms/src/main/java/com/redhat/thermostat/gateway/service/jvms/mongo/JvmInfoMongoStorageHandler.java Mon Aug 21 08:57:27 2017 -0400 +++ b/services/jvms/src/main/java/com/redhat/thermostat/gateway/service/jvms/mongo/JvmInfoMongoStorageHandler.java Mon Aug 21 16:44:08 2017 -0400 @@ -61,6 +61,7 @@ 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; import com.redhat.thermostat.gateway.common.mongodb.response.ArgumentRunnable; import com.redhat.thermostat.gateway.common.mongodb.response.MongoResponseBuilder; @@ -197,6 +198,7 @@ documents.forEach(new Block<Document>() { @Override public void apply(Document document) { + document.remove(KeycloakFields.REALMS_KEY); setIsAlive(document); String systemId = document.getString(StorageFields.SYSTEM_ID);
--- a/tests/integration-tests/src/test/java/com/redhat/thermostat/gateway/service/jvm/gc/JvmGcServiceIntegrationTest.java Mon Aug 21 08:57:27 2017 -0400 +++ b/tests/integration-tests/src/test/java/com/redhat/thermostat/gateway/service/jvm/gc/JvmGcServiceIntegrationTest.java Mon Aug 21 16:44:08 2017 -0400 @@ -46,6 +46,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; +import com.redhat.thermostat.gateway.common.core.auth.DefaultRealmAuthorizer; import com.redhat.thermostat.gateway.common.mongodb.servlet.RequestParameters; import com.redhat.thermostat.gateway.tests.integration.MongoIntegrationTest; import org.bson.Document; @@ -396,23 +397,23 @@ @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); + String data = "[{\"item\":1,}," + + "{\"item\":2}]"; - collection.insertMany(insertDocuments); + makeHttpMethodRequest(HttpMethod.POST, "", data, + "application/json", "", 200); String updateString = "{\"set\" : {\"realms\" : 1}}"; makeHttpMethodRequest(HttpMethod.PUT,"", updateString,"application/json","", 400); + MongoCollection<Document> collection = mongodTestUtil.getCollection(serviceName); + final Gson gson = new GsonBuilder().create(); + final Type listType = new TypeToken<List<Document>>() {}.getType(); 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)); + assertEquals(getRealmArray(DefaultRealmAuthorizer.DEFAULT_REALM), gson.toJson(document.get("realms"), listType)); } }); } @@ -448,72 +449,61 @@ @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); + String data = "[{\"item\":1,}," + + "{\"item\":2}]"; - collection.insertMany(insertDocuments); + makeHttpMethodRequest(HttpMethod.POST, "", data, + "application/json", "", 200); String updateString = "{\"set\" : {\"realms\" : 1, \"realms\" : 2}}"; makeHttpMethodRequest(HttpMethod.PUT,"", updateString,"application/json","", 400); + MongoCollection<Document> collection = mongodTestUtil.getCollection(serviceName); + final Gson gson = new GsonBuilder().create(); + final Type listType = new TypeToken<List<Document>>() {}.getType(); + 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)); + assertEquals(getRealmArray(DefaultRealmAuthorizer.DEFAULT_REALM), 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); + String data = "[{\"item\":1},{\"item\":2}]"; + makeHttpMethodRequest(HttpMethod.POST, "", data, + "application/json", "", 200); - collection.insertMany(insertDocuments); - - String expected = "{\"response\":[{\"item\":1.0}]}"; + String expected = "{\"response\":[{\"item\":1}]}"; 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); + String data = "[{\"item\":1},{\"item\":2}]"; + makeHttpMethodRequest(HttpMethod.POST, "", data, + "application/json", "", 200); - collection.insertMany(insertDocuments); - String expected = "{\"response\":[{\"item\":1.0}]}"; + String expected = "{\"response\":[{\"item\":1}]}"; - makeHttpGetRequest(gcUrl + "?" + RequestParameters.INCLUDE + "=realms,item",expected, 200); + makeHttpGetRequest(gcUrl + "?" + RequestParameters.INCLUDE + "=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); + String data = "[{\"item\":1},{\"item\":2}]"; + makeHttpMethodRequest(HttpMethod.POST, "", data, + "application/json", "", 200); - collection.insertMany(insertDocuments); ContentResponse response = client.newRequest(gcUrl) - .param(RequestParameters.QUERY, "realms==[\"a\"]").method(HttpMethod.GET).send(); + .param(RequestParameters.QUERY, "realms==" + getRealmArray(DefaultRealmAuthorizer.DEFAULT_REALM)) + .method(HttpMethod.GET).send(); assertEquals(400, response.getStatus()); } @@ -527,12 +517,14 @@ makeHttpMethodRequest(HttpMethod.POST, "", data, "application/json", "", 200); MongoCollection<Document> collection = mongodTestUtil.getCollection(serviceName); + final Gson gson = new GsonBuilder().create(); + final Type listType = new TypeToken<List<Document>>() {}.getType(); FindIterable<Document> documents = collection.find(); documents.forEach(new Block<Document>() { @Override public void apply(Document document) { - assertNull(document.get("realms")); + assertEquals(getRealmArray(DefaultRealmAuthorizer.DEFAULT_REALM), gson.toJson(document.get("realms"), listType)); } }); } @@ -561,4 +553,21 @@ } }); } + + private String getRealmArray(String... realms) { + if (realms != null) { + StringBuilder arrayBuilder = new StringBuilder(); + arrayBuilder.append("["); + + for (String s : realms) { + arrayBuilder.append("\"" + s + "\","); + } + arrayBuilder.deleteCharAt(arrayBuilder.length() - 1); + arrayBuilder.append("]"); + + return arrayBuilder.toString(); + } else { + return "[]"; + } + } }