it.geosolutions.geostore.services.rest.impl.RESTServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for it.geosolutions.geostore.services.rest.impl.RESTServiceImpl.java

Source

/* ====================================================================
 *
 * 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;
    }

}