Fix userattr bind rule GROUPDN keyword when using a url search failure . Issue 1596.
| | |
| | | * @param evalCtx The evaluation context to use in the evaluation. |
| | | * @param attributeType The attribute type of the entry to use to get the |
| | | * values for the groupd DNs. |
| | | * @param suffixDN The suffix that the groupDN must be under. If it's null, |
| | | * then the groupDN can be anywhere in the DIT. |
| | | * @return Enumeration evaluation result. |
| | | */ |
| | | public static EnumEvalResult evaluate (Entry e, AciEvalContext evalCtx, |
| | | AttributeType attributeType) { |
| | | AttributeType attributeType, |
| | | DN suffixDN) { |
| | | EnumEvalResult matched= EnumEvalResult.FALSE; |
| | | List<Attribute> attrs = e.getAttribute(attributeType); |
| | | LinkedHashSet<AttributeValue> vals = attrs.get(0).getValues(); |
| | | for(AttributeValue v : vals) { |
| | | try { |
| | | DN groupDN=DN.decode(v.getStringValue()); |
| | | if(suffixDN != null && |
| | | !groupDN.isDescendantOf(suffixDN)) |
| | | continue; |
| | | Group group = groupManager.getGroupInstance(groupDN); |
| | | if((group != null) && (evalCtx.isMemberOf(group))) { |
| | | matched=EnumEvalResult.TRUE; |
| | |
| | | import static org.opends.server.authorization.dseecompat.Aci.*; |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import java.util.StringTokenizer; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.regex.Pattern; |
| | | import java.util.regex.Matcher; |
| | | |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.LDAPURL; |
| | | import org.opends.server.types.DirectoryException; |
| | | |
| | | /** |
| | | * This class is used by USERDN and GROUPDN userattr types |
| | |
| | | */ |
| | | private String attrTypeStr; |
| | | |
| | | /* |
| | | * The base DN of a URL parsed from the rule. Used to make sure groupdn |
| | | * are under this suffix. Originally a way to search all nested groups |
| | | * under this suffix, so the behavior is slightly different. |
| | | */ |
| | | private DN baseDN=null; |
| | | |
| | | |
| | | /** |
| | | * Construct a class from the inheritance pattern. The skipParsing boolean |
| | |
| | | } |
| | | } |
| | | } else { |
| | | if((this.attributeType = |
| | | DirectoryServer.getAttributeType(pattern)) == null) |
| | | this.attributeType = |
| | | DirectoryServer.getDefaultAttributeType(pattern); |
| | | numLevels=1; |
| | | levels[0]=0; |
| | | attrTypeStr=pattern; |
| | | if(pattern.startsWith(NULL_LDAP_URL)) { |
| | | try { |
| | | LDAPURL url=LDAPURL.decode(pattern, true); |
| | | LinkedHashSet<String>attrs=url.getAttributes(); |
| | | if(attrs.size() != 1) { |
| | | int msgID = MSGID_ACI_SYNTAX_INVALID_USERATTR_ATTR_URL; |
| | | String message = getMessage(msgID, pattern); |
| | | throw new AciException(msgID, pattern); |
| | | } |
| | | baseDN=url.getBaseDN(); |
| | | if(baseDN.isNullDN()){ |
| | | int msgID = MSGID_ACI_SYNTAX_INVALID_USERATTR_BASEDN_URL; |
| | | String message = getMessage(msgID, pattern); |
| | | throw new AciException(msgID, message); |
| | | } |
| | | attrTypeStr=attrs.iterator().next(); |
| | | } catch (DirectoryException ex) { |
| | | int msgID = MSGID_ACI_SYNTAX_INVALID_USERATTR_URL; |
| | | String message = getMessage(msgID, ex.getErrorMessage()); |
| | | throw new AciException(msgID, message); |
| | | } |
| | | } |
| | | if((this.attributeType = |
| | | DirectoryServer.getAttributeType(attrTypeStr)) == null) |
| | | this.attributeType = |
| | | DirectoryServer.getDefaultAttributeType(attrTypeStr); |
| | | numLevels=1; |
| | | levels[0]=0; |
| | | } |
| | | } |
| | | |
| | |
| | | public String getAttrTypeStr() { |
| | | return attrTypeStr; |
| | | } |
| | | |
| | | /** |
| | | * Return the DN that groupdn must be under. |
| | | * |
| | | * @return DN that groupdn must be under. |
| | | */ |
| | | public DN getBaseDN() { |
| | | return baseDN; |
| | | } |
| | | } |
| | | |
| | |
| | | int numLevels=parentInheritance.getNumLevels(); |
| | | int[] levels=parentInheritance.getLevels(); |
| | | AttributeType attrType=parentInheritance.getAttributeType(); |
| | | DN baseDN=parentInheritance.getBaseDN(); |
| | | if(baseDN != null) { |
| | | if (evalCtx.getResourceEntry().hasAttribute(attrType)) |
| | | matched=GroupDN.evaluate(evalCtx.getResourceEntry(), |
| | | evalCtx,attrType, baseDN); |
| | | } else { |
| | | for(int i=0;((i < numLevels) && !stop); i++ ) { |
| | | //The ROLEDN keyword will always enter this statement. The others |
| | | //might. For the add operation, the resource itself (level 0) |
| | |
| | | undefined=true; |
| | | } else if (evalCtx.getResourceEntry().hasAttribute(attrType)) { |
| | | matched = |
| | | evalEntryAttr(evalCtx.getResourceEntry(), |
| | | evalCtx,attrType); |
| | | if(matched.equals(EnumEvalResult.TRUE)) |
| | | evalEntryAttr(evalCtx.getResourceEntry(), |
| | | evalCtx,attrType); |
| | | if(matched.equals(EnumEvalResult.TRUE)) |
| | | stop=true; |
| | | } |
| | | } else { |
| | |
| | | } |
| | | } |
| | | } |
| | | return matched.getRet(type, undefined); |
| | | } |
| | | return matched.getRet(type, undefined); |
| | | } |
| | | |
| | | /** |
| | |
| | | break; |
| | | } |
| | | case GROUPDN: { |
| | | result=GroupDN.evaluate(e, evalCtx, attributeType); |
| | | result=GroupDN.evaluate(e, evalCtx, attributeType, null); |
| | | break; |
| | | } |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if an "aci" attribute |
| | | * type value parse failed because a bind rule userattr LDAP URL failed |
| | | * to decode. This takes one argument the message from the LDAP |
| | | * URL decode DirectoryException. |
| | | */ |
| | | public static final int MSGID_ACI_SYNTAX_INVALID_USERATTR_URL = |
| | | CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 78; |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if an "aci" attribute |
| | | * type value parse failed because a bind rule userattr LDAP URL contained |
| | | * a null base DN. This takes one argument the ldap URL from the bind rule |
| | | * expression. |
| | | */ |
| | | public static final int MSGID_ACI_SYNTAX_INVALID_USERATTR_BASEDN_URL = |
| | | CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 79; |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if an "aci" attribute |
| | | * type value parse failed because a bind rule userattr LDAP URL attribute |
| | | * field either contained more than one attribute or the field was null. |
| | | * This takes one argument the ldap URL from the bind rule expression. |
| | | */ |
| | | public static final int MSGID_ACI_SYNTAX_INVALID_USERATTR_ATTR_URL = |
| | | CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 80; |
| | | |
| | | /** |
| | | * Associates a set of generic messages with the message IDs defined in |
| | | * this class. |
| | | */ |
| | |
| | | "invalid ACIs rules were detected either when the server " + |
| | | "was started or during a backend initialization"); |
| | | |
| | | |
| | | registerMessage(MSGID_ACI_SYNTAX_INVALID_USERATTR_URL, |
| | | "The provided Access Control Instruction (ACI) bind rule " + |
| | | "userattr expression value failed to URL decode for " + |
| | | "the following reason: %s"); |
| | | |
| | | |
| | | registerMessage(MSGID_ACI_SYNTAX_INVALID_USERATTR_BASEDN_URL, |
| | | "The provided Access Control Instruction (ACI) bind rule " + |
| | | "userattr expression value failed to parse because the " + |
| | | "ldap URL \"%s\" contains an empty base DN"); |
| | | |
| | | |
| | | registerMessage(MSGID_ACI_SYNTAX_INVALID_USERATTR_ATTR_URL, |
| | | "The provided Access Control Instruction (ACI) bind rule " + |
| | | "userattr expression value failed to parse because the " + |
| | | "attribute field of the ldap URL \"%s\" either contains more " + |
| | | "than one description or the field is empty"); |
| | | } |
| | | } |
| | |
| | | "objectclass: top", |
| | | "objectclass: groupOfNames", |
| | | "cn: group", |
| | | "member: uid=user.1,ou=People,o=test", |
| | | "member: uid=user.3,ou=People,o=test", |
| | | "", |
| | | "dn: uid=superuser,ou=admins,o=test", |
| | | "objectClass: top", |
| | |
| | | "sn: 1", |
| | | "cn: User1", |
| | | "l: Austin", |
| | | "manager: cn=group,ou=People,o=test", |
| | | "userPassword: password", |
| | | "", |
| | | "dn: uid=user.2,ou=People,o=test", |
| | |
| | | |
| | | |
| | | private static final |
| | | String grpAttrAci = "(targetattr=\"*\")" + |
| | | "(version 3.0; acl \"user attr URL example\"; " + |
| | | "allow (search,read) " + |
| | | "userattr=\"ldap:///ou=People,o=test?manager#GROUPDN\";)"; |
| | | |
| | | |
| | | private static final |
| | | String grp1AttrAci = "(targetattr=\"*\")" + |
| | | "(version 3.0; acl \"user attr1 URL example\"; " + |
| | | "allow (search,read) " + |
| | | "userattr=\"ldap:///ou=People1,o=test?manager#GROUPDN\";)"; |
| | | |
| | | private static final |
| | | String starAciAttrs = "(targetattr=\"* || aci\")" + |
| | | "(version 3.0;acl \"read/search all user, aci op\";" + |
| | | "allow (search, read) " + |
| | |
| | | deleteAttrFromEntry(user1, "aci"); |
| | | } |
| | | |
| | | /** |
| | | * Test two scenerios with userattr LDAP URL and groupdn keyword. |
| | | * |
| | | * @throws Exception Exception If test result is unexpected. |
| | | */ |
| | | @Test() |
| | | public void testTargetAttrGrpDN() throws Exception { |
| | | String aciLdif=makeAddAciLdif("aci", user1, grpAttrAci); |
| | | modEntries(aciLdif, DIR_MGR_DN, PWD); |
| | | String userResults = |
| | | LDAPSearchParams(user3, PWD, null, null, null, |
| | | user1, filter, attrList); |
| | | Assert.assertFalse(userResults.equals("")); |
| | | HashMap<String, String> attrMap=getAttrMap(userResults); |
| | | Assert.assertTrue(attrMap.containsKey("l")); |
| | | Assert.assertTrue(attrMap.containsKey("sn")); |
| | | Assert.assertTrue(attrMap.containsKey("uid")); |
| | | deleteAttrFromEntry(user1, "aci"); |
| | | String aciLdif1=makeAddAciLdif("aci", user1, grp1AttrAci); |
| | | modEntries(aciLdif1, DIR_MGR_DN, PWD); |
| | | String userResults1 = |
| | | LDAPSearchParams(user3, PWD, null, null, null, |
| | | user1, filter, attrList); |
| | | //This search should return nothing since the URL has a bogus DN. |
| | | Assert.assertTrue(userResults1.equals("")); |
| | | } |
| | | |
| | | private void |
| | | checkAttributeVal(HashMap<String, String> attrMap, String attr, |