diff --git a/config/alfresco/model/permissionDefinitions.xml b/config/alfresco/model/permissionDefinitions.xml index b6e956cba3..72e0226d8a 100644 --- a/config/alfresco/model/permissionDefinitions.xml +++ b/config/alfresco/model/permissionDefinitions.xml @@ -406,49 +406,49 @@ - --> + - --> + - --> + - --> + - --> + - --> + - --> + diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index 28f3553a6f..c48ef6c53f 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -22,6 +22,9 @@ alfresco/model/permissionDefinitions.xml + + alfresco/model/permissionSchema.dtd + diff --git a/source/java/org/alfresco/repo/audit/access/AccessAuditor.java b/source/java/org/alfresco/repo/audit/access/AccessAuditor.java index ff9b086b2b..c08d255e82 100644 --- a/source/java/org/alfresco/repo/audit/access/AccessAuditor.java +++ b/source/java/org/alfresco/repo/audit/access/AccessAuditor.java @@ -152,6 +152,14 @@ import org.springframework.beans.factory.InitializingBean; * /alfresco-access/transaction/sub-action/00/move/from/type=cm:folder * /alfresco-access/transaction/sub-action/01/action=readContent * + * The trace output from this class may be useful to developers as it logs method + * calls grouped by transaction. The debug output is of the audit records written + * and full inbound audit data. However for developers trace will provide a more + * readable form. Setting the following dev-log4j.properties: + *
+ *    log4j.appender.File.Threshold=trace
+ *    log4j.logger.org.alfresco.repo.audit.access.AccessAuditor=trace
+ * 
* * @author Alan Davis */ @@ -434,21 +442,48 @@ public class AccessAuditor implements InitializingBean, { if (logger.isDebugEnabled()) { - StringBuilder sb = new StringBuilder("\n\tAudit data:"); - for (String key : new TreeSet(recordedAuditMap.keySet())) + // Trace is used by a developer to produce a cut down log output that is simpler + // to read (no audit data section or summary keys) + boolean devOutput = logger.isTraceEnabled(); + + StringBuilder sb = new StringBuilder(); + StringBuilder subActions = new StringBuilder(""); + if (!devOutput) { - sb.append("\n\t\t").append(key).append('='); - appendAuditMapValue(sb, recordedAuditMap.get(key)); + sb.append("\n\tAudit data:"); + for (String key : new TreeSet(recordedAuditMap.keySet())) + { + sb.append("\n\t\t").append(key).append('='); + appendAuditMapValue(sb, recordedAuditMap.get(key)); + } + + sb.append("\n\n\tInbound audit values: "); } - sb.append('\n'); - sb.append("\n\tInbound audit values: "); for (String key : new TreeSet(auditMap.keySet())) { - sb.append("\n\t\t").append(rootPath).append('/').append(key).append('='); - appendAuditMapValue(sb, auditMap.get(key)); + if (!devOutput || !NodeChange.SUMMARY_KEYS.contains(key)) + { + StringBuilder output = (key.startsWith(NodeChange.SUB_ACTION_PREFIX)) + ? subActions : sb; + + output.append("\n\t\t").append(rootPath).append('/').append(key).append('='); + appendAuditMapValue(output, auditMap.get(key)); + } + } + if (subActions.length() > 0) + { + sb.append("\n\t\t--- sub actions ---"); + sb.append(subActions.toString()); + } + if (devOutput) + { + logger.trace(sb.toString()); + } + else + { + logger.debug(sb.toString()); } - logger.debug(sb.toString()); } return true; } diff --git a/source/java/org/alfresco/repo/audit/access/NodeChange.java b/source/java/org/alfresco/repo/audit/access/NodeChange.java index 7a7731edec..a4e8b236d9 100644 --- a/source/java/org/alfresco/repo/audit/access/NodeChange.java +++ b/source/java/org/alfresco/repo/audit/access/NodeChange.java @@ -22,6 +22,7 @@ import static org.alfresco.repo.audit.model.AuditApplication.AUDIT_PATH_SEPARATO import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -89,7 +90,7 @@ import org.alfresco.service.namespace.QName; private static final String PROPERTIES = "properties"; private static final String ASPECTS = "aspects"; private static final String VERSION_PROPERTIES = "version-properties"; - private static final String SUB_ACTION = "sub-action"; + public static final String SUB_ACTION = "sub-action"; private static final String DELETE_NODE = "deleteNode"; private static final String CREATE_NODE = "createNode"; @@ -110,6 +111,19 @@ import org.alfresco.service.namespace.QName; private static final Pattern INVALID_PATH_COMP_CHAR_PATTERN = Pattern.compile(AuditApplication.AUDIT_INVALID_PATH_COMP_CHAR_REGEX); + public static Collection SUMMARY_KEYS = new ArrayList(); + static + { + SUMMARY_KEYS.add(buildPath(PROPERTIES, ADD)); + SUMMARY_KEYS.add(buildPath(PROPERTIES, DELETE)); + SUMMARY_KEYS.add(buildPath(PROPERTIES, FROM)); + SUMMARY_KEYS.add(buildPath(PROPERTIES, TO)); + SUMMARY_KEYS.add(buildPath(ASPECTS, ADD)); + SUMMARY_KEYS.add(buildPath(ASPECTS, DELETE)); + } + + public static final String SUB_ACTION_PREFIX = SUB_ACTION + AUDIT_PATH_SEPARATOR; + private final NodeInfoFactory nodeInfoFactory; private final NamespaceService namespaceService; private NodeInfo nodeInfo; @@ -749,7 +763,7 @@ import org.alfresco.service.namespace.QName; * @param components String.. components of the path. * @return a component path of the supplied values. */ - private String buildPath(String... components) + private static String buildPath(String... components) { StringBuilder sb = new StringBuilder(); for (String component: components) diff --git a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java b/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java index 48bb02cc40..56eb9bbe04 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java @@ -18,8 +18,11 @@ */ package org.alfresco.repo.security.permissions.impl.model; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; @@ -50,11 +53,15 @@ import org.alfresco.service.namespace.DynamicNamespacePrefixResolver; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.Pair; +import org.alfresco.util.TempFileProvider; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; +import org.dom4j.DocumentType; import org.dom4j.Element; import org.dom4j.io.SAXReader; +import org.dom4j.tree.DefaultDocumentType; +import org.springframework.util.FileCopyUtils; /** * The implementation of the model DAO Reads and stores the top level model information Encapsulates access to this @@ -93,6 +100,8 @@ public class PermissionModel implements ModelDAO // Instance variables private String model; + private String dtdSchema; + private boolean validate = true; /* * (non-Javadoc) @@ -1142,6 +1151,26 @@ public class PermissionModel implements ModelDAO this.model = model; } + /** + * Set the dtd schema that is used to validate permission model + * + * @param dtdSchema + */ + public void setDtdSchema(String dtdSchema) + { + this.dtdSchema = dtdSchema; + } + + /** + * Indicates whether model should be validated on initialization against specified dtd + * + * @param validate + */ + public void setValidate(boolean validate) + { + this.validate = validate; + } + /** * Set the dictionary service * @@ -1261,6 +1290,7 @@ public class PermissionModel implements ModelDAO private Document createDocument(String model) { InputStream is = this.getClass().getClassLoader().getResourceAsStream(model); + URL dtdSchemaUrl = this.getClass().getClassLoader().getResource(dtdSchema); if (is == null) { throw new PermissionModelException("File not found: " + model); @@ -1268,21 +1298,63 @@ public class PermissionModel implements ModelDAO SAXReader reader = new SAXReader(); try { + if (validate) + { + if (dtdSchemaUrl != null) + { + is = processModelDocType(is, dtdSchemaUrl.toString()); + reader.setValidation(true); + } + else + { + throw new PermissionModelException("Couldn't obtain DTD schema to validate permission model."); + } + } + Document document = reader.read(is); is.close(); return document; } catch (DocumentException e) { - throw new PermissionModelException("Failed to create permission model document ", e); + throw new PermissionModelException("Failed to create permission model document: " + model, e); } catch (IOException e) { - throw new PermissionModelException("Failed to close permission model document ", e); + throw new PermissionModelException("Failed to close permission model document: " + model, e); } } + /* + * Replace or add correct DOCTYPE to the xml to allow validation against dtd + */ + private InputStream processModelDocType(InputStream is, String dtdSchemaUrl) throws DocumentException, IOException + { + SAXReader reader = new SAXReader(); + // read document without validation + Document doc = reader.read(is); + DocumentType docType = doc.getDocType(); + if (docType != null) + { + // replace DOCTYPE setting the full path to the xsd + docType.setSystemID(dtdSchemaUrl); + } + else + { + // add the DOCTYPE + docType = new DefaultDocumentType(doc.getRootElement().getName(), dtdSchemaUrl); + doc.setDocType(docType); + } + + File tempFile = TempFileProvider.createTempFile("permissionModel-", ".tmp"); + + // copy the modified permission model to the temp file + FileCopyUtils.copy(doc.asXML().getBytes(), tempFile); + + return new FileInputStream(tempFile); + } + /** * Set the default access status *