/**
* TNTConcept Easy Enterprise Management by Autentia Real Bussiness Solution S.L.
* Copyright (C) 2007 Autentia Real Bussiness Solution S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.autentia.tnt.manager.security.impl.fixed;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.acegisecurity.GrantedAuthority;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.autentia.tnt.dao.ITransferObject;
import com.autentia.tnt.manager.security.Permission;
import com.autentia.tnt.manager.security.Principal;
import com.autentia.tnt.manager.security.exception.SecConfigException;
import com.autentia.tnt.manager.workflow.Field;
import com.autentia.tnt.manager.workflow.State;
import com.autentia.tnt.manager.workflow.WorkFlow;
import com.autentia.tnt.util.ConfigurationUtil;
import com.autentia.tnt.xml.DOMUtils;
/**
* This class holds all security configuration for the "default fixed security"
* of TNTConcept.
*
* @author Ivan Zaera Avellon
*/
public final class DefaultSecurityConfiguration implements
ISecurityConfiguration, IWorkFlowConfiguration {
private Map<Integer, GrantedAuthority[]> rolePermissions;
private Map<AclMatrixKey, AclMatrixValue> writeMatrix;
private Map<AclMatrixKey, AclMatrixValue> deleteMatrix;
private Map<AclMatrixKey, AclMatrixValue> readMatrix;
private Map<String, WorkFlow> workFlowMatrix;
private Map<FieldAclMatrixKey, Boolean> detailViewMatrix;
private int roleAdminId;
private int roleSupervisorId;
private int roleStaffId;
private int roleUserId;
private int roleClientId;
private int roleProjectManagerId;
private static final Log logger = LogFactory
.getLog(DefaultSecurityConfiguration.class);
/**
* Constructor
*
* @param cfg
*/
public DefaultSecurityConfiguration(ConfigurationUtil cfg) {
try {
String path2File = cfg.getConfigDir() + cfg.getSecurityMatrix();
File confFile = new File(path2File);
Map<GrantedAuthority, boolean[]> permissionsMap = null;
if (!confFile.exists()) {
throw new SecConfigException(
"Fichero de configuración de seguridad no encontrado:"
+ path2File);
}
logger.info("Loading ACEGI configuration from " + confFile);
this.roleAdminId = cfg.getRoleAdminId();
this.roleSupervisorId = cfg.getRoleSupervisorId();
this.roleStaffId = cfg.getRoleStaffId();
this.roleUserId = cfg.getRoleUserId();
this.roleClientId = cfg.getRoleClientId();
this.roleProjectManagerId = cfg.getRoleProjectManagerId();
this.readMatrix = new HashMap<AclMatrixKey, AclMatrixValue>();
this.writeMatrix = new HashMap<AclMatrixKey, AclMatrixValue>();
this.deleteMatrix = new HashMap<AclMatrixKey, AclMatrixValue>();
this.rolePermissions = new HashMap<Integer, GrantedAuthority[]>();
this.workFlowMatrix = new HashMap<String, WorkFlow>();
this.detailViewMatrix = new HashMap<FieldAclMatrixKey, Boolean>();
permissionsMap = new HashMap<GrantedAuthority, boolean[]>();
DocumentBuilderFactory domFactory = DocumentBuilderFactory
.newInstance();
DocumentBuilder domBuilder = domFactory.newDocumentBuilder();
org.w3c.dom.Document document = domBuilder.parse(confFile);
this.loadEntities(document, permissionsMap);
this.loadActions(document, permissionsMap);
this.loadFlows(document, workFlowMatrix);
this.readMatrix = Collections.unmodifiableMap(this.readMatrix);
this.writeMatrix = Collections.unmodifiableMap(this.writeMatrix);
this.deleteMatrix = Collections.unmodifiableMap(this.deleteMatrix);
this.workFlowMatrix = Collections
.unmodifiableMap(this.workFlowMatrix);
this.detailViewMatrix = Collections.unmodifiableMap(this.detailViewMatrix);
this.rolePermissions.put(roleAdminId, loadPermsArray(
permissionsMap, 0));
this.rolePermissions.put(roleSupervisorId, loadPermsArray(
permissionsMap, 1));
this.rolePermissions.put(roleStaffId, loadPermsArray(
permissionsMap, 2));
this.rolePermissions.put(roleUserId, loadPermsArray(permissionsMap,
3));
this.rolePermissions.put(roleClientId, loadPermsArray(
permissionsMap, 4));
this.rolePermissions.put(roleProjectManagerId, loadPermsArray(
permissionsMap, 5));
logger.info("ACEGI configuration OK readed" + confFile);
} catch (Exception ex) {
logger.fatal(ex); // SAXException, ParserConfigurationException,
// IOException, SecConfigException
}
}
/**
* Inicializa las matrices de privilegios de operaciones de Lectura,
* Escritura y Borrado de cada una de las clases.
*
* @param permissionsMap
*/
private void loadEntities(org.w3c.dom.Document document,
Map<GrantedAuthority, boolean[]> permissionsMap)
throws SecConfigException {
NodeList entities = null;
Node entity = null;
String classname = null;
NodeList operations = null;
Node operation = null;
String operationName = null;
AclMatrixValue admin = null;
AclMatrixValue super1 = null;
AclMatrixValue staff = null;
AclMatrixValue user = null;
AclMatrixValue cli = null;
AclMatrixValue projectManager = null;
Map<AclMatrixKey, AclMatrixValue> targetMatrix = null;
logger.debug("Reading 'entities'");
try {
// Cargamos las entidades
entities = document.getElementsByTagName("entity");
for (int i = 0, iCount = entities.getLength(); i < iCount; i++) {
entity = entities.item(i);
if ("entity".equals(entity.getNodeName())) {
classname = DOMUtils.getAttribute(entity, "name");
if (logger.isDebugEnabled()){
logger.debug("Entity readed: " + classname);
}
classname = "com.autentia.tnt.businessobject." + classname;
operations = entity.getChildNodes();
for (int j = 0, jCount = operations.getLength(); j < jCount; j++) {
operation = operations.item(j);
// <operation name="delete" admin="deny" super="deny"
// staff="deny" user="deny" cli="deny"/>
if ("operation".equals(operation.getNodeName())) {
operationName = DOMUtils.getAttribute(operation,
"name");
if (logger.isDebugEnabled()){
logger.debug("Operation readed: " + operationName);
}
targetMatrix = null;
if ("read".equals(operationName)) {
targetMatrix = readMatrix;
} else if ("write".equals(operationName)) {
targetMatrix = writeMatrix;
} else if ("delete".equals(operationName)) {
targetMatrix = deleteMatrix;
} else if ("list".equals(operationName)
|| "create".equals(operationName)
|| "menu".equals(operationName)) {
// Nada
} else {
logger.fatal("Invalid Operation: "
+ operationName);
throw new SecConfigException(
"Invalid Operation: " + operationName);
}
if (targetMatrix != null) {
// Es una operacion del tipo: {delete, write,
// read}
admin = getVisibility(operation, "admin");
super1 = getVisibility(operation, "super");
staff = getVisibility(operation, "staff");
user = getVisibility(operation, "user");
cli = getVisibility(operation, "cli");
projectManager = getVisibility(operation, "projectManager");
Class _class = Class.forName(classname);
Class<? extends ITransferObject> t = _class;
putInMatrix(targetMatrix, t, admin, super1,
staff, user, cli, projectManager);
} else {
// Es una operacion del tipo: {list, create,
// menu}
Class parameter = Class.forName(classname);
boolean[] permision = this
.getPermisionArray(operation);
GrantedAuthority methodResult = null;
if ("list".equals(operationName)) {
methodResult = Permission
.Entity_List(parameter);
} else if ("create".equals(operationName)) {
methodResult = Permission
.Entity_Create(parameter);
} else if ("menu".equals(operationName)) {
methodResult = Permission
.Entity_Menu(parameter);
}
if (methodResult == null) {
throw new NullPointerException(
"Permission return null");
}
permissionsMap.put(methodResult, permision);
}
} else if ("detailView".equals(operation.getNodeName())) {
NodeList fields = operation.getChildNodes();
for (int k = 0, fCount = fields.getLength(); k < fCount; k++) {
Node field = fields.item(k);
if ("field".equals(field.getNodeName())) {
String fieldName = DOMUtils.getAttribute(field, "name");
Boolean adminView = getFieldVisibility(field, "admin");
Boolean superView = getFieldVisibility(field, "super");
Boolean staffView = getFieldVisibility(field, "staff");
Boolean userView = getFieldVisibility(field, "user");
Boolean cliView = getFieldVisibility(field, "cli");
Boolean projectManagerView = getFieldVisibility(field, "projectManager");
Class _class = Class.forName(classname);
Class<? extends ITransferObject> t = _class;
putInDetailViewMatrix(t, fieldName, adminView, superView, staffView, userView,
cliView, projectManagerView);
}
}
}
}
}
}
} catch (SecConfigException ex) {
throw ex;
} catch (Exception ex) {
logger.fatal(ex);
throw new SecConfigException("Invalid entity detected: "
+ classname, ex);
}
}
public Map<AclMatrixKey, AclMatrixValue> getWriteMatrix() {
return writeMatrix;
}
public Map<AclMatrixKey, AclMatrixValue> getReadMatrix() {
return readMatrix;
}
public Map<AclMatrixKey, AclMatrixValue> getDeleteMatrix() {
return deleteMatrix;
}
public Map<Integer, GrantedAuthority[]> getRolesMatrix() {
return rolePermissions;
}
/**
* @param document
* @param permissionsMap
*/
private void loadActions(org.w3c.dom.Document document,
Map<GrantedAuthority, boolean[]> permissionsMap)
throws SecConfigException {
Node action = null;
String name = null;
boolean[] permision = null;
java.lang.reflect.Field _field = null;
GrantedAuthority ga = null;
try {
NodeList actions = document.getElementsByTagName("action");
for (int i = 0, iCount = actions.getLength(); i < iCount; i++) {
action = actions.item(i);
name = DOMUtils.getAttribute(action, "name");
_field = Permission.class.getField(name);
permision = getPermisionArray(action);
ga = (GrantedAuthority) _field.get(null);
permissionsMap.put(ga, permision);
} // end for
} catch (Exception ex) {
logger.fatal(ex);
throw new SecConfigException(null, ex);
}
}
/**
* @param attrs
* @return
*/
private boolean[] getPermisionArray(Node node) {
boolean[] privileges = new boolean[6];
String[] profiles = { "admin", "super", "staff", "user", "cli", "projectManager" };
String pvalue;
for (int i = 0; i < profiles.length; i++) {
pvalue = DOMUtils.getAttribute(node, profiles[i]);
privileges[i] = "true".equals(pvalue);
}
return privileges;
}
/**
* Add levels to a matrix for a specified matrix entry
*
* @param matrix
* the matrix to add to
* @param type
* type of transfer object
* @param adminLevel
* level to assign to administrator users
* @param supervisorLevel
* level to assign to supervisor users
* @param staffLevel
* level to assign to staff users
* @param userLevel
* level to assign to normal users
* @param clientLevel
* level to assign to client users
*/
private void putInMatrix(Map<AclMatrixKey, AclMatrixValue> matrix,
Class<? extends ITransferObject> type, AclMatrixValue adminLevel,
AclMatrixValue supervisorLevel, AclMatrixValue staffLevel,
AclMatrixValue userLevel, AclMatrixValue clientLevel, AclMatrixValue projectManagerLevel) {
matrix.put(new AclMatrixKey(type, roleAdminId), adminLevel);
matrix.put(new AclMatrixKey(type, roleSupervisorId), supervisorLevel);
matrix.put(new AclMatrixKey(type, roleStaffId), staffLevel);
matrix.put(new AclMatrixKey(type, roleUserId), userLevel);
matrix.put(new AclMatrixKey(type, roleClientId), clientLevel);
matrix.put(new AclMatrixKey(type, roleProjectManagerId), projectManagerLevel);
}
/**
* Add levels to a matrix for a specified matrix entry
*
* @param type type of transfer object
* @param fieldName Name of the field in the entity
* @param adminView If is true lets administrator user to view the field.
* @param supervisorView If is true lets supervisor user to view the field.
* @param staffView If is true lets staff user to view the field.
* @param userView If is true lets normal user to view the field.
* @param clientView If is true lets client user to view the field.
*/
private void putInDetailViewMatrix(Class<? extends ITransferObject> type, String fieldName, Boolean adminView,
Boolean supervisorView, Boolean staffView, Boolean userView, Boolean clientView, Boolean projectManagerView) {
this.detailViewMatrix.put(new FieldAclMatrixKey(type, roleAdminId, fieldName), adminView);
this.detailViewMatrix.put(new FieldAclMatrixKey(type, roleSupervisorId, fieldName), supervisorView);
this.detailViewMatrix.put(new FieldAclMatrixKey(type, roleStaffId, fieldName), staffView);
this.detailViewMatrix.put(new FieldAclMatrixKey(type, roleUserId, fieldName), userView);
this.detailViewMatrix.put(new FieldAclMatrixKey(type, roleClientId, fieldName), clientView);
this.detailViewMatrix.put(new FieldAclMatrixKey(type, roleProjectManagerId, fieldName), projectManagerView);
}
private GrantedAuthority[] loadPermsArray(
Map<GrantedAuthority, boolean[]> permissionsMap, int index) {
List<GrantedAuthority> grant = new ArrayList<GrantedAuthority>();
Set<GrantedAuthority> perms = permissionsMap.keySet();
for (GrantedAuthority perm : perms) {
if (permissionsMap.get(perm)[index]) { // NOSONAR
grant.add(perm); // se emplea keyset porque se necesita tanto la key como el value
}
}
return grant.toArray(new GrantedAuthority[] {});
}
/**
* @param node
* Nodo 'operation'
* @param name
* Identificador de la propiedad
* @return Realiza la conversión entre String a uno de los valores de la
* enumeración AclMatrixValue
*/
private AclMatrixValue getVisibility(Node operation, String name) {
String value = null;
AclMatrixValue aclValue = null;
value = DOMUtils.getAttribute(operation, name);
if ("all".equals(value)) {
aclValue = AclMatrixValue.ALL;
} else if ("area".equals(value)) {
aclValue = AclMatrixValue.AREA;
} else if ("own".equals(value)) {
aclValue = AclMatrixValue.OWN;
} else if ("deny".equals(value)) {
aclValue = AclMatrixValue.DENY;
} else if ("owners".equals(value)) {
aclValue = AclMatrixValue.OWNERS;
} else {
logger.warn("Invalid visibility data. name=" + name + ", value="
+ value + ". Setting 'deny' value");
aclValue = AclMatrixValue.DENY;
}
return aclValue;
}
/**
* @param node Nodo 'field'
* @param name Nombre del campo
* @return Realiza la conversión entre String a su valor booleano
*/
private Boolean getFieldVisibility(Node field, String name) {
String value = null;
value = DOMUtils.getAttribute(field, name);
return Boolean.valueOf(value);
}
/**
* Inicializa las matrices de privilegios de operaciones de Lectura,
* Escritura y Borrado de cada una de las clases.
*
* @param permissionsMap
*/
private void loadFlows(org.w3c.dom.Document document,
Map<String, WorkFlow> workFlowMatrix) throws SecConfigException {
NodeList entities = null;
Node entity = null;
String name = null;
NodeList states = null;
Node state = null;
// Cargamos las entidades
entities = document.getElementsByTagName("workflow");
for (int i = 0, iCount = entities.getLength(); i < iCount; i++) {
entity = entities.item(i);
if ("workflow".equals(entity.getNodeName())) {
final WorkFlow workFlow = new WorkFlow();
name = DOMUtils.getAttribute(entity, "name");
if (logger.isDebugEnabled()) {
logger.debug("work readed: " + name);
}
workFlow.setName(name);
workFlow.setStates(new ArrayList<State>());
states = entity.getChildNodes();
for (int j = 0, jCount = states.getLength(); j < jCount; j++) {
state = states.item(j);
// <state name="CREATED" admin="true" super="true"
// staff="true" user="true" cli="true">
if ("state".equals(state.getNodeName())) {
final State st = loadState(state);
workFlow.getStates().add(st);
}
}
workFlowMatrix.put(name, workFlow);
}
}
}
/**
* @param node
* @return
* @throws SecConfigException
*/
private State loadState(Node node) throws SecConfigException {
final State state = new State();
final String name = DOMUtils.getAttribute(node, "name");
if (logger.isDebugEnabled()) {
logger.debug("State readed: " + name);
}
state.setName(name);
state
.setFields(new ArrayList<Field>());
final String admin = DOMUtils.getAttribute(node, "admin");
final String super1 = DOMUtils.getAttribute(node, "super");
final String staff = DOMUtils.getAttribute(node, "staff");
final String user = DOMUtils.getAttribute(node, "user");
final String cli = DOMUtils.getAttribute(node, "cli");
final String projectManager = DOMUtils.getAttribute(node, "projectManager");
if (admin != null) {
state.setPermAdmin(Boolean.valueOf(admin.toLowerCase()));
}
if (super1 != null) {
state.setPermSuper(Boolean.valueOf(super1.toLowerCase()));
}
if (staff != null) {
state.setPermStaff(Boolean.valueOf(staff.toLowerCase()));
}
if (user != null) {
state.setPermUser(Boolean.valueOf(user.toLowerCase()));
}
if (cli != null) {
state.setPermCli(Boolean.valueOf(cli.toLowerCase()));
}
if (projectManager != null) {
state.setPermProjectManager(Boolean.valueOf(projectManager.toLowerCase()));
}
final NodeList fieldList = node.getChildNodes();
for (int i = 0, iCount = fieldList.getLength(); i < iCount; i++) {
Node field = fieldList.item(i);
if ("field".equals(field.getNodeName())) {
final Field f = loadField(field);
state.getFields().add(f);
}
}
return state;
}
/**
* @param node
* @return
* @throws SecConfigException
*/
private Field loadField(Node node) throws SecConfigException {
final Field field = new Field();
final String xname = DOMUtils.getAttribute(node, "name");
final String xadmin = DOMUtils.getAttribute(node, "admin");
final String xsuper1 = DOMUtils.getAttribute(node, "super");
final String xstaff = DOMUtils.getAttribute(node, "staff");
final String xuser = DOMUtils.getAttribute(node, "user");
final String xcli = DOMUtils.getAttribute(node, "cli");
final String projectManager = DOMUtils.getAttribute(node, "projectManager");
if (logger.isDebugEnabled()) {
logger.debug("Field readed: " + xname);
}
field.setName(xname);
if (xadmin != null) {
field
.setPermAdmin(com.autentia.tnt.manager.workflow.Field.Permission
.valueOf(xadmin.toUpperCase()));
}
if (xsuper1 != null) {
field
.setPermSuper(com.autentia.tnt.manager.workflow.Field.Permission
.valueOf(xsuper1.toUpperCase()));
}
if (xstaff != null) {
field
.setPermStaff(com.autentia.tnt.manager.workflow.Field.Permission
.valueOf(xstaff.toUpperCase()));
}
if (xuser != null) {
field
.setPermUser(com.autentia.tnt.manager.workflow.Field.Permission
.valueOf(xuser.toUpperCase()));
}
if (xcli != null) {
field
.setPermCli(com.autentia.tnt.manager.workflow.Field.Permission
.valueOf(xcli.toUpperCase()));
}
if (projectManager != null) {
field
.setPermProjectManager(com.autentia.tnt.manager.workflow.Field.Permission
.valueOf(projectManager.toUpperCase()));
}
return field;
}
public Map<String, WorkFlow> getWorkFlowMatrix() {
return workFlowMatrix;
}
public boolean allowFieldAccess(Class<? extends ITransferObject> clazz, int roleId, String fieldName) {
FieldAclMatrixKey fieldAclMatrixKey = new FieldAclMatrixKey(clazz, roleId, fieldName);
Boolean ret = this.detailViewMatrix.get(fieldAclMatrixKey);
if (ret == null) {
ret = Boolean.FALSE;
}
return ret;
}
public boolean isUserInAdminRole(Principal principal) {
return this.roleAdminId == principal.getRoleId();
}
}