Class ActiveDirectoryRealm
- java.lang.Object
-
- org.apache.catalina.util.LifecycleBase
-
- org.apache.catalina.util.LifecycleMBeanBase
-
- org.apache.catalina.realm.RealmBase
-
- net.sf.michaelo.tomcat.realm.ActiveDirectoryRealmBase
-
- net.sf.michaelo.tomcat.realm.ActiveDirectoryRealm
-
- All Implemented Interfaces:
MBeanRegistration,Contained,JmxEnabled,Lifecycle,Realm
public class ActiveDirectoryRealm extends ActiveDirectoryRealmBase
A realm which retrieves already authenticated users from Active Directory via LDAP.Configuration
Following options can be configured:dirContextSourceName: the name of theDirContextSourcein JNDI with which principals will be retrieved.localDirContextSource: whether thisDirContextSourceis locally configured in thecontext.xmlor globally configured in theserver.xml(optional). Default value isfalse.roleFormats: comma-separated list of role formats to be applied to user security groups. The following values are possible:sidretrieves theobjectSidandsIDHistoryattribute values,nameretrieves themsDS-PrincipalNameattribute value representing the down-level logon name format:{netbiosDomain}\{samAccountName}, andnameExretrieves thedistinguishedNameandsAMAccountNameattribute values and converts the DC RDNs from the DN to the Kerberos realm and appends thesAMAccountName(reversed RFC 2247) with format{realm}\{samAccountName}. Default issid.prependRoleFormat: whether the role format is prepended to the role as{roleFormat}:{role}. Default isfalse.additionalAttributes: comma-separated list of attributes to be retrieved for the principal. Binary attributes must end with;binaryand will be stored asbyte[], ordinary attributes will be stored asString. If an attribute is multivalued, it will be stored asList.connectionPoolSize: the maximum amount of directory server connections the pool will hold. Default is zero which means no connections will be pooled.maxIdleTime: the maximum amount of time in milliseconds a directory server connection should remain idle before it is closed. Default value is 15 minutes.
Connection Pooling
This realm offers a poor man's directory server connection pooling which can drastically improve access performance for non-session (stateless) applications. It utilizes a LIFO structure based onSynchronizedStack. No background thread is managing the connections. They are acquired, validated, eventually closed and opened whengetPrincipal(GSSName, GSSCredential)is invoked. Validation involves a minimal and limited query with at most 500 ms of wait time just to verify the connection is alive and healthy. If this query fails, the connection is closed immediately. If the amount of requested connections exceeds the ones available in the pool, new ones are opened and pushed onto the pool. If the pool does not accept any addtional connections they are closed immediately.Note: This connection pool feature has to be explicitly enabled by setting
connectionPoolSizeto greater than zero.On Usernames
This realm processes supplied usernames with different types.Supported Types
Only a subset of username types are accepted in contrast to other realm implementations. Namely, this realm must know what type is passed to properly map it into Active Directory search space with aUsernameSearchMapperimplementation. The supported username types are:GSSNameby inspecting the string name type,X509Certificateby extracting theSAN:otherNamefield and matching for MS UPN type id (1.3.6.1.4.1.311.20.2.3).
Note: Both types represent already authenticated users by means of a GSS and/or TLScontext.
Canonicalization
This realm will always try to canonicalize a given username type to a realGSSNamewith the string name type ofKRB5_NT_PRINCIPAL(1.2.840.113554.1.2.2.1) similar to thecanonicalizeflag in thekrb5.conffile. This makes the finalGSSNamefully usable in subsequent GSS-API calls.Referral Handling
Active Directory uses two type of responses when it cannot complete a search request: referrals and search result references. Both are different in nature, read more about them here. For this section I will use the term referral for both types synomously as does the JNDI/LDAP Provider documentation.
When working with the default LDAP ports (not GC) or in a multi-forest environment, it is highly likely to receive referrals (either subordinate or cross) during a search or lookup. Sun's JNDI/LDAP Provider takes the following approach to handle referrals with thejava.naming.referralproperty and its values:ignore,throw, andfollow. You can ignore referrals altogether, but the provider will still signal aPartialResultExceptionwhen aNamingEnumerationis iterated. The reason is because it adds aManageReferralControlwhenignoreis set and assumes that the target server will ignore referrals, but this is a misconception in this provider implementation, see here and here. It is also unclear whether Microsoft Active Directory supports this control.
This realm will catch this exception and continue to process the enumeration. If theDirContextSourceis set tothrow, this realm will catch theReferralExceptionalso, but avoid following referrals manually (for several reasons) and will continue with the process. Following referrals automatically is a completely opaque operation to the application, noReferralExceptionis thrown, but the referrals are handled internally and referral contexts are queried and closed. If you choose tofollowreferrals you must use my Active Directory DNS Locator otherwise the queries will fail and you will suffer from JDK-8161361 and JDK-8160768!Why do you need to use my Active Directory DNS Locator? Microsoft takes a very sophisticated approach on not to rely on hostnames because servers can be provisioned and decommissioned any time. Instead, they heavily rely on DNS domain names and DNS SRV records at runtime. I.e., an initial or a referral URL does not contain a hostname, but only a domain name. While you can connect to the service with this name, you cannot easily authenticate against it with Kerberos because one cannot bind the same SPN
ldap/<dnsDomainName>@<REALM>, e.g.,ldap/example.com@EXAMPLE.COMto more than one account. If you try authenticate anyway, you will receive a "Server not found in Kerberos database (7)" error. Therefore, one has to perform a DNS SRV query (_ldap._tcp.<dnsDomainName>) to test whether this name is a hostname or a domain name served by one or more servers. If it turns out to be a domain name, you have to select one target host from the query response (according to RFC 2782), construct a domain-based SPNldap/<targetHost>/<dnsDomainName>@<REALM>or a host-based oneldap/<targetHost>@<REALM>, obtain a service ticket for and connect to that target host.How to handle referrals? There are several ways depending on your setup: Use the Global Catalog (port 3268) with a single forest and set referrals to
ignore, or with multiple forests and set referrals to eitherfollowwith aDirContextSourcein your home forest and use my Active Directory DNS Locator, orignorewith multipleDirContextSources, and create aCombinedRealmwith oneActiveDirectoryRealmper forest.
You will then have the principal properly looked up in Active Directory.
Further references: How DNS Support for Active Directory Works is a good read on the DNS topic as well as Global Catalog and LDAP Searches and LDAP Referrals.
Note: Always remember, referrals incur an amplification in time and space and make the entire process slower.
Tip: Consider using thePacDataActiveDirectoryRealmif you don't need all features and useSPNEGO authenticationonly since it is orders of magnitude faster, but remember though thatX.509 authenticationstill requires this realm.- See Also:
ActiveDirectoryPrincipal
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description protected static classActiveDirectoryRealm.DirContextConnectionprotected static classActiveDirectoryRealm.User-
Nested classes/interfaces inherited from class org.apache.catalina.realm.RealmBase
RealmBase.AllRolesMode
-
Nested classes/interfaces inherited from interface org.apache.catalina.Lifecycle
Lifecycle.SingleUse
-
-
Field Summary
Fields Modifier and Type Field Description protected String[]additionalAttributesprotected String[]attributesprotected SynchronizedStack<ActiveDirectoryRealm.DirContextConnection>connectionPoolprotected intconnectionPoolSizeprotected StringdirContextSourceNameprotected booleanlocalDirContextSourceprotected longmaxIdleTimeprotected booleanprependRoleFormatprotected String[]roleAttributesprotected String[]roleFormats-
Fields inherited from class net.sf.michaelo.tomcat.realm.ActiveDirectoryRealmBase
logger, sm
-
Fields inherited from class org.apache.catalina.realm.RealmBase
allRolesMode, container, containerLog, realmPath, stripRealmForGss, support, USER_ATTRIBUTES_DELIMITER, USER_ATTRIBUTES_WILDCARD, userAttributes, userAttributesList, validate, x509UsernameRetriever, x509UsernameRetrieverClassName
-
Fields inherited from class org.apache.catalina.util.LifecycleMBeanBase
mserver
-
Fields inherited from interface org.apache.catalina.Lifecycle
AFTER_DESTROY_EVENT, AFTER_INIT_EVENT, AFTER_START_EVENT, AFTER_STOP_EVENT, BEFORE_DESTROY_EVENT, BEFORE_INIT_EVENT, BEFORE_START_EVENT, BEFORE_STOP_EVENT, CONFIGURE_START_EVENT, CONFIGURE_STOP_EVENT, PERIODIC_EVENT, START_EVENT, STOP_EVENT
-
-
Constructor Summary
Constructors Constructor Description ActiveDirectoryRealm()
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description protected ActiveDirectoryRealm.DirContextConnectionacquire()protected voidclose(NamingEnumeration<?> results)protected voidclose(ActiveDirectoryRealm.DirContextConnection connection)protected NamegetDistinguishedName(DirContext context, Name baseName, SearchResult result)Returns the distinguished name of a search result.protected static StringgetNextConnectionId()protected PrincipalgetPrincipal(X509Certificate userCert)protected PrincipalgetPrincipal(GSSName gssName, GSSCredential gssCredential)protected PrincipalgetPrincipal(GSSName gssName, GSSCredential gssCredential, boolean retry)protected NamegetRelativeName(DirContext context, String distinguishedName)protected List<String>getRoles(DirContext context, ActiveDirectoryRealm.User user)protected ActiveDirectoryRealm.UsergetUser(DirContext context, GSSName gssName)protected voidinitInternal()protected voidopen(ActiveDirectoryRealm.DirContextConnection connection)protected voidrelease(ActiveDirectoryRealm.DirContextConnection connection)voidsetAdditionalAttributes(String additionalAttributes)Sets a comma-separated list of Active Directory attributes retreived and stored for the user principal.voidsetConnectionPoolSize(int connectionPoolSize)Sets the maximum amount of directory server connections the pool will hold.voidsetDirContextSourceName(String dirContextSourceName)Sets the name of theDirContextSourcevoidsetLocalDirContextSource(boolean localDirContextSource)Sets whether theDirContextSourceis locally (context.xmldefined or globallyserver.xml.voidsetMaxIdleTime(long maxIdleTime)Sets the maximum amount of time in milliseconds a directory server connection should remain idle before it is closed.voidsetPrependRoleFormat(boolean prependRoleFormat)Sets whether the role format is prepended to the role.voidsetRoleFormats(String roleFormats)Sets a comma-separated list of role formats to be applied to user security groups from Active Directory.protected voidstartInternal()protected voidstopInternal()protected booleanvalidate(ActiveDirectoryRealm.DirContextConnection connection)-
Methods inherited from class net.sf.michaelo.tomcat.realm.ActiveDirectoryRealmBase
getPassword, getPrincipal, getRoles, hasRoleInternal
-
Methods inherited from class org.apache.catalina.realm.RealmBase
addPropertyChangeListener, authenticate, authenticate, authenticate, authenticate, authenticate, authenticate, authenticate, backgroundProcess, findSecurityConstraints, getAllRolesMode, getContainer, getCredentialHandler, getDigest, getDigest, getDomainInternal, getObjectNameKeyProperties, getPrincipal, getPrincipal, getRealmPath, getRealmSuffix, getServer, getTransportGuaranteeRedirectStatus, getUserAttributes, getValidate, getX509UsernameRetrieverClassName, hasMessageDigest, hasResourcePermission, hasRole, hasUserDataPermission, isStripRealmForGss, main, parseUserAttributes, removePropertyChangeListener, setAllRolesMode, setContainer, setCredentialHandler, setRealmPath, setStripRealmForGss, setTransportGuaranteeRedirectStatus, setUserAttributes, setValidate, setX509UsernameRetrieverClassName, toString
-
Methods inherited from class org.apache.catalina.util.LifecycleMBeanBase
destroyInternal, getDomain, getObjectName, postDeregister, postRegister, preDeregister, preRegister, register, setDomain, unregister, unregister
-
Methods inherited from class org.apache.catalina.util.LifecycleBase
addLifecycleListener, destroy, findLifecycleListeners, fireLifecycleEvent, getState, getStateName, getThrowOnFailure, init, removeLifecycleListener, setState, setState, setThrowOnFailure, start, stop
-
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
-
Methods inherited from interface org.apache.catalina.Realm
isAvailable
-
-
-
-
Field Detail
-
localDirContextSource
protected boolean localDirContextSource
-
dirContextSourceName
protected String dirContextSourceName
-
attributes
protected String[] attributes
-
additionalAttributes
protected String[] additionalAttributes
-
roleFormats
protected String[] roleFormats
-
roleAttributes
protected String[] roleAttributes
-
prependRoleFormat
protected boolean prependRoleFormat
-
connectionPoolSize
protected int connectionPoolSize
-
maxIdleTime
protected long maxIdleTime
-
connectionPool
protected SynchronizedStack<ActiveDirectoryRealm.DirContextConnection> connectionPool
-
-
Method Detail
-
getNextConnectionId
protected static String getNextConnectionId()
-
setLocalDirContextSource
public void setLocalDirContextSource(boolean localDirContextSource)
Sets whether theDirContextSourceis locally (context.xmldefined or globallyserver.xml.- Parameters:
localDirContextSource- the local directory context source indication
-
setDirContextSourceName
public void setDirContextSourceName(String dirContextSourceName)
Sets the name of theDirContextSource- Parameters:
dirContextSourceName- the directory context source name
-
setAdditionalAttributes
public void setAdditionalAttributes(String additionalAttributes)
Sets a comma-separated list of Active Directory attributes retreived and stored for the user principal.- Parameters:
additionalAttributes- the additional attributes
-
setRoleFormats
public void setRoleFormats(String roleFormats)
Sets a comma-separated list of role formats to be applied to user security groups from Active Directory.- Parameters:
roleFormats- the role formats
-
setPrependRoleFormat
public void setPrependRoleFormat(boolean prependRoleFormat)
Sets whether the role format is prepended to the role.- Parameters:
prependRoleFormat- the prepend role format indication
-
setConnectionPoolSize
public void setConnectionPoolSize(int connectionPoolSize)
Sets the maximum amount of directory server connections the pool will hold.- Parameters:
connectionPoolSize- the connection pool size
-
setMaxIdleTime
public void setMaxIdleTime(long maxIdleTime)
Sets the maximum amount of time in milliseconds a directory server connection should remain idle before it is closed.- Parameters:
maxIdleTime- the maximum idle time
-
getPrincipal
protected Principal getPrincipal(X509Certificate userCert)
- Overrides:
getPrincipalin classRealmBase
-
getPrincipal
protected Principal getPrincipal(GSSName gssName, GSSCredential gssCredential)
- Overrides:
getPrincipalin classRealmBase
-
getPrincipal
protected Principal getPrincipal(GSSName gssName, GSSCredential gssCredential, boolean retry)
-
acquire
protected ActiveDirectoryRealm.DirContextConnection acquire() throws NamingException
- Throws:
NamingException
-
validate
protected boolean validate(ActiveDirectoryRealm.DirContextConnection connection)
-
release
protected void release(ActiveDirectoryRealm.DirContextConnection connection)
-
open
protected void open(ActiveDirectoryRealm.DirContextConnection connection) throws NamingException
- Throws:
NamingException
-
close
protected void close(ActiveDirectoryRealm.DirContextConnection connection)
-
close
protected void close(NamingEnumeration<?> results)
-
initInternal
protected void initInternal() throws LifecycleException- Overrides:
initInternalin classRealmBase- Throws:
LifecycleException
-
startInternal
protected void startInternal() throws LifecycleException- Overrides:
startInternalin classRealmBase- Throws:
LifecycleException
-
stopInternal
protected void stopInternal() throws LifecycleException- Overrides:
stopInternalin classRealmBase- Throws:
LifecycleException
-
getUser
protected ActiveDirectoryRealm.User getUser(DirContext context, GSSName gssName) throws NamingException
- Throws:
NamingException
-
getRoles
protected List<String> getRoles(DirContext context, ActiveDirectoryRealm.User user) throws NamingException
- Throws:
NamingException
-
getDistinguishedName
protected Name getDistinguishedName(DirContext context, Name baseName, SearchResult result) throws NamingException
Returns the distinguished name of a search result.- Parameters:
context- Our DirContextbaseName- The base DNresult- The search result- Returns:
- String containing the distinguished name
- Throws:
NamingException- if DN cannot be build
-
getRelativeName
protected Name getRelativeName(DirContext context, String distinguishedName) throws NamingException
- Throws:
NamingException
-
-