/* * Copyright 2015 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.module.sandbox; import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.SetMultimap; import com.google.common.collect.Sets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.security.Permission; import java.util.Iterator; import java.util.Set; /** * A permission set is a group of Permissions and class access that can be granted together to a module. * * @author Immortius */ public class PermissionSet { private static final Logger logger = LoggerFactory.getLogger(PermissionSet.class); private final Set<Class<?>> apiClasses = Sets.newHashSet(); private final Set<String> apiPackages = Sets.newHashSet(); private final Set<Class<? extends Permission>> globallyAllowedPermissionsTypes = Sets.newHashSet(); private final Set<Permission> globallyAllowedPermissionsInstances = Sets.newHashSet(); private final SetMultimap<Class<? extends Permission>, Class<?>> allowedPermissionsTypes = HashMultimap.create(); private final SetMultimap<Permission, Class<?>> allowedPermissionInstances = HashMultimap.create(); private final SetMultimap<Class<? extends Permission>, String> allowedPackagePermissionsTypes = HashMultimap.create(); private final SetMultimap<Permission, String> allowedPackagePermissionInstances = HashMultimap.create(); /** * @param type The type to check whether access is permitted to * @return Whether access to this type is granted by the permission set */ public boolean isPermitted(Class<?> type) { return apiClasses.contains(type) || apiPackages.contains(type.getPackage().getName()); } /** * @param permission The permission to check * @param context The context to check * @return Whether the given permission is granted in the given context, by this permission set */ public boolean isPermitted(Permission permission, Class<?> context) { return globallyAllowedPermissionsTypes.contains(permission.getClass()) || globallyAllowedPermissionsInstances.contains(permission) || allowedPermissionInstances.get(permission).contains(context) || allowedPermissionsTypes.get(permission.getClass()).contains(context) || allowedPackagePermissionInstances.get(permission).contains(context.getPackage().getName()) || allowedPackagePermissionsTypes.get(permission.getClass()).contains(context.getPackage().getName()); } /** * Registers a global permission that all modules are granted * * @param permission The class of permission to grant */ public void grantPermission(Class<? extends Permission> permission) { Preconditions.checkNotNull(permission); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_ALLOWED_PERMISSIONS); } globallyAllowedPermissionsTypes.add(permission); logger.debug("Globally granted permission '{}'", permission); } /** * Registers a global permission that all modules are granted * * @param permission The permission to grant */ public void grantPermission(Permission permission) { Preconditions.checkNotNull(permission); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_ALLOWED_PERMISSIONS); } globallyAllowedPermissionsInstances.add(permission); logger.debug("Globally granted permission '{}'", permission); } /** * Registers a permission that modules are granted when working (directly or indirectly) through the given apiType * * @param apiType The type that requires the permission * @param permission The class of permission to grant */ public void grantPermission(Class<?> apiType, Class<? extends Permission> permission) { Preconditions.checkNotNull(apiType); Preconditions.checkNotNull(permission); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_ALLOWED_PERMISSIONS); } allowedPermissionsTypes.put(permission, apiType); logger.debug("Granted permission '{}' to '{}'", permission, apiType); } /** * Registers a permission that modules are granted when working (directly or indirectly) through the given apiType * * @param apiType The type that requires the permission * @param permission The permission to grant */ public void grantPermission(Class<?> apiType, Permission permission) { Preconditions.checkNotNull(apiType); Preconditions.checkNotNull(permission); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_ALLOWED_PERMISSIONS); } allowedPermissionInstances.put(permission, apiType); logger.debug("Granted permission '{}' to '{}'", permission, apiType); } /** * Registers a permission that modules are granted when working (directly or indirectly) through the given package * * @param packageName The package that requires the permission * @param permission The class of permission to grant */ public void grantPermission(String packageName, Class<? extends Permission> permission) { Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(permission); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_ALLOWED_PERMISSIONS); } allowedPackagePermissionsTypes.put(permission, packageName); logger.debug("Granted permission '{}' to '{}.*'", permission, packageName); } /** * Registers a permission that modules are granted when working (directly or indirectly) through the given package * * @param packageName The package that requires the permission * @param permission The permission to grant */ public void grantPermission(String packageName, Permission permission) { Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(permission); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_ALLOWED_PERMISSIONS); } allowedPackagePermissionInstances.put(permission, packageName); logger.debug("Granted permission '{}' to '{}.*'", permission, packageName); } /** * Adds a class to the API - this allows it to be used directly from a module context. * * @param clazz The class to add to the API. */ public void addAPIClass(Class<?> clazz) { Preconditions.checkNotNull(clazz); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_API_CLASSES); } apiClasses.add(clazz); logger.debug("Added API class '{}'", clazz); } /** * Adds a package to the API - this allows any class from the package to be used directly from a module context. * * @param packageName The package to add to the API */ public void addAPIPackage(String packageName) { Preconditions.checkNotNull(packageName); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_ALLOWED_PERMISSIONS); } apiPackages.add(packageName); logger.debug("Added API classes '{}.*'", packageName); } /** * Removes a permission that has previously been globally allowed * * @param permission The permission to revoke * @return Whether the permission was previously globally allowed */ public boolean revokePermission(Class<? extends Permission> permission) { Preconditions.checkNotNull(permission); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_ALLOWED_PERMISSIONS); } logger.debug("Revoking global permission '{}'", permission); return globallyAllowedPermissionsTypes.remove(permission); } /** * Removes a permission that has previously been globally allowed * * @param permission The permission to revoke * @return Whether the permission was previously globally allowed */ public boolean revokePermission(Permission permission) { Preconditions.checkNotNull(permission); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_ALLOWED_PERMISSIONS); } logger.debug("Revoking global permission '{}'", permission); return globallyAllowedPermissionsInstances.remove(permission); } /** * Remove a permission that has previously been granted to calls passing through a given class * WARNING: Does not revoke permissions granted at a package level * * @param apiType The api class to revoke the permission from * @param permission The permission to revoke * @return whether the permission had previously been granted to the given class. */ public boolean revokePermission(Class<?> apiType, Class<? extends Permission> permission) { Preconditions.checkNotNull(apiType); Preconditions.checkNotNull(permission); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_ALLOWED_PERMISSIONS); } logger.debug("Revoking permission '{}' from '{}'", permission, apiType); return allowedPermissionsTypes.remove(permission, apiType); } /** * Remove a permission that has previously been granted to calls passing through a given class * WARNING: Does not revoke permissions granted at a package level * * @param apiType The api class to revoke the permission from * @param permission The permission to revoke * @return whether the permission had previously been granted to the given class. */ public boolean revokePermission(Class<?> apiType, Permission permission) { Preconditions.checkNotNull(apiType); Preconditions.checkNotNull(permission); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_ALLOWED_PERMISSIONS); } logger.debug("Revoking permission '{}' from '{}'", permission, apiType); return allowedPermissionInstances.remove(permission, apiType); } /** * Remove a permission that has previously been granted to a calls passing through a given package. * This will also revoke permissions granted at a class level, for classes within the package * * @param packageName The package to revoke the permission from * @param permission The class of permission to revoke */ public void revokePermission(String packageName, Class<? extends Permission> permission) { Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(permission); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_ALLOWED_PERMISSIONS); } logger.debug("Revoking permission '{}' from '{}.*'", permission, packageName); allowedPackagePermissionsTypes.remove(permission, packageName); Iterator<Class<?>> iterator = allowedPermissionsTypes.get(permission).iterator(); while (iterator.hasNext()) { Class<?> clazz = iterator.next(); if (packageName.equals(clazz.getPackage().getName())) { iterator.remove(); } } } /** * Remove a permission that has previously been granted to a calls passing through a given package. * This will also revoke permissions granted at a class level, for classes within the package * * @param packageName The package to revoke the permission from * @param permission The permission to revoke */ public void revokePermission(String packageName, Permission permission) { Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(permission); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_ALLOWED_PERMISSIONS); } logger.debug("Revoking permission '{}' from '{}.*'", permission, packageName); allowedPackagePermissionInstances.remove(permission, packageName); Iterator<Class<?>> iterator = allowedPermissionInstances.get(permission).iterator(); while (iterator.hasNext()) { Class<?> clazz = iterator.next(); if (packageName.equals(clazz.getPackage().getName())) { iterator.remove(); } } } /** * Removes a specific class from the list of API classes. * WARNING: This does not revoke access if granted at the package level. * * @param clazz The class to remove from the API * @return Whether the class was perviously an API class */ public boolean revokeAPIClass(Class<?> clazz) { Preconditions.checkNotNull(clazz); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_API_CLASSES); } logger.debug("Removing from API '{}'", clazz); return apiClasses.remove(clazz); } /** * Removes a package and all contained classes from the list of API classes and packages. * * @param packageName The package to remove from the API */ public void revokeAPIPackage(String packageName) { Preconditions.checkNotNull(packageName); if (System.getSecurityManager() != null) { System.getSecurityManager().checkPermission(ModuleSecurityManager.UPDATE_ALLOWED_PERMISSIONS); } logger.debug("Removing from API '{}.*'", packageName); apiPackages.remove(packageName); Iterator<Class<?>> iterator = apiClasses.iterator(); while (iterator.hasNext()) { Class<?> clazz = iterator.next(); if (packageName.equals(clazz.getPackage().getName())) { iterator.remove(); } } } }