I just noticed (saw Les's post on Grails-user) that the discussion moved on
to jsecurity-dev, I'll go join that list. . For others' reference

http://markmail.org/message/hnex52p2puw2pip5

- Animesh

On Tue, Dec 16, 2008 at 1:06 AM, Jeremy Haile <[email protected]> wrote:

> If you were trying to make it transparent then it would be more like
> // This would return the user ID that the current user is running as
> (whether it's themselves or a "run as" user)
> String userId = ((UserPrincipal)subject.getPrincipal()).getUserId();
>
> In your logging code - if you wanted to log the actual user ID (not the
> "run as" user ID) you would do:
> String authenticatedUserId =
> ((UserPrincipal)subject.getPrincipal()).getAuthenticatedUserId();
>
> I'd compare it to:
> User user = getUser( ((UserPrincipal)subject.getPrincipal()).getUserId() );
>
> But actually in my code it just looks like this - so it really doesn't
> matter much if there's a few more characters:
> User user = userManager.getCurrentUser();
>
> Plus, my principal already has more than one ID encapsulated in it.
>
>
> On Dec 15, 2008, at 2:29 PM, Les Hazlewood wrote:
>
> On Mon, Dec 15, 2008 at 2:18 PM, Jeremy Haile <[email protected]> wrote:
>
>> Well, I think you could structure it so the programmers making calls on
>> the subject don't have to know and still follow the approach where
>> everything is encapsulated in the principal.  Just depends on how your
>> principal encapsulates the information.
>>
>
>> I like the Principal approach because it seems simpler, more transparent
>> (i.e. no proxy classes pulling data from the session, etc.), and requires
>> less overriding/etc to accomplish since you just implement the logic in your
>> realm and return a principal that encapsulates any necessary information.
>>
>
> So how do you deal with user-specific functionality pertaining to the
> logged in user?  Do you do something like this?
>
> UserIdentity identity = (UserIdentity)Subject.getPrincipal();
> Long userId;
> if ( identity.isAssumed() ) {
>     userId = identity.getAssumedId();
> } else {
>     userId = identity.getId();
> }
>
> User user = getUser(userId);
> System.out.println( "Welcome " + user.getGivenName() + "!" );
>
> Compare that to this:
>
> User user = getUser( (Long)Subject.getPrincipal() );
> System.out.println( "Welcome " + user.getGivenName() + "!" );
>
> Thanks for any clarity - I'm still trying to understand your approach...
>
>
>>
>> I too like hearing about other approaches to this problem though.
>>
>>
>>
>> On Dec 15, 2008, at 1:53 PM, Les Hazlewood wrote:
>>
>> But this approach requires the GUI programmer (or the one making calls on
>> the subject) to 'know' about this 'run as' check, everywhere in the
>> application, right?  Doesn't that sound a little invasive as to opposed to
>> just calling Subject.getPrincipal() in all locations?
>>
>> I surface this only for clarity - I'm not shooting down any approaches.  I
>> just haven't seen a way to 1) offer transparent 'run as' functionality while
>> 2) still retaining accurate traceability.
>>
>> My solution achieves both points, but I'm very open to any suggestions on
>> how else it may be done - if there is a cleaner way, etc.  If there's
>> anything else up your sleeves, I'm all ears ;)
>>
>> On Mon, Dec 15, 2008 at 11:14 AM, Jeremy Haile <[email protected]>wrote:
>>
>>> Another "clean" way to do this would be to do it similar to my approach,
>>> but actually have the principal object have the assumed identity AND actual
>>> identity.  In most cases these would be the same, but in a "super user"
>>> situation, they could be different.
>>> Most (non-auditing) code that needs to determine who the current user is
>>> would always retrieve the principal and use the "assumed identity".  Any
>>> auditing or event logging code could then log both of these fields by
>>> examining the principal.
>>>
>>>
>>> On Dec 15, 2008, at 10:32 AM, Les Hazlewood wrote:
>>>
>>> This is pretty cool - I like seeing how people come up with their
>>> respective solutions.  It is interesting to see others' thought process for
>>> solving these things.
>>>
>>> I do this functionality as well in my applications, but I humbly offer my
>>> solution as a little more elegant and 'traceable' from a security
>>> perspective.
>>>
>>> The purpose of 'run as' functionality is almost always to see things
>>> exactly as that particular user would see things, so you can verify their
>>> experience, or to perform logic as that individual.  But it is still
>>> important in secure applications (IMHO) to never lose track of who is
>>> actually performing the button clicks, especially if they've assumed someone
>>> else's identity.
>>>
>>> And if it ever comes to government or financial related applications or
>>> any application that has to adhere to government regulations or oversight,
>>> you always want to be able to show any government official/reviewer,
>>> "Although it appears User X did this, it was _really_ User Y - don't blame
>>> User X".
>>>
>>> Here's how I solve this problem:
>>>
>>> A user logs in with their own username and password always.
>>> They perform some sort of user search, which then shows a paginated
>>> results page.
>>>
>>> If the current user has the "assumeIdentity" permission, only then is an
>>> additional 'assume identity' link available on each line-item that is shown
>>> in the results page.  If they don't have this permission, they never see the
>>> link and thus can't click on it.
>>>
>>> The current user (if permitted) clicks the 'assume identity' link, which
>>> sends a request to the server and then adds the target user's identity (in
>>> our system a Long primary key of the assumed User) to the current user's
>>> session.  Our session table in the database has a 'assumed_identity' column
>>> that is a foreign key to the user's table.
>>>
>>> We also have event tracking in place, where any operation deemed as
>>> noteworthy is logged and has a foreign key back to the sessions table.
>>>
>>> Therefore, we can always tell for *every* action (logged event) which
>>> session it was attributed to.  Then, if the entry in the sessions table has
>>> an assumed identity, then we know that the event was really attributed to
>>> the 'owning' or original user, not the user of the assumed identity.
>>>
>>> In our application, the Subject.getPrincipal() method returns the user's
>>> long ID primary key.  We added a little Subject wrapper/proxy around the
>>> existing Subject implementation that first checks the session, and if there
>>> is an assumed identity, returns that.  If there is no assumed identity, it
>>> returns the id of the user that actually authenticated as normal.
>>>
>>> This is to ensure that all application functionality built around the
>>> Subject.getPrincipal code still returns the expected data so further
>>> information can be looked up (user name, first name, etc).  Works perfectly
>>> if there is an assumed identity or not, but the session always 'remember's
>>> who the 'real' user is executing the logic for traceability purposes.
>>>
>>> I hope that gives some insight how this works :)  I've always wanted to
>>> add this in a clean way to JSecurity.  Maybe it should be a 1.0 feature...
>>>
>>> On Mon, Dec 15, 2008 at 9:18 AM, Jeremy Haile <[email protected]>wrote:
>>>
>>>> Animesh,
>>>>
>>>> You can definitely support super user authentication with JSecurity - we
>>>> do this in our application.  The way we do it is by having our realm accept
>>>> multiple types of tokens - a regular UsernamePasswordToken and also a
>>>> SuperUserToken.  The SuperUserToken contains an additional field called
>>>> "runAsUser".  (in other words, it has a username, password, and a "run as
>>>> username" property)
>>>>
>>>> The realm will then authenticate the normal username and password, but
>>>> return back the principal of the "run as user".  Since our realm extends
>>>> from AuthorizingRealm, it simply returns an instance of
>>>> SimpleAuthenticationInfo that contains the principal of the "run as user"
>>>> but the credentials of the user who is authenticating.
>>>>
>>>> Since this is potentially a very dangerous feature, we only enable it
>>>> for accounts that have the admin flag set on them and ensure that the
>>>> password for this account is very secure, limited, and changed on a regular
>>>> basis.  This functionality is also only available from a secret URL that we
>>>> don't link to in any way.
>>>>
>>>> Here's an excerpt code snippet from our codebase:
>>>>
>>>> User user = userManager.getActiveUserByEmail( organizationId,
>>>> token.getUsername());
>>>> if( user == null ) {
>>>>    throw new UnknownAccountException( "No user account found for [" +
>>>> token.getUsername() + "] for org ID [" + organizationId + "]" );
>>>> }
>>>>
>>>>  if( token instanceof SuperUserToken ) {
>>>>            if( !user.isAdmin() ) {
>>>>                final String message = "Attempt to login as superuser by
>>>> non-admin account: [" + token.getUsername() + "]";
>>>>                log.error(message);
>>>>                throw new UnauthorizedException( message );
>>>>            }
>>>>
>>>>            Contact runAsContact = contactManager.getContactByEmail(
>>>> ((SuperUserToken)token).getRunAsEmail() );
>>>>            if( runAsContact == null ) {
>>>>                throw new UnknownAccountException( "No user found with
>>>> email [" + ((SuperUserToken)token).getRunAsEmail() + "]" );
>>>>            }
>>>>            UserPrincipal runAsPrincipal = new
>>>> UserPrincipal(runAsContact.getUser().getId(), runAsContact.getId());
>>>>            return new SimpleAuthenticationInfo( runAsPrincipal,
>>>> user.getEncryptedPassword(), getName() );
>>>>
>>>>  } else {
>>>>  // Do regular authentication here...
>>>> }
>>>>
>>>> Let me know if you have any questions or problems with this approach.
>>>>
>>>> Jeremy Haile
>>>>
>>>>
>>>>
>>>>
>>>> On Dec 15, 2008, at 4:57 AM, Animesh Jain wrote:
>>>>
>>>>  Hi
>>>>>
>>>>> Is there some way to create a super user sort of entity which can
>>>>> authenticate itself as any subject it wants. It probably is not desired to
>>>>> have such functionality but I'm wondering if there's some way to achieve
>>>>> that if needed.
>>>>>
>>>>> Kind regards
>>>>> Animesh
>>>>>
>>>>
>>>>
>>>
>>>
>>
>>
>
>

Reply via email to