All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.henkexbg.gallery.service.impl.GalleryAuthorizationServiceSSImpl Maven / Gradle / Ivy

Go to download

There is a newer version: 1.1.1
Show newest version
/**
 * Copyright (c) 2016 Henrik Bjerne
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:The above copyright
 * notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * 
 */
package com.github.henkexbg.gallery.service.impl;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;

import com.github.henkexbg.gallery.bean.GalleryRootDir;
import com.github.henkexbg.gallery.service.GalleryAuthorizationService;
import com.github.henkexbg.gallery.service.GalleryRootDirChangeListener;
import com.github.henkexbg.gallery.service.exception.NotAllowedException;

/**
 * Implementation of the {@link GalleryAuthorizationService} using Spring
 * Security framework. The roles of the current user will be evaluated towards
 * the roles of each root dir to see whether a user has the right to access that
 * root dir or not. This class also listens to
 * {@link GalleryRootDirChangeListener} in order to be able to get runtime
 * changes of the root dirs.
 * 
 * @author Henrik Bjerne
 *
 */
public class GalleryAuthorizationServiceSSImpl implements GalleryAuthorizationService, GalleryRootDirChangeListener {

	private final Logger LOG = LoggerFactory.getLogger(getClass());

	/**
	 * Contains a map where each key is a role, and each value is a map where the
	 * key is the root path name and the value if the File of that root path
	 */
	Map> rootPathsPerRoleMap = new HashMap<>();

	@Override
	public File getRealFileOrDir(String publicPath) throws IOException, NotAllowedException {
		LOG.debug("Entering getRealFileOrDir(publicPath={})", publicPath);
		if (StringUtils.isBlank(publicPath)) {
			throw new NotAllowedException("Could not extract code from empty path!");
		}
		if (rootPathsPerRoleMap == null || rootPathsPerRoleMap.isEmpty()) {
			throw new NotAllowedException("Public path " + publicPath + " not allowed!");
		}
		int relativePathStartIndex = publicPath.indexOf("/");
		String baseDirCode = (relativePathStartIndex < 0) ? publicPath
				: publicPath.substring(0, relativePathStartIndex);
		LOG.debug("baseDirCode: {}", baseDirCode);

		Collection currentUserRoles = getCurrentUserRoles();

		File baseDir = null;
		for (String oneRole : currentUserRoles) {
			Map rootPathsOneRoleMap = rootPathsPerRoleMap.get(oneRole);
			if (rootPathsOneRoleMap != null) {
				baseDir = rootPathsOneRoleMap.get(baseDirCode);
			}
			if (baseDir != null) {
				break;
			}
		}
		if (baseDir == null) {
			String errorMessage = String.format("Could not find basedir for base dir code {}", baseDirCode);
			LOG.error(errorMessage);
			throw new NotAllowedException(errorMessage);
		}
		File file = null;
		if (relativePathStartIndex >= 0) {
			String relativePath = publicPath.substring(relativePathStartIndex, publicPath.length());
			LOG.debug("Relative path: {}", relativePath);
			file = new File(baseDir, relativePath);
			if (!isCanonicalChild(baseDir, file)) {
				throw new NotAllowedException("File " + file + " not allowed!");
			}

		} else {
			// No relative path - just use baseDir itself
			file = baseDir;
		}
		return file;
	}

	/**
	 * Internally converts the root dirs to a more efficient lookup structure and
	 * stores it in {@link #rootPathsPerRoleMap}
	 */
	@Override
	public void setRootDirs(Collection rootDirs) {
		LOG.debug("Updating rootDirs");
		Collection allRoles = rootDirs.stream().map(r -> r.getRole()).collect(Collectors.toSet());
		Map> rootPathsPerRoleMap = new HashMap<>();
		for (String oneRole : allRoles) {
			Map rootPathsForRoles = rootDirs.stream().filter(rd -> oneRole.equals(rd.getRole()))
					.collect(Collectors.toMap(GalleryRootDir::getName, GalleryRootDir::getDir, (dir1, dir2) -> {
						return dir1;
					}));
			rootPathsPerRoleMap.put(oneRole, rootPathsForRoles);
		}
		this.rootPathsPerRoleMap = rootPathsPerRoleMap;
	}

	@Override
	public Map getRootPathsForCurrentUser() {
		Collection currentUserRoles = getCurrentUserRoles();
		Map rootPathsForCurrentUser = new HashMap<>();
		rootPathsPerRoleMap.forEach((role, rps) -> {
			if (currentUserRoles.contains(role)) {
				rootPathsForCurrentUser.putAll(rps);
			}
		});
		return rootPathsForCurrentUser;
	}

	/**
	 * Simpler helper to validate that the child is indeed a canonical child of the
	 * parent file.
	 * 
	 * @param parent Supposed parent file
	 * @param child  Supposed child file
	 * @return True if child is a proper canonical child of parent
	 */
	private boolean isCanonicalChild(File parent, File child) {
		try {
			return child.getCanonicalPath().startsWith(parent.getCanonicalPath());
		} catch (IOException ioe) {
			return false;
		}
	}

	private Collection getCurrentUserRoles() {
		Collection authorities = SecurityContextHolder.getContext().getAuthentication()
				.getAuthorities();
		Collection currentUserRoles = authorities.stream().map(e -> e.getAuthority())
				.collect(Collectors.toSet());
		LOG.debug("Roles for current user: {}", currentUserRoles);
		return currentUserRoles;
	}

	@Override
	public void loginAdminUser() {
		Authentication auth = new Authentication() {

			private static final long serialVersionUID = -7444637463199474476L;

			@Override
			public String getName() {
				return "admin";
			}

			@Override
			public Collection getAuthorities() {
				return rootPathsPerRoleMap.keySet().stream().map(role -> new SimpleGrantedAuthority(role))
						.collect(Collectors.toList());
			}

			@Override
			public Object getCredentials() {
				return null;
			}

			@Override
			public Object getDetails() {
				return null;
			}

			@Override
			public Object getPrincipal() {
				return "admin";
			}

			@Override
			public boolean isAuthenticated() {
				return true;
			}

			@Override
			public void setAuthenticated(boolean arg0) throws IllegalArgumentException {
			}

		};
		SecurityContextHolder.getContext().setAuthentication(auth);
	}

	@Override
	public void logoutAdminUser() {
		SecurityContextHolder.clearContext();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy