com.sun.enterprise.security.provider.PolicyConfigurationImpl Maven / Gradle / Ivy
Show all versions of payara-micro Show documentation
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.enterprise.security.provider;
import javax.security.jacc.*;
import java.io.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.Map;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.lang.reflect.Constructor;
import java.security.*;
import javax.security.auth.Subject;
import java.util.logging.*;
import com.sun.logging.LogDomains;
import com.sun.enterprise.util.LocalStringManagerImpl;
import com.sun.enterprise.security.SecurityRoleMapperFactoryGen;
import com.sun.enterprise.security.provider.PolicyParser.GrantEntry;
import com.sun.enterprise.security.provider.PolicyParser.ParsingException;
import com.sun.enterprise.security.provider.PolicyParser.PermissionEntry;
import com.sun.enterprise.security.provider.PolicyParser.PrincipalEntry;
import org.glassfish.deployment.common.SecurityRoleMapper;
import org.glassfish.deployment.common.SecurityRoleMapperFactory;
/**
* Implementation of Jacc PolicyConfiguration Interface
* @author Harpreet Singh ([email protected])
* @author Ron Monzillo
*/
public class PolicyConfigurationImpl implements PolicyConfiguration {
private static Logger logger =
Logger.getLogger(LogDomains.SECURITY_LOGGER);
private static LocalStringManagerImpl localStrings =
new LocalStringManagerImpl(PolicyConfigurationImpl.class);
//package access
String CONTEXT_ID = null;
// Excluded permissions
private Permissions excludedPermissions = null;
// Unchecked permissions
private Permissions uncheckedPermissions = null;
// permissions mapped to roles.
private HashMap rolePermissionsTable = null;
//private /*TODO: static */ SecurityRoleMapperFactory factory = SecurityRoleMapperFactoryGen.getSecurityRoleMapperFactory();
private static String policySuffix = ".policy";
private static String PROVIDER_URL = "policy.url.";
private static final Class[] permissionParams = { String.class, String.class};
// These are the 3 possible states that this object can be in.
public static final int OPEN_STATE = 0;
public static final int INSERVICE_STATE = 2;
public static final int DELETED_STATE = 3;
// new instances are created in the open state.
protected int state = OPEN_STATE;
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true);
private Lock rLock = rwLock.readLock();
private Lock wLock = rwLock.writeLock();
// this bit is used to optimize commit processing
private boolean writeOnCommit = true;
// this bit is used to optimize refresh processing
private boolean wasRefreshed = false;
private Policy policy = null;
private String policyUrlValue = null;
// policy file mod times
private long[] lastModTimes = new long[2];
private final Object refreshLock = new Object();
private String repository = null;
private Permission setPolicyPermission = null;
private PolicyConfigurationFactoryImpl fact=null;
protected PolicyConfigurationImpl(String contextId, PolicyConfigurationFactoryImpl fact){
CONTEXT_ID = contextId;
this.fact = fact;
repository = fact.getRepository();
// initialize(open,remove,!fromFile)
// initializeRepository();
initialize(true,true,false);
}
/**
* @param applicationPolicyDirectory, need to have absolute path
* @param open, then mark state as open
* @param remove, then remove any existing policy statements
*/
protected PolicyConfigurationImpl
(File applicationPolicyDirectory, boolean open, boolean remove, PolicyConfigurationFactoryImpl fact) {
this.fact = fact;
CONTEXT_ID = applicationPolicyDirectory.getParentFile().getName() +
'/' + applicationPolicyDirectory.getName();
repository = fact.getRepository();
//initializeRepository();
String name = getPolicyFileName(true);
File f = new File(name);
if (!f.exists()) {
String defMsg="Unable to open Policy file: "+name;
String msg= localStrings.getLocalString("pc.file_not_found",defMsg,new Object []{ name});
logger.log(Level.SEVERE,msg);
throw new RuntimeException(defMsg);
}
// initialize(open,remove,fromFile)
initialize(open,remove,true);
}
/**
* This method returns this object's policy context identifier.
* @return this object's policy context identifier.
*
* @throws java.lang.SecurityException
* if called by an AccessControlContext that has not been
* granted the "setPolicy" SecurityPermission.
*
* @throws javax.security.jacc.PolicyContextException
* if the implementation throws a checked exception that has not been
* accounted for by the getContextID method signature. The exception thrown
* by the implementation class will be encapsulated (during construction)
* in the thrown PolicyContextException.
*/
public String getContextID() throws PolicyContextException {
checkSetPolicyPermission();
return this.CONTEXT_ID;
}
/**
* Used to add permissions to a named role in this PolicyConfiguration.
* If the named Role does not exist in the PolicyConfiguration, it is
* created as a result of the call to this function.
*
* It is the job of the Policy provider to ensure that all the permissions
* added to a role are granted to principals "mapped to the role".
*
* @param roleName the name of the Role to which the permissions are
* to be added.
*
* @param permissions the collection of permissions to be added
* to the role. The collection may be either a homogenous or
* heterogenous collection.
*
* @throws java.lang.SecurityException
* if called by an AccessControlContext that has not been
* granted the "setPolicy" SecurityPermission.
*
* @throws java.lang.UnsupportedOperationException
* if the state of the policy context whose interface is this
* PolicyConfiguration Object is "deleted" or "inService" when this
* method is called.
*
* @throws javax.security.jacc.PolicyContextException
* if the implementation throws a checked exception that has not been
* accounted for by the addToRole method signature. The exception thrown
* by the implementation class will be encapsulated (during construction)
* in the thrown PolicyContextException.
*/
public void addToRole(String roleName, PermissionCollection permissions)
throws PolicyContextException
{
assertStateIsOpen();
if (roleName != null && permissions != null) {
checkSetPolicyPermission();
for(Enumeration e = permissions.elements(); e.hasMoreElements();) {
this.getRolePermissions(roleName).add((Permission)e.nextElement());
writeOnCommit = true;
}
}
}
/**
* Used to add a single permission to a named role in this
* PolicyConfiguration.
* If the named Role does not exist in the PolicyConfiguration, it is
* created as a result of the call to this function.
*
* It is the job of the Policy provider to ensure that all the permissions
* added to a role are granted to principals "mapped to the role".
*
* @param roleName the name of the Role to which the permission is
* to be added.
*
* @param permission the permission to be added
* to the role.
*
* @throws java.lang.SecurityException
* if called by an AccessControlContext that has not been
* granted the "setPolicy" SecurityPermission.
*
* @throws java.lang.UnsupportedOperationException
* if the state of the policy context whose interface is this
* PolicyConfiguration Object is "deleted" or "inService" when this
* method is called.
*
* @throws javax.security.jacc.PolicyContextException
* if the implementation throws a checked exception that has not been
* accounted for by the addToRole method signature. The exception thrown
* by the implementation class will be encapsulated (during construction)
* in the thrown PolicyContextException.
*/
public void addToRole(String roleName, Permission permission)
throws PolicyContextException {
assertStateIsOpen();
if (roleName != null && permission != null) {
checkSetPolicyPermission();
this.getRolePermissions(roleName).add(permission);
writeOnCommit = true;
}
}
/**
* Used to add unchecked policy statements to this PolicyConfiguration.
*
* @param permissions the collection of permissions to be added
* as unchecked policy statements. The collection may be either
* a homogenous or heterogenous collection.
*
* @throws java.lang.SecurityException
* if called by an AccessControlContext that has not been
* granted the "setPolicy" SecurityPermission.
*
* @throws java.lang.UnsupportedOperationException
* if the state of the policy context whose interface is this
* PolicyConfiguration Object is "deleted" or "inService" when this
* method is called.
*
* @throws javax.security.jacc.PolicyContextException
* if the implementation throws a checked exception that has not been
* accounted for by the addToUncheckedPolicy method signature.
* The exception thrown
* by the implementation class will be encapsulated (during construction)
* in the thrown PolicyContextException.
*/
public void addToUncheckedPolicy(PermissionCollection permissions)
throws PolicyContextException {
assertStateIsOpen();
if (permissions != null) {
checkSetPolicyPermission();
for(Enumeration e = permissions.elements(); e.hasMoreElements();){
this.getUncheckedPermissions().add((Permission) e.nextElement());
writeOnCommit = true;
}
}
}
/**
* Used to add a single unchecked policy statement to this
* PolicyConfiguration.
*
* @param permission the permission to be added
* to the unchecked policy statements.
*
* @throws java.lang.SecurityException
* if called by an AccessControlContext that has not been
* granted the "setPolicy" SecurityPermission.
*
* @throws java.lang.UnsupportedOperationException
* if the state of the policy context whose interface is this
* PolicyConfiguration Object is "deleted" or "inService" when this
* method is called.
*
* @throws javax.security.jacc.PolicyContextException
* if the implementation throws a checked exception that has not been
* accounted for by the addToUncheckedPolicy method signature.
* The exception thrown
* by the implementation class will be encapsulated (during construction)
* in the thrown PolicyContextException.
*/
public void addToUncheckedPolicy(Permission permission)
throws PolicyContextException{
assertStateIsOpen();
if (permission != null) {
checkSetPolicyPermission();
this.getUncheckedPermissions().add(permission);
writeOnCommit = true;
}
}
/**
* Used to add excluded policy statements to this PolicyConfiguration.
*
* @param permissions the collection of permissions to be added
* to the excluded policy statements. The collection may be either
* a homogenous or heterogenous collection.
*
* @throws java.lang.SecurityException
* if called by an AccessControlContext that has not been
* granted the "setPolicy" SecurityPermission.
*
* @throws java.lang.UnsupportedOperationException
* if the state of the policy context whose interface is this
* PolicyConfiguration Object is "deleted" or "inService" when this
* method is called.
*
* @throws javax.security.jacc.PolicyContextException
* if the implementation throws a checked exception that has not been
* accounted for by the addToExcludedPolicy method signature.
* The exception thrown
* by the implementation class will be encapsulated (during construction)
* in the thrown PolicyContextException.
*/
public void addToExcludedPolicy(PermissionCollection permissions)
throws PolicyContextException {
assertStateIsOpen();
if (permissions != null) {
checkSetPolicyPermission();
for(Enumeration e = permissions.elements(); e.hasMoreElements();){
this.getExcludedPermissions().add((Permission) e.nextElement());
writeOnCommit = true;
}
}
}
/**
* Used to add a single excluded policy statement to this
* PolicyConfiguration.
*
* @param permission the permission to be added
* to the excluded policy statements.
*
* @throws java.lang.SecurityException
* if called by an AccessControlContext that has not been
* granted the "setPolicy" SecurityPermission. fa
*
* @throws java.lang.UnsupportedOperationException
* if the state of the policy context whose interface is this
* PolicyConfiguration Object is "deleted" or "inService" when this
* method is called.
*
* @throws javax.security.jacc.PolicyContextException
* if the implementation throws a checked exception that has not been
* accounted for by the addToExcludedPolicy method signature.
* The exception thrown
* by the implementation class will be encapsulated (during construction)
* in the thrown PolicyContextException.
*/
public void addToExcludedPolicy(Permission permission)
throws PolicyContextException{
assertStateIsOpen();
if (permission != null) {
checkSetPolicyPermission();
this.getExcludedPermissions().add(permission);
writeOnCommit = true;
}
}
/**
* Used to remove a role and all its permissions from this
* PolicyConfiguration.
*
* @param roleName the name of the role to remove from this
* PolicyConfiguration. If the value of the roleName parameter is "*"
* and no role with name "*" exists in this PolicyConfiguration,
* then all roles must be removed from this PolicyConfiguration.
*
* @throws java.lang.SecurityException
* if called by an AccessControlContext that has not been
* granted the "setPolicy" SecurityPermission.
*
* @throws java.lang.UnsupportedOperationException
* if the state of the policy context whose interface is this
* PolicyConfiguration Object is "deleted" or "inService" when this
* method is called.
*
* @throws javax.security.jacc.PolicyContextException
* if the implementation throws a checked exception that has not been
* accounted for by the removeRole method signature. The exception thrown
* by the implementation class will be encapsulated (during construction)
* in the thrown PolicyContextException.
*/
public void removeRole(String roleName)
throws PolicyContextException{
assertStateIsOpen();
if(roleName != null && rolePermissionsTable != null) {
checkSetPolicyPermission();
if (rolePermissionsTable.remove(roleName) != null) {
if (rolePermissionsTable.isEmpty()) {
rolePermissionsTable = null;
}
writeOnCommit = true;
} else if (roleName.equals("*")) {
boolean wasEmpty = rolePermissionsTable.isEmpty();
if (!wasEmpty) {
rolePermissionsTable.clear();
}
rolePermissionsTable = null;
if (!wasEmpty) {
writeOnCommit = true;
}
}
}
}
/**
* Used to remove any unchecked policy statements from this
* PolicyConfiguration.
*
* @throws java.lang.SecurityException
* if called by an AccessControlContext that has not been
* granted the "setPolicy" SecurityPermission.
*
* @throws java.lang.UnsupportedOperationException
* if the state of the policy context whose interface is this
* PolicyConfiguration Object is "deleted" or "inService" when this
* method is called.
*
* @throws javax.security.jacc.PolicyContextException
* if the implementation throws a checked exception that has not been
* accounted for by the removeUncheckedPolicy method signature.
* The exception thrown
* by the implementation class will be encapsulated (during construction)
* in the thrown PolicyContextException.
*/
public void removeUncheckedPolicy()
throws PolicyContextException{
assertStateIsOpen();
checkSetPolicyPermission();
if (uncheckedPermissions != null) {
uncheckedPermissions = null;
writeOnCommit = true;
}
}
/**
* Used to remove any excluded policy statements from this
* PolicyConfiguration.
*
* @throws java.lang.SecurityException
* if called by an AccessControlContext that has not been
* granted the "setPolicy" SecurityPermission.
*
* @throws java.lang.UnsupportedOperationException
* if the state of the policy context whose interface is this
* PolicyConfiguration Object is "deleted" or "inService" when this
* method is called.
*
* @throws javax.security.jacc.PolicyContextException
* if the implementation throws a checked exception that has not been
* accounted for by the removeExcludedPolicy method signature.
* The exception thrown
* by the implementation class will be encapsulated (during construction)
* in the thrown PolicyContextException.
*/
public void removeExcludedPolicy()
throws PolicyContextException{
assertStateIsOpen();
checkSetPolicyPermission();
if (excludedPermissions != null) {
excludedPermissions = null;
writeOnCommit = true;
}
}
/**
* This method is used to set to "inService" the state of the policy context
* whose interface is this PolicyConfiguration Object. Only those policy
* contexts whose state is "inService" will be included in the policy
* contexts processed by the Policy.refresh method. A policy context whose
* state is "inService" may be returned to the "open" state by calling the
* getPolicyConfiguration method of the PolicyConfiguration factory
* with the policy context identifier of the policy context.
*
* When the state of a policy context is "inService", calling any method
* other than commit, delete, getContextID, or inService on its
* PolicyConfiguration Object will cause an UnsupportedOperationException
* to be thrown.
*
* @throws java.lang.SecurityException
* if called by an AccessControlContext that has not been
* granted the "setPolicy" SecurityPermission.
*
* @throws java.lang.UnsupportedOperationException
* if the state of the policy context whose interface is this
* PolicyConfiguration Object is "deleted" when this
* method is called.
*
* @throws javax.security.jacc.PolicyContextException
* if the implementation throws a checked exception that has not been
* accounted for by the commit method signature. The exception thrown
* by the implementation class will be encapsulated (during construction)
* in the thrown PolicyContextException.
*/
public void commit() throws PolicyContextException{
synchronized(refreshLock) {
if(stateIs(DELETED_STATE)){
String defMsg="Cannot perform Operation on a deleted PolicyConfiguration";
String msg=localStrings.getLocalString("pc.invalid_op_for_state_delete",defMsg);
logger.log(Level.WARNING,msg);
throw new UnsupportedOperationException(defMsg);
} else {
try {
checkSetPolicyPermission();
if (stateIs(OPEN_STATE)) {
generatePermissions();
setState(INSERVICE_STATE);
}
} catch(Exception e){
String defMsg="commit fail for contextod "+CONTEXT_ID;
String msg=localStrings.getLocalString("pc.commit_failure",defMsg,new Object[]{CONTEXT_ID,e});
logger.log(Level.SEVERE,msg);
throw new PolicyContextException(e);
}
if (logger.isLoggable(Level.FINE)){
logger.fine("JACC Policy Provider: PC.commit "+CONTEXT_ID);
}
}
}
}
/**
* Creates a relationship between this configuration and another
* such that they share the same principal-to-role mappings.
* PolicyConfigurations are linked to apply a common principal-to-role
* mapping to multiple seperately manageable PolicyConfigurations,
* as is required when an application is composed of multiple
* modules.
*
* Note that the policy statements which comprise a role, or comprise
* the excluded or unchecked policy collections in a PolicyConfiguration
* are unaffected by the configuration being linked to another.
*
* @param link a reference to a different PolicyConfiguration than this
* PolicyConfiguration.
*
* The relationship formed by this method is symetric, transitive
* and idempotent. If the argument PolicyConfiguration does not have a
* different Policy context identifier than this PolicyConfiguration
* no relationship is formed, and an exception, as described below, is
* thrown.
*
* @throws java.lang.SecurityException
* if called by an AccessControlContext that has not been
* granted the "setPolicy" SecurityPermission.
*
* @throws java.lang.UnsupportedOperationException
* if the state of the policy context whose interface is this
* PolicyConfiguration Object is "deleted" or "inService" when this
* method is called.
*
* @throws java.lang.IllegalArgumentException
* if called with an argument PolicyConfiguration whose Policy context
* is equivalent to that of this PolicyConfiguration.
*
* @throws javax.security.jacc.PolicyContextException
* if the implementation throws a checked exception that has not been
* accounted for by the linkConfiguration method signature. The exception
* thrown
* by the implementation class will be encapsulated (during construction)
* in the thrown PolicyContextException.
*/
public void linkConfiguration(PolicyConfiguration link) throws PolicyContextException {
assertStateIsOpen();
String linkId = link.getContextID();
if (this.CONTEXT_ID.equals(linkId)) {
String defMsg="Operation attempted to link PolicyConfiguration to itself.";
String msg=localStrings.getLocalString("pc.unsupported_link_operation",defMsg);
logger.log(Level.WARNING,msg);
throw new IllegalArgumentException(defMsg);
}
checkSetPolicyPermission();
updateLinkTable(linkId);
}
/**
* Causes all policy statements to be deleted from this PolicyConfiguration
* and sets its internal state such that calling any method, other than
* delete, getContextID, or inService on the PolicyConfiguration will
* be rejected and cause an UnsupportedOperationException to be thrown.
*
* This operation has no affect on any linked PolicyConfigurations
* other than removing any links involving the deleted PolicyConfiguration.
*
* @throws java.lang.SecurityException
* if called by an AccessControlContext that has not been
* granted the "setPolicy" SecurityPermission.
*
* @throws javax.security.jacc.PolicyContextException
* if the implementation throws a checked exception that has not been
* accounted for by the delete method signature. The exception thrown
* by the implementation class will be encapsulated (during construction)
* in the thrown PolicyContextException.
*/
public void delete() throws PolicyContextException
{
checkSetPolicyPermission();
synchronized(refreshLock) {
try {
removePolicy();
} finally {
setState(DELETED_STATE);
}
}
}
/**
* This method is used to determine if the policy context whose interface is
* this PolicyConfiguration Object is in the "inService" state.
*
* @return true if the state of the associated policy context is
* "inService"; false otherwise.
*
* @throws java.lang.SecurityException
* if called by an AccessControlContext that has not been
* granted the "setPolicy" SecurityPermission.
*
* @throws javax.security.jacc.PolicyContextException
* if the implementation throws a checked exception that has not been
* accounted for by the inService method signature. The exception thrown
* by the implementation class will be encapsulated (during construction)
* in the thrown PolicyContextException.
*/
public boolean inService() throws PolicyContextException{
checkSetPolicyPermission();
boolean rvalue = stateIs(INSERVICE_STATE);
if (logger.isLoggable(Level.FINE)) {
logger.fine("JACC Policy Provider: inService: " +
(rvalue ? "true " : "false ") +
CONTEXT_ID);
}
return rvalue;
}
// The following methods are implementation specific
protected void checkSetPolicyPermission() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
if (setPolicyPermission == null) {
setPolicyPermission = new java.security.SecurityPermission("setPolicy");
}
sm.checkPermission(setPolicyPermission);
}
}
// get the policy object
protected java.security.Policy getPolicy(){
if (stateIs(INSERVICE_STATE)) {
return this.policy;
}
if (logger.isLoggable(Level.FINEST)) {
logger.finest("JACC Policy Provider: getPolicy ("+CONTEXT_ID+") is NOT in service");
}
return null;
}
// get the policy object
protected Permissions getExcludedPolicy(){
return stateIs(INSERVICE_STATE) ? this.excludedPermissions : null;
}
// called by PolicyWrapper to refresh context specific policy object.
protected void refresh(boolean force){
synchronized(refreshLock){
if (stateIs(INSERVICE_STATE) &&
(wasRefreshed == false || force || filesChanged())) {
// find open policy.url
int i = 0;
String value = null;
String urlKey = null;
while (true) {
urlKey = PROVIDER_URL+(++i);
value = getSecurityProperty(urlKey);
if (value == null || value.equals("")) {
break;
}
}
try {
setSecurityProperty(urlKey, policyUrlValue);
if (fileChanged(false)) {
excludedPermissions = loadExcludedPolicy();
}
// capture time before load, to ensure that we
// have a time that precedes load
captureFileTime(true);
if (policy == null) {
policy = getNewPolicy();
} else {
policy.refresh();
if (logger.isLoggable(Level.FINE)){
logger.fine("JACC Policy Provider: Called Policy.refresh on contextId: "+CONTEXT_ID+" policyUrlValue was "+policyUrlValue);
}
}
wasRefreshed = true;
} finally {
// can't setProperty back to null, workaround is to
// use empty string
setSecurityProperty(urlKey, "");
}
}
}
}
private java.security.Policy getNewPolicy() {
Object wrapper = java.security.Policy.getPolicy();
if (wrapper != null && wrapper instanceof BasePolicyWrapper) {
return ((BasePolicyWrapper) wrapper).getNewPolicy();
} else {
return new sun.security.provider.PolicyFile();
}
}
private void captureFileTime(boolean granted) {
String name = getPolicyFileName(granted);
File f = new File(name);
lastModTimes[(int) (granted ? 1 : 0)] = f.lastModified();
}
private boolean _fileChanged(boolean granted, File f) {
return !(lastModTimes[(int) (granted ? 1 : 0)] == f.lastModified());
}
private boolean fileChanged(boolean granted) {
String name = getPolicyFileName(granted);
File f = new File(name);
return _fileChanged(granted,f);
}
private boolean filesChanged() {
return (fileChanged(true) || fileChanged(false));
}
/**
* tests if policy file has arrived (via synchronization system).
* if File exists, also checks last modified time, in case file was
* not deleted on transition out of inservice state. Called when context
* is not inService to determine if it was needs to be transitioned
* because of file distribution.
* @param granted selects granted or excluded policy file
* @return true if new file has arrived.
*/
private boolean fileArrived(boolean granted) {
String name = getPolicyFileName(granted);
File f = new File(name);
boolean rvalue = ( f.exists() && _fileChanged(granted,f) );
if (logger.isLoggable(Level.FINE)){
logger.fine("JACC Policy Provider: file arrival check" +
" type: " + (granted? "granted " : "excluded ") +
" arrived: " + rvalue +
" exists: " + f.exists() +
" lastModified: " + f.lastModified() +
" storedTime: " + lastModTimes[(int) (granted ? 1 : 0)] +
" state: " + (this.state == OPEN_STATE ? "open " : "deleted ") +
CONTEXT_ID);
}
return rvalue;
}
// initilaize the internal data structures.
// if open, then mark state as open
// if remove, then remove any existing policy statements
// if fromFile (and not remove), then mark state as in service,
// and not requiring write on commit
// if fromFile (and remove), then remove and mark state as open
protected void initialize(boolean open, boolean remove, boolean fromFile) {
synchronized(refreshLock) {
String name = getPolicyFileName(true);
if (open || remove) {
setState(OPEN_STATE);
} else {
setState(INSERVICE_STATE);
}
try {
if (remove) {
removePolicy();
}
policyUrlValue =
sun.net.www.ParseUtil.fileToEncodedURL(new File(name)).toString();
if (fromFile && !remove) {
uncheckedPermissions = null;
rolePermissionsTable = null;
excludedPermissions = loadExcludedPolicy();
initLinkTable();
captureFileTime(true);
writeOnCommit = false;
}
wasRefreshed = false;
} catch (java.net.MalformedURLException mue) {
String defMsg="Unable to convert Policy file Name to URL: "+name;
String msg=localStrings.getLocalString("pc.file_to_url",defMsg, new Object[]{name,mue});
logger.log(Level.SEVERE,msg);
throw new RuntimeException(defMsg);
}
}
}
private String getPolicyFileName(boolean granted) {
return granted ?
getContextDirectoryName()+File.separator+"granted"+policySuffix :
getContextDirectoryName()+File.separator+"excluded"+policySuffix;
}
private String getContextDirectoryName() {
if (repository == null) {
throw new RuntimeException("JACC Policy provider: repository not initialized");
}
return fact.getContextDirectoryName(CONTEXT_ID);
}
// remove the directory used ot hold the context's policy files
private void removePolicyContextDirectory(){
String directoryName = getContextDirectoryName();
File f = new File(directoryName);
if(f.exists()){
// WORKAROUND: due to existence of timestamp file in given directory
// for SE/EE synchronization
File[] files = f.listFiles();
if (files != null && files.length > 0) {
for (int i = 0; i < files.length; i++) {
if(!files[i].delete()) {
String msg = localStrings.getLocalString("pc.file_delete_error","Error while deleting policy file");
logger.log(Level.SEVERE,msg);
throw new RuntimeException(msg);
}
}
}
//WORKAROUND: End
if (!f.delete()) {
String defMsg = "Failure removing policy context directory: "+directoryName;
String msg=localStrings.getLocalString("pc.file_delete_error", defMsg);
logger.log(Level.SEVERE,msg);
throw new RuntimeException(defMsg);
} else if(logger.isLoggable(Level.FINE)){
logger.fine("JACC Policy Provider: Policy context directory removed: "+directoryName);
}
File appDir = f.getParentFile();
// WORKAROUND: due to existence of timestamp file in given directory
// for SE/EE synchronization
File[] fs = appDir.listFiles();
if (fs != null && fs.length > 0) {
boolean hasDir = false;
for (int i = 0; i < fs.length; i++) {
if (fs[i].isDirectory()) {
hasDir = true;
break;
}
}
if (!hasDir) {
for (int i = 0; i < fs.length; i++) {
fs[i].delete();
}
}
}
//WORKAROUND: End
File[] moduleDirs = appDir.listFiles();
if (moduleDirs == null || moduleDirs.length == 0) {
if (!appDir.delete()) {
String defMsg = "Failure removing policy context directory: " + appDir;
String msg = localStrings.getLocalString("pc.file_delete_error", defMsg);
logger.log(Level.SEVERE,msg);
throw new RuntimeException(defMsg);
}
}
}
}
// remove the external (file) policy statements.
private void removePolicyFile(boolean granted){
String fileName = getPolicyFileName(granted);
File f = new File(fileName);
if(f.exists()){
if (!f.delete()) {
String defMsg = "Failure removing policy file: "+fileName;
String msg=localStrings.getLocalString("pc.file_delete_error", defMsg,new Object []{ fileName} );
logger.log(Level.SEVERE,msg);
throw new RuntimeException(defMsg);
} else if(logger.isLoggable(Level.FINE)){
logger.fine("JACC Policy Provider: Policy file removed: "+fileName);
}
}
}
// remove the internal and external (file) policy statements.
private void removePolicy(){
excludedPermissions = null;
uncheckedPermissions = null;
rolePermissionsTable = null;
removePolicyFile(true);
removePolicyFile(false);
removePolicyContextDirectory();
initLinkTable();
policy = null;
writeOnCommit = true;
}
private void initLinkTable() {
synchronized(refreshLock) {
// get the linkSet corresponding to this context.
Set linkSet = (Set) fact.getLinkTable().get(CONTEXT_ID);
// remobe this context id from the linkSet (which may be shared
// with other contexts), and unmap the linkSet form this context.
if (linkSet != null) {
linkSet.remove(CONTEXT_ID);
fact.getLinkTable().remove(CONTEXT_ID);
}
// create a new linkSet with onlythis context id, and put it in the table.
linkSet = new HashSet();
linkSet.add(CONTEXT_ID);
fact.getLinkTable().put(CONTEXT_ID,linkSet);
}
}
private void updateLinkTable(String otherId) {
synchronized(refreshLock) {
// get the linkSet corresponding to this context
Set linkSet = (Set) fact.getLinkTable().get(CONTEXT_ID);
// get the linkSet corresponding to the context being linked to this
Set otherLinkSet = (Set) fact.getLinkTable().get(otherId);
if (otherLinkSet == null) {
String defMsg="Linked policy configuration ("+otherId+") does not exist";
//String msg = localStrings.getLocalString("pc.invalid_link_target",defMsg, new Object []{otherId});
logger.log(Level.SEVERE,"pc.invalid_link_target",otherId);
throw new RuntimeException(defMsg);
} else {
Iterator it = otherLinkSet.iterator();
// for each context (id) linked to the context being linked to this
while (it.hasNext()) {
String id = (String) it.next();
//add the id to this linkSet
linkSet.add(id);
//replace the linkset mapped to all the contexts being linked
//to this context, with this linkset.
fact.getLinkTable().put(id,linkSet);
}
}
}
}
private void setState(int stateValue) {
wLock.lock();
try {
this.state = stateValue;
} finally {
wLock.unlock();
}
}
private boolean _stateIs(int stateValue) {
rLock.lock();
try {
return (this.state == stateValue);
} finally {
rLock.unlock();
}
}
/**
* checks if PolicyContex is in agrument state.
* Detects implicpit state changes resulting from
* distribution of policy files by synchronization
* system.
* @param stateValue state the context is tested for
* @return true if in state.
*/
private boolean stateIs(int stateValue) {
boolean inState = _stateIs(stateValue);
if (stateValue == INSERVICE_STATE && !inState) {
if (fileArrived(true) || fileArrived(false)) {
if (logger.isLoggable(Level.FINE)){
logger.fine("JACC Policy Provider: file arrived transition to inService: " +
" state: " + (this.state == OPEN_STATE ? "open " : "deleted ") +
CONTEXT_ID);
}
// initialize(!open,!remove,fromFile)
initialize(false,false,true);
}
inState = _stateIs(INSERVICE_STATE);
}
return inState;
}
private void assertStateIsOpen() {
if (!stateIs(OPEN_STATE)){
String defMsg="Operation invoked on closed or deleted PolicyConfiguration.";
String msg = localStrings.getLocalString("pc.op_requires_state_open",defMsg);
logger.log(Level.WARNING, msg);
throw new UnsupportedOperationException(defMsg);
}
}
private Permissions getUncheckedPermissions() {
if (uncheckedPermissions == null) {
uncheckedPermissions = new Permissions();
}
return uncheckedPermissions;
}
private Permissions getExcludedPermissions() {
if (excludedPermissions == null) {
excludedPermissions = new Permissions();
}
return excludedPermissions;
}
private Permissions getRolePermissions(String roleName) {
if (rolePermissionsTable == null) rolePermissionsTable = new HashMap();
Permissions rolePermissions = (Permissions) rolePermissionsTable.get(roleName);
if (rolePermissions == null) {
rolePermissions = new Permissions();
rolePermissionsTable.put(roleName,rolePermissions);
}
return rolePermissions;
}
// This method workarounds a bug in PolicyParser.write(...).
private String escapeName(String name) {
return (name != null && name.indexOf('"') > 0) ?
name.replaceAll("\"", "\\\\\"") : name;
}
private void generatePermissions()
throws java.io.FileNotFoundException, java.io.IOException {
// optimization - return if the rules have not changed
if (!writeOnCommit) return;
// otherwise proceed to write policy file
Map roleToSubjectMap = null;
SecurityRoleMapperFactory factory=SecurityRoleMapperFactoryGen.getSecurityRoleMapperFactory();
if (rolePermissionsTable != null) {
// Make sure a role to subject map has been defined for the Policy Context
if (factory != null) {
// the rolemapper is stored against the
// appname, for a web app get the appname for this contextid
SecurityRoleMapper srm = factory.getRoleMapper(CONTEXT_ID);
if (srm != null) {
roleToSubjectMap = srm.getRoleToSubjectMapping();
}
if (roleToSubjectMap != null) {
// make sure all liked PC's have the same roleToSubjectMap
Set linkSet = (Set) fact.getLinkTable().get(CONTEXT_ID);
if (linkSet != null) {
Iterator it = linkSet.iterator();
while (it.hasNext()) {
String contextId = (String)it.next();
if (!CONTEXT_ID.equals(contextId)) {
SecurityRoleMapper otherSrm = factory.getRoleMapper(contextId);
Map otherRoleToSubjectMap = null;
if (otherSrm != null) {
otherRoleToSubjectMap = otherSrm.getRoleToSubjectMapping();
}
if (otherRoleToSubjectMap != roleToSubjectMap) {
String defMsg="Linked policy contexts have different roleToSubjectMaps ("+CONTEXT_ID+")<->("+contextId+")";
String msg=localStrings.getLocalString("pc.linked_with_different_role_maps",defMsg,new Object []{CONTEXT_ID,contextId});
logger.log(Level.SEVERE,msg);
throw new RuntimeException(defMsg);
}
}
}
}
}
}
}
if (roleToSubjectMap == null && rolePermissionsTable != null) {
String defMsg="This application has no role mapper factory defined";
String msg=localStrings.getLocalString("pc.role_map_not_defined_at_commit",defMsg,new Object []{CONTEXT_ID});
logger.log(Level.SEVERE,msg);
throw new RuntimeException
(localStrings.getLocalString
("enterprise.deployment.deployment.norolemapperfactorydefine",defMsg));
}
PolicyParser parser = new PolicyParser(false);
// load unchecked grants in parser
if (uncheckedPermissions != null) {
Enumeration pEnum = uncheckedPermissions.elements();
if (pEnum.hasMoreElements()) {
GrantEntry grant = new GrantEntry();
while (pEnum.hasMoreElements()) {
Permission p = (Permission) pEnum.nextElement();
PermissionEntry entry =
new PermissionEntry(p.getClass().getName(),
p.getName(),p.getActions());
grant.add(entry);
}
parser.add(grant);
}
}
// load role based grants in parser
if (rolePermissionsTable != null) {
Iterator roleIt = rolePermissionsTable.keySet().iterator();
while (roleIt.hasNext()) {
boolean withPrincipals = false;
String roleName = (String) roleIt.next();
Permissions rolePerms = getRolePermissions(roleName);
Subject rolePrincipals = (Subject) roleToSubjectMap.get(roleName);
if (rolePrincipals != null) {
Iterator pit = rolePrincipals.getPrincipals().iterator();
while (pit.hasNext()){
Principal prin = (Principal) pit.next();
if (prin != null) {
withPrincipals = true;
PrincipalEntry prinEntry =
new PrincipalEntry(prin.getClass().getName(),
escapeName(prin.getName()));
GrantEntry grant = new GrantEntry();
grant.principals.add(prinEntry);
Enumeration pEnum = rolePerms.elements();
while (pEnum.hasMoreElements()) {
Permission perm = (Permission) pEnum.nextElement();
PermissionEntry permEntry =
new PermissionEntry(perm.getClass().getName(),
perm.getName(),
perm.getActions());
grant.add(permEntry);
}
parser.add(grant);
}
else {
String msg = localStrings.getLocalString("pc.non_principal_mapped_to_role",
"non principal mapped to role "+roleName,new Object[]{prin,roleName});
logger.log(Level.WARNING,msg);
}
}
}
/**
* JACC MR8 add grant for the any authenticated user role '**'
*/
if (!withPrincipals && ("**".equals(roleName))) {
withPrincipals = true;
PrincipalEntry prinEntry = new PrincipalEntry(
PrincipalEntry.WILDCARD_CLASS,PrincipalEntry.WILDCARD_NAME);
GrantEntry grant = new GrantEntry();
grant.principals.add(prinEntry);
Enumeration pEnum = rolePerms.elements();
while (pEnum.hasMoreElements()) {
Permission perm = (Permission) pEnum.nextElement();
PermissionEntry permEntry =
new PermissionEntry(perm.getClass().getName(),
perm.getName(),
perm.getActions());
grant.add(permEntry);
}
parser.add(grant);
if(logger.isLoggable (Level.FINE)){
logger.fine("JACC Policy Provider: added role grant for any authenticated user");
}
}
if (!withPrincipals) {
String msg = localStrings.getLocalString("pc.no_principals_mapped_to_role",
"no principals mapped to role "+roleName, new Object []{ roleName});
logger.log(Level.WARNING,msg);
}
}
}
writeOnCommit = createPolicyFile(true,parser,writeOnCommit);
// load excluded perms in excluded parser
if (excludedPermissions != null) {
PolicyParser excludedParser = new PolicyParser(false);
Enumeration pEnum = excludedPermissions.elements();
if (pEnum.hasMoreElements()) {
GrantEntry grant = new GrantEntry();
while (pEnum.hasMoreElements()) {
Permission p = (Permission) pEnum.nextElement();
PermissionEntry entry =
new PermissionEntry(p.getClass().getName(),
p.getName(),p.getActions());
grant.add(entry);
}
excludedParser.add(grant);
}
writeOnCommit = createPolicyFile(false,excludedParser,writeOnCommit);
}
if (!writeOnCommit) wasRefreshed = false;
}
private void createPolicyContextDirectory() {
String contextDirectoryName = getContextDirectoryName();
File d = new File(contextDirectoryName);
String defMsg = "unable to create policy context directory";
String msg = localStrings.getLocalString("pc.unable_to_create_context_directory",
defMsg, new Object[]{contextDirectoryName});
if (d.exists()) {
if (!d.isDirectory()) {
logger.log(Level.SEVERE, msg);
throw new RuntimeException(defMsg);
}
} else {
if (!d.mkdirs()) {
logger.log(Level.SEVERE, msg);
throw new RuntimeException(defMsg);
}
}
}
// returns false if write succeeds. otherwise returns input woc (i.e. writeOnCommit)
private boolean createPolicyFile
(boolean granted, PolicyParser parser, boolean woc) throws java.io.IOException {
boolean result = woc;
createPolicyContextDirectory();
removePolicyFile(granted);
String name = getPolicyFileName(granted);
OutputStreamWriter writer = null;
try {
if(logger.isLoggable (Level.FINE)){
logger.fine("JACC Policy Provider: Writing grant statements to policy file: "+name);
}
writer = new OutputStreamWriter(new FileOutputStream(name), "UTF-8");
parser.write(writer);
result = false;
} catch(java.io.FileNotFoundException fnfe) {
String msg=localStrings.getLocalString("pc.file_error","file not found "+name,
new Object []{name, fnfe});
logger.log(Level.SEVERE,msg);
throw fnfe;
} catch(java.io.IOException ioe){
String msg=localStrings.getLocalString("pc.file_write_error","file IO error on file "+name,
new Object []{name,ioe});
logger.log(Level.SEVERE,msg);
throw ioe;
} finally {
if (writer != null) {
try {
writer.close();
captureFileTime(granted);
} catch (Exception e) {
String defMsg="Unable to close Policy file: "+name;
String msg=localStrings.getLocalString("pc.file_close_error",defMsg,new Object []{name,e});
logger.log(Level.SEVERE,msg);
throw new RuntimeException(defMsg);
}
}
}
return result;
}
private Permission loadPermission(String className,String name,String actions){
Class clazz = null;
Permission permission = null;
try{
clazz = Class.forName(className);
Constructor c = clazz.getConstructor(permissionParams);
permission = (Permission) c.newInstance(new Object[] { name, actions });
} catch(Exception e){
String defMsg="PolicyConfiguration error loading permission";
String msg=localStrings.getLocalString("pc.permission_load_error",defMsg,
new Object []{className, e});
logger.log(Level.SEVERE,msg);
throw new RuntimeException(defMsg,e);
}
return permission;
}
private Permissions loadExcludedPolicy() {
Permissions result = null;
String name = getPolicyFileName(false);
FileReader reader = null;
PolicyParser parser = new PolicyParser(false);
try {
captureFileTime(false);
reader = new FileReader(name);
parser.read(reader);
} catch (java.io.FileNotFoundException fnf) {
//Just means there is no excluded Policy file, which
// is the typical case
parser = null;
} catch (java.io.IOException ioe) {
String defMsg="Error reading Policy file: "+name;
String msg=localStrings.getLocalString("pc.file_read_error",defMsg,
new Object []{name, ioe});
logger.log(Level.SEVERE,msg);
throw new RuntimeException(defMsg);
} catch ( ParsingException pe) {
String defMsg="Unable to parse Policy file: "+name;
String msg=localStrings.getLocalString("pc.policy_parsing_exception",defMsg,
new Object []{name,pe});
logger.log(Level.SEVERE,msg);
throw new RuntimeException(defMsg);
} finally {
if (reader != null) {
try {
reader.close();
} catch (Exception e) {
String defMsg="Unable to close Policy file: "+name;
String msg=localStrings.getLocalString("pc.file_close_error",defMsg,
new Object []{name,e});
logger.log(Level.SEVERE,msg);
throw new RuntimeException(defMsg);
}
}
}
if (parser != null) {
Enumeration grants = parser.grantElements();
while (grants.hasMoreElements()) {
GrantEntry grant = (GrantEntry) grants.nextElement();
if (grant.codeBase != null || grant.signedBy != null ||
grant.principals.size() != 0) {
String msg=localStrings.getLocalString("pc.excluded_grant_context_ignored",
"ignore excluded grant context", new Object []{grant});
logger.log(Level.WARNING,msg);
} else {
Enumeration perms = grant.permissionEntries.elements();
while (perms.hasMoreElements()) {
PermissionEntry entry = (PermissionEntry) perms.nextElement();
Permission p =
loadPermission(entry.permission,entry.name,entry.action);
if (result == null) {
result = new Permissions();
}
result.add(p);
}
}
}
}
return result;
}
private void setSecurityProperty(final String key, final String value) {
if (System.getSecurityManager() == null) {
java.security.Security.setProperty(key, value);
} else {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public java.lang.Object run() {
java.security.Security.setProperty(key, value);
return null;
}
});
}
}
private String getSecurityProperty(final String key) {
if (System.getSecurityManager() == null) {
return java.security.Security.getProperty(key);
} else {
return java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public String run() {
return java.security.Security.getProperty(key);
}
});
}
}
}