/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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 Lesser General Public License for more
* details.
*/
package com.liferay.portal.security.pacl;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.url.URLContainer;
import com.liferay.portal.kernel.util.FileUtil;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.security.pacl.checker.AuthorizationProperty;
import com.liferay.portal.security.pacl.checker.Checker;
import com.liferay.portal.security.pacl.checker.JNDIChecker;
import com.liferay.portal.security.pacl.checker.SQLChecker;
import com.liferay.portal.util.PropsValues;
import java.io.IOException;
import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedAction;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author Raymond Augé
*/
public class GeneratingPACLPolicy extends ActivePACLPolicy {
public GeneratingPACLPolicy(
String contextName, URLContainer urlContainer, ClassLoader classLoader,
Properties properties) {
super(contextName, urlContainer, classLoader, properties);
}
@Override
public boolean hasJNDI(String name) {
JNDIChecker jndiChecker = getJndiChecker();
if (!jndiChecker.hasJNDI(name)) {
AuthorizationProperty authorizationProperty =
jndiChecker.generateAuthorizationProperty(name);
if (authorizationProperty == null) {
return false;
}
mergeAuthorizationProperty(authorizationProperty);
}
return true;
}
@Override
public boolean hasSQL(String sql) {
SQLChecker sqlChecker = getSqlChecker();
if (!sqlChecker.hasSQL(sql)) {
AuthorizationProperty authorizationProperty =
sqlChecker.generateAuthorizationProperty(sql);
if (authorizationProperty == null) {
return false;
}
mergeAuthorizationProperty(authorizationProperty);
}
return true;
}
@Override
public boolean implies(Permission permission) {
Checker checker = getChecker(permission.getClass());
if (checker.implies(permission)) {
return true;
}
try {
AuthorizationProperty authorizationProperty =
checker.generateAuthorizationProperty(permission);
if (authorizationProperty == null) {
return false;
}
mergeAuthorizationProperty(authorizationProperty);
}
catch (Exception e) {
return false;
}
return true;
}
protected void mergeAuthorizationProperty(
AuthorizationProperty authorizationProperty) {
AccessController.doPrivileged(
new AuthorizationPropertyPrivilegedAction(authorizationProperty));
}
protected void mergeExistingProperties() {
// Merge existing properties so that the the written policy is the
// complete picture rather than only a list of the modified properties.
// Therefore, the developer needs only to copy the entire policy.
Properties properties = getProperties();
Enumeration<Object> enumeration = properties.keys();
while (enumeration.hasMoreElements()) {
String key = (String)enumeration.nextElement();
if (_properties.containsKey(key) ||
!key.startsWith("security-manager-") ||
key.equals("security-manager-enabled")) {
continue;
}
_properties.put(key, getPropertySet(key));
}
}
protected void writePACLPolicyFile() {
try {
StringBundler sb = new StringBundler();
for (Map.Entry<String, Set<String>> entry :
_properties.entrySet()) {
String key = entry.getKey();
sb.append(key);
sb.append(StringPool.EQUAL);
Set<String> values = entry.getValue();
for (String value : values) {
sb.append(StringPool.BACK_SLASH);
sb.append(StringPool.NEW_LINE);
sb.append(StringPool.FOUR_SPACES);
sb.append(value);
sb.append(StringPool.COMMA);
}
sb.setIndex(sb.index() - 1);
sb.append("\n\n");
}
if (sb.length() > 0) {
sb.setIndex(sb.index() - 1);
}
FileUtil.write(
PropsValues.LIFERAY_HOME + "/pacl-policy",
getContextName() + ".pacl-policy", sb.toString());
}
catch (IOException ioe) {
_log.error(ioe, ioe);
}
}
private static final Log _log = LogFactoryUtil.getLog(
GeneratingPACLPolicy.class);
private final Map<String, Set<String>> _properties =
new ConcurrentSkipListMap<>();
private final ReentrantLock _reentrantLock = new ReentrantLock();
private class AuthorizationPropertyPrivilegedAction
implements PrivilegedAction<Void> {
public AuthorizationPropertyPrivilegedAction(
AuthorizationProperty authorizationProperty) {
_authorizationProperty = authorizationProperty;
}
@Override
public Void run() {
String key = _authorizationProperty.getKey();
Set<String> values = _properties.get(key);
boolean modified = false;
if (values == null) {
values = getPropertySet(key);
modified = true;
}
for (String value : _authorizationProperty.getValues()) {
if (!values.contains(value)) {
values.add(value);
modified = true;
}
}
if (!modified) {
return null;
}
_reentrantLock.lock();
try {
if (_log.isDebugEnabled()) {
_log.debug(
getContextName() +
" generated authorization property " +
_authorizationProperty);
}
_properties.put(key, values);
mergeExistingProperties();
writePACLPolicyFile();
}
finally {
_reentrantLock.unlock();
}
return null;
}
private final AuthorizationProperty _authorizationProperty;
}
}