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