/* * Copyright © 2014 Cask Data, Inc. * * 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 co.cask.cdap.security; import java.security.Permission; import java.security.PermissionCollection; /** * This class is a extention of the existing java {@link SecurityManager} to allow * managing the permission of containers of application started by app-fabric. */ public final class ApplicationSecurity extends SecurityManager { /** * Collection of security permissions. */ private final PermissionCollection permissions; /** * Admin class as set that has ability to by-pass the security manager. */ private final Class<?> adminClass; /** * Invoked by the builder only. * * @param permissions a collection of {@link Permission} objects. * @param adminClass Class that had administratives */ private ApplicationSecurity(PermissionCollection permissions, Class<?> adminClass) { this.permissions = permissions; this.adminClass = adminClass; } /** * Throws a {@link SecurityException} if the requested access, specified by the given * <code>permission</code>, is not permitted based on the {@link Permission} objects * contained in the {@link ApplicationPermissionCollection}. * * @param permission the requested permission */ @Override public void checkPermission(final Permission permission) { // We get the current execution stack as an array of classes. Class<?>[] klasses = getClassContext(); boolean isAdminClass = false; // Iterate and see if the stack has a call that's coming from // the adminClass. If it is, then consider this check coming // the admin class. if (adminClass != null) { for (Class<?> k : klasses) { if (k == this.adminClass) { isAdminClass = true; break; } } } // If admin found on stack, we return, that class is given admin rights. if (isAdminClass) { return; } // If there is a security manager already installed, this method first calls the security manager's // checkPermission method with a RuntimePermission("setSecurityManager") permission to ensure it's ok to // replace the existing security manager. This may result in throwing a SecurityException. if (permission instanceof RuntimePermission && "setSecurityManager".equals(permission.getName())) { throw new SecurityException("Cannot set security manager"); } // For all other permissions we check by invoking implies across all the collection. if (permissions.implies(permission)) { return; } else { throw new SecurityException( "Access denied to " + permission.getClass() + ", name : " + permission.getName() + ", actions : " + permission.getActions() ); } } /** * Static builder for constructing list of permissions. * * @return An instance of builder */ public static Builder builder() { return new Builder(); } /** * Builder for constructing the permissions collection. * The permission collection is a allowed permission list. */ public static class Builder { private final PermissionCollection perms; private Class<?> klass = null; /** * Invoked by the {@link #builder()}. */ private Builder() { perms = new ApplicationPermissionCollection(); } /** * Adds a {@link Permission} object to the {@link ApplicationPermissionCollection}. * * @param permission the permission to be added. * @return this builder. */ public Builder add(Permission permission) { perms.add(permission); return this; } /** * Defines the admin class that has capability to operate outside of restriction * as set by {@link ApplicationSecurity}. * * @param klass that has admin rights within this security manager. * @return this builder. */ public Builder adminClass(Class<?> klass) { this.klass = klass; return this; } /** * Applies the permission. Replaces the security manager for the JVM if there * none set. */ public void apply() { System.setSecurityManager(new ApplicationSecurity(perms, klass)); } } }