001/*
002 * Copyright 2012 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.ole.sec.document;
017
018import java.util.HashMap;
019import java.util.Map;
020
021import org.apache.commons.lang.StringUtils;
022import org.joda.time.DateTime;
023import org.kuali.ole.sec.SecConstants;
024import org.kuali.ole.sec.businessobject.AbstractSecurityModelDefinition;
025import org.kuali.ole.sec.businessobject.SecurityModelMember;
026import org.kuali.ole.sec.identity.SecKimAttributes;
027import org.kuali.ole.sys.context.SpringContext;
028import org.kuali.ole.sys.document.FinancialSystemMaintainable;
029import org.kuali.rice.core.api.criteria.PredicateFactory;
030import org.kuali.rice.core.api.criteria.QueryByCriteria;
031import org.kuali.rice.core.api.membership.MemberType;
032import org.kuali.rice.kim.api.KimConstants;
033import org.kuali.rice.kim.api.role.Role;
034import org.kuali.rice.kim.api.role.RoleMember;
035import org.kuali.rice.kim.api.role.RoleMemberQueryResults;
036import org.kuali.rice.kim.api.role.RoleService;
037import org.kuali.rice.kim.api.services.KimApiServiceLocator;
038import org.kuali.rice.kns.document.MaintenanceDocument;
039import org.kuali.rice.krad.bo.PersistableBusinessObject;
040
041public abstract class AbstractSecurityModuleMaintainable extends FinancialSystemMaintainable {
042
043    /**
044     * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#refresh(java.lang.String, java.util.Map,
045     *      org.kuali.rice.kns.document.MaintenanceDocument)
046     */
047    @Override
048    public void refresh(String refreshCaller, Map fieldValues, MaintenanceDocument document) {
049        super.refresh(refreshCaller, fieldValues, document);
050
051        getBusinessObject().refreshNonUpdateableReferences();
052        for (PersistableBusinessObject businessObject : newCollectionLines.values() ) {
053            businessObject.refreshNonUpdateableReferences();
054        }
055    }
056
057    protected String getDefaultRoleTypeId() {
058        return KimApiServiceLocator.getKimTypeInfoService().findKimTypeByNameAndNamespace(KimConstants.KIM_TYPE_DEFAULT_NAMESPACE, KimConstants.KIM_TYPE_DEFAULT_NAME).getId();
059    }
060    
061    protected Map<String,String> getRoleQualifiersFromSecurityModelDefinition( AbstractSecurityModelDefinition def ) {
062        Map<String,String> membershipQualifications = new HashMap<String,String>(4);
063        membershipQualifications.put(SecKimAttributes.CONSTRAINT_CODE, def.getConstraintCode());
064        membershipQualifications.put(SecKimAttributes.OPERATOR, def.getOperatorCode());
065        membershipQualifications.put(SecKimAttributes.PROPERTY_VALUE, def.getAttributeValue());
066        membershipQualifications.put(SecKimAttributes.OVERRIDE_DENY, Boolean.toString(def.isOverrideDeny()));
067        
068        return membershipQualifications;
069    }
070    
071    protected void updateSecurityModelRoleMember( Role modelRole, SecurityModelMember modelMember, String memberTypeCode, String memberId, Map<String,String> roleQualifiers ) {
072        RoleService roleService = KimApiServiceLocator.getRoleService();
073
074        RoleMember existingRoleMember = getRoleMembershipForMemberType(modelRole.getId(), memberId, memberTypeCode, roleQualifiers);
075        
076        if ( existingRoleMember == null ) {
077            // new role member
078            if ( memberTypeCode.equals( MemberType.PRINCIPAL.getCode() ) ) {
079                roleService.assignPrincipalToRole(memberId, modelRole.getNamespaceCode(), modelRole.getName(), roleQualifiers);
080            } else if ( memberTypeCode.equals( MemberType.GROUP.getCode() ) ) {
081                roleService.assignGroupToRole(memberId, modelRole.getNamespaceCode(), modelRole.getName(), roleQualifiers);
082            } else if ( memberTypeCode.equals( MemberType.ROLE.getCode() ) ) {
083                roleService.assignRoleToRole(memberId, modelRole.getNamespaceCode(), modelRole.getName(), roleQualifiers);
084            } else {
085                throw new RuntimeException( "Invalid role member type code: " + memberTypeCode );
086            }
087            // now, we need to re-retrieve it in order to set the from/to dates
088            existingRoleMember = getRoleMembershipForMemberType(modelRole.getId(), memberId, memberTypeCode, roleQualifiers);
089            if ( existingRoleMember == null ) {
090                throw new RuntimeException( "Role member was not saved properly.  Retrieval of role member after save failed for role: " + modelRole.getId() + " and Member Type/ID: " + memberTypeCode + "/" + memberId );
091            }
092        } 
093        RoleMember.Builder updatedRoleMember = RoleMember.Builder.create(existingRoleMember);
094        updatedRoleMember.setAttributes(new HashMap<String,String>(0));
095        updatedRoleMember.setType(MemberType.fromCode(memberTypeCode));
096        updatedRoleMember.setMemberId(memberId);
097        updatedRoleMember.setActiveFromDate( (modelMember.getActiveFromDate()==null)?null:new DateTime( modelMember.getActiveFromDate().getTime() ) );
098        updatedRoleMember.setActiveToDate( (modelMember.getActiveToDate()==null)?null:new DateTime( modelMember.getActiveToDate().getTime() ) );
099        roleService.updateRoleMember(updatedRoleMember.build());
100        
101    }
102
103    /**
104     * Finds the role membership (if exists) for the given role and member id
105     * 
106     * @param roleId id of role to find member for
107     * @param memberRoleId id of member role
108     * @param membershipQualifications Qualifications to match role membership
109     * @return RoleMembership containing information on the member record, or null if the member id is not assigned to the role
110     */
111    protected RoleMember getRoleMembershipForMemberType(String roleId, String memberId, String memberType, Map<String,String> membershipQualifications) {
112        RoleService roleService = KimApiServiceLocator.getRoleService();
113
114        RoleMemberQueryResults results = roleService.findRoleMembers( 
115                QueryByCriteria.Builder.fromPredicates( 
116                        PredicateFactory.equal("roleId", roleId),
117                        PredicateFactory.equal("typeCode", memberType),
118                        PredicateFactory.equal("memberId", memberId) ) );
119        for (RoleMember roleMembershipInfo : results.getResults() ) {
120            // no qualifiers - then an automatic match
121            if (membershipQualifications == null || membershipQualifications.isEmpty() ) {
122                return roleMembershipInfo;
123            }
124            // otherwise, check the qualifier attributes
125            if (doQualificationsMatch(membershipQualifications, roleMembershipInfo.getAttributes())) {
126                return roleMembershipInfo;
127            }
128        }
129
130        return null;
131    }
132
133    /**
134     * Determines whether an Map<String,String> has the same keys and values as another Map<String,String>
135     * 
136     * @param qualfiicationToMatch Map<String,String> to match keys and values
137     * @param qualfication Map<String,String> for matching
138     * @return boolean if second Map<String,String> has same keys and values as first
139     */
140    protected boolean doQualificationsMatch(Map<String,String> qualificationToMatch, Map<String,String> qualification) {
141        for (String key : qualificationToMatch.keySet()) {
142            if (qualification.containsKey(key)) {
143                String matchValue = qualification.get(key);
144                String value = qualificationToMatch.get(key);
145
146                if ( !StringUtils.equals(value, matchValue) ) {
147                    return false;
148                }
149            } else {
150                return false;
151            }
152        }
153
154        return true;
155    }
156
157    /**
158     * Determines whether each of the qualifying values in the given qualification (Map<String,String>) match the given corresponding values
159     * 
160     * @param membershipQualifications Map<String,String> containing qualifying values to check
161     * @param constraintCode constraint code value to match
162     * @param operator operator value to match
163     * @param attributeValue attribute value to match
164     * @return boolean true if all qualifying values match the given values, false if at least one qualifying value does not match
165     */
166    protected boolean doMembershipQualificationsMatchValues(Map<String,String> membershipQualifications, String constraintCode, String operator, String attributeValue) {
167        String constraintQualifyValue = membershipQualifications.get(SecKimAttributes.CONSTRAINT_CODE);
168        String operatorQualifyValue = membershipQualifications.get(SecKimAttributes.OPERATOR);
169        String propertyValueQualifyValue = membershipQualifications.get(SecKimAttributes.PROPERTY_VALUE);
170
171        if (!StringUtils.equals(propertyValueQualifyValue, attributeValue)) {
172            return false;
173        }
174
175        if (!StringUtils.equals(constraintQualifyValue, constraintCode)) {
176            return false;
177        }
178
179        if (!StringUtils.equals(operatorQualifyValue, operator)) {
180            return false;
181        }
182
183        return true;
184    }
185}