001/**************************************************************** 002 * Licensed to the Apache Software Foundation (ASF) under one * 003 * or more contributor license agreements. See the NOTICE file * 004 * distributed with this work for additional information * 005 * regarding copyright ownership. The ASF licenses this file * 006 * to you under the Apache License, Version 2.0 (the * 007 * "License"); you may not use this file except in compliance * 008 * with the License. You may obtain a copy of the License at * 009 * * 010 * http://www.apache.org/licenses/LICENSE-2.0 * 011 * * 012 * Unless required by applicable law or agreed to in writing, * 013 * software distributed under the License is distributed on an * 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * 015 * KIND, either express or implied. See the License for the * 016 * specific language governing permissions and limitations * 017 * under the License. * 018 ****************************************************************/ 019package org.apache.james.user.ldap; 020 021import org.apache.commons.configuration.HierarchicalConfiguration; 022 023import javax.naming.NamingEnumeration; 024import javax.naming.NamingException; 025import javax.naming.directory.Attribute; 026import javax.naming.directory.Attributes; 027import javax.naming.ldap.LdapContext; 028import java.util.ArrayList; 029import java.util.Collection; 030import java.util.Collections; 031import java.util.HashMap; 032import java.util.List; 033import java.util.Map; 034 035/** 036 * <p> 037 * Encapsulates the information required to restrict users to LDAP groups or 038 * roles. Instances of this type are populated from the contents of the 039 * <code><users-store></code> configuration child-element 040 * <code><restriction><code>. 041 * </p> 042 * 043 * @see ReadOnlyUsersLDAPRepository 044 * @see ReadOnlyLDAPUser 045 */ 046 047public class ReadOnlyLDAPGroupRestriction { 048 /** 049 * The name of the LDAP attribute name which holds the unique names 050 * (distinguished-names/DNs) of the members of the group/role. 051 */ 052 private String memberAttribute; 053 054 /** 055 * The distinguished-names of the LDAP groups/roles to which James users 056 * must belong. A user who is not a member of at least one of the groups or 057 * roles specified here will not be allowed to authenticate against James. 058 * If the list is empty, group/role restriction will be disabled. 059 */ 060 private final List<String> groupDNs; 061 062 /** 063 * Initialises an instance from the contents of a 064 * <code><restriction><code> configuration XML 065 * element. 066 * 067 * @param configuration The avalon configuration instance that encapsulates the 068 * contents of the <code><restriction><code> XML element. 069 * @throws org.apache.commons.configuration.ConfigurationException 070 * If an error occurs extracting values from the configuration 071 * element. 072 */ 073 public ReadOnlyLDAPGroupRestriction(HierarchicalConfiguration configuration) { 074 groupDNs = new ArrayList<String>(); 075 076 if (configuration != null) { 077 memberAttribute = configuration.getString("[@memberAttribute]"); 078 079 if (configuration.getKeys("group").hasNext()) { 080 Collections.addAll(groupDNs, configuration.getStringArray("group")); 081 } 082 } 083 } 084 085 /** 086 * Indicates if group/role-based restriction is enabled for the the 087 * user-store, based on the information encapsulated in the instance. 088 * 089 * @return <code>True</code> If there list of group/role distinguished names 090 * is not empty, and <code>false</code> otherwise. 091 */ 092 protected boolean isActivated() { 093 return !groupDNs.isEmpty(); 094 } 095 096 /** 097 * Converts an instance of this type to a string. 098 * 099 * @return A string representation of the instance. 100 */ 101 public String toString() { 102 return "Activated=" + isActivated() + "; Groups=" + groupDNs; 103 } 104 105 /** 106 * Returns the distinguished-names (DNs) of all the members of the groups 107 * specified in the restriction list. The information is organised as a list 108 * of <code>"<groupDN>=< 109 * [userDN1,userDN2,...,userDNn]>"</code>. Put differently, each 110 * <code>groupDN</code> is associated to a list of <code>userDNs</code>. 111 * 112 * @return Returns a map of groupDNs to userDN lists. 113 * @throws NamingException Propagated from underlying LDAP communication layer. 114 */ 115 protected Map<String, Collection<String>> getGroupMembershipLists(LdapContext ldapContext) throws NamingException { 116 Map<String, Collection<String>> result = new HashMap<String, Collection<String>>(); 117 118 for (String groupDN : groupDNs) { 119 result.put(groupDN, extractMembers(ldapContext.getAttributes(groupDN))); 120 } 121 122 return result; 123 } 124 125 /** 126 * Extracts the DNs for members of the group with the given LDAP context 127 * attributes. This is achieved by extracting all the values of the LDAP 128 * attribute, with name equivalent to the field value 129 * {@link #memberAttribute}, from the attributes collection. 130 * 131 * @param groupAttributes The attributes taken from the group's LDAP context. 132 * @return A collection of distinguished-names for the users belonging to 133 * the group with the specified attributes. 134 * @throws NamingException Propagated from underlying LDAP communication layer. 135 */ 136 private Collection<String> extractMembers(Attributes groupAttributes) throws NamingException { 137 Collection<String> result = new ArrayList<String>(); 138 Attribute members = groupAttributes.get(memberAttribute); 139 NamingEnumeration<?> memberDNs = members.getAll(); 140 141 while (memberDNs.hasMore()) 142 result.add(memberDNs.next().toString()); 143 144 return result; 145 } 146}