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
>>>>
>>>
>>>
>>
>>
>
>