001/**
002 * Copyright 2005-2017 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}