001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.kew.doctype; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.impex.xml.XmlConstants; 020import org.kuali.rice.core.api.util.ConcreteKeyValue; 021import org.kuali.rice.core.api.util.KeyValue; 022import org.kuali.rice.kew.api.WorkflowRuntimeException; 023import org.kuali.rice.kew.api.exception.WorkflowException; 024import org.kuali.rice.kew.rule.xmlrouting.XPathHelper; 025import org.kuali.rice.kew.util.Utilities; 026import org.kuali.rice.kim.api.group.Group; 027import org.kuali.rice.kim.api.services.KimApiServiceLocator; 028import org.w3c.dom.Element; 029import org.w3c.dom.NamedNodeMap; 030import org.w3c.dom.Node; 031import org.w3c.dom.NodeList; 032import org.xml.sax.InputSource; 033import org.xml.sax.SAXException; 034 035import javax.xml.parsers.DocumentBuilderFactory; 036import javax.xml.parsers.ParserConfigurationException; 037import javax.xml.xpath.XPath; 038import javax.xml.xpath.XPathConstants; 039import java.io.BufferedReader; 040import java.io.IOException; 041import java.io.Serializable; 042import java.io.StringReader; 043import java.util.ArrayList; 044import java.util.List; 045 046 047public class DocumentTypeSecurity implements Serializable { 048 049 private static final long serialVersionUID = -1886779857180381404L; 050 051 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentTypeSecurity.class); 052 053 private Boolean active; 054 055 private Boolean initiatorOk; 056 private Boolean routeLogAuthenticatedOk; 057 private List<KeyValue> searchableAttributes = new ArrayList<KeyValue>(); 058 private List<Group> workgroups = new ArrayList<Group>(); 059 private List<SecurityPermissionInfo> permissions = new ArrayList<SecurityPermissionInfo>(); 060 private List<String> allowedRoles = new ArrayList<String>(); 061 private List<String> disallowedRoles = new ArrayList<String>(); 062 private List<String> securityAttributeExtensionNames = new ArrayList<String>(); 063 private List<String> securityAttributeClassNames = new ArrayList<String>(); 064 065 private static XPath xpath = XPathHelper.newXPath(); 066 067 public DocumentTypeSecurity() {} 068 069 /** parse <security> XML to populate security object 070 * @throws ParserConfigurationException 071 * @throws IOException 072 * @throws SAXException */ 073 public DocumentTypeSecurity(String standardApplicationId, String documentTypeSecurityXml) 074 { 075 try { 076 if (org.apache.commons.lang.StringUtils.isEmpty(documentTypeSecurityXml)) { 077 return; 078 } 079 080 InputSource inputSource = new InputSource(new BufferedReader(new StringReader(documentTypeSecurityXml))); 081 Element securityElement = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputSource).getDocumentElement(); 082 083 String active = (String) xpath.evaluate("./@active", securityElement, XPathConstants.STRING); 084 if (org.apache.commons.lang.StringUtils.isEmpty(active) || "true".equals(active.toLowerCase())) { 085 // true is the default 086 this.setActive(Boolean.valueOf(true)); 087 } 088 else { 089 this.setActive(Boolean.valueOf(false)); 090 } 091 092 // there should only be one <initiator> tag 093 NodeList initiatorNodes = (NodeList) xpath.evaluate("./initiator", securityElement, XPathConstants.NODESET); 094 if (initiatorNodes != null && initiatorNodes.getLength()>0) { 095 Node initiatorNode = initiatorNodes.item(0); 096 String value = initiatorNode.getTextContent(); 097 if (org.apache.commons.lang.StringUtils.isEmpty(value) || value.toLowerCase().equals("true")) { 098 this.setInitiatorOk(Boolean.valueOf(true)); 099 } 100 else { 101 this.initiatorOk = Boolean.valueOf(false); 102 } 103 } 104 105 // there should only be one <routeLogAuthenticated> tag 106 NodeList routeLogAuthNodes = (NodeList) xpath.evaluate("./routeLogAuthenticated", securityElement, XPathConstants.NODESET); 107 if (routeLogAuthNodes != null && routeLogAuthNodes.getLength()>0) { 108 Node routeLogAuthNode = routeLogAuthNodes.item(0); 109 String value = routeLogAuthNode.getTextContent(); 110 if (org.apache.commons.lang.StringUtils.isEmpty(value) || value.toLowerCase().equals("true")) { 111 this.routeLogAuthenticatedOk = Boolean.valueOf(true); 112 } 113 else { 114 this.routeLogAuthenticatedOk = Boolean.valueOf(false); 115 } 116 } 117 118 NodeList searchableAttributeNodes = (NodeList) xpath.evaluate("./searchableAttribute", securityElement, XPathConstants.NODESET); 119 if (searchableAttributeNodes != null && searchableAttributeNodes.getLength()>0) { 120 for (int i = 0; i < searchableAttributeNodes.getLength(); i++) { 121 Node searchableAttributeNode = searchableAttributeNodes.item(i); 122 String name = (String) xpath.evaluate("./@name", searchableAttributeNode, XPathConstants.STRING); 123 String idType = (String) xpath.evaluate("./@idType", searchableAttributeNode, XPathConstants.STRING); 124 if (!org.apache.commons.lang.StringUtils.isEmpty(name) && !org.apache.commons.lang.StringUtils.isEmpty(idType)) { 125 KeyValue searchableAttribute = new ConcreteKeyValue(name, idType); 126 searchableAttributes.add(searchableAttribute); 127 } 128 } 129 } 130 131 NodeList workgroupNodes = (NodeList) xpath.evaluate("./workgroup", securityElement, XPathConstants.NODESET); 132 if (workgroupNodes != null && workgroupNodes.getLength()>0) { 133 LOG.warn("Document Type Security XML is using deprecated element 'workgroup', please use 'groupName' instead."); 134 for (int i = 0; i < workgroupNodes.getLength(); i++) { 135 Node workgroupNode = workgroupNodes.item(i); 136 String value = workgroupNode.getTextContent().trim(); 137 if (!org.apache.commons.lang.StringUtils.isEmpty(value)) { 138 value = Utilities.substituteConfigParameters(value); 139 String namespaceCode = Utilities.parseGroupNamespaceCode(value); 140 String groupName = Utilities.parseGroupName(value); 141 Group groupObject = KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName(namespaceCode, 142 groupName); 143 if (groupObject == null) { 144 throw new WorkflowException("Could not find group: " + value); 145 } 146 workgroups.add(groupObject); 147 } 148 } 149 } 150 151 NodeList groupNodes = (NodeList) xpath.evaluate("./groupName", securityElement, XPathConstants.NODESET); 152 if (groupNodes != null && groupNodes.getLength()>0) { 153 for (int i = 0; i < groupNodes.getLength(); i++) { 154 Node groupNode = groupNodes.item(i); 155 if (groupNode.getNodeType() == Node.ELEMENT_NODE) { 156 String groupName = groupNode.getTextContent().trim(); 157 if (!org.apache.commons.lang.StringUtils.isEmpty(groupName)) { 158 groupName = Utilities.substituteConfigParameters(groupName).trim(); 159 String namespaceCode = Utilities.substituteConfigParameters(((Element) groupNode).getAttribute(XmlConstants.NAMESPACE)).trim(); 160 Group groupObject = KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName(namespaceCode, 161 groupName); 162 163 164 if (groupObject != null) { 165 workgroups.add(groupObject); 166 } else { 167 LOG.warn("Could not find group with name '" + groupName + "' and namespace '" + namespaceCode + "' which was defined on Document Type security"); 168 } 169// if (groupObject == null) { 170// throw new WorkflowException("Could not find group with name '" + groupName + "' and namespace '" + namespaceCode + "'"); 171// } 172 173 174 } 175 } 176 } 177 } 178 179 NodeList permissionNodes = (NodeList) xpath.evaluate("./permission", securityElement, XPathConstants.NODESET); 180 if (permissionNodes != null && permissionNodes.getLength()>0) { 181 for (int i = 0; i < permissionNodes.getLength(); i++) { 182 Node permissionNode = permissionNodes.item(i); 183 if (permissionNode.getNodeType() == Node.ELEMENT_NODE) { 184 SecurityPermissionInfo securityPermission = new SecurityPermissionInfo(); 185 securityPermission.setPermissionName(Utilities.substituteConfigParameters(((Element) permissionNode).getAttribute(XmlConstants.NAME)).trim()); 186 securityPermission.setPermissionNamespaceCode(Utilities.substituteConfigParameters(((Element) permissionNode).getAttribute(XmlConstants.NAMESPACE)).trim()); 187 if (!StringUtils.isEmpty(securityPermission.getPermissionName()) && !StringUtils.isEmpty(securityPermission.getPermissionNamespaceCode())) { 188 //get details and qualifications 189 if (permissionNode.hasChildNodes()) { 190 NodeList permissionChildNodes = permissionNode.getChildNodes(); 191 for (int j = 0; j <permissionChildNodes.getLength(); j++) { 192 Node permissionChildNode = permissionChildNodes.item(j); 193 if (permissionChildNode.getNodeType() == Node.ELEMENT_NODE) { 194 String childAttributeName = Utilities.substituteConfigParameters(((Element) permissionChildNode).getAttribute(XmlConstants.NAME)).trim(); 195 String childAttributeValue = permissionChildNode.getTextContent().trim(); 196 if (!StringUtils.isEmpty(childAttributeValue)) { 197 childAttributeValue = Utilities.substituteConfigParameters(childAttributeValue).trim(); 198 } 199 if (!StringUtils.isEmpty(childAttributeValue)) { 200 childAttributeValue = Utilities.substituteConfigParameters(childAttributeValue).trim(); 201 } 202 if (permissionChildNode.getNodeName().trim().equals("permissionDetail")) { 203 securityPermission.getPermissionDetails().put(childAttributeName, childAttributeValue); 204 } 205 if (permissionChildNode.getNodeName().trim().equals("qualification")) { 206 securityPermission.getQualifications().put(childAttributeName, childAttributeValue); 207 } 208 } 209 } 210 } 211 212 //if ( KimApiServiceLocator.getPermissionService().isPermissionDefined(securityPermission.getPermissionNamespaceCode(), securityPermission.getPermissionName(), securityPermission.getPermissionDetails())) { 213 permissions.add(securityPermission); 214 //} else { 215 // LOG.warn("Could not find permission with name '" + securityPermission.getPermissionName() + "' and namespace '" + securityPermission.getPermissionNamespaceCode() + "' which was defined on Document Type security"); 216 //} 217 } 218 } 219 } 220 } 221 222 NodeList roleNodes = (NodeList) xpath.evaluate("./role", securityElement, XPathConstants.NODESET); 223 if (roleNodes != null && roleNodes.getLength()>0) { 224 for (int i = 0; i < roleNodes.getLength(); i++) { 225 Element roleElement = (Element)roleNodes.item(i); 226 String value = roleElement.getTextContent().trim(); 227 String allowedValue = roleElement.getAttribute("allowed"); 228 if (StringUtils.isBlank(allowedValue)) { 229 allowedValue = "true"; 230 } 231 if (!org.apache.commons.lang.StringUtils.isEmpty(value)) { 232 if (Boolean.parseBoolean(allowedValue)) { 233 allowedRoles.add(value); 234 } else { 235 disallowedRoles.add(value); 236 } 237 } 238 } 239 } 240 241 NodeList attributeNodes = (NodeList) xpath.evaluate("./securityAttribute", securityElement, XPathConstants.NODESET); 242 if (attributeNodes != null && attributeNodes.getLength()>0) { 243 for (int i = 0; i < attributeNodes.getLength(); i++) { 244 Element attributeElement = (Element)attributeNodes.item(i); 245 NamedNodeMap elemAttributes = attributeElement.getAttributes(); 246 // can be an attribute name or an actual classname 247 String attributeOrClassName = null; 248 String applicationId = standardApplicationId; 249 if (elemAttributes.getNamedItem("name") != null) { 250 // found a name attribute so find the class name 251 String extensionName = elemAttributes.getNamedItem("name").getNodeValue().trim(); 252 this.securityAttributeExtensionNames.add(extensionName); 253 } else if (elemAttributes.getNamedItem("class") != null) { 254 // class name defined 255 String className = elemAttributes.getNamedItem("class").getNodeValue().trim(); 256 this.securityAttributeClassNames.add(className); 257 } else { 258 throw new WorkflowException("Cannot find attribute 'name' or attribute 'class' for securityAttribute Node"); 259 } 260 } 261 } 262 } catch (Exception err) { 263 throw new WorkflowRuntimeException(err); 264 } 265 } 266 267 public List<String> getSecurityAttributeExtensionNames() { 268 return this.securityAttributeExtensionNames; 269 } 270 271 public void setSecurityAttributeExtensionNames(List<String> securityAttributeExtensionNames) { 272 this.securityAttributeExtensionNames = securityAttributeExtensionNames; 273 } 274 275 public List<String> getSecurityAttributeClassNames() { 276 return securityAttributeClassNames; 277 } 278 279 public void setSecurityAttributeClassNames(List<String> securityAttributeClassNames) { 280 this.securityAttributeClassNames = securityAttributeClassNames; 281 } 282 283 public Boolean getInitiatorOk() { 284 return initiatorOk; 285 } 286 public void setInitiatorOk(Boolean initiatorOk) { 287 this.initiatorOk = initiatorOk; 288 } 289 290 public Boolean getRouteLogAuthenticatedOk() { 291 return routeLogAuthenticatedOk; 292 } 293 public void setRouteLogAuthenticatedOk(Boolean routeLogAuthenticatedOk) { 294 this.routeLogAuthenticatedOk = routeLogAuthenticatedOk; 295 } 296 297 public List<String> getAllowedRoles() { 298 return allowedRoles; 299 } 300 301 public void setAllowedRoles(List<String> allowedRoles) { 302 this.allowedRoles = allowedRoles; 303 } 304 305 public List<String> getDisallowedRoles() { 306 return disallowedRoles; 307 } 308 309 public void setDisallowedRoles(List<String> disallowedRoles) { 310 this.disallowedRoles = disallowedRoles; 311 } 312 313 public List<KeyValue> getSearchableAttributes() { 314 return searchableAttributes; 315 } 316 317 public void setSearchableAttributes(List<KeyValue> searchableAttributes) { 318 this.searchableAttributes = searchableAttributes; 319 } 320 321 public List<Group> getWorkgroups() { 322 return workgroups; 323 } 324 325 public void setWorkgroups(List<Group> workgroups) { 326 this.workgroups = workgroups; 327 } 328 329 public List<SecurityPermissionInfo> getPermissions() { 330 return this.permissions; 331 } 332 333 public void setPermissions(List<SecurityPermissionInfo> permissions) { 334 this.permissions = permissions; 335 } 336 337 public Boolean getActive() { 338 return active; 339 } 340 341 public void setActive(Boolean active) { 342 this.active = active; 343 } 344 345 public boolean isActive() { 346 if (active != null) { 347 return active.booleanValue(); 348 } 349 else { 350 return false; 351 } 352 } 353}