Class ActiveDirectoryRealm
- All Implemented Interfaces:
MBeanRegistration,Contained,JmxEnabled,Lifecycle,Realm
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 when
getPrincipal(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
connectionPoolSize to 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 aUsernameSearchMapper
implementation. 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 realGSSName with the string name type
of KRB5_NT_PRINCIPAL (1.2.840.113554.1.2.2.1) similar to the canonicalize flag in the krb5.conf file. This
makes the final GSSName fully 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 the
java.naming.referral property and its values: ignore,
throw, and follow. You can ignore referrals altogether, but the provider will still signal a
PartialResultException when a NamingEnumeration is iterated. The reason is because it adds a
ManageReferralControl when ignore is 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 the
DirContextSource is set
to throw, this realm will catch the ReferralException also, 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, no ReferralException is thrown, but the referrals are handled internally and
referral contexts are queried and closed. If you choose to follow referrals you must use my
Active Directory DC 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 DC 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.COM to 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 SPN ldap/<targetHost>/<dnsDomainName>@<REALM> or a host-based one
ldap/<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 either
followwith aDirContextSourcein your home forest and use my Active Directory DC 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 the PacDataActiveDirectoryRealm if you don't need all features and use
SPNEGO authentication only since it is orders of magnitude faster, but remember though
that X.509 authentication still requires this realm.
- See Also:
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprotected static classprotected static classNested classes/interfaces inherited from class org.apache.catalina.realm.RealmBase
RealmBase.AllRolesModeNested classes/interfaces inherited from interface org.apache.catalina.Lifecycle
Lifecycle.SingleUse -
Field Summary
FieldsModifier and TypeFieldDescriptionprotected String[]protected String[]protected intprotected Stringprotected booleanprotected longprotected booleanprotected String[]protected String[]Fields inherited from class net.sf.michaelo.tomcat.realm.ActiveDirectoryRealmBase
logger, smFields inherited from class org.apache.catalina.realm.RealmBase
allRolesMode, container, containerLog, realmPath, stripRealmForGss, support, USER_ATTRIBUTES_DELIMITER, USER_ATTRIBUTES_WILDCARD, userAttributes, userAttributesList, validate, x509UsernameRetriever, x509UsernameRetrieverClassNameFields inherited from class org.apache.catalina.util.LifecycleMBeanBase
mserverFields 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 -
Method Summary
Modifier and TypeMethodDescriptionacquire()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 Stringprotected PrincipalgetPrincipal(X509Certificate userCert) protected PrincipalgetPrincipal(GSSName gssName, GSSCredential gssCredential) protected PrincipalgetPrincipal(GSSName gssName, GSSCredential gssCredential, boolean retry) protected NamegetRelativeName(DirContext context, String distinguishedName) getRoles(DirContext context, ActiveDirectoryRealm.User user) protected ActiveDirectoryRealm.UsergetUser(DirContext context, GSSName gssName) protected voidprotected 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 voidprotected voidprotected booleanvalidate(ActiveDirectoryRealm.DirContextConnection connection) Methods inherited from class net.sf.michaelo.tomcat.realm.ActiveDirectoryRealmBase
getPassword, getPrincipal, getRoles, hasRoleInternalMethods 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, toStringMethods inherited from class org.apache.catalina.util.LifecycleMBeanBase
destroyInternal, getDomain, getObjectName, postDeregister, postRegister, preDeregister, preRegister, register, setDomain, unregister, unregisterMethods inherited from class org.apache.catalina.util.LifecycleBase
addLifecycleListener, destroy, findLifecycleListeners, fireLifecycleEvent, getState, getStateName, getThrowOnFailure, init, removeLifecycleListener, setState, setState, setThrowOnFailure, start, stopMethods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, waitMethods inherited from interface org.apache.catalina.Realm
isAvailable
-
Field Details
-
localDirContextSource
protected boolean localDirContextSource -
dirContextSourceName
-
attributes
-
additionalAttributes
-
roleFormats
-
roleAttributes
-
prependRoleFormat
protected boolean prependRoleFormat -
connectionPoolSize
protected int connectionPoolSize -
maxIdleTime
protected long maxIdleTime -
connectionPool
-
-
Constructor Details
-
ActiveDirectoryRealm
public ActiveDirectoryRealm()
-
-
Method Details
-
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
Sets the name of theDirContextSource- Parameters:
dirContextSourceName- the directory context source name
-
setAdditionalAttributes
Sets a comma-separated list of Active Directory attributes retreived and stored for the user principal.- Parameters:
additionalAttributes- the additional attributes
-
setRoleFormats
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
- Overrides:
getPrincipalin classRealmBase
-
getPrincipal
- Overrides:
getPrincipalin classRealmBase
-
getPrincipal
-
acquire
- Throws:
NamingException
-
validate
-
release
-
open
- Throws:
NamingException
-
close
-
close
-
initInternal
- Overrides:
initInternalin classRealmBase- Throws:
LifecycleException
-
startInternal
- Overrides:
startInternalin classRealmBase- Throws:
LifecycleException
-
stopInternal
- 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
- Throws:
NamingException
-