/* ==================================================================== * * Copyright (C) 2012 - 2016 GeoSolutions S.A.S. * http://www.geo-solutions.it * * GPLv3 + Classpath exception * * This program 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 of the License, or * (at your option) any later version. * * This program 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 this program. * * ==================================================================== * * This software consists of voluntary contributions made by developers * of GeoSolutions. For more information on GeoSolutions, please see * <http://www.geo-solutions.it/>. * */ package it.geosolutions.geostore.services.rest.impl; import it.geosolutions.geostore.core.model.SecurityRule; import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.core.model.enums.Role; import it.geosolutions.geostore.core.model.enums.UserReservedNames; import it.geosolutions.geostore.services.SecurityService; import it.geosolutions.geostore.services.UserService; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; import it.geosolutions.geostore.services.rest.exception.InternalErrorWebEx; import java.security.Principal; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.ws.rs.core.SecurityContext; import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.GrantedAuthorityImpl; /** * Class RESTServiceImpl. * * This is the super class for each RESTServices implementation * * @author ETj (etj at geo-solutions.it) * @author DamianoG */ public abstract class RESTServiceImpl{ private final static Logger LOGGER = Logger.getLogger(RESTServiceImpl.class); @Autowired UserService userService; protected abstract SecurityService getSecurityService(); /** * @return User - The authenticated user that is accessing this service, or null if guest access. */ protected User extractAuthUser(SecurityContext sc) throws InternalErrorWebEx { if (sc == null) throw new InternalErrorWebEx("Missing auth info"); else { Principal principal = sc.getUserPrincipal(); if (principal == null) { // If I'm here I'm sure that the service is running is allowed for the unauthenticated users // due to service-based authorization step that uses annotations on services declaration (seee module geostore-rest-api). // So I'm going to create a Principal to be used during resources-based authorization. principal = createGuestPrincipal(); } if (!(principal instanceof UsernamePasswordAuthenticationToken)) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Mismatching auth principal"); } throw new InternalErrorWebEx("Mismatching auth principal (" + principal.getClass() + ")"); } UsernamePasswordAuthenticationToken usrToken = (UsernamePasswordAuthenticationToken) principal; //DamianoG 06/03/2014 Why create a new Instance when we can deal with the object taken from the DB? Being the instance taken from DB Transient we avoid problems saving security rules... // User user = new User(); // user.setName(usrToken.getName()); // for (GrantedAuthority authority : usrToken.getAuthorities()) { // if (authority != null) { // if (authority.getAuthority() != null // && authority.getAuthority().contains("ADMIN")) // user.setRole(Role.ADMIN); // // if (authority.getAuthority() != null // && authority.getAuthority().contains("USER") && user.getRole() == null) // user.setRole(Role.USER); // // if (user.getRole() == null) // user.setRole(Role.GUEST); // } // } User user = (User)usrToken.getPrincipal(); LOGGER.info("Accessing service with user " + user.getName() + " and role " + user.getRole()); return user; } } /** * This operation is responsible for check if a resource is accessible to an user to perform WRITE operations (update/delete). * this operation must checks first if the user has the right permissions then, if not, check if its group is allowed. * * @param resource * @return boolean */ public boolean resourceAccessWrite(User authUser, long resourceId) { if (authUser.getRole().equals(Role.ADMIN)) { return true; } // else if(belongTo(authUser, GroupReservedNames.ALLRESOURCES.toString())){ // return true; // } else { List<SecurityRule> userSecurityRules = getSecurityService().getUserSecurityRule( authUser.getName(), resourceId); if (userSecurityRules != null && userSecurityRules.size() > 0){ SecurityRule sr = userSecurityRules.get(0); if (sr.isCanWrite()){ return true; } } List<String> groupNames = extratcGroupNames(authUser.getGroups()); if(groupNames != null && groupNames.size() > 0){ List<SecurityRule> groupSecurityRules = getSecurityService().getGroupSecurityRule( groupNames, resourceId); if (groupSecurityRules != null && groupSecurityRules.size() > 0){ // Check if at least one user group has write permission for(SecurityRule sr : groupSecurityRules){ if (sr.isCanWrite()){ return true; } } } } } return false; } /** * This operation is responsible for check if a resource is accessible to an user to perform READ operations. * this operation must checks first if the user has the right permissions then, if not, check if its group is allowed. * * @param resource * @return boolean */ public boolean resourceAccessRead(User authUser, long resourceId) { if (authUser.getRole().equals(Role.ADMIN)) { return true; } // else if(belongTo(authUser, GroupReservedNames.ALLRESOURCES.toString())){ // return true; // } else { List<SecurityRule> userSecurityRules = getSecurityService().getUserSecurityRule( authUser.getName(), resourceId); if (userSecurityRules != null && userSecurityRules.size() > 0){ SecurityRule sr = userSecurityRules.get(0); if (sr.isCanRead()){ return true; } } List<String> groupNames = extratcGroupNames(authUser.getGroups()); if(groupNames != null && groupNames.size() > 0){ List<SecurityRule> groupSecurityRules = getSecurityService().getGroupSecurityRule( groupNames, resourceId); if (groupSecurityRules != null && groupSecurityRules.size() > 0){ // Check if at least one user group has read permission for(SecurityRule sr : groupSecurityRules){ if (sr.isCanRead()){ return true; } } } } } return false; } public ResourceAuth getResourceAuth(User authUser, long resourceId) { if (authUser.getRole().equals(Role.ADMIN)) { return new ResourceAuth(true, true); } List<SecurityRule> userSecurityRules = getSecurityService().getUserSecurityRule(authUser.getName(), resourceId); ResourceAuth ret = new ResourceAuth(); if (CollectionUtils.isNotEmpty(userSecurityRules)){ // take the more permissive grants for (SecurityRule rule : userSecurityRules) { ret.canRead |= rule.isCanRead(); ret.canWrite |= rule.isCanWrite(); if(ret.canRead && ret.canWrite) { // short circuit return ret; } } } List<String> groupNames = extratcGroupNames(authUser.getGroups()); if(groupNames != null && groupNames.size() > 0){ List<SecurityRule> groupSecurityRules = getSecurityService().getGroupSecurityRule(groupNames, resourceId); if (CollectionUtils.isNotEmpty(groupSecurityRules)){ // take the more permissive grants for(SecurityRule rule : groupSecurityRules){ ret.canRead |= rule.isCanRead(); ret.canWrite |= rule.isCanWrite(); if(ret.canRead && ret.canWrite) { // short circuit return ret; } } } } return ret; } /** * Creates a Guest principal with Username="guest" password="" and role ROLE_GUEST. * The guest principal should be used with unauthenticated users. * * @return the Principal instance */ public Principal createGuestPrincipal(){ List<GrantedAuthority> authorities = new ArrayList<>(); authorities.add(new GrantedAuthorityImpl("ROLE_GUEST")); try { User u = userService.get(UserReservedNames.GUEST.userName()); return new UsernamePasswordAuthenticationToken(u,"", authorities); } catch (NotFoundServiceEx e) { if(LOGGER.isDebugEnabled()){ LOGGER.debug("User GUEST is not configured, creating on-the-fly a default one"); } } User guest = new User(); guest.setName("guest"); guest.setRole(Role.GUEST); guest.setGroups(new HashSet<UserGroup>()); Principal principal = new UsernamePasswordAuthenticationToken(guest,"", authorities); return principal; } /** * Given a Group Set returns a List that contains all the group names * * @param groups * @return */ public static List<String> extratcGroupNames(Set<UserGroup> groups){ List<String> groupNames = new ArrayList<>(groups.size() + 1); for(UserGroup ug : groups){ groupNames.add(ug.getGroupName()); } return groupNames; } protected static class ResourceAuth { public ResourceAuth() { this(false, false); } public ResourceAuth(boolean canRead, boolean canWrite) { this.canRead = canRead; this.canWrite = canWrite; } boolean canRead; boolean canWrite; } }