Contents

[divider]

Registration + ACL’s + Filter Data for User + Securing Data + Adding Image Uploads

Now we have a Grid, A Form on a Dashboard, and some logic to upload/save files to a file system.

Let’s add User Registration.  The best way to do this quickly is to use one that’s already out there: Spring Security.  So google Spring Security grails plugin and add it to your project.

At the time of this writing, I needed to add this to my BuildConfig.groovy’s plugin closure:

compile ‘:spring-security-core:2.0-RC2’
compile “:spring-security-ui:1.0-RC1”

Compile your project and it should pull in all the dependancies.

The documentation will need to be followed here:

http://grails-plugins.github.io/grails-spring-security-core/docs/manual/

According to their tutorial written at the time of this writing:

http://grails-plugins.github.io/grails-spring-security-core/docs/manual/guide/tutorials.html

you would run this command:

s2-quickstart [Your Application] User Role

You can change User and Role to be a different name.  This will create all the modeling and logic needed to get started with registration.

You should now be able to edit a controller (i.e. dashboard) and add this at the top

@Secured([‘ROLE_ADMIN’])

That annotation will insure only a user with that role of Admin can see this page (we won’t want this for real, but it’s a good test that it works.)  Now restart the app and go to your dashboard page.  You should be prompted to login, and you should be told you are not allowed (since we haven’t added any users yet!)

Adding Users in Bootstrap

Within conf is a file called bootstrap.groovy.  This file loads data before the app starts.  We want to load some users of for our testing.  So add this in your Bootstrap’s init closure:

def adminRole = new Role(authority: ‘ROLE_ADMIN’).save(flush: true)
def userRole = new Role(authority: ‘ROLE_USER’).save(flush: true)

def testUser = new User(username: ‘me’, password: ‘me’)
testUser.save(flush: true)
def altUser = new User(username: ‘user’, password: ‘user’)
altUser.save(flush: true)

UserRole.create testUser, adminRole, true
UserRole.create altUser, userRole, true

Restart the application, and this data will seed the database.  We are creating two users, one named me and one named user.  Me is an admin and User is a user role.

Once again, go to your dashboard page, and first try to see it as a user (user/user), it should reject you.  Now try as me/me, it should allow you.

If it’s working, your registration/security is working!  So let’s modify the controller to allow both users and admins to see the page:

Change the annotation to:

@Secured([‘ROLE_ADMIN’, ‘ROLE_USER’])

What’s important here is to follow their tools to generate your User and Roles.  This will set up a User model along with a Role model – which is used to create access levels to your application.

Assigning Userid to each Post

Once Spring Security is in place we can do all kinds of cool things.  We can create a new domain field userid and populate it with the logged in user’s id on each post.

This is important when we want to:

  • Make sure only the poster see’s their own data (and not other users)
  • The editor/poster can only control (create, edit/delete) their own data (and not other users)

Back at the TradingCardInventory domain, add this field at the top:

String userid

In the static constraints add:

userid nullable: true

Switch to the TradingCardInventoryController.  Somewhere near the top of the class add this:

def springSecurityService (in intellij it will tag it with a  green circle if it found it correctly)

Go to the save() action and above the .save method add this line:

tradingCardInventoryInstance.userid = springSecurityService.currentUser.id

We are saying that on a save, we are taking the user’s id stored in their session and setting it to the field value userid on the domain.

Now if you run the app, add some data via the form, and check dbconsole you should see data with userid = 1 (your user id.)

Show Only The Logged In User’s Data

So now we have a registration system, the data that is saved has the logged in user’s id associating them to their post… let’s make sure when they go to the dashboard they only see their data (and not everyone’s data.)

To make this change all we need to do is set up a global filter on Easygrid.  If we open our tradingCardInventoryGrid closure in the DashboardController, we see a block of things at the top… like this:

def tradingCardInventoryGrid = {
dataSourceType ‘gorm’
domainClass TradingCardInventory

Just above that def tradingCardInventoryGrid… add

def springSecurityService

Now, go back to that tradingCardInventoryGrid closure… before you get to the columns section, add this:

globalFilterClosure {     eq(‘userid’, springSecurityService.currentUser.id)
}

That’s a global filter, it says we’ll return the data but constrain it to only be for the current logged in user’s ID.  So if my ID is 1, and I’ve added 5 posts, and another user with the ID 2 has added 3, I will only see my 5 posts.

Protecting Post Data

Just because I don’t see the posts doesn’t really protect my data or other member’s data.  I could easily figure out that if I send localhost:8080/tradingCardInventory/show/[some random number]

It will pull up the details of that item!  I could edit or delete another user’s data.

What we can do to help prevent this is to go to our TradingCardInventoryController.  We want to add some logic to these spots:

edit()update()
show()
delete()

The logic will be like this:

       def loggedInUser = springSecurityService.currentUser.id

       if( loggedInUser != tradingCardInventoryInstance.userid){
            flash.message =”Sorry, you can only edit your own entries.”
            redirect(uri: ‘/’)
        }else if(loggedInUser == tradingCardInventoryInstance.userid){
            tradingCardInventoryInstance.userid = springSecurityService.currentUser.id
            tradingCardInventoryInstance.delete flush:true
        }

That bit in red, you’ll modify for each section… BTW, make sure to move the .delete action into the closure, you don’t want it called outside of the else if closure.  Otherwise it defeats the purpose and deletes the object anyway. i.e.

For the edit and show, it would just be:

def loggedInUser = springSecurityService.currentUser.id

        if( loggedInUser != tradingCardInventoryInstance.userid){
            flash.message =”Sorry, you can only edit your own entries.”
            redirect(uri: ‘/’)
        }

For Update it would be:

        def loggedInUser = springSecurityService.currentUser.id

        if( loggedInUser != tradingCardInventoryInstance.userId){
            flash.message =”Sorry, you can only edit your own entries.”
            redirect(uri: ‘/’)
        }else if(loggedInUser == tradingCardInventoryInstance.userId){
            tradingCardInventoryInstance.userId = springSecurityService.currentUser.id
            tradingCardInventoryInstance.save flush:true
        }

We’re setting up a condition that if the logged in user isn’t associated with the userid that created the record we redirect the session back to the home page and flash a message as well.  Otherwise, if it’s a match, we allow the action.

Once that’s all done, lets restart the application and try a few tests:

Test #1: Verify a user see’s their own data

You have two users now: me and user.  Login as me and create some data through the form.

Log out (by going to /logout in your URL in the browser) and relogin as user.  As user, go to your dashboard.  Do you see me’s posts?  You shouldn’t based on the userid work we did in the GRID in the DashboardController.

Test #2: Verify you can not view another user’s data

With data from our first test (me’s data) as user ‘user’ attempt to go to /show/1  that should show me’s record, except we have logic that says “if I am not the userid on the record don’t show it to me.” So now it should redirect me instead to the home page.

Test #3: Try the above but with edit and delete functions

If it’s all working, you’re good to go. If you have problems, revisit the logic we put in place to constrain the user’s data to their logged in userid.

Image Uploads to User Directories

Early on when we did the image uploads, we stored the images in ../gallery/test, remember?  Well now that we have spring security we can have it go to ../gallery/[the logged in username or userid]

To do this, let’s revisit the TradingCardInventoryController and go to the save() action.  In this action let’s update it a bit.  We want the userDir to now be their logged in userid.

First make sure you have a reference to the springSecurityService in the class.  You do this by adding:

def springSecurityService near the top of the class.

Then in the save() action change the def userDir to be this:

def userDir = new File(webRootDir, “/${springSecurityService.currentUser.id}”)

If you test that out, it should now do this… if you login as userid = 1, and upload a image, it should create it in ../gallery/1/  and if you’re user 2 it should be ../gallery/2/

 

 

#

2 Responses

Leave a Reply

Your email address will not be published. Required fields are marked *

Archives
Categories