001/*
002 * The Kuali Financial System, a comprehensive financial management system for higher education.
003 * 
004 * Copyright 2005-2014 The Kuali Foundation
005 * 
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU Affero General Public License as
008 * published by the Free Software Foundation, either version 3 of the
009 * License, or (at your option) any later version.
010 * 
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU Affero General Public License for more details.
015 * 
016 * You should have received a copy of the GNU Affero General Public License
017 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
018 */
019package org.kuali.rice.kim.impl.jaxb;
020
021import java.io.Serializable;
022import java.util.HashMap;
023import java.util.Map;
024
025import javax.xml.bind.Unmarshaller;
026import javax.xml.bind.annotation.XmlAccessType;
027import javax.xml.bind.annotation.XmlAccessorType;
028import javax.xml.bind.annotation.XmlElement;
029import javax.xml.bind.annotation.XmlTransient;
030import javax.xml.bind.annotation.XmlType;
031import javax.xml.bind.annotation.adapters.NormalizedStringAdapter;
032import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
033
034import org.apache.commons.lang.StringUtils;
035import org.joda.time.DateTime;
036import org.kuali.rice.core.api.membership.MemberType;
037import org.kuali.rice.core.api.util.jaxb.DateTimeAdapter;
038import org.kuali.rice.core.util.jaxb.NameAndNamespacePair;
039import org.kuali.rice.core.util.jaxb.NameAndNamespacePairValidatingAdapter;
040import org.kuali.rice.kim.api.group.GroupContract;
041import org.kuali.rice.kim.api.identity.principal.PrincipalContract;
042import org.kuali.rice.kim.api.jaxb.QualificationListAdapter;
043import org.kuali.rice.kim.api.role.RoleContract;
044import org.kuali.rice.kim.api.role.RoleMember;
045import org.kuali.rice.kim.api.services.KimApiServiceLocator;
046
047/**
048 * Base class representing an unmarshalled &lt;roleMember&gt; element.
049 * Refer to the static inner classes for more information about the specific contexts.
050 *
051 * @author Kuali Rice Team (rice.collab@kuali.org)
052 */
053@XmlTransient
054public abstract class RoleMemberXmlDTO implements Serializable {
055
056    private static final long serialVersionUID = 1L;
057
058    @XmlElement(name="principalId")
059    @XmlJavaTypeAdapter(NormalizedStringAdapter.class)
060    private String principalId;
061
062    @XmlElement(name="principalName")
063    @XmlJavaTypeAdapter(NormalizedStringAdapter.class)
064    private String principalName;
065
066    @XmlElement(name="groupId")
067    @XmlJavaTypeAdapter(NormalizedStringAdapter.class)
068    private String groupId;
069
070    @XmlElement(name="groupName")
071    @XmlJavaTypeAdapter(NameAndNamespacePairValidatingAdapter.class)
072    private NameAndNamespacePair groupName;
073
074    @XmlElement(name="roleIdAsMember")
075    @XmlJavaTypeAdapter(NormalizedStringAdapter.class)
076    private String roleIdAsMember;
077
078    @XmlElement(name="roleNameAsMember")
079    @XmlJavaTypeAdapter(NameAndNamespacePairValidatingAdapter.class)
080    private NameAndNamespacePair roleNameAsMember;
081
082    @XmlElement(name="activeFromDate")
083    @XmlJavaTypeAdapter(DateTimeAdapter.class)
084    private DateTime activeFromDate;
085
086    @XmlElement(name="activeToDate")
087    @XmlJavaTypeAdapter(DateTimeAdapter.class)
088    private DateTime activeToDate;
089
090    @XmlElement(name="qualifications")
091    @XmlJavaTypeAdapter(QualificationListAdapter.class)
092    private Map<String, String> qualifications;
093
094    @XmlTransient
095    private MemberType memberType;
096
097    /**
098     * Constructs an empty RoleMemberXmlDTO instance.
099     */
100    public RoleMemberXmlDTO() {}
101
102    /**
103     * Constructs a RoleMemberXmlDTO instance that is populated with the info from the given role member.
104     *
105     * @param roleMember The role member that this DTO should populate its data from.
106     * @param populateMemberId If true, the member principal/group/role ID will get populated; otherwise, only
107     * the member principal/group/role name and (if applicable) namespace will get populated.
108     * @throws IllegalArgumentException if roleMember is null, has an invalid member type, or refers to a nonexistent principal/group/role.
109     */
110    public RoleMemberXmlDTO(RoleMember roleMember, boolean populateMemberId) {
111        if (roleMember == null) {
112            throw new IllegalArgumentException("roleMember cannot be null");
113        }
114        this.memberType = roleMember.getType();
115        this.activeFromDate = roleMember.getActiveFromDate();
116        this.activeToDate = roleMember.getActiveToDate();
117        this.qualifications = (roleMember.getAttributes() != null) ? roleMember.getAttributes() : new HashMap<String, String>();
118
119        if (MemberType.PRINCIPAL.equals(memberType)) {
120            if (populateMemberId) {
121                this.principalId = roleMember.getMemberId();
122            }
123            PrincipalContract principal = KimApiServiceLocator.getIdentityService().getPrincipal(
124                    roleMember.getMemberId());
125            if (principal == null) {
126                throw new IllegalArgumentException("Cannot find principal with ID \"" +  roleMember.getMemberId() + "\"");
127            }
128            this.principalName = principal.getPrincipalName();
129        } else if (MemberType.GROUP.equals(memberType)) {
130            if (populateMemberId) {
131                this.groupId = roleMember.getMemberId();
132            }
133            GroupContract group = KimApiServiceLocator.getGroupService().getGroup(roleMember.getMemberId());
134            if (group == null) {
135                throw new IllegalArgumentException("Cannot find group with ID \"" + roleMember.getMemberId() + "\"");
136            }
137            this.groupName = new NameAndNamespacePair(group.getNamespaceCode(), group.getName());
138        } else if (MemberType.ROLE.equals(memberType)) {
139            if (populateMemberId) {
140                this.roleIdAsMember = roleMember.getMemberId();
141            }
142            RoleContract role = KimApiServiceLocator.getRoleService().getRole(roleMember.getMemberId());
143            if (role == null) {
144                throw new IllegalArgumentException("Cannot find role with ID \"" + roleMember.getMemberId() + "\"");
145            }
146            this.roleNameAsMember = new NameAndNamespacePair(role.getNamespaceCode(), role.getName());
147        } else {
148            throw new IllegalArgumentException("Cannot construct a RoleMemberXmlDTO from a role member with an unrecognized member type code of \"" +
149                    memberType + "\"");
150        }
151    }
152
153    /**
154     * @return the principalId
155     */
156    public String getPrincipalId() {
157        return this.principalId;
158    }
159
160    /**
161     * @param principalId the principalId to set
162     */
163    public void setPrincipalId(String principalId) {
164        this.principalId = principalId;
165    }
166
167    /**
168     * @return the principalName
169     */
170    public String getPrincipalName() {
171        return this.principalName;
172    }
173
174    /**
175     * @param principalName the principalName to set
176     */
177    public void setPrincipalName(String principalName) {
178        this.principalName = principalName;
179    }
180
181    /**
182     * @return the groupId
183     */
184    public String getGroupId() {
185        return this.groupId;
186    }
187
188    /**
189     * @param groupId the groupId to set
190     */
191    public void setGroupId(String groupId) {
192        this.groupId = groupId;
193    }
194
195    /**
196     * @return the groupName
197     */
198    public NameAndNamespacePair getGroupName() {
199        return this.groupName;
200    }
201
202    /**
203     * @param groupName the groupName to set
204     */
205    public void setGroupName(NameAndNamespacePair groupName) {
206        this.groupName = groupName;
207    }
208
209    /**
210     * @return the roleIdAsMember
211     */
212    public String getRoleIdAsMember() {
213        return this.roleIdAsMember;
214    }
215
216    /**
217     * @param roleIdAsMember the roleIdAsMember to set
218     */
219    public void setRoleIdAsMember(String roleIdAsMember) {
220        this.roleIdAsMember = roleIdAsMember;
221    }
222
223    /**
224     * @return the roleNameAsMember
225     */
226    public NameAndNamespacePair getRoleNameAsMember() {
227        return this.roleNameAsMember;
228    }
229
230    /**
231     * @param roleNameAsMember the roleNameAsMember to set
232     */
233    public void setRoleNameAsMember(NameAndNamespacePair roleNameAsMember) {
234        this.roleNameAsMember = roleNameAsMember;
235    }
236
237    /**
238     * @return the activeFromDate
239     */
240    public DateTime getActiveFromDate() {
241        return this.activeFromDate;
242    }
243
244    /**
245     * @param activeFromDate the activeFromDate to set
246     */
247    public void setActiveFromDate(DateTime activeFromDate) {
248        this.activeFromDate = activeFromDate;
249    }
250
251    /**
252     * @return the activeToDate
253     */
254    public DateTime getActiveToDate() {
255        return this.activeToDate;
256    }
257
258    /**
259     * @param activeToDate the activeToDate to set
260     */
261    public void setActiveToDate(DateTime activeToDate) {
262        this.activeToDate = activeToDate;
263    }
264
265    /**
266     * @return the qualifications
267     */
268    public Map<String, String> getQualifications() {
269        return this.qualifications;
270    }
271
272    /**
273     * @param qualifications the qualifications to set
274     */
275    public void setQualifications(Map<String, String> qualifications) {
276        this.qualifications = qualifications;
277    }
278
279    /**
280     * Retrieves the member type.
281     *
282     * <p>If the member type is null at the time that this method is invoked, an attempt will be made to set its
283     * value based on any populated member principal/group/role ID/name information.
284     *
285     * @return the member type, or null if no membership identification information has been set on this member.
286     * @throws IllegalStateException if the role member is populated simultaneously with multiple member ID/name information
287     */
288    public MemberType getMemberType() {
289        if (memberType == null) {
290            boolean foundMemberInfo = false;
291
292            if (StringUtils.isNotBlank(principalId) || StringUtils.isNotBlank(principalName)) {
293                memberType = MemberType.PRINCIPAL;
294                foundMemberInfo = true;
295            }
296
297            if (StringUtils.isNotBlank(groupId) || groupName != null) {
298                if (foundMemberInfo) {
299                    memberType = null;
300                    throw new IllegalStateException("Cannot have a role member that is simultaneously populated with member principal, member group, and/or member role information");
301                }
302                memberType = MemberType.GROUP;
303                foundMemberInfo = true;
304            }
305
306            if (StringUtils.isNotBlank(roleIdAsMember) || roleNameAsMember != null) {
307                if (foundMemberInfo) {
308                    memberType = null;
309                    throw new IllegalStateException("Cannot have a role member that is simultaneously populated with member principal, member group, and/or member role information");
310                }
311                memberType = MemberType.ROLE;
312                foundMemberInfo = true;
313            }
314        }
315        return this.memberType;
316    }
317
318    /**
319     * Retrieves the role member's ID, based on the member type and any populated member principal/group/role IDs.
320     *
321     * <p>If the member type is null at the time that this method is invoked, an attempt will be made to set its
322     * value based on any populated member principal/group/role ID/name information.
323     *
324     * @return The member's ID, or null if the member type is null or the associated member ID information is null.
325     */
326    public String getMemberId() {
327        if (MemberType.PRINCIPAL.equals(getMemberType())) {
328            return principalId;
329        } else if (MemberType.GROUP.equals(getMemberType())) {
330            return groupId;
331        } else if (MemberType.ROLE.equals(getMemberType())) {
332            return roleIdAsMember;
333        }
334        return null;
335    }
336
337    /**
338     * Retrieves the role member's name, based on the member type and any populated member principal/group/role names.
339     *
340     * <p>If the member type is null at the time that this method is invoked, an attempt will be made to set its
341     * value based on any populated member principal/group/role ID/name information.
342     *
343     * @return The member's name, or null if the member type is null or the associated member name information is null.
344     */
345    public String getMemberName() {
346        if (MemberType.PRINCIPAL.equals(getMemberType())) {
347            return principalName;
348        } else if (MemberType.GROUP.equals(getMemberType())) {
349            return (groupName != null) ? groupName.getName() : null;
350        } else if (MemberType.ROLE.equals(getMemberType())) {
351            return (roleNameAsMember != null) ? roleNameAsMember.getName() : null;
352        }
353        return null;
354    }
355
356    /**
357     * Retrieves the role member's namespace code, based on the member type and any populated member principal/group/role names.
358     *
359     * <p>If the member type is null at the time that this method is invoked, an attempt will be made to set its
360     * value based on any populated member principal/group/role ID/name information.
361     *
362     * @return The member's namespace code, or null if the member type is null, the associated member name information is null,
363     * or the role member is a principal.
364     */
365    public String getMemberNamespaceCode() {
366        if (MemberType.PRINCIPAL.equals(getMemberType())) {
367            return null;
368        } else if (MemberType.GROUP.equals(getMemberType())) {
369            return (groupName != null) ? groupName.getName() : null;
370        } else if (MemberType.ROLE.equals(getMemberType())) {
371            return (roleNameAsMember != null) ? roleNameAsMember.getName() : null;
372        }
373        return null;
374    }
375
376    /**
377     * Retrieves the ID of the role that this member belongs to.
378     * Subclasses are responsible for implementing this method so that it does so.
379     *
380     * @return The role ID of the role that this member belongs to.
381     */
382    public abstract String getRoleId();
383
384    // =======================================================================================================
385
386    /**
387     * This class represents a &lt;roleMember&gt; element that is not a descendant of a &lt;role&gt; element.
388     *
389     * @author Kuali Rice Team (rice.collab@kuali.org)
390     */
391    @XmlAccessorType(XmlAccessType.FIELD)
392    @XmlType(name="StandaloneRoleMemberType", propOrder={
393            "roleId", "roleNameAndNamespace", "principalId", "principalName", "groupId", "groupName", "roleIdAsMember",
394                    "roleNameAsMember", "activeFromDate", "activeToDate", "qualifications"
395    })
396    public static class OutsideOfRole extends RoleMemberXmlDTO {
397
398        private static final long serialVersionUID = 1L;
399
400        @XmlElement(name="roleId")
401        @XmlJavaTypeAdapter(NormalizedStringAdapter.class)
402        private String roleId;
403
404        @XmlElement(name="roleName")
405        @XmlJavaTypeAdapter(NameAndNamespacePairValidatingAdapter.class)
406        private NameAndNamespacePair roleNameAndNamespace;
407
408        public OutsideOfRole() {
409            super();
410        }
411
412        public OutsideOfRole(RoleMember roleMember, boolean populateMemberId) {
413            super(roleMember, populateMemberId);
414            this.roleId = roleMember.getRoleId();
415            RoleContract tempRole = KimApiServiceLocator.getRoleService().getRole(roleId);
416            if (tempRole == null) {
417                throw new IllegalArgumentException("Cannot find role with ID \"" + roleId + "\"");
418            }
419            this.roleNameAndNamespace = new NameAndNamespacePair(tempRole.getNamespaceCode(), tempRole.getName());
420        }
421
422        /**
423         * @see org.kuali.rice.kim.impl.jaxb.RoleMemberXmlDTO#getRoleId()
424         */
425        @Override
426        public String getRoleId() {
427            return roleId;
428        }
429
430        /**
431         * @param roleId the roleId to set
432         */
433        public void setRoleId(String roleId) {
434            this.roleId = roleId;
435        }
436
437        /**
438         * @return the roleNameAndNamespace
439         */
440        public NameAndNamespacePair getRoleNameAndNamespace() {
441            return this.roleNameAndNamespace;
442        }
443
444        /**
445         * @param roleNameAndNamespace the roleNameAndNamespace to set
446         */
447        public void setRoleNameAndNamespace(NameAndNamespacePair roleNameAndNamespace) {
448            this.roleNameAndNamespace = roleNameAndNamespace;
449        }
450
451        /**
452         * Retrieves the role name from the role-name-and-namespace combo.
453         *
454         * @return The name of the role that this member belongs to, or null if the role-name-and-namespace combo is null.
455         */
456        public String getRoleName() {
457            return (roleNameAndNamespace != null) ? roleNameAndNamespace.getName() : null;
458        }
459
460        /**
461         * Retrieves the role namespace code from the role-name-and-namespace combo.
462         *
463         * @return The namespace code of the role that this member belongs to, or null if the role-name-and-namespace combo is null.
464         */
465        public String getRoleNamespaceCode() {
466            return (roleNameAndNamespace != null) ? roleNameAndNamespace.getNamespaceCode() : null;
467        }
468    }
469
470    // =======================================================================================================
471
472    /**
473     * This class represents a &lt;roleMember&gt; element that is a descendant of a &lt;role&gt; element.
474     *
475     * @author Kuali Rice Team (rice.collab@kuali.org)
476     */
477    @XmlAccessorType(XmlAccessType.FIELD)
478    @XmlType(name="RoleMemberType", propOrder={
479            "principalId", "principalName", "groupId", "groupName", "roleIdAsMember",
480                    "roleNameAsMember", "activeFromDate", "activeToDate", "qualifications"
481    })
482    public static class WithinRole extends RoleMemberXmlDTO {
483
484        private static final long serialVersionUID = 1L;
485
486        @XmlTransient
487        private String roleId;
488
489        public WithinRole() {
490            super();
491        }
492
493        public WithinRole(RoleMember roleMember, boolean populateMemberId) {
494            super(roleMember, populateMemberId);
495            this.roleId = roleMember.getRoleId();
496        }
497
498        void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) {
499            if (parent instanceof RoleMembersXmlDTO.WithinRole) {
500                this.roleId = ((RoleMembersXmlDTO.WithinRole)parent).getRoleId();
501            }
502        }
503
504        /**
505         * @see org.kuali.rice.kim.impl.jaxb.RoleMemberXmlDTO#getRoleId()
506         */
507        @Override
508        public String getRoleId() {
509            return roleId;
510        }
511
512    }
513}