-
1. Re: external authentication-any pointers for a beginner?
mwkohout Jul 23, 2007 2:15 PM (in response to mwkohout)also, I'm using Seam 2 beta.
-
2. Re: external authentication-any pointers for a beginner?
shane.bryzak Jul 23, 2007 8:41 PM (in response to mwkohout)Thankfully, the servlet spec provides us with session management for free so that we don't need to check each request for a session cookie ourselves. The best thing I can suggest is to read the security chapter of the Seam reference documentation to become familiar with how security works in Seam.
-
3. Re: external authentication-any pointers for a beginner?
mwkohout Jul 24, 2007 4:07 PM (in response to mwkohout)After reviewing the seam security documentation, I've written some code:
1) A JAAS Module:public class CustomLoginModule extends SeamLoginModule { private static final LogProvider log = Logging.getLogProvider(SeamLoginModule.class); public CustomLoginModule() { } @Override public boolean login() throws LoginException { boolean isLoggedIn = false; javax.faces.context.FacesContext ctx = javax.faces.context.FacesContext.getCurrentInstance(); javax.servlet.http.Cookie cookie = (javax.servlet.http.Cookie) ctx.getExternalContext().getRequestCookieMap().get("umnAuthV2"); log.debug("in Module. cookie == "+cookie); try{ if (cookie == null) //if we can't find it, redirect them to the auth server. the auth server will redirect them back, using the desturl param ctx.getExternalContext().redirect("https://authserver.somewhere?desurl=" + ((javax.servlet.http.HttpServletRequest) ctx.getExternalContext().getRequest()).getRequestURL()); } catch (IOException ex) { Logger.getLogger("global").log(Level.SEVERE, null, ex); } org.jboss.seam.core.Expressions.MethodExpression mb = org.jboss.seam.security.Identity.instance().getAuthenticateMethod(); if (mb == null) { throw new java.lang.IllegalStateException("No authentication method defined - please define <security:authenticate-method/> for <security:identity/> in components.xml"); } try { isLoggedIn = (java.lang.Boolean) mb.invoke(); } catch (java.lang.Exception ex) { log.error("Error invoking login method", ex); } return isLoggedIn; } }
And a new security configuration factory(that exposes my JAAS module):@Name("org.jboss.seam.security.configurationFactory") @BypassInterceptors @Scope(ScopeType.STATELESS) public class JAASConfigFactory { @Logger private Log log; public JAASConfigFactory() { } static final String DEFAULT_JAAS_CONFIG_NAME = "custom"; protected javax.security.auth.login.Configuration createConfiguration() { return new javax.security.auth.login.Configuration() { private AppConfigurationEntry[] aces = { createAppConfigurationEntry() }; @Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { return DEFAULT_JAAS_CONFIG_NAME.equals(name) ? aces : null; } @Override public void refresh() {} }; } protected AppConfigurationEntry createAppConfigurationEntry() { log.debug("in JAASConfigFactory.."); return new AppConfigurationEntry( CustomLoginModule.class.getName(), LoginModuleControlFlag.REQUIRED, new HashMap<String,String>() ); } @Factory(value="org.jboss.seam.security.configuration", autoCreate=true, scope=APPLICATION) public javax.security.auth.login.Configuration getConfiguration() { return createConfiguration(); } public static javax.security.auth.login.Configuration instance() { if ( !Contexts.isApplicationContextActive() ) { throw new IllegalStateException("No active application scope"); } return (javax.security.auth.login.Configuration) Component.getInstance("org.jboss.seam.security.configuration"); } }
I've also altered the security:identity component:<security:identity authenticate-method="#{authBean.authenticate}" security-rules="#{securityRules}" authenticate-every-request="true" auto-create="true" jaas-config-name="custom"/>
However, Seam does not seem to be installing my JAAS module(it fails to output any of my logs). Am I missing something?
Any ideas would be helpful
Thanks
Mike Kohout -
4. Re: external authentication-any pointers for a beginner?
shane.bryzak Jul 25, 2007 10:58 PM (in response to mwkohout)Does getConfiguration() get hit when you set a breakpoint there? You may need to extend Identity and override the getLoginContext() method.
-
5. Re: external authentication-any pointers for a beginner?
mwkohout Jul 26, 2007 10:40 AM (in response to mwkohout)No, it wasn't hit by a breakpoint. I'm going to try your suggestion because it sounds cleaner, but I ended up overriding the getDefaultCallbackHander method of Identity to return my preferred callbackhandler and setting up my login module via jboss confuse-igation files....it's still not working, but now it's failing in my code(I think because of some error in my jboss configs) :-).
-
6. Re: external authentication-any pointers for a beginner?
mwkohout Jul 27, 2007 4:12 PM (in response to mwkohout)Thanks for your continuing suggestions and patience, Shane.
I reimplemented by overriding getLoginContext() and it's still going boom.
here's my JAAS config factory method:@Factory(value="org.jboss.seam.security.configuration", autoCreate=true, scope=APPLICATION) public javax.security.auth.login.Configuration getConfiguration() { log.error("in my getConfiguration()"); return new javax.security.auth.login.Configuration() { private AppConfigurationEntry[] aces = { new AppConfigurationEntry( X500LoginModule.class.getName(), LoginModuleControlFlag.REQUIRED, new HashMap<String,String>() ) }; @Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { List<AppConfigurationEntry> entries = new ArrayList<AppConfigurationEntry>(); for( AppConfigurationEntry entry : aces) { if( entry.getLoginModuleName().equals(name)) entries.add(entry); } return entries.toArray(new AppConfigurationEntry[0]); } public String toString() { return "appConfigurationEntries="+Arrays.asList(aces); } }; }
And my subclass of identity@Name(value = "org.jboss.seam.security.identity") @Scope(value = SESSION) //@BypassInterceptors @Startup public class X500Identity extends Identity { private static final LogProvider log = Logging.getLogProvider(X500Identity.class); @In(value="org.jboss.seam.security.configuration") Configuration config; @In(value="org.jboss.seam.core.expressions") Expressions expressionFactory; public X500Identity() { setJaasConfigName(X500LoginModule.class.getName()); setAuthenticateEveryRequest(true); log.error("in X500Identity constructor. jaas config name = "+this.getJaasConfigName()); } @Override protected LoginContext getLoginContext() throws LoginException { log.error("in my getLoginContext()"); if (getJaasConfigName() == null) { throw new RuntimeException("In X500Identity. JAAS config name not set. Please set it up."); } if( config == null ) throw new RuntimeException("In X500Identity. \"org.jboss.seam.security.configuration\" component not injected. Please set it up."); log.error( "new LoginContext(getJaasConfigName(), getSubject(), getDefaultCallbackHandler(), config)=+new LoginContext("+getJaasConfigName()+","+ getSubject()+","+ getDefaultCallbackHandler()+","+ config+")"); return new LoginContext(getJaasConfigName(), getSubject(), getDefaultCallbackHandler(), config); } @Override public CallbackHandler getDefaultCallbackHandler() { log.error("in my getDefaultCallbackHandler()"); return new CookieCallbackHandler(); } @Override public void checkRestriction(String expr) { log.error("in my checkRestriction(String expr) expr=" + expr); if (!evaluateExpression(expr)) { if (!isLoggedIn()) { this.login(); } else { Events.instance().raiseEvent("org.jboss.seam.notAuthorized"); throw new AuthorizationException(String.format("Authorization check failed for expression [%s]", expr)); } } } @Override public boolean isLoggedIn(boolean attemptLogin) { log.error("in my isLoggedIn(boolean attemptLogin) attemptLogin = " + attemptLogin); boolean isLoggedIn = super.isLoggedIn(attemptLogin); log.error("exiting isLoggedIn(boolean attemptLogin). isLoggedIn = " + isLoggedIn); return isLoggedIn; } @Override public Expressions.MethodExpression getAuthenticateMethod() { log.error("in my getAuthenticateMethod():"+expressionFactory.createMethodExpression("#{authBean.authenticate()}")); return expressionFactory.createMethodExpression("#{authBean.authenticate()}"); } public static Identity instance() { if (!Contexts.isSessionContextActive()) { throw new IllegalStateException("No active session context"); } Identity instance = (Identity) Component.getInstance(X500Identity.class, ScopeType.SESSION); if (instance == null) { throw new IllegalStateException("No Identity could be created"); } return instance; } }
For some reason, it doesn't appear to be hitting my authenticateMethod anymore....ideas? -
7. Re: external authentication-any pointers for a beginner?
smokingapipe Jul 29, 2007 12:59 AM (in response to mwkohout)This is a very interesting thread, I will be following closely.
-
8. Re: external authentication-any pointers for a beginner?
mwkohout Jul 29, 2007 2:43 PM (in response to mwkohout)I think core to my problem is that I'm not really understanding how Seam 2's default login module is deployed.
I've looked and found several jboss service xml deployment files but (perhaps due to my limited experience with those files) it doesn't seem that it's how the seam login module is activated.
Could someone with more insight or ability confirm this? And if so talk me through it?
thanks
Mike -
9. Re: external authentication-any pointers for a beginner?
shane.bryzak Jul 29, 2007 10:35 PM (in response to mwkohout)This is getting too complex. Try simply overriding the configuration factory class like this:
@Name("org.jboss.seam.security.configurationFactory") @BypassInterceptors @Scope(ScopeType.STATELESS) @Install(precedence = DEPLOYMENT) public class MyConfigFactory extends Configuration { protected AppConfigurationEntry createAppConfigurationEntry() { return new AppConfigurationEntry( CustomLoginModule.class.getName(), LoginModuleControlFlag.REQUIRED, new HashMap<String,String>() ); } }
Unfortunately you need to set the install precedence to DEPLOYMENT because the configuration factory in Seam defaults to APPLICATION (I've fixed this in CVS now, so if you're using latest CVS you don't need the @Install line). -
10. Re: external authentication-any pointers for a beginner?
mwkohout Jul 31, 2007 1:06 PM (in response to mwkohout)After updating to the head of cvs, I'm able to get this stuff to work.
For the most part.
But, one problem remains-on the first view of a protected resource(like wildcarded restriction below), the user is not being forced to authenticate:-). On the second request, when the jsessionid cookie of the server is set, authentication occurs and the correct things seem to happen.<page view-id="*"> <restrict>#{identity.isLoggedIn(true)}</restrict> <navigation> <rule if-outcome="home"> <redirect view-id="/home.xhtml"/> </rule> </navigation> </page>
if my description is vague, here's a list of actions and their results.
1)A user makes a request to the server(let's say it's http://localhost/JAASTest). The user doesn't have a jsessionid cookie.
2)the server, upon reciept of the user's request, creates a jesssionid cookie and sends it back on the response. The server also renders the protected resource and returns that back to the user.
*****at this step, the user should have been forced to authenticate****
3)the user then makes another request to the protected resource.
4)The server then forces authentication and good things seem to happen.
I'm thinking my error is occurring in my custom Identity class-maybe I'm missing a critial annotation or I'm misunderstanding about when Seam starts a session or something. Does anyone see what I'm doing wrong?import static org.jboss.seam.ScopeType.SESSION; import edu.umn.ictr.mentor.action.CookieCallbackHandler; import edu.umn.ictr.mentor.action.X500LoginModule; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import javax.servlet.http.Cookie; import org.jboss.seam.Component; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.annotations.Startup; import org.jboss.seam.annotations.intercept.BypassInterceptors; import org.jboss.seam.contexts.Contexts; import org.jboss.seam.core.Events; import org.jboss.seam.core.Expressions; import org.jboss.seam.security.AuthorizationException; import org.jboss.seam.security.Identity; import org.jboss.seam.log.LogProvider; import org.jboss.seam.log.Logging; import org.jboss.seam.security.RuleBasedIdentity; /** * * @author mwkohout */ @Name(value = "org.jboss.seam.security.identity") @Scope(value = SESSION) @Startup public class X500Identity extends Identity { private static final LogProvider log = Logging.getLogProvider(X500Identity.class); @In("org.jboss.seam.security.configuration") Configuration config; private Cookie X500Cookie; public X500Identity() { setJaasConfigName("default"); setAuthenticateEveryRequest(true); log.error("in X500Identity constructor. jaas config name = "+this.getJaasConfigName()); } @Override public void create() { super.create(); } public Cookie getX500Cookie() { return X500Cookie; } public void setX500Cookie(Cookie X500Cookie) { this.X500Cookie = X500Cookie; } public Configuration getConfig() { return config; } public void setConfig(Configuration config) { log.error("in setConfig. config = "+config); this.config = config; } @Override protected LoginContext getLoginContext() throws LoginException { log.error("in my getLoginContext()"); if (getJaasConfigName() == null) { throw new RuntimeException("In X500Identity. JAAS config name not set. Please set it up."); } if( config == null ) throw new RuntimeException("In X500Identity. \"org.jboss.seam.security.configuration\" component not injected. Please set it up."); log.error( "new LoginContext(getJaasConfigName(), getSubject(), getDefaultCallbackHandler(), config)=+new LoginContext("+getJaasConfigName()+","+ getSubject()+","+ getDefaultCallbackHandler()+","+ config+")"); log.error("config's # of app configurationEntry's entries= "+config.getAppConfigurationEntry("default").length); log.error("config's app configurationEntry's LoginModuleName= "+config.getAppConfigurationEntry("default")[0].getLoginModuleName()); return new LoginContext(getJaasConfigName(), getSubject(), getDefaultCallbackHandler(), config); } @Override public CallbackHandler getDefaultCallbackHandler() { log.error("in my getDefaultCallbackHandler()"); return new CookieCallbackHandler(); } @Override public void checkRestriction(String expr) { log.error("in my checkRestriction(String expr) expr=" + expr); if (!evaluateExpression(expr)) { if (!isLoggedIn()) { this.login(); } else { Events.instance().raiseEvent("org.jboss.seam.notAuthorized"); throw new AuthorizationException(String.format("Authorization check failed for expression [%s]", expr)); } } } public static X500Identity instance() { if (!Contexts.isSessionContextActive()) { throw new IllegalStateException("No active session context"); } X500Identity instance = (X500Identity) Component.getInstance(X500Identity.class, ScopeType.SESSION); if (instance == null) { throw new IllegalStateException("No Identity could be created"); } return instance; } }
thanks
Mike Kohout -
11. Re: external authentication-any pointers for a beginner?
shane.bryzak Jul 31, 2007 11:01 PM (in response to mwkohout)You currently can't specify a restriction on the "*" view like that (in fact you should be using the login-required attribute anyway). There's an outstanding JIRA issue to that effect:
http://jira.jboss.org/jira/browse/JBSEAM-1009
What happens when you move your restriction to a more specific view? Do you get the same behaviour that you described? -
12. Re: external authentication-any pointers for a beginner?
mwkohout Aug 1, 2007 11:06 AM (in response to mwkohout)It's still not working. Setting the pages.xml entries(and updating to HEAD of cvs) like so:
<navigation> <rule if-outcome="home"> <redirect view-id="/home.xhtml"/> </rule> </navigation> </page> <page view-id="/home.xhtml" scheme="https"> <restrict>#{identity.isLoggedIn(true)}</restrict> </page>
resulted in identical behavior.
Setting the login-requred parameter to true resulted in me being forwarded to seam-gen's login.xhtml view. I understand why that's happening-because of the exception handler listed below...but I'm not sure what I should do to make it hit my authentication code. Subclass Pages?<page view-id="*"> <navigation> <rule if-outcome="home"> <redirect view-id="/home.xhtml"/> </rule> </navigation> </page> <page view-id="/home.xhtml" login-required="true" scheme="https"> </page> <exception class="org.jboss.seam.security.NotLoggedInException"> <redirect view-id="/login.xhtml"/> </exception>
any ideas? where in the seam code itself should I be looking so I can debug this behavior?
thanks again
Mike Kohout -
13. Re: external authentication-any pointers for a beginner?
shane.bryzak Aug 1, 2007 8:43 PM (in response to mwkohout)Setting the login-requred parameter to true resulted in me being forwarded to seam-gen's login.xhtml view.
Isn't that the result that you wanted? BTW the exception handler for NotLoggedInException is unrelated to the login-required attribute. -
14. Re: external authentication-any pointers for a beginner?
mwkohout Aug 2, 2007 1:01 AM (in response to mwkohout)Thanks for your help so far, Shane.
No....My users are authenticating against a web application on a different server developed by a different group...so I've got to forward them to a login page that isn't in my Seam app.
This is the simplified sequence of events:
1) an unauthenticated user tried to access my seam app.
2) my app(via my JAAS Handler) looks for a "secure" cookie for the domain. It doesn't see it. So, it forwards them on to http://authenticate.institution.edu where they are presented with a form and authenticate to that app. That app then sets the domain-wide cookie and then forwards them back to my seam app.
3) my app sees the cookie and from the cookie knows who they are and they are then authenticated. Then they are then assigned roles(assigning roles is trivial and is not something I'm having trouble with) and they use my seam app.
4) After authentication, for every request I check(through a WS) and make sure the cookie is still valid.
I do believe(because I'm not at work and don't have my app in front of me) the NotLoggedInException is thrown from Pages.redirectToLoginView() if the login view isn't set. Why didn't I set my login view? Because I don't want to redirect to a view in my app-I wanna force the JAAS Handler I wrote to run instead of redirect them. My JAAS Handler will force a redirect if necessary.
Instead of forwarding to a view from the exception handler in pages.xml, is there any way I can force the authentication stuff to happen?