/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.authorization.xml.internal;
import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.osgi.framework.BundleContext;
import de.rcenvironment.core.authorization.AuthorizationStore;
import de.rcenvironment.core.authorization.AuthorizationStoreException;
import de.rcenvironment.core.authorization.rbac.Permission;
import de.rcenvironment.core.authorization.rbac.RBACObject;
import de.rcenvironment.core.authorization.rbac.Role;
import de.rcenvironment.core.authorization.rbac.Subject;
import de.rcenvironment.core.configuration.ConfigurationService;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.core.utils.incubator.Assertions;
import de.rcenvironment.core.utils.incubator.xml.XMLIOSupport;
import de.rcenvironment.core.utils.incubator.xml.XMLSupport;
/**
*
* Provides an implementation of {@link AuthorizationStore} by using an XML document as store.
*
* @author Doreen Seider
*/
public class XMLAuthorizationStore implements AuthorizationStore {
/**
* Constant for a logger or exception message.
*/
private static final String ERROR_PARSING_DOCUMENT = "Error while parsing the XML document.";
/**
* Constant for a logger or exception message.
*/
private static final String ERROR_ARGUMENT_IS_NULL = "Argument for parameter %s is null.";
/**
* XPath query to select permission elements.
*/
private static final String XPATH_QUERY_SELECT_DESCRIPTION = "./description";
/**
* XPath query to select role elements.
*/
private static final String XPATH_QUERY_SELECT_ROLES = "child::role";
/**
* XPath query to select permission elements.
*/
private static final String XPATH_QUERY_SELECT_PERMISSIONS = "child::permission";
/**
* XPath query to select an users.
*/
private static final String XPATH_QUERY_SELECT_SUBJECT = "//subject[@id='%s']";
/**
* XPath query to select a group.
*/
private static final String XPATH_QUERY_SELECT_ROLE = "//role[@id='%s']";
/**
* XPath query to select an extension.
*/
private static final String XPATH_QUERY_SELECT_ALL_ROLES = "//roles/*";
/**
* XPath query to select an extension.
*/
private static final String XPATH_QUERY_SELECT_PERMISSION = "//permission[@id='%s']";
/**
* XPath query to select an extension.
*/
private static final String XPATH_QUERY_SELECT_ALL_PERMISSIONS = "//permissions/*";
/**
* XPath query to select an extension.
*/
private static final String XPATH_QUERY_SELECT_ID = "attribute::id";
/**
* Logger for this class.
*/
private static final Log LOGGER = LogFactory.getLog(XMLAuthorizationStore.class);
/**
* Configuration of this bundle.
*/
private XMLAuthorizationConfiguration myConfiguration;
/**
* The internal representation of the XML file.
*/
private Document myXMLDocument = null;
private ConfigurationService configurationService;
private String bundleSymbolicName;
protected void activate(BundleContext context) {
bundleSymbolicName = context.getBundle().getSymbolicName();
// note: disabled old configuration loading for 6.0.0 as it is not being used anyway
// myConfiguration = configurationService.getConfiguration(context.getBundle().getSymbolicName(),
// XMLAuthorizationConfiguration.class);
// TODO using default values until reworked or removed
myConfiguration = new XMLAuthorizationConfiguration();
}
protected void bindConfigurationService(ConfigurationService newConfigurationService) {
this.configurationService = newConfigurationService;
}
@Override
public void initialize() throws AuthorizationStoreException {
// Try to load the XML file and store it as a 'Document' representation.
try {
String absPath = configurationService.resolveBundleConfigurationPath(bundleSymbolicName, myConfiguration.getXmlFile());
if (absPath != null && new File(absPath).exists()) {
myXMLDocument = XMLIOSupport.readXML(absPath);
} else {
myXMLDocument = XMLIOSupport.readXML(getClass().getResourceAsStream("/placeholder.xml"));
LOGGER.info("No authorization store is given under: " + absPath + ". Using an empty one.");
}
} catch (DocumentException e) {
throw new AuthorizationStoreException(ERROR_PARSING_DOCUMENT, e);
}
LOGGER.info("XML authorization store initialized.");
}
@Override
public Permission lookupPermission(String permissionID) {
Assertions.isDefined(permissionID, StringUtils.format(ERROR_ARGUMENT_IS_NULL, "permissionID"));
try {
LOGGER.debug("Retrieving entry for permission: " + permissionID);
return getPermission(permissionID);
} catch (DocumentException e) {
return null;
}
}
@Override
public Role lookupRole(String roleID) {
Assertions.isDefined(roleID, StringUtils.format(ERROR_ARGUMENT_IS_NULL, "roleID"));
try {
LOGGER.debug("Retrieving entry for role: " + roleID);
return getRole(roleID);
} catch (DocumentException e) {
return null;
}
}
@Override
public Subject lookupSubject(String subjectID) {
Assertions.isDefined(subjectID, StringUtils.format(ERROR_ARGUMENT_IS_NULL, "subjectID"));
try {
LOGGER.debug("Retrieving entry for subject: " + subjectID);
return getSubject(subjectID);
} catch (DocumentException e) {
return null;
}
}
/**
*
* Creates a new {@link Subject} object from the underlying entry in the XML document.
*
* @param subjectID The ID of the specified {@link Subject}.
* @return the created {@link Subject} object.
* @throws DocumentException if the specified arguments are illegal.
*/
private Subject getSubject(String subjectID) throws DocumentException {
Node subjectNode = XMLSupport.selectNode(myXMLDocument, StringUtils.format(XPATH_QUERY_SELECT_SUBJECT, subjectID));
return getSubject(subjectNode);
}
/**
*
* Creates a new {@link Role} object from the underlying entry in the XML document.
*
* @param roleID The ID of the specified {@link Role}.
* @return the created {@link Role} object.
* @throws DocumentException if the specified arguments are illegal.
*/
private Role getRole(String roleID) throws DocumentException {
Node roleNode = XMLSupport.selectNode(myXMLDocument, StringUtils.format(XPATH_QUERY_SELECT_ROLE, roleID));
return getRole(roleNode);
}
/**
*
* Creates a new {@link Subject} object from the underlying entry in the XML document.
*
* @param permissionID The ID of the specified {@link Permission}.
* @return the created {@link Permission} object.
* @throws DocumentException if the specified arguments are illegal.
*/
private Permission getPermission(String permissionID) throws DocumentException {
Node permissionNode = XMLSupport.selectNode(myXMLDocument, StringUtils.format(XPATH_QUERY_SELECT_PERMISSION, permissionID));
return getPermission(permissionNode);
}
/**
*
* Creates a new {@link Subject} object from the underlying entry in the XML document.
*
* @param subjectNode The node describing the {@link Subject} entry.
* @return the created {@link Subject} object.
* @throws DocumentException if the specified arguments are illegal.
*/
private Subject getSubject(Node subjectNode) throws DocumentException {
String id = getID(subjectNode);
String description = getDescription(subjectNode);
List<Node> roleNodes = XMLSupport.selectNodes(subjectNode, XPATH_QUERY_SELECT_ROLES);
Set<Role> roles = new HashSet<Role>();
// Add all defined roles to this subject
for (Node roleNode : roleNodes) {
Set<String> roleIDs = getMatchingRoleIDs(roleNode.getText());
for (String roleID : roleIDs) {
roles.add(getRole(roleID));
}
}
if (description.isEmpty()) {
return new Subject(id, roles);
} else {
return new Subject(id, description, roles);
}
}
/**
*
* Creates a new {@link Role} object from the underlying entry in the XML document.
*
* @param roleNode The node describing the {@link Role} entry.
* @return the created {@link Role} object.
* @throws DocumentException if the specified arguments are illegal.
*/
private Role getRole(Node roleNode) throws DocumentException {
// Create a new role object using the information provided by the underlying XML file.
String id = getID(roleNode);
String description = getDescription(roleNode);
List<Node> permissionNodes = XMLSupport.selectNodes(roleNode, XPATH_QUERY_SELECT_PERMISSIONS);
Set<Permission> permissions = new HashSet<Permission>();
// Add all defined permissions to this role.
for (Node permissionNode : permissionNodes) {
Set<String> permissionIDs = getMatchingPermissionIDs(permissionNode.getText());
for (String permissionID : permissionIDs) {
permissions.add(getPermission(permissionID));
}
}
if (description.isEmpty()) {
return new Role(id, permissions);
} else {
return new Role(id, description, permissions);
}
}
/**
*
* Creates a new {@link Permission} object from the underlying entry in the XML document.
*
* @param permissionNode The {@link Node} describing the permission entry.
* @return the created {@link Permission} object.
* @throws DocumentException if the specified arguments are illegal.
*/
private Permission getPermission(Node permissionNode) throws DocumentException {
String id = getID(permissionNode);
String description = getDescription(permissionNode);
if (description.isEmpty()) {
return new Permission(id);
} else {
return new Permission(id, description);
}
}
/**
*
* Returns the description of an {@link RBACObject} from the underlying entry in the XML document.
*
* @param node The node of the authorization entry.
* @return the description of the authorization object or an empty.
* @throws DocumentException if the specified arguments are illegal.
*/
private String getDescription(Node node) throws DocumentException {
Node descriptionNode = XMLSupport.selectNode(node, XPATH_QUERY_SELECT_DESCRIPTION);
if (descriptionNode != null) {
return descriptionNode.getText();
} else {
return "";
}
}
/**
*
* Returns the ID of an <code>RBACObject</code> from the underlying entry in the XML document.
*
* @param node The node of the authorization entry.
* @return the ID of the authorization object.
* @throws DocumentException if the specified arguments are illegal.
*/
private String getID(Node node) throws DocumentException {
Node idNode = XMLSupport.selectNode(node, XPATH_QUERY_SELECT_ID);
if (idNode != null) {
return idNode.getText();
} else {
throw new DocumentException(ERROR_PARSING_DOCUMENT);
}
}
/**
*
* Returns all role IDs of the underlying XML document matching the given regular expression.
*
* @param regExpressedRoleID The regular expression.
* @return a set of matching role IDs.
* @throws DocumentException if the specified arguments are illegal.
*/
private Set<String> getMatchingRoleIDs(String regExpressedRoleID) throws DocumentException {
Set<String> permissionIDs = new HashSet<String>();
List<Node> roleNodes = XMLSupport.selectNodes(myXMLDocument, XPATH_QUERY_SELECT_ALL_ROLES);
for (Node roleNode : roleNodes) {
String id = getID(roleNode);
if (id.matches(regExpressedRoleID)) {
permissionIDs.add(id);
}
}
return permissionIDs;
}
/**
*
* Returns all permission IDs of the underlying XML document matching the given regular expression.
*
* @param regExpressedPermissionID The regular expression.
* @return a set of matching permission IDs.
* @throws DocumentException if the specified arguments are illegal.
*/
private Set<String> getMatchingPermissionIDs(String regExpressedPermissionID) throws DocumentException {
Set<String> permissionIDs = new HashSet<String>();
List<Node> permissionNodes = XMLSupport.selectNodes(myXMLDocument, XPATH_QUERY_SELECT_ALL_PERMISSIONS);
for (Node permissionNode : permissionNodes) {
String id = getID(permissionNode);
if (id.matches(regExpressedPermissionID)) {
permissionIDs.add(id);
}
}
return permissionIDs;
}
}