blob: 2a81fd3e9fa511848f266a9d0aee66c40912e8dc [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>ActiveDirectoryRealm.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Apache Shiro :: Core</a> &gt; <a href="index.source.html" class="el_package">org.apache.shiro.realm.activedirectory</a> &gt; <span class="el_source">ActiveDirectoryRealm.java</span></div><h1>ActiveDirectoryRealm.java</h1><pre class="source lang-java linenums">/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* &quot;License&quot;); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* &quot;AS IS&quot; 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.apache.shiro.realm.activedirectory;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.ldap.AbstractLdapRealm;
import org.apache.shiro.realm.ldap.LdapContextFactory;
import org.apache.shiro.realm.ldap.LdapUtils;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapContext;
import java.util.*;
/**
* A {@link Realm} that authenticates with an active directory LDAP
* server to determine the roles for a particular user. This implementation
* queries for the user's groups and then maps the group names to roles using the
* {@link #groupRolesMap}.
*
* @since 0.1
*/
<span class="fc" id="L53">public class ActiveDirectoryRealm extends AbstractLdapRealm {</span>
//TODO - complete JavaDoc
/*--------------------------------------------
| C O N S T A N T S |
============================================*/
<span class="fc" id="L61"> private static final Logger log = LoggerFactory.getLogger(ActiveDirectoryRealm.class);</span>
private static final String ROLE_NAMES_DELIMETER = &quot;,&quot;;
/*--------------------------------------------
| I N S T A N C E V A R I A B L E S |
============================================*/
/**
* Mapping from fully qualified active directory
* group names (e.g. CN=Group,OU=Company,DC=MyDomain,DC=local)
* as returned by the active directory LDAP server to role names.
*/
private Map&lt;String, String&gt; groupRolesMap;
/*--------------------------------------------
| C O N S T R U C T O R S |
============================================*/
public void setGroupRolesMap(Map&lt;String, String&gt; groupRolesMap) {
<span class="nc" id="L81"> this.groupRolesMap = groupRolesMap;</span>
<span class="nc" id="L82"> }</span>
/*--------------------------------------------
| M E T H O D S |
============================================*/
/**
* Builds an {@link AuthenticationInfo} object by querying the active directory LDAP context for the
* specified username. This method binds to the LDAP server using the provided username and password -
* which if successful, indicates that the password is correct.
* &lt;p/&gt;
* This method can be overridden by subclasses to query the LDAP server in a more complex way.
*
* @param token the authentication token provided by the user.
* @param ldapContextFactory the factory used to build connections to the LDAP server.
* @return an {@link AuthenticationInfo} instance containing information retrieved from LDAP.
* @throws NamingException if any LDAP errors occur during the search.
*/
protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token, LdapContextFactory ldapContextFactory) throws NamingException {
<span class="nc" id="L103"> UsernamePasswordToken upToken = (UsernamePasswordToken) token;</span>
// Binds using the username and password provided by the user.
<span class="nc" id="L106"> LdapContext ctx = null;</span>
try {
<span class="nc" id="L108"> ctx = ldapContextFactory.getLdapContext(upToken.getUsername(), String.valueOf(upToken.getPassword()));</span>
} finally {
<span class="nc" id="L110"> LdapUtils.closeContext(ctx);</span>
<span class="nc" id="L111"> }</span>
<span class="nc" id="L113"> return buildAuthenticationInfo(upToken.getUsername(), upToken.getPassword());</span>
}
protected AuthenticationInfo buildAuthenticationInfo(String username, char[] password) {
<span class="nc" id="L117"> return new SimpleAuthenticationInfo(username, password, getName());</span>
}
/**
* Builds an {@link org.apache.shiro.authz.AuthorizationInfo} object by querying the active directory LDAP context for the
* groups that a user is a member of. The groups are then translated to role names by using the
* configured {@link #groupRolesMap}.
* &lt;p/&gt;
* This implementation expects the &lt;tt&gt;principal&lt;/tt&gt; argument to be a String username.
* &lt;p/&gt;
* Subclasses can override this method to determine authorization data (roles, permissions, etc) in a more
* complex way. Note that this default implementation does not support permissions, only roles.
*
* @param principals the principal of the Subject whose account is being retrieved.
* @param ldapContextFactory the factory used to create LDAP connections.
* @return the AuthorizationInfo for the given Subject principal.
* @throws NamingException if an error occurs when searching the LDAP server.
*/
protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals, LdapContextFactory ldapContextFactory) throws NamingException {
<span class="nc" id="L138"> String username = (String) getAvailablePrincipal(principals);</span>
// Perform context search
<span class="nc" id="L141"> LdapContext ldapContext = ldapContextFactory.getSystemLdapContext();</span>
Set&lt;String&gt; roleNames;
try {
<span class="nc" id="L146"> roleNames = getRoleNamesForUser(username, ldapContext);</span>
} finally {
<span class="nc" id="L148"> LdapUtils.closeContext(ldapContext);</span>
<span class="nc" id="L149"> }</span>
<span class="nc" id="L151"> return buildAuthorizationInfo(roleNames);</span>
}
protected AuthorizationInfo buildAuthorizationInfo(Set&lt;String&gt; roleNames) {
<span class="nc" id="L155"> return new SimpleAuthorizationInfo(roleNames);</span>
}
private Set&lt;String&gt; getRoleNamesForUser(String username, LdapContext ldapContext) throws NamingException {
Set&lt;String&gt; roleNames;
<span class="nc" id="L160"> roleNames = new LinkedHashSet&lt;String&gt;();</span>
<span class="nc" id="L162"> SearchControls searchCtls = new SearchControls();</span>
<span class="nc" id="L163"> searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);</span>
<span class="nc" id="L165"> String userPrincipalName = username;</span>
<span class="nc bnc" id="L166" title="All 2 branches missed."> if (principalSuffix != null) {</span>
<span class="nc" id="L167"> userPrincipalName += principalSuffix;</span>
}
//SHIRO-115 - prevent potential code injection:
<span class="nc" id="L171"> String searchFilter = &quot;(&amp;(objectClass=*)(userPrincipalName={0}))&quot;;</span>
<span class="nc" id="L172"> Object[] searchArguments = new Object[]{userPrincipalName};</span>
<span class="nc" id="L174"> NamingEnumeration answer = ldapContext.search(searchBase, searchFilter, searchArguments, searchCtls);</span>
<span class="nc bnc" id="L176" title="All 2 branches missed."> while (answer.hasMoreElements()) {</span>
<span class="nc" id="L177"> SearchResult sr = (SearchResult) answer.next();</span>
<span class="nc bnc" id="L179" title="All 2 branches missed."> if (log.isDebugEnabled()) {</span>
<span class="nc" id="L180"> log.debug(&quot;Retrieving group names for user [&quot; + sr.getName() + &quot;]&quot;);</span>
}
<span class="nc" id="L183"> Attributes attrs = sr.getAttributes();</span>
<span class="nc bnc" id="L185" title="All 2 branches missed."> if (attrs != null) {</span>
<span class="nc" id="L186"> NamingEnumeration ae = attrs.getAll();</span>
<span class="nc bnc" id="L187" title="All 2 branches missed."> while (ae.hasMore()) {</span>
<span class="nc" id="L188"> Attribute attr = (Attribute) ae.next();</span>
<span class="nc bnc" id="L190" title="All 2 branches missed."> if (attr.getID().equals(&quot;memberOf&quot;)) {</span>
<span class="nc" id="L192"> Collection&lt;String&gt; groupNames = LdapUtils.getAllAttributeValues(attr);</span>
<span class="nc bnc" id="L194" title="All 2 branches missed."> if (log.isDebugEnabled()) {</span>
<span class="nc" id="L195"> log.debug(&quot;Groups found for user [&quot; + username + &quot;]: &quot; + groupNames);</span>
}
<span class="nc" id="L198"> Collection&lt;String&gt; rolesForGroups = getRoleNamesForGroups(groupNames);</span>
<span class="nc" id="L199"> roleNames.addAll(rolesForGroups);</span>
}
<span class="nc" id="L201"> }</span>
}
<span class="nc" id="L203"> }</span>
<span class="nc" id="L204"> return roleNames;</span>
}
/**
* This method is called by the default implementation to translate Active Directory group names
* to role names. This implementation uses the {@link #groupRolesMap} to map group names to role names.
*
* @param groupNames the group names that apply to the current user.
* @return a collection of roles that are implied by the given role names.
*/
protected Collection&lt;String&gt; getRoleNamesForGroups(Collection&lt;String&gt; groupNames) {
<span class="nc" id="L215"> Set&lt;String&gt; roleNames = new HashSet&lt;String&gt;(groupNames.size());</span>
<span class="nc bnc" id="L217" title="All 2 branches missed."> if (groupRolesMap != null) {</span>
<span class="nc bnc" id="L218" title="All 2 branches missed."> for (String groupName : groupNames) {</span>
<span class="nc" id="L219"> String strRoleNames = groupRolesMap.get(groupName);</span>
<span class="nc bnc" id="L220" title="All 2 branches missed."> if (strRoleNames != null) {</span>
<span class="nc bnc" id="L221" title="All 2 branches missed."> for (String roleName : strRoleNames.split(ROLE_NAMES_DELIMETER)) {</span>
<span class="nc bnc" id="L223" title="All 2 branches missed."> if (log.isDebugEnabled()) {</span>
<span class="nc" id="L224"> log.debug(&quot;User is member of group [&quot; + groupName + &quot;] so adding role [&quot; + roleName + &quot;]&quot;);</span>
}
<span class="nc" id="L227"> roleNames.add(roleName);</span>
}
}
<span class="nc" id="L231"> }</span>
}
<span class="nc" id="L233"> return roleNames;</span>
}
}
</pre><div class="footer"><span class="right">Created with <a href="http://www.eclemma.org/jacoco">JaCoCo</a> 0.7.7.201606060606</span></div></body></html>