Jeremy,
you are absolutely right and I'm not proud at all from this solution, but it is
the fastest implementation I've managed to get to work. I treat this as a
temporary solution until jSecurity 1.0.
Anyway, obviously I was doing something wrong while trying to make your
solution working.
I had problems with getting to Subjects session to store the authorization info
- SecurityUtils.getSubject() was returning a null value. I guess the issue is
that this object does not exist in this stage, but I couldn't get any other
idea how to get into the Session object.
Another issue was getting the AuthorizationInfo to work. Without setting
systemUsername and systemPassword AuthorizationInfo was trying to obtain data
from Active Directory with a null user and this failed. I think I should get
this information using LdapContext (a new object, not the default one) with
credentials passed by this user, but the queryForAuthorizationInfo uses
getRoleNamesForUser(...) private method so I'd have to copy paste this method
with LdapContext ldapContext = ldapContextFactory.getLdapContext(username,
password); used instead the LdapContext ldapContext =
ldapContextFactory.getSystemLdapContext(); one. This bugs me because when I'm
copy-pasting then I'm doing something obviously wrong so I guess this way is
not the correct way.
Thanks for pointing me out possible issues with this solution. Now I'm getting
worried ;)
Maciej
Marciej,
This approach isn't the most secure since each time a new user
authenticates, the system username and password will be updated to
store their username and password.
For example:
1) Bob logs in. His username/password are set as the system username/
password
2) Bob does a permission check and it uses bob's account to access LDAP
3) Sue logs in. Her username/password are set as the system username/
password
4) Bob does another permission check. this time Sue's credentials are
used to access LDAP
In this scenario, it's impossible to predict which user and password
will be used to authenticate.
Even neglecting the security implications, there is a possible race
condition that could result in errors obtaining authorization
information. Since DefaultLdapContextFactory is a singleton and is
not synchronized in any way, it's possible that an authorization check
will occur in one thread while in another thread the system username
and password are being updated due to another user authenticating.
This means that username could be set to "Sue" while the password is
still set to Bob's password. This would result in an LDAP error since
Bob's password is invalid for Sue's account.
If you really want to just store the user's credentials, I would
suggest storing them in the user's principal and pulling them out of
the Principal object when you need to check for authorization info.
That being said, holding onto a user's credentials in memory is
considered a bad security practice, since if anyone hacks your machine
or gets access to RAM, they could grab all of your user's
credentials. Also, since the credentials would be stored in a
session, and sessions are sometimes serialized to disk, a hacker could
possible just get access to the serialized sessions file and obtain
their credentials from the file.
My 2 cents,
Jeremy
On Mar 30, 2009, at 10:43 AM, Maciej Pigulski wrote:
>
> Hello,
>
> thanks for the clarification. I've done it in a 3rd way by
> overriding DefaultLdapContextFactory settings in
> queryForAuthenticationInfo:
>
>
> /* (non-Javadoc)
> * @see
> org
> .jsecurity
> .realm
> .activedirectory
> .ActiveDirectoryRealm
> #queryForAuthenticationInfo(org.jsecurity.authc.AuthenticationToken,
> org.jsecurity.realm.ldap.LdapContextFactory)
> */
> @Override
> protected AuthenticationInfo
> queryForAuthenticationInfo(AuthenticationToken token,
> LdapContextFactory ldapContextFactory) throws
> NamingException {
>
> UsernamePasswordToken upToken = (UsernamePasswordToken) token;
> DefaultLdapContextFactory defaultLdapContextFactory =
> (DefaultLdapContextFactory) ldapContextFactory;
>
> defaultLdapContextFactory.setSystemUsername(upToken.getUsername());
>
> defaultLdapContextFactory
> .setSystemPassword(upToken.getPassword().toString());
>
> return super.queryForAuthenticationInfo(token,
> ldapContextFactory);
> }
>
>
> Maybe it is not the best workaround but I had no time to do it like
> you suggested. Looking forward for jSecurity 1.0, keep up the good
> work!
>
> Greetings,
> Maciej
>
>
> Maciej,
>
> The problem is that if the LDAP server requires credentials to login
> (as they typically do), there is no way to obtain authorization
> information at a later point without having a username/password to
> login to the LDAP server. Due to the way JSecurity works,
> authentication and authorization happen independently of each other,
> and so when authorization occurs we do not have the username/password
> that was originally used to authenticate (as we shouldn't).
>
> I think this brings to light an option that JSecurity should offer -
> which is to allow authorization information to be obtained at login
> and cached for the duration of a user's session. This is the way many
> security frameworks operate, and usually we tout the fact that
> JSecurity doesn't work this way as an advantage (i.e. dynamic security
> updates, flexible caching, etc.)
>
> However - in this case it's a disadvantage because login is the only
> time when we have the information we need to obtain the authorization
> information (since the authentication info is needed to obtain it). I
> think there will be other scenarios where this is the case (external
> authorization systems, SSO systems, etc.) so I do think JSecurity
> should offer this mechanism as an option. I'll open a JIRA issue to
> address this for the 1.0 release.
>
> As far as a short-term workaround, you could either:
> 1) configure the system username and password for now (as I think
> you've already done)
> or
> 2) extend the Active Directory realm, and override
> queryForAuthenticationInfo to grab the AuthorizationInfo (similar to
> how queryForAuthorizationInfo does) at login. You could then cache
> the AuthorizationInfo in the subject's session and override
> queryForAuthorizationInfo to return the session-cached authorization
> information. This is similar to how JSecurity would probably do this
> in the future, but obviously you'd have to manually implement it.
>
> Please let me know if you have any further questions or ideas!
>
> Jeremy
>
> On Mar 25, 2009, at 12:24 PM, Maciej Pigulski wrote:
>
>>
>> Unfortunately this is still an issue to me.
>>
>>
>> Jeremy or Tim, do you know if you'd be able to help out Maciej? I
>> don't
>> have any experience with the LDAP/AD stuff you guys wrote. Maciej,
>> have
>> you been able to work through this issue?
>>
>> On Thu, Mar 19, 2009 at 9:46 AM, Maciej Pigulski
>> <[email protected]>wrote:
>>
>>>
>>> Hello,
>>>
>>> I have a following problem with jSecurity, ActiveDirectoryRealm and
>>> Groups
>>> mappings.
>>>
>>> I have an AD setup on one server (WHEEL) with a simple user called
>>> user1.
>>> This user is in ldap group called
>>> "login" (CN=login,OU=Groups,DC=WHEEL).
>>>
>>> Next I'm trying to login and retrieve roles for this user. Login
>>> works fine
>>> but when it comes to user roles I have to additionally provide
>>> username
>>> and
>>> password in activeDirectoryRealm.setSystemUsername/Password. I've
>>> found in
>>> the API that it is a pretty normal behaviour (but IMHO very
>>> inconvenient)
>>> (
>>> http://www.jsecurity.org/releases/0.9.0-beta2/docs/api/org/jsecurity/realm/ldap/DefaultLdapContextFactory.html#setSystemUsername(java.lang.String)
>>> <http://www.jsecurity.org/releases/0.9.0-beta2/docs/api/org/jsecurity/realm/ldap/DefaultLdapContextFactory.html#setSystemUsername%28java.lang.String%29
>>>>
>>> :
>>> <cite>
>>> systemUsername - the username to use when logging into the LDAP
>>> server for
>>> authorization.
>>> </cite>
>>>
>>> Is there any tricky way to bypass this? Setting same credentials on
>>> two
>>> objects to authorize and authenticate one user seems to be quite
>>> wrong.
>>>
>>> I've managed to obtain this by creating a super user (with
>>> enterprise
>>> administrator rights) that has hardcoded username and password in
>>> application (systemUsername and systemPassword) and this works for
>>> authenticating other users but I'd like to avoid using such
>>> powerfull user
>>> just for groups fetching as it seems to be an huge overkill for me.
>>>
>>> Here is a class I'm using to test with AD:
>>>
>>> import java.util.HashMap;
>>> import java.util.Map;
>>>
>>> import org.jsecurity.authc.UsernamePasswordToken;
>>> import org.jsecurity.mgt.DefaultSecurityManager;
>>> import org.jsecurity.realm.activedirectory.ActiveDirectoryRealm;
>>> import org.jsecurity.subject.Subject;
>>>
>>> public class TestJSec {
>>>
>>> private DefaultSecurityManager securityManager = new
>>> DefaultSecurityManager();
>>> private ActiveDirectoryRealm activeDirectoryRealm = new
>>> ActiveDirectoryRealm();
>>>
>>> public TestJSec() {
>>> activeDirectoryRealm.setSearchBase("DC=WHEEL");
>>> activeDirectoryRealm.setUrl("ldap://ldap-host:389");
>>>
>>> activeDirectoryRealm.setSystemUsername("us...@wheel"); //
>>> if this is
>>> missing user wont fetch his roles
>>> activeDirectoryRealm.setSystemPassword("user1");
>>> // if this
>>> is missing user wont fetch his roles
>>> Map<String, String> map = new HashMap<String,
>>> String>();
>>> map.put("CN=login,OU=Groups,DC=WHEEL", "login");
>>> activeDirectoryRealm.setGroupRolesMap(map);
>>>
>>> securityManager.setRealm(activeDirectoryRealm);
>>> }
>>>
>>> private void testLogin() {
>>> UsernamePasswordToken userToken = new
>>> UsernamePasswordToken("us...@wheel",
>>> "user1");
>>>
>>> Subject subject = securityManager.login(userToken);
>>> if (subject.hasRole("login")) {
>>> System.out.println("User in role");
>>> } else {
>>> System.out.println("User has no role");
>>> }
>>> }
>>>
>>> public static void main(String[] args) {
>>> TestJSec tjs = new TestJSec();
>>> tjs.testLogin();
>>> }
>>> }
>>>
>>>
>>> For example in jBoss this config works without a super user:
>>>
>>>
>>> <application-policy name="DLG_REGW_POLICY">
>>> <authentication>
>>> <login-module
>>> code="org.jboss.security.auth.spi.LdapLoginModule"
>>> flag="required" >
>>> <module-option
>>> name="java.naming.provider.url">ldap://ldap-host:389/</module-
>>> option>
>>> <module-option
>>> name="rolesCtxDN">OU=Groups,DC=WHEEL</module-option>
>>> <module-option
>>> name="matchOnUserDN">false</module-option>
>>> <module-option
>>> name="uidAttributeID">sAMAccountName</module-option>
>>> <module-option
>>> name="roleAttributeID">memberOf</module-option>
>>> <module-option
>>> name="roleAttributeIsDN">true</module-option>
>>> <module-option
>>> name="roleNameAttributeID">name</module-option>
>>> <module-option
>>> name="searchTimeLimit">5000</module-option>
>>> <module-option
>>> name="allowEmptyPasswords">false</module-option>
>>> <module-option
>>> name="searchScope">SUBTREE_SCOPE</module-option>
>>> </login-module>
>>> </authentication>
>>> </application-policy>
>>>
>>> --
>>> View this message in context:
>>> http://n2.nabble.com/Reading-user-roles-from-Active-Directory-tp2503002p2503002.html
>>> Sent from the JSecurity User mailing list archive at Nabble.com.
>>>
>>>
>>
>>
>>
>> --
>> View this message in context:
>> http://n2.nabble.com/Reading-user-roles-from-Active-Directory-tp2503002p2533411.html
>> Sent from the JSecurity User mailing list archive at Nabble.com.
>>
>
>
>
>
> --
> View this message in context:
> http://n2.nabble.com/Reading-user-roles-from-Active-Directory-tp2503002p2557591.html
> Sent from the JSecurity User mailing list archive at Nabble.com.
>
--
View this message in context:
http://n2.nabble.com/Reading-user-roles-from-Active-Directory-tp2503002p2561437.html
Sent from the JSecurity User mailing list archive at Nabble.com.