public class LdapAccessControl extends Object implements UserAccessControl
LdapAccessControl class is a reference implementation
of the UserAccessControl interface. This class only performs
user authorization (authZ) and not user authentication (authN). This
class implements an authZ model in a similar style as the
Role
Based Access Control (RBAC) model and the
Attribute
Based Access Control (ABAC) model. However, this pedagogical implementation
is much simpler and limited than the two formal models.
Usage examples and semantics can be found in the javadoc of the interface
that this class implements (UserAccessControl).
The LdapAccessControl implementation makes calls to an
LDAP server (e.g. Microsoft's Active Directory (AD) server) when performing
a lookup to determine if a user has one or more attributes defined. The
LDAP queries are based on LDAP search/fiter criteria(s) defined at the time
an instance of this class is placed into service. An instance of this class
is considered to be in service after invoking the init method.
In the SPNEGO Library, the SpnegoHttpFilter class is the mechanism
that performs user authentication (authN) whilst the LdapAccessControl
class is the default mechanism that performs user authorization (authZ). This
default can be replaced by any class that implement the UserAccessControl
interface. To change the default, specify the new class in the SPNEGO Library's
filter definition section of the web.xml file.
Authorization (authZ) is an optional feature of the SPNEGO Library. The SPNEGO
Library provides an interface, SpnegoAccessControl, to applications
that need authZ capability. Applications can check a user's authZ by calling
methods defined in the SpnegoAccessControl interface.
The LdapAccessControl class is configured within the same web.xml
filter section as the SpnegoHttpFilter class. The configuration is
specified by adding additional filter parameters to the SpnegoHttpFilter
filter definition.
Example web.xml Configuration:
<filter>
<filter-name>SpnegoHttpFilter</filter-name>
<filter-class>net.sourceforge.spnego.SpnegoHttpFilter</filter-class>
<!-- spnego http filter params (authN) -->
... existing authN params here just as before ...
<!-- spnego http filter params (authZ) -->
<init-param>
<param-name>spnego.authz.class</param-name>
<param-value>net.sourceforge.spnego.LdapAccessControl</param-value>
</init-param>
<init-param>
<param-name>spnego.authz.ldap.url</param-name>
<param-value>ldap://athena.local:389</param-value>
</init-param>
<!-- an example user-defined resource label -->
<init-param>
<param-name>spnego.authz.resource.name.1</param-name>
<param-value>admin-buttons</param-value>
</init-param>
<init-param>
<param-name>spnego.authz.resource.access.1</param-name>
<param-value>Biz. Analyst,Los Angeles,IT Group</param-value>
</init-param>
<init-param>
<param-name>spnego.authz.resource.type.1</param-name>
<param-value>has</param-value>
</init-param>
<!-- CDATA required since specifying filter(s) in web.xml (vs. a policy file) -->
<!-- also notice the %1$s and the %2$s tokens (always required) -->
<init-param>
<param-name>spnego.authz.ldap.filter.1</param-name>
<param-value><![CDATA[(&(sAMAccountName=%1$s)(memberOf:1.2.840.113556.1.4.1941:=CN=%2$s,OU=Groups,OU=Los Angeles,DC=athena,DC=local))]]></param-value>
</init-param>
<init-param>
<param-name>spnego.authz.ldap.filter.2</param-name>
<param-value><![CDATA[(&(sAMAccountType=805306368)(sAMAccountName=%1$s)(&(sAMAccountType=805306368)(department=%2$s)))]]></param-value>
</init-param>
</filter>
As an alternative option, the spnego.authz.ldap.filter.[i] parameters and
the spnego.authz.resource.[name|access|type].[i] parameters may be specified
in a policy file.
Example Policy File Configuration:
<filter>
<filter-name>SpnegoHttpFilter</filter-name>
<filter-class>net.sourceforge.spnego.SpnegoHttpFilter</filter-class>
<!-- spnego http filter params (authN) -->
... existing authN params here just as before ...
<!-- spnego http filter params (authZ) -->
<init-param>
<param-name>spnego.authz.class</param-name>
<param-value>net.sourceforge.spnego.LdapAccessControl</param-value>
</init-param>
<init-param>
<param-name>spnego.authz.ldap.url</param-name>
<param-value>ldap://athena.local:389</param-value>
</init-param>
<init-param>
<param-name>spnego.authz.policy.file</param-name>
<param-value>C:/Apache Software Foundation/Tomcat 7.0/conf/spnego.policy</param-value>
</init-param>
</filter>
Policy file contents:
# an example user-defined resource label spnego.authz.resource.name.1=admin-buttons spnego.authz.resource.access.1=Biz. Analyst,Los Angeles,IT Group spnego.authz.resource.type.1=has # do NOT use CDATA like in the web.xml file # the %1$s and the %2$s tokens are always required spnego.authz.ldap.filter.1=(&(sAMAccountName=%1$s)(memberOf:1.2.840.113556.1.4.1941:=CN=%2$s,OU=Groups,OU=Los Angeles,DC=athena,DC=local)) spnego.authz.ldap.filter.2=(&(sAMAccountType=805306368)(sAMAccountName=%1$s)(&(sAMAccountType=805306368)(department=%2$s)))
For more information on how a web application/service can leverage access controls
or to view some usage examples, please read the javadoc of the SpnegoAccessControl
interface and the javadoc of the UserAccessControl interface.
Also, take a look at the reference docs for a complete list of configuration parameters.
Finally, to see a working example and instructions, take a look at the authZ for standalone apps example and the enable authZ with LDAP guide.
| Modifier and Type | Field and Description |
|---|---|
private static String |
ANY |
private String |
deecee
DC= base portionS of the ldap search filter.
|
private static long |
DEFAULT_TTL
default is 20 minutes.
|
private Hashtable<String,String> |
environment |
private long |
expiration
determines how long to keep in cache.
|
private static String |
HAS |
private static String |
KRB5_PASSWORD |
private static String |
KRB5_USERNAME |
private static String |
LDAP_AUTHN |
private static String |
LDAP_DEECE |
private static String |
LDAP_FACTORY |
private static String |
LDAP_PASSWORD |
private static String |
LDAP_POOL |
private static String |
LDAP_URL |
private static String |
LDAP_USERNAME |
private static Logger |
LOGGER |
private Map<String,Long> |
matchedList
cache LDAP results to minimize trips to ldap server.
|
private static int |
MAX_NUM_FILTERS
maximum number of ldap filters is 200
|
private Set<String> |
policy
ldap search filter(s).
|
private static String |
POLICY_FILE |
private static String |
PREFIX_ACCESS |
private static String |
PREFIX_FILTER |
private static String |
PREFIX_NAME |
private static String |
PREFIX_TYPE |
private Lock |
readLock |
private ReentrantReadWriteLock |
readWriteLock
read lock for reading instance variables and write lock for ldap search.
|
private Map<String,Map<String,String[]>> |
resources
access resources by using a user-defined label.
|
private static String |
SERVER_REALM |
private SearchControls |
srchCntrls |
private static String |
TTL |
private static String |
UNIQUE |
private boolean |
uniqueOnly
determines if an exception should be thrown if it finds a duplicate.
|
private Map<String,Long> |
unMatchedList |
private static String |
USER_INFO
case-sensitive.
|
private static String |
USER_INFO_FILTER
e.g.
|
private String |
userInfoFilter |
private List<String> |
userInfoLabels |
private Map<String,UserInfo> |
userInfoList |
private Lock |
writeLock |
| Constructor and Description |
|---|
LdapAccessControl()
Default constructor.
|
| Modifier and Type | Method and Description |
|---|---|
boolean |
anyAccess(String username,
String... resources)
Checks to see if the given user has at least one of the passed-in
user-defined resource labels.
|
boolean |
anyRole(String username,
String... attributes)
Checks to see if the given user has at least one of the passed-in attributes.
|
private UserInfo |
cacheUserInfo(String username) |
void |
destroy()
Used for clean-up when usage of the object is no longer needed and no other
method calls will be made on this instance.
|
UserInfo |
getUserInfo(String username)
Returns a user info object if specified in web.xml or the spnego.policy file.
|
boolean |
hasAccess(String username,
String resource)
Checks to see if the passed-in user has access to the
user-defined resource label.
|
boolean |
hasAccess(String username,
String resourceX,
String... resourceYs)
Checks to see if the given user has the first resource label
AND has at least one of the passed-in resource labels.
|
boolean |
hasRole(String username,
String attribute)
Checks to see if the given user has the passed-in attribute.
|
boolean |
hasRole(String username,
String attributeX,
String... attributeYs)
Checks to see if the given user has the first attribute
AND has at least one of the passed-in attributes.
|
void |
init(Properties props)
Method is used for initialization prior to use/calling any other method.
|
private void |
loadPolicies(Properties props,
Properties policies) |
private void |
loadResourceNames(Properties props,
Properties policies) |
private boolean |
matchedExpired(String key,
long now) |
private boolean |
unMatchedExpired(String key,
long now) |
private static final String POLICY_FILE
private static final String SERVER_REALM
private static final String LDAP_FACTORY
private static final String LDAP_AUTHN
private static final String LDAP_POOL
private static final String LDAP_DEECE
private static final String LDAP_URL
private static final String LDAP_USERNAME
private static final String KRB5_USERNAME
private static final String LDAP_PASSWORD
private static final String KRB5_PASSWORD
private static final String TTL
private static final String UNIQUE
private static final String PREFIX_FILTER
private static final String PREFIX_NAME
private static final String PREFIX_TYPE
private static final String PREFIX_ACCESS
private static final String HAS
private static final String ANY
private static final String USER_INFO
private static final String USER_INFO_FILTER
private static final long DEFAULT_TTL
private static final int MAX_NUM_FILTERS
private final transient ReentrantReadWriteLock readWriteLock
private final transient Map<String,Long> matchedList
private final transient Map<String,Long> unMatchedList
private final transient Map<String,UserInfo> userInfoList
private transient Hashtable<String,String> environment
private transient SearchControls srchCntrls
private transient long expiration
private transient boolean uniqueOnly
private transient Map<String,Map<String,String[]>> resources
private transient List<String> userInfoLabels
private transient String userInfoFilter
public LdapAccessControl()
public void destroy()
UserAccessControlCalling this method is an indication that no other method calls will be called on this instance. If method calls must resume on this instance, the init method MUST be called before this instance can be placed back into service.
If this method has been called and a reference to the instance is maintained, the init method must be called again to re-initialize the object's instance variables.
destroy in interface UserAccessControlpublic boolean anyRole(String username, String... attributes)
UserAccessControl
String[] attributes = new String[] {"Developer", "Los Angeles", "Manager"};
if (accessControl.anyRole("dfelix", attributes)) {
// will be in here if dfelix has at least one matching attribute
}
anyRole in interface UserAccessControlusername - e.g. dfelixattributes - e.g. Team Lead, IT, Developerpublic boolean hasRole(String username, String attribute)
UserAccessControl
String attribute = "Los Angeles";
if (accessControl.hasRole("dfelix", attribute)) {
// will be in here if dfelix has that one attribute
}
hasRole in interface UserAccessControlusername - e.g. dfelixattribute - e.g. ITpublic boolean hasRole(String username, String attributeX, String... attributeYs)
UserAccessControl
String attributeX = "Los Angeles";
String[] attributeYs = new String[] {"Developer", "Manager"};
if (accessControl.hasRole("dfelix", attributeX, attributeYs)) {
// will be in here if dfelix has attributeX
// AND has at least one of the attributeYs.
}
hasRole in interface UserAccessControlusername - e.g. dfelixattributeX - e.g. Information TechnologyattributeYs - e.g. Team Lead, IT-Architecture-DLpublic boolean anyAccess(String username, String... resources)
UserAccessControl
if (accessControl.anyAccess("dfelix", "admin-links", "buttons-for-ops")) {
// will be in here if dfelix has at least one matching resource
}
anyAccess in interface UserAccessControlusername - e.g. dfelixresources - e.g. admin-links, ops-buttonspublic boolean hasAccess(String username, String resource)
UserAccessControl
UserAccessControl accessControl = ...;
boolean editAndAdminBtns = false;
if (accessControl.hasAccess("dfelix", "admin-buttons")) {
editAndAdminBtns = true;
}
hasAccess in interface UserAccessControlusername - e.g. dfelixresource - e.g. admin-buttonspublic boolean hasAccess(String username, String resourceX, String... resourceYs)
UserAccessControl
String resourceX = "phone-list";
String[] resourceYs = new String[] {"staff-directory", "procedure-manual"};
if (accessControl.hasAccess("dfelix", resourceX, resourceYs)) {
// will be in here if dfelix has resourceX
// AND has at least one of the resourceYs.
}
hasAccess in interface UserAccessControlusername - e.g. dfelixresourceX - e.g. phone-listresourceYs - e.g. staff-directory, procedure-manual, emergency-contact-listpublic UserInfo getUserInfo(String username)
Case-sensitive
web.xml Example:
...
<init-param>
<param-name>spnego.authz.user.info</param-name>
<param-value>mail,department,memberOf,displayName</param-value>
</init-param>
<init-param>
<param-name>spnego.authz.ldap.user.filter</param-name>
<param-value><![CDATA[(&(sAMAccountType=805306368)(sAMAccountName=%1$s))]]></param-value>
</init-param>
...
spnego.policy File Example:
... # case-sensitive spnego.authz.user.info=mail,department,memberOf,displayName spnego.authz.ldap.user.filter=(&(sAMAccountType=805306368)(sAMAccountName=%1$s)) ...
getUserInfo in interface UserAccessControlusername - e.g. dfelixpublic void init(Properties props)
UserAccessControlCalling this method is an indication that this instance is in service/active and any method can be called at anytime for the purpose of servicing a request.
If this method has been called and a reference to the instance is maintained, this method should not be called again unless the destroy method is called first.
init in interface UserAccessControlprivate boolean matchedExpired(String key, long now)
private boolean unMatchedExpired(String key, long now)
private void loadPolicies(Properties props, Properties policies)
private void loadResourceNames(Properties props, Properties policies)
private UserInfo cacheUserInfo(String username) throws NamingException
NamingException