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()
UserAccessControl
Calling 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 UserAccessControl
public 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 UserAccessControl
username
- 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 UserAccessControl
username
- 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 UserAccessControl
username
- 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 UserAccessControl
username
- 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 UserAccessControl
username
- 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 UserAccessControl
username
- 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 UserAccessControl
username
- e.g. dfelixpublic void init(Properties props)
UserAccessControl
Calling 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 UserAccessControl
private 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