Notes: Using Betamax in Grails (2.4.x)

Betamax is a great tool that records the output of a web service, and serializes the full request to a file. Once the request has been recorded, it can be played back in a test. This comes without any setup on your HTTP requests. This means that your system test no longer has a need to be connected to a working service, or has to be mocked based on a hand created mock.

Since the 2.3.8 release of GRAILs, there have been a few quirks to getting Betamax to work in your tests.

A few things need to be considered when using Betamax with Groovy Grails:

 

  1. Conflicting dependencies with the build process and SnakeYML
    https://github.com/robfletcher/betamax/issues/153
  2. The configuration file isn’t respected.
    Use the rule:

    @Rule
     Recorder recorder = new Recorder(
            tapeRoot: new File(BuildSettingsHolder.settings?.baseDir, 'test/resources/tapes'),
           ignoreLocalhost: true,
          sslSupport: true)
    //The sort property isn’t set: set this up in the test setup
    TapePropertyUtils.metaClass.sort = { Set properties, List names ->
    new LinkedHashSet(properties.sort(true, new OrderedPropertyComparator(names)))
    }
    
  1. Use Groovy to rewrite your RestClient

    def meetupBase = new RESTClient(url)
    BetamaxRoutePlanner.configure(meetupBase.client)
    meetupBase.client.removeRequestInterceptorByClass(ContentEncoding.RequestInterceptor.class)
    meetupBase.client.removeResponseInterceptorByClass(ContentEncoding.ResponseInterceptor.class)
    meetupBase
    
  1. Location of tape files in the project
    ~/ProjectFolder/test/resources/tapes
  2. Note about the hostnames host reference. (All of the tapes are based on the mocked name, so be prepared to modify this if need be.)

 

Spring Security OAuth- The missing Instructions

The problem and lead up to the solution

I have been working on an authentication issue for more than a month. This has been incredibly irritating, and I have not found anything related to this online – not even in Stack Overflow questions. The issue is that I followed the instructions for installing Spring Security, Spring Security OAuth, and the Google [etc] plugins for my existing application. After following the instructions to a T, I clicked on the authentication button for the Google OAuth. What happened? I was sent right back to the login screen.

 

This was rather weird. My first few thoughts were:

  1. Did the authentication fail?
    After an investigation, found this to be false. Chrome’s developer tools showed that the network path redirected to the /oauth/callback and then to /oauth/google/success. So that was a no.
  2. Was the redirection page not working?
    I really had no idea on this. It seemed to work for the locked-down resources. But I still seemed to have received the login page after coming back from Google OAuth. That’s logged within the Spring Security plugin depths.
  3. Was the authentication return point getting rejected?
    Here we would have a loop back to the authentication page.
  4. What controllers were getting hit?
    The debugger won’t go into the controller call itself. Also, the expected controller wasn’t getting hit. So this wasn’t of any help.
  5. Were the Plugins incompatible with my Grails 2.4.3 application?
    I was using the most up to date plugins for Spring Security. (The most recent publication date of May 2014 didn’t help with this concern)

 

After these thoughts, I was stuck. There were other scenarios on Stack Overflow and mailing lists that made claims to the plugin not working. However, most of those issues were resolved by switching plugins or due to syntax errors in the config. Additionally, I checked the plugins’ issue pages and there was apparently nothing about the issue I was having.

 

To answer the third idea of where the problem could be, I looked over the following static set of rules configured in the config:

 

grails.plugin.springsecurity.controllerAnnotations.staticRules = [

‘/’           : [‘permitAll’],

‘/index’      : [‘permitAll’],

‘/index.gsp’ : [‘permitAll’],

‘/assets/**’ : [‘permitAll’],

‘/**/js/**’   : [‘permitAll’],

‘/login/auth’ : [‘permitAll’],

‘/**/css/**’ : [‘permitAll’],

‘/**/images/**’  : [‘permitAll’],

‘/**/favicon.ico’: [‘permitAll’],

‘/oauth/**’   : [‘permitAll’]

]

 

After all the addition of the login/oauth bits were recommended in the documentation.

I even tried testing Google’s side with the console. Everything worked on their end. My assumption was that if one of the plugins was misconfigured it would try to alert me via the logs. (Well, as I learned that assumption made an ass of me.) Finally I did some research within the plugin. I found the package structure in which I could bring in the logs.

 

trace ‘grails.plugin.springsecurity’

That eventually lead me to a lot of logging about only the filters/interception of web traffic, which didn’t help very much, and it still lead me to question what was going on. So I tried logging the service and controller methods. (That lead me to realize that I was never getting the actual OAuth plugin to kick back in on the way back. Additionally I found this bit in the logs (that were enabled earlier):

 

intercept.AnnotationFilterInvocationDefinition no config for ‘/springsecurityoauth/onsuccess’  (which was followed by a redirect to /login/auth.)

 

Google was not much of a help for this method. Nor was it a lot of help for the ‘springsecurityoauth/onsuccess’ path. This meant that the access to the actual controller SpringSecurityOAuth::onsuccess method couldn’t be reached. This was a bit confusing since the Spring Seucrity OAuth defines it’s own URL mappings in: SpringSecurityOauthUrlMappings. Alas, it’s a static call. I’m not able to debug that low.

 

This led to another path: Well how are the other path’s defined, and why isn’t /springSecurityOAuth/onSuccess allowed. SpringSecurityOauthUrlMappings maps the controller to /oauth/$provider/success, and that’s allowed under my rules.

 

The Solution

Well that path isn’t allowed. Why? Spring Security has 2 ways to lock down your application.

  1. InterceptUrlMap Method: Statically define all of your paths/roles via a static map in the config.
  2. Controller Annotations Method: This allows for you to define some static rules requests are allowed and to what accounts in the config file. It also allows for you annotate your controllers/methods to what is allowed by what role(s).

 

Guess which way I went. Yep, I went the progressive route and chose the controller annotations method. That worked really well for what I had, and with authentication outside of this plugin. But it failed when trying to return. Why did this still fail? Well the configuration of springsecurity.controlelerAnnotation.staticRules = [ …. ] was missing the reference to the controller (that looks like a url): ‘/springSecurityOAuth/**’: [‘permitAll’]

After adding that small change: everything worked. A lot of time was lost. I’m going to need a break away from Spring Security. That and probably quite a few drinks.

What could have been improved?

The documentation needs to be updated to mention controller annotations.

Things I’ve been Learning This Week (13 April 15)

Just as a quick summary of the things I’ve been up to this past week:

  1. Reading:
    1. The Last Girlfriend on Earth (Fiction)
    2. Grails In Action-  2nd Edition (Great book btw) [I’ve read the first one, and like it. I’m loving this edition as that it’s more recent and includes the new toys that the later Grails and Groovy editions offer you]
    3. Soft Skills (Also a great book)
  2. paramValidFields within generated Grails tests is meant as something you’re supposed to implement yourself. (This intended to create params that are valid.)
  3. The Fixture’s plugin for Grails has a few quirks
    1. All of the fixtures, unlike in Ruby with the equivalent, are based on a DSL in Groovy (so you have more Groovy files in your projects, and those are compiled in)
    2. The Fixture’s plugin intentionally tries to create valid domain objects (based on your set attributes)
  4. The fairly ubiquitous class Expando is something you can use yourself. It basically allows for you to dynamically define and expand your class (as you would with Python or Ruby)
  5. DropWizard: This has been a little bit of a challenge to get setup and working. However from the looks of it, it doesn’t look too bad.
  6. Chipotle isn’t horrible, its also not horrible. (I’ve been a bit resistant as that it’s a chain and is owned by McDonalds).  On that front I’m still waiting for Nandos to come to Chicago.
  7. How to use a Mac. As a linux person: it’s disappointed me quiet a bit> It’s ridged, not so user friendly, for my purposes, and it has some strong unforgiving opinions about how you’re to use a computer. Would I buy one? Nope.
  8. Using Hibernate via the JPA Annotations (I’ve always depended on the HBM and the Generation afforded).

Weirdness with GORM and Inheritance

Within the world of GRAILs, GORM is your object manager. Think of this as Hibernate with some rather nice dynamic syntactic sugar.  For the most part, it is fairly consistent and does what you want it to. However, I’ve recently ran into 2 weird issues with it.

For these issues lets assume we have 2 classes. One called Users, which has an email address and a name, and another called SuperUser, which inherits from Users but adds nothing new.

  1. One of the dynamic functions is findOrSaveWhere. The method enables you to guarantee that a result is returned. If the conditions for the data item wasn’t found, then it will be created for you and saved. If you are attempting to do a Users.findOrSaveWhere and you don’t give it enough parameters to make a valid instance, or if you have invalid data, the object won’t be saved. This is ok, as that the returned object exists, but will tell you that it was invalid. However within Grails/GORM, you’re supposed to be able to get an exception if you enable the property failOnSave within the method (it is valid within the save method). Unfortunately, that is not a valid method in the findOrSaveWhere dynamic method. It turns out that this is an old issue that has been unaddressed.
  2. This is an issue between a controller and it’s GSP view. If you are trying to list all of the users, in the example, the view won’t bind properly to the generated GSP view. The GSP View will assume that the variable for index will be UsersInstanceList. However, given the mixed types, the control returns some other type. (I’m not sure what this is). The solution to this is to explicitly define the list in the model. Here is the StackOverflow Reference.