/**
* Licensed to Apereo under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright ownership. Apereo
* licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the License at the
* following location:
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apereo.portal.layout.dlm.providers;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import org.apereo.portal.EntityIdentifier;
import org.apereo.portal.groups.GroupsException;
import org.apereo.portal.groups.IEntityGroup;
import org.apereo.portal.groups.IGroupConstants;
import org.apereo.portal.groups.IGroupMember;
import org.apereo.portal.layout.dlm.Evaluator;
import org.apereo.portal.layout.dlm.EvaluatorFactory;
import org.apereo.portal.security.IPerson;
import org.apereo.portal.services.GroupService;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
/**
* Answers isApplicable() in the affirmative if the user represented by the passed in IPerson is a
* member of the group whose name is passed to the constructor of this class.
*
* <p>Added support for a 'deepMemberOf' mode, from 2.5 patches (UP-1284) and cache group key rather
* than the group itself (UP-1532). d.e 2006/12/19.
*
* @since 2.5
*/
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class GroupMembershipEvaluator extends Evaluator {
private static final int MEMBER_OF_MODE = 0;
private static final int DEEP_MEMBER_OF_MODE = 1;
@Column(name = "GROUP_NAME")
private String groupName = null;
@Column(name = "GROUP_KEY")
private String groupKey = null;
@Column(name = "EVALUATOR_MODE")
private int evaluatorMode;
/** Zero-arg constructor required by JPA. Other Java code should not use it. */
public GroupMembershipEvaluator() {}
public GroupMembershipEvaluator(String mode, String name) {
if (mode.equals("memberOf")) {
evaluatorMode = MEMBER_OF_MODE;
} else if (mode.equals("deepMemberOf")) {
evaluatorMode = DEEP_MEMBER_OF_MODE;
} else {
throw new RuntimeException(
"Unsupported mode '"
+ mode
+ "' specified. Only 'memberOf' and 'deepMemberOf' are "
+ "supported at this time.");
}
this.groupName = name;
this.groupKey = getGroupKey();
}
private String getGroupKey() {
EntityIdentifier[] groups = null;
try {
groups = GroupService.searchForGroups(groupName, IGroupConstants.IS, IPerson.class);
} catch (GroupsException e1) {
throw new RuntimeException(
"An exception occurred searching for " + "the group " + groupName + ".", e1);
}
if (groups == null || groups.length == 0)
throw new RuntimeException(
"Group with name '"
+ groupName
+ "' not found for "
+ this.getClass().getName()
+ ". All evaluations will return false.");
return groups[0].getKey();
}
private IEntityGroup getGroup(String key) {
try {
return GroupService.findGroup(key);
} catch (GroupsException e) {
throw new RuntimeException(
"An exception occurred retrieving " + "the group " + groupName + ".", e);
}
}
@Override
public boolean isApplicable(IPerson p) {
if (groupKey == null || p == null) return false;
IEntityGroup group = getGroup(groupKey);
// Should not happen, but with the XML to Entity PAGS change some sites may have altered their configuration
// but not updated their group keys in the database or vice versa, especially with an update. To help
// troubleshoot this, try to catch this error and give a bit more useful error message than you'd get from
// the lower-level methods.
if (group == null) {
throw new RuntimeException(
"Error in evaluation. Group key "
+ groupKey
+ " for group name "
+ groupName
+ " did not find a group.");
}
EntityIdentifier ei = p.getEntityIdentifier();
try {
IGroupMember groupMember = GroupService.getGroupMember(ei);
boolean isMember = false;
if (evaluatorMode == MEMBER_OF_MODE) {
isMember = groupMember.isMemberOf(group);
} else {
isMember = groupMember.isDeepMemberOf(group);
}
return isMember;
} catch (Exception e) {
throw new RuntimeException(
"Unable to determine if user '"
+ p.getFullName()
+ "' is in group '"
+ groupName
+ "'",
e);
}
}
@Override
public void toElement(Element parent) {
// Assertions.
if (parent == null) {
String msg = "Argument 'parent' cannot be null.";
throw new IllegalArgumentException(msg);
}
String mde = null;
switch (this.evaluatorMode) {
case MEMBER_OF_MODE:
mde = "memberOf";
break;
case DEEP_MEMBER_OF_MODE:
mde = "deepMemberOf";
break;
default:
throw new IllegalStateException(
"Unrecognized evaluatorMode constant: " + this.evaluatorMode);
}
Element rslt = DocumentHelper.createElement("attribute");
rslt.addAttribute("mode", mde);
rslt.addAttribute("name", this.groupName);
parent.add(rslt);
}
@Override
public Class<? extends EvaluatorFactory> getFactoryClass() {
return GroupMembershipEvaluatorFactory.class;
}
@Override
public String getSummary() {
StringBuilder rslt = new StringBuilder();
rslt.append("(");
rslt.append("MEMBER OF '").append(this.groupName).append("'");
if (evaluatorMode == DEEP_MEMBER_OF_MODE) {
rslt.append(" OR ANY DESCENDANT GROUP");
}
rslt.append(")");
return rslt.toString();
}
}