Archive for January, 2016

Hacking Delhi

Tuesday, January 19th, 2016

I did a talk at GR8Conf India on Saturday called “Fun With Spring Security” where I presented a few sample applications demonstrating various non-standard techniques for authentication, restricting access to URLs, etc. The test applications are available at GitHub and each has a README.adoc but I wanted to summarize the applications in a combined blog post.

All of the apps use Grails 3 and spring-security-core 3.x but would be easily converted to Grails 2 and version 2.x of the plugin since nothing depends on the newer versions.

Some items that are common to all or most of the apps:

  • when you start the app the index.gsp page has some links to test the various actions
  • debug/trace logging for the plugin and Spring Security is configured but commented out in logback.groovy
  • most of the applications are intentionally stripped-down:
    • no static resources
    • the GSPs are very minimal
    • unused attributes were removed from the grails.plugin.springsecurity block in application.groovy

autorole


The first example is the “autorole” application. It demonstrates how to infer one or more roles from persisted user information without explicitly storing roles in the traditional many-to-many format. In this example there isn’t even a Role or UserRole domain class, only the User class; all of the work is done in autorole.AutoRoleUserDetailsService, the custom UserDetailsService implementation.

A more realistic implementation would probably use a hybrid approach, storing some role information in the database and inferring the rest, with the UserDetailsService merging the “real” and “virtual” roles as needed.

Items of note:

  • test.User is the user domain class generated by the s2-quickstart script with one modification, adding a boolean admin property
  • the Role and UserRole classes generated by the s2-quickstart script were deleted since they’re not used
  • two users are created in BootStrap.groovy; user “admin” (password “password”) has the admin boolean set to true and will be auto-granted ROLE_ADMIN, and user “user” (password “password”) has the default value for admin (false) which will result in a grant of ROLE_USER
  • autorole.AutoRoleUserDetailsService is registered in grails-app/conf/spring/resources.groovy as the userDetailsService bean
  • secured.SecureController has two annotated actions; /secure requires ROLE_USER (or ROLE_ADMIN since hierarchical roles are configured) and /secure/admin requires ROLE_ADMIN

noroles


The next example is the “noroles” application. It shows how to use expressions to guard access when access rules are simple and roles aren’t required. In this example there isn’t even a Role or UserRole domain class, only the User class.

Items of note:

  • test.User is the user domain class generated by the s2-quickstart script with a few modifications:
    • a final authorities = [] property so GormUserDetailsService works correctly, but doesn’t grant any roles since there aren’t any (for demo purposes here, since there is a custom UserDetailsService)
    • a UserType userType property
    • a String businessUnit property
    • a boolean developer property
  • the Role and UserRole classes generated by the s2-quickstart script were deleted since they’re not used
  • four users are created in BootStrap.groovy, all with password “password”:
    • admin1 has UserType admin, businessUnit: ‘group1’
    • admin2 has UserType admin, businessUnit: ‘group2’
    • salesdude has UserType sales, businessUnit: ‘group1’
    • codemonkey has UserType other, businessUnit: ‘it’, developer true
  • secured.SecureController has several annotated actions using expressions to guard access
  • a custom UserDetailsService creates an extended UserDetails instance to cache nonstandard user properties for use in expressions

hacking_newdelhi


This is an update of the demo app from Greach 2015 (“hacking_madrid”) which was an update of the demo app from GGX 2011 (“hacking_london”). I updated it to Grails 3 and spring-security-core 3.0.3.

It adds an “organization” drop-down to the login page in addition to username and password, and a customized Authentication class, servlet filter, and AuthenticationProvider.

Items of note:

  • grails-app/views/login/auth.gsp is the same as auth.gsp from the core plugin, with the addition of a to select the user’s Organization during login
  • hacking.extralogin.OrganizationAuthentication extends UsernamePasswordAuthenticationToken to add a String organizationName property
  • hacking.extralogin.ui.OrganizationFilter extends the core plugin’s GrailsUsernamePasswordAuthenticationFilter and is registered as the authenticationProcessingFilter bean to process form logins; it creates OrganizationAuthentication instances from POST parameters for authentication
  • hacking.extralogin.auth.OrganizationAuthenticationProvider uses the data in OrganizationAuthentication to authenticate
    • It’s based on DaoAuthenticationProvider but directly accesses the database using GORM instead of delegating to a UserDetailsService
  • two new domain classes, Organization and OrgUser are used to persist the user/organization relationship
    • OrgUser is the many-to-many join class which uses two 1-to-many relationships instead of the traditional GORM static hasMany mapped collections
  • BootStrap.groovy creates test data:
    • two Organization instances, “Org1” and “Org2”
    • a user with ROLE_ADMIN (“admin”/”password”) in “Org1”
    • a user with ROLE_USER (“user”/”password”) in “Org2”
    • a user with ROLE_USER and enabled set to false (“disabled”/”password”) in “Org1”
  • rather than copying and pasting the entire bean definitions into resources.groovy to override the bean class for the authenticationProcessingFilter and daoAuthenticationProvider beans, hacking.HackingBeanFactoryPostProcessor is registered in resources.groovy and updates the bean class in the bean definition. This approach retains all of the dependency injections and configuration updates and helps prevent the app from breaking if updated to a newer version of the plugin that has different dependencies and/or config options for the beans
  • NoStackBadCredentialsException is thrown as needed instead of BadCredentialsException; it’s similar to the core plugin’s NoStackUsernameNotFoundException which avoids filling in the stack trace to reduce creation cost
  • secured.SecureController has two annotated actions; /secure requires ROLE_USER (or ROLE_ADMIN since hierarchical roles are configured) and /secure/admin requires ROLE_ADMIN

lockout


This application shows how to use events to lock a user account after a fixed number of failed login attempts.

Items of note:

  • lockout.FailureEventListener is registered to listen for AuthenticationFailureBadCredentialsEvent
  • lockout.SuccessEventListener is registered to listen for AuthenticationSuccessEvent
  • the User domain class has int badCredentialsCount to track failed logins
  • UserService increments badCredentialsCount for failures and resets to 0 on success
  • secured.SecureController has two annotated actions; /secure requires ROLE_USER (or ROLE_ADMIN since hierarchical roles are configured) and /secure/admin requires ROLE_ADMIN

x509


This application showing how to use X.509 browser certificates to authenticate.

Items of note:

  • X.509 is enabled by adding useX509 = true in application.groovy
  • two users (“dianne” and “scott”) are created in BootStrap.groovy, both with password “not_used” since it’s unused with certificate authentication
  • add the dianne.p12 and/or scott.p12 certificate to your browser to authenticate as that person
  • you must use SSL with X.509 authentication; I tested by building a WAR file and deploying it to Tomcat 8, and configuring run-app similarly is left as an exercise for the reader
    • To test, run `grails war` and copy build/libs/x509-0.1.war to the Tomcat webapps folder, renaming the war to ROOT.war so it uses the default context
    • be sure to access the application with SSL URLs, e.g. https://localhost:8443/secure/index
  • configure server.jks as the keystore and truststore; server.xml is an example Tomcat 8 config file that does this, expecting that server.jks is in the conf directory
  • secured.SecureController has two annotated actions; /secure requires ROLE_USER (or ROLE_ADMIN since hierarchical roles are configured) and /secure/admin requires ROLE_ADMIN

x509chained


This last application shows how to use X.509 browser certificates and a second authentication provider to authenticate.

Note that this has been only lightly tested and should be used with caution. I have no idea if there are gaps in the implementation. Test anything based on this approach and/or code extensively before using in a real application. If you find problems with the approach or code let me know so I can update the code.

Items of note:

  • X.509 is enabled by adding useX509 = true in application.groovy
  • two users (“dianne” and “scott”) are created in BootStrap.groovy, both with password “password” since the password is needed for the second form-auth phase
  • add the dianne.p12 and/or scott.p12 certificate to your browser to authenticate as that person
  • you must use SSL with X.509 authentication; I tested by building a WAR file and deploying it to Tomcat 8, and configuring run-app similarly is left as an exercise for the reader
    • To test, run `grails war` and copy build/libs/x509chained-0.1.war to the Tomcat webapps folder, renaming the war to ROOT.war so it uses the default context
    • be sure to access the application with SSL URLs, e.g. https://localhost:8443/secure/index
  • configure server.jks as the keystore and truststore; server.xml is an example Tomcat 8 config file that does this, expecting that server.jks is in the conf directory
  • x509chained.LoginController extends the plugin’s LoginController to not redirect to successHandler.defaultTargetUrl if authenticated. This is needed because the chained authentication happens in two requests with a redirect. If the first phase (X.509) succeeds, there will be an active authentication, but it’s incomplete and cannot be used yet. Filter chain processing must be allowed to happen to allow the second authentication phase to run.
  • x509chained.ChainedX509AuthenticationFilter extends the standard X509AuthenticationFilter to replace the Authentication in successfulAuthentication with one with all of the real roles replaced with ROLE_CHAINED_X509 as a marker to indicate that the first authentication phase succeeded. The second authentication phase will create a standard Authentication with the real roles.
  • x509chained.ChainedAuthenticationProcessingFilter extends the plugin’s form authentication filter (GrailsUsernamePasswordAuthenticationFilter). It detects that the X.509 phase has occurred and redirects to the login page, replacing the credentials (since they’re unused by X.509) with a marker string so downstream processing is aware of the current state in the workflow.
  • secured.SecureController has two annotated actions; /secure requires ROLE_USER (or ROLE_ADMIN since hierarchical roles are configured) and /secure/admin requires ROLE_ADMIN

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.