geronimo-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From David Jencks <david_jen...@yahoo.com>
Subject Jetty Security refactoring for JASPI
Date Tue, 14 Oct 2008 00:03:30 GMT
Greg and Jan were kind enough to create a branch for me to play around  
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  
the client you can add auth info to a request and validate auth info  
on a response.  On the server you can validate auth info on a request  
and add auth info to a response.  The auth code can conduct arbitrary  
message exchanges 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 and don't  
allow stuff like lazy authentication for unsecured resources so I'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  
implementations easier. I expect these changes will also simplify  
integration with e.g. jboss and glassfish but I haven't seriously  
tried to verify this.

Currently all the authentication code (replacing the *Authenticator  
classes) is implemented in terms of jaspi but I plan to change this  
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 template  
method that runs through the following structure calling out to  
subclasses 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)  
to determine if the request actually needs authentication.  If it does  
not we can often delay authentication until a method relying on auth  
results is called (such as getUserPrincipal or isUserInRole).  Again  
this can be implemented using constraints or JACC.

3. packs the request, response, and authManditory into a  
JettyMessageInfo holder object which can also pass various auth info  
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 the  
ServerAuthentication which could be JAAS, Hash, JDBC, etc etc.
   Lazy auth results in returning a lazy result that only attempts  
authentication when info is actually needed.  In this case no message  
exchange with the client is possible.
<<<<<<<<<<<<<<<<<<<<
5. Assuming that authentication succeeded (this includes the lazy case  
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 implementation and  
actually 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 for  
jetty) and have a nearly-untested openid implementation.   This  
(theoretically) lets you openid-enable your app by supplying an  
appropriate login page and useing the openid auth module.

Theres also a glassfish implementation that I haven't looked at and  
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) or the  
authentication 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 either  
directly set in the request (possibly using lazy evaluation, with code  
again in Request) or stuffed into a Principal implementation via the  
UserRealm.  This really overloaded the idea of a Principal for no  
apparent reason and made integration into app servers slightly  
convoluted.  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 of  
AbstractSecurityHandler can provide their own implementations of this  
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 now in LoginService.  Credential validation may not even be  
needed by the server (e.g. openid).  If needed it's called from  
something that extracts credentials from the request.  Implementations  
are going to do something like look up the user in a file or table or  
delegate to JAAS.  On the other hand the role handling is called by  
jetty or by the application and the implementation is done by the app  
server (jetty or e.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)  
throws AuthException;

     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 in  
some callback handers.  This lets ServerAuthentication support lazy  
auth.

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 to  
delegate to one or more ServerAuthModules, which have the same  
interface).

LoginService is a pretty straightforward way of asking for password  
validation and getting some info back.  JASPI has a peculiar IMO  
system based on Callbacks.  The container (jetty) supplies the auth  
context with 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, and  
TrustStoreCallback 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 in some  
way, such as by assuming the groups and roles are the same.

The use of callbacks here still seems rather weird to me but may make  
more sense in the context of other messaging systems: jaspi is  
supposed to 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 ServerAuthResult  
object 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  
part of the message exchange involves a request to j_security_check  
which is normally 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 only  
thing I can see that is possible with constraint implementations that  
is not possible with jacc is redirecting an http request to the  
"equivalent" https request if a user data constraint is violated.  I'm  
curious about whether this is something people want to do or usually  
set up.

Many thanks,
david jencks







Mime
View raw message