View Javadoc

1   /*
2    * Copyright 2007-2008 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.xml;
17  
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.List;
23  
24  import javax.xml.parsers.ParserConfigurationException;
25  
26  import org.jdom.Document;
27  import org.jdom.Element;
28  import org.jdom.JDOMException;
29  import org.kuali.rice.kew.exception.InvalidXmlException;
30  import org.kuali.rice.kew.util.KEWConstants;
31  import org.kuali.rice.kew.util.Utilities;
32  import org.kuali.rice.kew.util.XmlHelper;
33  import org.kuali.rice.kim.bo.Group;
34  import org.kuali.rice.kim.bo.entity.KimPrincipal;
35  import org.kuali.rice.kim.bo.group.dto.GroupInfo;
36  import org.kuali.rice.kim.bo.types.dto.AttributeSet;
37  import org.kuali.rice.kim.bo.types.dto.KimTypeAttributeInfo;
38  import org.kuali.rice.kim.bo.types.dto.KimTypeInfo;
39  import org.kuali.rice.kim.service.IdentityManagementService;
40  import org.kuali.rice.kim.service.KIMServiceLocator;
41  import org.kuali.rice.kim.util.KimConstants;
42  import org.xml.sax.SAXException;
43  
44  
45  
46  /**
47   * Parses groups from XML.
48   *
49   * @see KimGroups
50   *
51   * @author Kuali Rice Team (rice.collab@kuali.org) *
52   * @author Kuali Rice Team (rice.collab@kuali.org)
53   *
54   */
55  public class GroupXmlParser implements XmlConstants {
56      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(GroupXmlParser.class);
57      private static final boolean DEFAULT_ACTIVE_VALUE = true;
58      private static final String DEFAULT_GROUP_DESCRIPTION = "";
59      private HashMap<String, List<String>> memberGroupIds = new HashMap<String, List<String>>();
60      private HashMap<String, List<String>> memberGroupNames = new HashMap<String, List<String>>();
61      private HashMap<String, List<String>> memberPrincipalIds = new HashMap<String, List<String>>();
62      private AttributeSet groupAttributes = new AttributeSet();
63  
64      public List<GroupInfo> parseGroups(InputStream input) throws IOException, InvalidXmlException {
65          try {
66              Document doc = XmlHelper.trimSAXXml(input);
67              Element root = doc.getRootElement();
68              return parseGroups(root);
69          } catch (JDOMException e) {
70              throw new InvalidXmlException("Parse error.", e);
71          } catch (SAXException e){
72              throw new InvalidXmlException("Parse error.",e);
73          } catch(ParserConfigurationException e){
74              throw new InvalidXmlException("Parse error.",e);
75          }
76      }
77  
78  
79      /**
80       * Parses and saves groups
81       * @param element top-level 'data' element which should contain a <groups> child element
82       * @return a list of parsed and saved, current, groups;
83       * @throws InvalidXmlException
84       */
85      @SuppressWarnings("unchecked")
86  	public List<GroupInfo> parseGroups(Element element) throws InvalidXmlException {
87          List<GroupInfo> groupInfos = new ArrayList<GroupInfo>();
88          for (Element groupsElement: (List<Element>) element.getChildren(GROUPS, GROUP_NAMESPACE)) {
89  
90              for (Element groupElement: (List<Element>) groupsElement.getChildren(GROUP, GROUP_NAMESPACE)) {
91                  groupInfos.add(parseGroup(groupElement));
92              }
93          }
94          for (GroupInfo groupInfo : groupInfos) {
95              IdentityManagementService identityManagementService = KIMServiceLocator.getIdentityManagementService();
96  
97              // check if group already exists
98              GroupInfo foundGroup = identityManagementService.getGroupByName(groupInfo.getNamespaceCode(), groupInfo.getGroupName());
99  
100             if (foundGroup == null) {
101                 if ( LOG.isInfoEnabled() ) {
102                 	LOG.info("Group named '" + groupInfo.getGroupName() + "' not found, creating new group named '" + groupInfo.getGroupName() + "'");
103                 }
104                 try {
105                     GroupInfo newGroupInfo =  identityManagementService.createGroup(groupInfo);
106 
107                     String key = newGroupInfo.getNamespaceCode().trim() + KEWConstants.KIM_GROUP_NAMESPACE_NAME_DELIMITER_CHARACTER + newGroupInfo.getGroupName().trim();
108                     addGroupMembers(newGroupInfo, key);
109                 } catch (Exception e) {
110                     throw new RuntimeException("Error creating group with name '" + groupInfo.getGroupName() + "'", e);
111                 }
112             } else {
113             	if ( LOG.isInfoEnabled() ) {
114             		LOG.info("Group named '" + groupInfo.getGroupName() + "' found, creating a new version");
115             	}
116                 try {
117                     groupInfo.setGroupId(foundGroup.getGroupId());
118                     identityManagementService.updateGroup(foundGroup.getGroupId(), groupInfo);
119 
120                     //delete existing group members and replace with new
121                     identityManagementService.removeAllGroupMembers(foundGroup.getGroupId());
122 
123                     String key = groupInfo.getNamespaceCode().trim() + KEWConstants.KIM_GROUP_NAMESPACE_NAME_DELIMITER_CHARACTER + groupInfo.getGroupName().trim();
124                     addGroupMembers(groupInfo, key);
125 
126                 } catch (Exception e) {
127                     throw new RuntimeException("Error updating group.", e);
128                 }
129             }
130         }
131         return groupInfos;
132     }
133 
134     @SuppressWarnings("unchecked")
135 	private GroupInfo parseGroup(Element element) throws InvalidXmlException {
136         GroupInfo groupInfo = new GroupInfo();
137         IdentityManagementService identityManagementService = KIMServiceLocator.getIdentityManagementService();
138         groupInfo.setGroupName(element.getChildText(NAME, GROUP_NAMESPACE));
139 
140         if (groupInfo.getGroupName() == null) {
141             throw new InvalidXmlException("Group must have a name.");
142         }
143 
144         String groupNamespace = element.getChildText(NAMESPACE, GROUP_NAMESPACE);
145         if (groupNamespace != null) {
146             groupInfo.setNamespaceCode(groupNamespace.trim());
147         } else {
148             throw new InvalidXmlException("Namespace must have a value.");
149         }
150 
151         String id = element.getChildText(ID, GROUP_NAMESPACE);
152         if (id != null) {
153             groupInfo.setGroupId(id.trim());
154         }
155 
156         String description = element.getChildText(DESCRIPTION, GROUP_NAMESPACE);
157         if (description != null && !description.trim().equals("")) {
158             groupInfo.setGroupDescription(description);
159         }
160 
161         // Type element and children (namespace and name)
162         String typeId = null;
163         List<KimTypeAttributeInfo> kimTypeAttributes = new ArrayList<KimTypeAttributeInfo>();
164         if (element.getChild(TYPE, GROUP_NAMESPACE) != null) {
165             Element typeElement = element.getChild(TYPE, GROUP_NAMESPACE);
166             String typeNamespace = typeElement.getChildText(NAMESPACE, GROUP_NAMESPACE);
167             String typeName = typeElement.getChildText(NAME, GROUP_NAMESPACE);
168             KimTypeInfo kimTypeInfo = KIMServiceLocator.getTypeInfoService().getKimTypeByName(typeNamespace, typeName);
169             if (kimTypeInfo != null) {
170             	typeId = kimTypeInfo.getKimTypeId();
171                 kimTypeAttributes = kimTypeInfo.getAttributeDefinitions();
172             } else  {
173                 throw new InvalidXmlException("Invalid type name and namespace specified.");
174             }
175         } else { //set to default type
176             KimTypeInfo kimTypeDefault = KIMServiceLocator.getTypeInfoService().getKimTypeByName(KimConstants.KIM_TYPE_DEFAULT_NAMESPACE, KimConstants.KIM_TYPE_DEFAULT_NAME);
177             if (kimTypeDefault != null) {
178             	typeId = kimTypeDefault.getKimTypeId();
179                 kimTypeAttributes = kimTypeDefault.getAttributeDefinitions();
180             } else {
181             	throw new RuntimeException("Failed to locate the 'Default' group type!  Please ensure that it's in your database.");
182             }
183         }
184         groupInfo.setKimTypeId(typeId);
185 
186         //Active Indicator
187         groupInfo.setActive(DEFAULT_ACTIVE_VALUE);
188         if (element.getChildText(ACTIVE, GROUP_NAMESPACE) != null) {
189             String active = element.getChildText(ACTIVE, GROUP_NAMESPACE).trim();
190             if (active.toUpperCase().equals("N") || active.toUpperCase().equals("FALSE")) {
191                 groupInfo.setActive(false);
192             }
193         }
194 
195         //Get list of attribute keys
196         List<String> validAttributeKeys = new ArrayList<String>();
197         for (KimTypeAttributeInfo attribute : kimTypeAttributes) {
198             validAttributeKeys.add(attribute.getAttributeName());
199         }
200         //Group attributes
201         if (element.getChild(ATTRIBUTES, GROUP_NAMESPACE) != null) {
202             List<Element> attributes = element.getChild(ATTRIBUTES, GROUP_NAMESPACE).getChildren();
203             AttributeSet attributeSet = new AttributeSet();
204             for (Element attr : attributes ) {
205                 String key = attr.getAttributeValue(KEY);
206                 String value = attr.getAttributeValue(VALUE);
207                 attributeSet.put(key, value);
208                 if (!validAttributeKeys.contains(key)) {
209                     throw new InvalidXmlException("Invalid attribute specified.");
210                 }
211             }
212             if (attributeSet.size() > 0) {
213                 groupInfo.setAttributes(attributeSet);
214             }
215         }
216 
217         //Group members
218 
219         List<Element> members = element.getChild(MEMBERS, GROUP_NAMESPACE).getChildren();
220         for (Element member : members) {
221             String elementName = member.getName().trim();
222             if (elementName.equals(PRINCIPAL_NAME)) {
223                 String principalName = member.getText().trim();
224                 KimPrincipal principal = identityManagementService.getPrincipalByPrincipalName(principalName);
225                 if (principal != null) {
226                     addPrincipalToGroup(groupInfo.getNamespaceCode(), groupInfo.getGroupName(), principal.getPrincipalId());
227                 } else {
228                     throw new InvalidXmlException("Principal Name "+principalName+" cannot be found.");
229                 }
230             } else if (elementName.equals(PRINCIPAL_ID)) {
231                 String xmlPrincipalId = member.getText().trim();
232                 KimPrincipal principal = identityManagementService.getPrincipal(xmlPrincipalId);
233                 if (principal != null) {
234                     addPrincipalToGroup(groupInfo.getNamespaceCode(), groupInfo.getGroupName(), principal.getPrincipalId());
235                 } else {
236                     throw new InvalidXmlException("Principal Id "+xmlPrincipalId+" cannot be found.");
237                 }
238             // Groups are handled differently since the member group may not be saved yet.  Therefore they need to be validated after the groups are saved.
239             } else if (elementName.equals(GROUP_ID)) {
240                 String xmlGroupId = member.getText().trim();
241                 addGroupToGroup(groupInfo.getNamespaceCode(), groupInfo.getGroupName(), xmlGroupId);
242             } else if (elementName.equals(GROUP_NAME)) {
243                 String xmlGroupName = member.getChildText(NAME, GROUP_NAMESPACE).trim();
244                 String xmlGroupNamespace = member.getChildText(NAMESPACE, GROUP_NAMESPACE).trim();
245                 addGroupNameToGroup(groupInfo.getNamespaceCode(), groupInfo.getGroupName(), xmlGroupNamespace, xmlGroupName);
246             } else {
247                 LOG.error("Unknown member element: " + elementName);
248             }
249 
250 
251         }
252 
253         return groupInfo;
254 
255     }
256 
257     private void addPrincipalToGroup(String groupNamespace, String groupName, String principalId) {
258         String key = groupNamespace.trim() + KEWConstants.KIM_GROUP_NAMESPACE_NAME_DELIMITER_CHARACTER + groupName.trim();
259         List<String> principalIds = memberPrincipalIds.get(key);
260         if (principalIds == null) {
261             principalIds = new ArrayList<String>();
262         }
263         principalIds.add(principalId);
264         memberPrincipalIds.put(key, principalIds);
265     }
266 
267     private void addGroupToGroup(String groupNamespace, String groupName, String groupId) {
268         String key = groupNamespace.trim() + KEWConstants.KIM_GROUP_NAMESPACE_NAME_DELIMITER_CHARACTER + groupName.trim();
269         List<String> groupIds = memberGroupIds.get(key);
270         if (groupIds == null) {
271             groupIds = new ArrayList<String>();
272         }
273         groupIds.add(groupId);
274         memberGroupIds.put(key, groupIds);
275     }
276 
277     private void addGroupNameToGroup(String groupNamespace, String groupName, String memberGroupNamespace, String memberGroupName) {
278         String key = groupNamespace.trim() + KEWConstants.KIM_GROUP_NAMESPACE_NAME_DELIMITER_CHARACTER + groupName.trim();
279         List<String> groupNames = memberGroupNames.get(key);
280         if (groupNames == null) {
281             groupNames = new ArrayList<String>();
282         }
283         groupNames.add(memberGroupNamespace.trim() + KEWConstants.KIM_GROUP_NAMESPACE_NAME_DELIMITER_CHARACTER + memberGroupName.trim());
284         memberGroupNames.put(key, groupNames);
285     }
286 
287     private void addGroupMembers(GroupInfo groupInfo, String key) throws InvalidXmlException {
288         IdentityManagementService identityManagementService = KIMServiceLocator.getIdentityManagementService();
289         List<String> groupIds = memberGroupIds.get(key);
290         if (groupIds != null) {
291             for (String groupId : groupIds) {
292                 Group group = identityManagementService.getGroup(groupId);
293                 if (group != null) {
294                     identityManagementService.addGroupToGroup(group.getGroupId(), groupInfo.getGroupId());
295                 } else {
296                     throw new InvalidXmlException("Group Id "+groupId+" cannot be found.");
297                 }
298             }
299         }
300         List<String> groupNames = memberGroupNames.get(key);
301         if (groupNames != null) {
302             for (String groupName : groupNames) {
303                 Group group = identityManagementService.getGroupByName(Utilities.parseGroupNamespaceCode(groupName), Utilities.parseGroupName(groupName));
304                 if (group != null) {
305                 	identityManagementService.addGroupToGroup(group.getGroupId(), groupInfo.getGroupId());
306                 } else {
307                     throw new InvalidXmlException("Group "+groupName+" cannot be found.");
308                 }
309             }
310         }
311         List<String> principalIds = memberPrincipalIds.get(key);
312         if (principalIds != null) {
313             for (String principalId : principalIds) {
314             	identityManagementService.addPrincipalToGroup(principalId, groupInfo.getGroupId());
315             }
316         }
317 
318     }
319 }