View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.shiro.realm.ldap;
20  
21  import org.apache.shiro.authc.AuthenticationException;
22  import org.apache.shiro.authc.AuthenticationInfo;
23  import org.apache.shiro.authc.AuthenticationToken;
24  import org.apache.shiro.authz.AuthorizationException;
25  import org.apache.shiro.authz.AuthorizationInfo;
26  import org.apache.shiro.realm.AuthorizingRealm;
27  import org.apache.shiro.subject.PrincipalCollection;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  
31  import javax.naming.NamingException;
32  
33  /**
34   * <p>A {@link org.apache.shiro.realm.Realm} that authenticates with an LDAP
35   * server to build the Subject for a user.  This implementation only returns roles for a
36   * particular user, and not permissions - but it can be subclassed to build a permission
37   * list as well.</p>
38   *
39   * <p>Implementations would need to implement the
40   * {@link #queryForAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken ,LdapContextFactory)} and
41   * {@link #queryForAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection ,LdapContextFactory)} abstract methods.</p>
42   *
43   * <p>By default, this implementation will create an instance of {@link DefaultLdapContextFactory} to use for
44   * creating LDAP connections using the principalSuffix, searchBase, url, systemUsername, and systemPassword properties
45   * specified on the realm.  The remaining settings use the defaults of {@link DefaultLdapContextFactory}, which are usually
46   * sufficient.  If more customized connections are needed, you should inject a custom {@link LdapContextFactory}, which
47   * will cause these properties specified on the realm to be ignored.</p>
48   *
49   * @see #queryForAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken , LdapContextFactory)
50   * @see #queryForAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection , LdapContextFactory)
51   * @since 0.1
52   */
53  public abstract class AbstractLdapRealm extends AuthorizingRealm {
54  
55      //TODO - complete JavaDoc
56  
57      /*--------------------------------------------
58      |             C O N S T A N T S             |
59      ============================================*/
60  
61      private static final Logger log = LoggerFactory.getLogger(AbstractLdapRealm.class);
62  
63      /*--------------------------------------------
64      |    I N S T A N C E   V A R I A B L E S    |
65      ============================================*/
66      protected String principalSuffix = null;
67  
68      protected String searchBase = null;
69  
70      protected String url = null;
71  
72      protected String systemUsername = null;
73  
74      protected String systemPassword = null;
75  
76      //SHIRO-115 - prevent potential code injection:
77      protected String searchFilter = "(&(objectClass=*)(userPrincipalName={0}))";
78  
79      private LdapContextFactory ldapContextFactory = null;
80  
81      /*--------------------------------------------
82      |         C O N S T R U C T O R S           |
83      ============================================*/
84  
85      /*--------------------------------------------
86      |  A C C E S S O R S / M O D I F I E R S    |
87      ============================================*/
88  
89      /*--------------------------------------------
90      |               M E T H O D S               |
91      ============================================*/
92  
93  
94      /**
95       * Used when initializing the default {@link LdapContextFactory}.  This property is ignored if a custom
96       * <tt>LdapContextFactory</tt> is specified.
97       *
98       * @param principalSuffix the suffix.
99       * @see DefaultLdapContextFactory#setPrincipalSuffix(String)
100      */
101     public void setPrincipalSuffix(String principalSuffix) {
102         this.principalSuffix = principalSuffix;
103     }
104 
105     /**
106      * Used when initializing the default {@link LdapContextFactory}.  This property is ignored if a custom
107      * <tt>LdapContextFactory</tt> is specified.
108      *
109      * @param searchBase the search base.
110      * @see DefaultLdapContextFactory#setSearchBase(String)
111      */
112     public void setSearchBase(String searchBase) {
113         this.searchBase = searchBase;
114     }
115 
116     /**
117      * Used when initializing the default {@link LdapContextFactory}.  This property is ignored if a custom
118      * <tt>LdapContextFactory</tt> is specified.
119      *
120      * @param url the LDAP url.
121      * @see DefaultLdapContextFactory#setUrl(String)
122      */
123     public void setUrl(String url) {
124         this.url = url;
125     }
126 
127     /**
128      * Used when initializing the default {@link LdapContextFactory}.  This property is ignored if a custom
129      * <tt>LdapContextFactory</tt> is specified.
130      *
131      * @param systemUsername the username to use when logging into the LDAP server for authorization.
132      * @see DefaultLdapContextFactory#setSystemUsername(String)
133      */
134     public void setSystemUsername(String systemUsername) {
135         this.systemUsername = systemUsername;
136     }
137 
138 
139     /**
140      * Used when initializing the default {@link LdapContextFactory}.  This property is ignored if a custom
141      * <tt>LdapContextFactory</tt> is specified.
142      *
143      * @param systemPassword the password to use when logging into the LDAP server for authorization.
144      * @see DefaultLdapContextFactory#setSystemPassword(String)
145      */
146     public void setSystemPassword(String systemPassword) {
147         this.systemPassword = systemPassword;
148     }
149 
150 
151     /**
152      * Configures the {@link LdapContextFactory} implementation that is used to create LDAP connections for
153      * authentication and authorization.  If this is set, the {@link LdapContextFactory} provided will be used.
154      * Otherwise, a {@link DefaultLdapContextFactory} instance will be created based on the properties specified
155      * in this realm.
156      *
157      * @param ldapContextFactory the factory to use - if not specified, a default factory will be created automatically.
158      */
159     public void setLdapContextFactory(LdapContextFactory ldapContextFactory) {
160         this.ldapContextFactory = ldapContextFactory;
161     }
162 
163 
164     public void setSearchFilter(String searchFilter) {
165         this.searchFilter = searchFilter;
166     }
167 
168     /*--------------------------------------------
169     |               M E T H O D S                |
170     ============================================*/
171 
172     protected void onInit() {
173         super.onInit();
174         ensureContextFactory();
175     }
176 
177     private LdapContextFactory ensureContextFactory() {
178         if (this.ldapContextFactory == null) {
179 
180             if (log.isDebugEnabled()) {
181                 log.debug("No LdapContextFactory specified - creating a default instance.");
182             }
183 
184             DefaultLdapContextFactoryy.html#DefaultLdapContextFactory">DefaultLdapContextFactory defaultFactory = new DefaultLdapContextFactory();
185             defaultFactory.setPrincipalSuffix(this.principalSuffix);
186             defaultFactory.setSearchBase(this.searchBase);
187             defaultFactory.setUrl(this.url);
188             defaultFactory.setSystemUsername(this.systemUsername);
189             defaultFactory.setSystemPassword(this.systemPassword);
190 
191             this.ldapContextFactory = defaultFactory;
192         }
193         return this.ldapContextFactory;
194     }
195 
196 
197     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
198         AuthenticationInfo info;
199         try {
200             info = queryForAuthenticationInfo(token, ensureContextFactory());
201         } catch (javax.naming.AuthenticationException e) {
202             throw new AuthenticationException("LDAP authentication failed.", e);
203         } catch (NamingException e) {
204             String msg = "LDAP naming error while attempting to authenticate user.";
205             throw new AuthenticationException(msg, e);
206         }
207 
208         return info;
209     }
210 
211 
212     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
213         AuthorizationInfo info;
214         try {
215             info = queryForAuthorizationInfo(principals, ensureContextFactory());
216         } catch (NamingException e) {
217             String msg = "LDAP naming error while attempting to retrieve authorization for user [" + principals + "].";
218             throw new AuthorizationException(msg, e);
219         }
220 
221         return info;
222     }
223 
224 
225     /**
226      * <p>Abstract method that should be implemented by subclasses to builds an
227      * {@link AuthenticationInfo} object by querying the LDAP context for the
228      * specified username.</p>
229      *
230      * @param token              the authentication token given during authentication.
231      * @param ldapContextFactory factory used to retrieve LDAP connections.
232      * @return an {@link AuthenticationInfo} instance containing information retrieved from the LDAP server.
233      * @throws NamingException if any LDAP errors occur during the search.
234      */
235     protected abstract AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token, LdapContextFactory ldapContextFactory) throws NamingException;
236 
237 
238     /**
239      * <p>Abstract method that should be implemented by subclasses to builds an
240      * {@link AuthorizationInfo} object by querying the LDAP context for the
241      * specified principal.</p>
242      *
243      * @param principal          the principal of the Subject whose AuthenticationInfo should be queried from the LDAP server.
244      * @param ldapContextFactory factory used to retrieve LDAP connections.
245      * @return an {@link AuthorizationInfo} instance containing information retrieved from the LDAP server.
246      * @throws NamingException if any LDAP errors occur during the search.
247      */
248     protected abstract AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principal, LdapContextFactory ldapContextFactory) throws NamingException;
249 
250 }