Hi Jan, I'm about to hop on a plane so will respond in more detail later.
I share your concerns about efficiency of the jaspi model, which is why I tried to put another hopefully efficient layer of interfaces in between the AbstractSecurityHandler and the jaspi auth modules. I was hoping that we could simply implement the known auth methods (FORM, BASIC,...) in terms of the ServerAuthentication interface directly and retain all possible efficiencies. Not having done it yet I might have missed some important points :-)
I'll think some more about your comments and get back to you. thanks david jencks (forgot reply-all on my first attempt to respond :-) On Nov 3, 2008, at 1:56 AM, Jan Bartel wrote:
Hi David, Having pored over the jaspi spec a few more times, and then having looked more closely at the code, I'm in a position to give some more detailed comments. Firstly, I like the cleaner distinction in functionality made with the UserIdentity and LoginService as compared with the previous UserPrincipal and UserRealm. I also like very much the refactoring of Abstract/ConstraintSecurityHandler methods. Here's the place where your antennae should sense a "but" coming :) But ... I have some reservations about the efficiency of the Jaspi Way. In particular, every request for which there is a role restriction will result in the user being fully reauthenticated. I understand that even this is an optimization and departure from the jaspi spec, which requires validateRequest to be called on each and every request, unless you know apriori that there is an exclusion constraint for the resource of the request. BTW the lazy authentication when there are no role constraints is another good optimization. As there is going to be more authenticating going on as compared with the previous situation, my next reservation takes on more significance, and that is the amount of object creation required to satisfy the convoluted jaspi callback design. Finally, IIUC the FormAuthenticator will call session.setAttribute(__J_AUTHENTICATED, form_cred) every time authentication is done (see line 365 of FormAuthenticator). In a clustered environment that would be undesirable. It seems to me that although we could tweak things a bit, to make them more efficient, we'd be getting ever farther away from the spec which does not seem to have efficiency as a design goal. Do you agree, or do you have some optimizations in mind? I'm wondering whether we could give the user the choice of security implmentation, but making both jetty "default" security AND jaspi security pluggable alternatives? I've had a brief poke around and I don't think it would take that much to achieve, but at this stage its a thought experiment without code to show. The ideas I've been tossing around to make it pluggable are to modify some of the interfaces of UserIdentity and LoginService and introduce a SecurityManager class to orchestrate things a little: UserIdentity ------------ Principal getUserPrincipal() String getAuthMethod() boolean isUserInRole(String role) setRunAsRole(RunAsToken) setServletHandler(ServletHandler) UserRealm (was LoginService) --------- UserIdentity authenticate (String user, Object credential) boolean reauthenticate (UserIdentity) SecurityManager -------------- UserIdentity authenticate (Request, Response) DefaultSecurityManager //implements SecurityManager ---------------------- JaspiSecurityManager //implements SecurityManager -------------------- AbstractSecurityHandler ---------------------- + setSecurityManager (SecurityManager) The AbstractSecurityHandler would be pretty much unchanged as it is now, except for the addition of a setter and getter for a SecurityManager instance, and the invocation of that managerwhere it currently invokes JaspiServerAuthentication.validateRequest(...)(around line 169). The DefaultSecurityManager implementation would call the authenticator(Basic, Form, Credential etc) directly, much as the ConstraintSecurityHandlerdid in the pre-jaspi version. The JaspiSecurityManager implementation would be equivalent to the JaspiServerAuthentication class functionality. Perhaps the biggest change would be to the LoginService, which I've named back to UserRealm, simply because its behaviour is moreauthentication related, rather than strictly login related. No problem thoughto keep the name LoginService if preferred. The authenticate() methodreturns a UserIdentity object, instead of ultimately setting a LoginCallbackinstance on the Subject (via the ServletCallbackHandler). I don't see that as a major problem - the ServletCallbackHandler could set the UserIdentity object on the Subject instead. Note that in a jaspi implementation, I expect that reauthenticate would never be called, or if it was, it would call authenticate() instead. The other issue is the Form, Basic, Digest etc AuthModules. I think we'd need another set for the default jetty implementation that had no jaspi-style interfaces in it. I think though that they should be able to share a majority of code - avoiding duplication would be highly desirable. From the user's perspective, it would be simple to configure jaspi: WebAppContext webApp = ...;webApp.getSecurityHandler().setSecurityManager(new JaspiSecurityManager());I'm sure I haven't considered all aspects of pluggability. I'll tryand get some time to turn the thoughts into code, which are a) more easilycomprehended and b) will show up any areas I've neglected. cheers Jan David Jencks wrote:Yup, that's wrong.... should be fixed now hoping to read your messages carefully before replying in the future, thanks david jencks On Oct 31, 2008, at 12:42 AM, Jan Bartel wrote:Hi David, No, I'm referring to this code: ConstraintSecurityHandler.checkUserDataPermissions line 235 and 259. It is doing a redirect there to get the request to come in again on the right connector (either the confidential or integral port as appropriate). cheers Jan David Jencks wrote:On Oct 30, 2008, at 10:54 PM, Jan Bartel wrote:Hi David,I'll reply to your reply in a later posting. For now, I just noticedsomething odd in the ConstraintSecurityHandler. If checkUserDataPermissions()notices the request was received on the wrong connector (ie on httpinstead of https) and does a redirect, the AbstractSecurityHandler.handle() method goesahead and subjects the request to JASPI authentication. It seems to methatat that point we want to stop processing the request altogether. Itwillbe the redirected request that we're interested in processing further(either doing the auth or doing a redirect to a login form).I think you are referring to this code? if (!checkUserDataPermissions(pathInContext, base_request, base_response, constraintInfo)) { if (!base_request.isHandled()) { response.sendError(Response.SC_FORBIDDEN); base_request.setHandled(true); } return; }I think there's something odd here, but IIUC something other than whatyou see.This is not proposing a redirect, it is plainly denying the request.I've been worrying about this because it prevents redirecting http requests to the equivalent https requests. Until recently I didn'tthink it was possible to do this redirect using jacc permissions but Ithink there is a solution.... If the actual request is denied and is http we could create a newrequest with the url converted to https and checkUserDataPermissions on it.... if that check succeeds we can redirect to the more secure url. This is somewhat analogous to the way we determine if authentication ismandatory, namely by doing a web resource permission check with the unauthenticated user. I might also have missed what you are looking at... thanks david jenckscheers Jan David Jencks wrote:Hi Jan, On Oct 29, 2008, at 7:37 PM, Jan Bartel wrote:Hi David,I'm still snatching time to tiptoe further around the jaspi branch.A couple of thoughts to run by you: 1. UserIdentity and LoginService classnames. These are quasi analogous to UserPrincipal and UserRealm (although the behaviour has been refactored).I'm wondering whether it might not be a good idea to retain the old classnames, just so it might be easier for jetty users/ developersto ease into understanding the new security structures?I'm not sure that keeping the old names would help anyone understand thenew code, I rather think it would be confusing. I'd really rather not call UserIdentity a Principal since it isn't a Principal and depending on the security handler implementation can contain rather differentthings. The main point of introducing it was that in jetty integrations (Geronimo and from distant memory JBoss) the UserPrincipal wasridiculously overloaded to contain incredible amounts of non- principal information associated with the user's identity. I think that instead it makes sense to have an object that supplies the UserPrincipal, pluswhatever else the security system needs. I don't have strong objection to calling the LoginService UserRealm but I think its going to be confusing and less informative since it doesn't have the non-login-service methods any more.1a. Actually thinking about this, it will probably be quite important for Jetty users to be able to make a smooth transition over to a jaspi-based implementation. Do you think we can retain a UserRealm and a UserPrincipalwith all their methods intact, but just "blend in" the jaspi- nesswithsome extra methods and some changed implementations of the existingapis?Maybe. I think the new interfaces are a lot clearer and moredescriptive for embedding jetty that the old ones. I could look intowriting adapters from UserIdentity to UserPrincipal and LoginService to UserRealm but I'm not entirely sure it will work. In particular I'm notat all sure the non login-service methods on UserRealm could plausiblybe called.2. We allow a UserRealm to be explicitly set on a WebAppContext (well, strictly speaking its WebAppContext.getSecurityHandler().setUserRealm(UserRealm)). I couldn't see specific support for that, only getting a list of LoginServices from the Server instance. Should be easy enough to put in though?I'm not sure how my code is different, except the LoginService is finaland set in the constructor of ServletCallbackHandler, around line 1042 of WebXmlConfiguration. I don't recall changing this code much...3. With the JAAS stuff, which has its own set of callbacks it uses to obtain info, we used a DefaultCallbackHandler to plug in the right info, such as credentials, passwords, usernames andalso extra request parameters from the login. I notice you're usingan anonymous CallbackHandler instead to pass into the JAAS LoginContext.Is it possible to use the DefaultCallbackHandler instead? It supports a couple more callback types that some LoginModule implementationsmay depend on.I could misunderstand the DefaultCallbackHandler but I think that the extensions to a user-password callback handler all involve extracting credentials from the request. In the jaspi architecture this is the function of the auth module, not the password validation service. Alogin module that fishes directly in the request ought to be refactoredinto a plain login module that just validates the credentials and anauth module that extracts the credentials from the message. Despite all the weirdness in jaspi I think this is a good idea and worth enforcing. I guess someone who really really wanted to preserve their login module could write a subclass of LoginCallback that dealt with request parameters, and a JAASLoginService subclass. This would be made easier by factoring out the CallbackHandler creation in JAASLoginService into a protected method. Looks like I left out some exception handling there too :-( I'd rather not encourage this however.4. Minor thing - is there a lot of value in the RunAsToken markerinterfaceas opposed to just having a String? The roles and role mappings arethemselves just Strings, so I was wondering what the utility is?This is an embedding thing also. It's pretty unclear what run- as isactually supposed to mean and how things like supplying the identity fora web service client or other remote call is supposed to work. (If theweb service is supposed to be called as the user, rather than theserver's identity, and you are in a run-as role, what credentials does this run-as-role identity supply????) In Geronimo we represent therun-as role by a Subject obtained by logging into a security realm. So,the geronimo run-as token has this Subject in it. We might want tostore a UserIdentity there instead..... anyway I don't think constraining the representation of the run-as identity is wise. BTW remember that the current auth modules implementingBASIC/DIGEST/FORM auth are more or less temporary until we more orlessagree on the main interfaces, at which time I plan to rewrite them inmore jetty-friendly form (also after apachecon :-) Many thanks! david jencksbest regards Jan David Jencks wrote:On Oct 16, 2008, at 11:59 PM, Jan Bartel wrote:Hi David, Firstly, let me genuflect in recognition of your extraordinary effortsfor a) reading the spec b) being able to make heads or tails ofit c) coming up with an implementation based on it!:-DI'm surpressing the urge to have a bit of rant at yet another jcpspec that is at the same time heavy on the verbiage and light on comprehensibility. Your email was way more informative than what 29 people managed to produce in the spec. Anyway, looking at the code in the jetty-7-jaspi branch, and I admitthat so far I've only just had a cursory nosey around, where would we integrate the JAAS side of things? Implement a JAASLoginService?see org.mortbay.jetty.plus.jaas in modules/plus/jetty-plusNot sure if it is ideal, it's pretty much a simple modification ofthe former JAASUserRealmI'll have a deeper look at the code and get back to you with more informed comments. This mail is to re-assure you that your posthasn't fallen into the void and that we are looking forward to integrating this into jetty-7 trunk!The main thing to remember might be that the current implementations ofbuilt-in security (FORM, BASIC, DIGEST etc) are in jaspi "modules"onlyuntil we agree on the jetty api at which point I was thinking toconvertthem back into more jetty specific code. Of course if you decideyou really like jaspi.... :-)Jan PS I love this code-comment in ServletCallbackHandler: * Idiot class required by jaspi stupidity @#*($)#@&^)[EMAIL PROTECTED]&*$@Several parts of the jaspi spec look to me as if they are sort ofstuck on at the end when someone realized it was incomplete, and the heavy use of CallbackHandler for two way communication between the jaspi modules and the container strikes me as one such point. thanks david jencks:) David Jencks wrote:Greg and Jan were kind enough to create a branch for me to playaround with a JASPI (Java Authentication Service Provider Interface) integration with jetty and its getting to a point where I'm willing to talk about it. Code is at https://svn.codehaus.org/jetty/jetty/branches/jetty-7-jaspi JASPI attempts to provide a uniform framework for messaging systems, both client and server side, to plug in message authentication. On theclient you can add auth info to a request and validate auth infoon aresponse. On the server you can validate auth info on a requestand add auth info to a response. The auth code can conduct arbitrary messageexchanges to negotiate what info is needed and transmit the info.I've been working on the server side auth for jetty.The actual spec jaspi interfaces are not 100% ideal for http anddon'tallow stuff like lazy authentication for unsecured resources soI've come up with interfaces similar in spirit to the jaspi ones. I've also tried to rework the implementation so it is more friendly to integration with other app servers with their own ideas about security frameworks such as geronimo and in particular make jacc implementationseasier. I expect these changes will also simplify integration withe.g.jboss and glassfish but I haven't seriously tried to verify this.Currently all the authentication code (replacing the *Authenticatorclasses) is implemented in terms of jaspi but I plan to changethis soon to use the jetty specific interfaces directly. So.... lets follow a HttpServletRequest/Response pair on its voyage through the security system... ... it arrives at AbstractSecurityHandler.handle. This is a templatemethod that runs through the following structure calling out tosubclasses and the authentication system:1. calls checkUserDataPermissions(pathInContext, base_request,base_response, constraintInfo). This checks the user data constraints, basically that the request arrived over the right kind of connection (http/https). Two obvious implementations of this are the existing jetty constraint based implementation or one based on JACC. 2. calls isAuthMandatory(base_request, base_response, constraintInfo) todetermine if the request actually needs authentication. If itdoes notwe can often delay authentication until a method relying on authresultsis called (such as getUserPrincipal or isUserInRole). Again thiscan be implemented using constraints or JACC. 3. packs the request, response, and authManditory into aJettyMessageInfo holder object which can also pass various authinfo in a map.4. delegates the authentication to the jaspi-like ServerAuthResult authResult = serverAuthentication.validateRequest(messageInfo);assuming we are not doing lazy auth, this will extract the credentials from the request (possibly conducing a multi-message exchange with the client to request the credentials) and validate them. Validation can use a LoginService possibly provided to theServerAuthentication which could be JAAS, Hash, JDBC, etc etc. Lazy auth results in returning a lazy result that only attemptsauthentication when info is actually needed. In this case no message exchange with the client is possible. <<<<<<<<<<<<<<<<<<<<5. Assuming that authentication succeeded (this includes the lazycase where the request would be allowed even without authentication), we wrap up the result in an identity delegate: UserIdentity userIdentity = newUserIdentity(authResult); base_request.setUserIdentity(userIdentity);The UserIdentity is the delegate for run-as role implementationandactually answering auth questions from the application program.This allows app servers to handle run-as roles however they want. 6. Assuming authentication is mandatory, now that we know the user, we can find out if they are in the appropriate roles: checkWebResourcePermissions(pathInContext, base_request, base_response, constraintInfo, userIdentity) 7. On success, we can actually handle the request: getHandler().handle(pathInContext, messageInfo.getRequestMessage(), messageInfo.getResponseMessage(), dispatch); 8. Assuming no exceptions were thrown, we can now secure the response (normally a no-op for http): serverAuthentication.secureResponse(messageInfo, authResult); ------------------------------------------- JASPI implementations I wrote a fairly complete jaspi framework implementation for geronimo(rather than the bits actually needed for http which I wrote forjetty) and have a nearly-untested openid implementation. This (theoretically)lets you openid-enable your app by supplying an appropriate loginpage and useing the openid auth module.Theres also a glassfish implementation that I haven't looked atand someone wrote a SPNEGO auth module that works with it. http://spnego.ocean.net.au/ -------------------------------------------- How does this differ from what's there now?SecurityHandler: AbstractSecurityHandler now just has the basic workflow described about and delegates all actual work to either subclasses (for authorization decisions and object creation) ortheauthentication delegate. This makes it easy to plug in alternate implementations such as a JACC implementation for an EE server.Authentication results and run-as roles: Formerly these were eitherdirectly set in the request (possibly using lazy evaluation, withcode again in Request) or stuffed into a Principal implementation via theUserRealm. This really overloaded the idea of a Principal for no apparent reason and made integration into app servers slightlyconvoluted. This is replaced with a UserIdentity interface providing separate access to the auth results (user principal) and role handling (isUserInRole, and run-as handling). Subclasses ofAbstractSecurityHandler can provide their own implementations ofthis interface. These typically delegate to implementations of ServerAuthResult, which can handle lazy authentication if necessary. UserRealm IMO glues together a lot of unrelated functions, primarily the role handling code now in UserIdentity and the credential validation nowin LoginService. Credential validation may not even be needed bytheserver (e.g. openid). If needed it's called from something that extracts credentials from the request. Implementations are goingto dosomething like look up the user in a file or table or delegate toJAAS.On the other hand the role handling is called by jetty or by theapplication and the implementation is done by the app server (jetty ore.g. geronimo). Aside from being related somehow to security,these are totally unrelated concerns. --------------------------------------------------How does ServerAuthentication and LoginService relate to JASPI?The JASPI interface similar to ServerAuthentication is ServerAuthContext:void cleanSubject(MessageInfo messageInfo, Subject subject) throwsAuthException; AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException; AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException;The main difference is that ServerAuthentication packages all the results into a ServerAuthResult object rather than modifying the clientSubject directly and hiding user principal and group info insomecallback handers. This lets ServerAuthentication support lazyauth. As far as configuration goes. you get a ServerAuthContext by calling a whole lotta methods on some other stuff. or.... you can just create one and stuff it into an adapter, JaspiServerAuthentication. Probably we want to implement the built in auth methods as direct ServerAuthentication implementations rather than the current ServerAuthModule implementations (a ServerAuthContext is supposed todelegate to one or more ServerAuthModules, which have the sameinterface). LoginService is a pretty straightforward way of asking for passwordvalidation and getting some info back. JASPI has a peculiar IMOsystem based on Callbacks. The container (jetty) supplies the auth contextwith a CallbackHandler that enables bi-directional communication.Callbacks providing services to the auth module: PasswordValidationCallback: this lets the auth module ask for password validation: this is the closest to LoginService. CertStoreCallback, PrivateKeyCallback, SecretKeyCallback, andTrustStoreCallback all let the auth module ask for certificate services. AFAICT these are mostly for securing response messages,which is typically not done for http. Callbacks letting the auth module pass info to the server: CallerPrincipalCallback: supplies the caller principal so getCallerPrincipal can return something.GroupPrincipalCallback supplies "groups" the user may be in. The meaning here is rather undefined but can be mapped to roles insome way, such as by assuming the groups and roles are the same.The use of callbacks here still seems rather weird to me but maymakemore sense in the context of other messaging systems: jaspi issupposedto be applicable to all sorts of messaging, including ejb calls,jms, web services, etc etc.I've put the caller principal and groups into the ServerAuthResultobject where they can be accessed directly (although possibly determined lazily). -------------------------------------------------------------- Comments... Right now it looks to me as if form auth needs to be non-lazy since partof the message exchange involves a request to j_security_checkwhich isnormally not a secured response. Trying to evaluate auth for this lazily doesn't work... you never get back to the original request.I don't see how this implementation could be significantly simplified or sped up.... I'm certainly willing to look at problems.I've been discussing JACC with Greg for a long time now. The onlythingI can see that is possible with constraint implementations thatis not possible with jacc is redirecting an http request to the "equivalent"https request if a user data constraint is violated. I'm curiousaboutwhether this is something people want to do or usually set up.Many thanks, david jencks --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email-- Jan Bartel, Webtide LLC | [EMAIL PROTECTED] | http://www.webtide.com --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email--------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email-- Jan Bartel, Webtide LLC | [EMAIL PROTECTED] | http://www.webtide.com--------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email--Jan Bartel, Webtide LLC | [EMAIL PROTECTED] | http:// www.webtide.com--------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email-- Jan Bartel, Webtide LLC | [EMAIL PROTECTED] | http://www.webtide.com --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email--------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email-- Jan Bartel, Webtide LLC | [EMAIL PROTECTED] | http://www.webtide.com
