Annotations « Intelligrape Groovy & Grails Blogs

Posts Tagged ‘ Annotations ’

Annotation for checking required session fields

Posted by Uday Pratap Singh on September 21st, 2011

Recently I worked on a project where I used spring security plugin. Its a very wonderful plugin for making your application secured from unauthorized users. It gives you a simple annotation @Secured to add security to your action and controller. Thats the first time I got to know the real use case of annotation. So I started reading about annotation and few days later I found the use case to implement my own annotation.

All the projects I have worked on had login functionality where we put the userId and projectId into the session. Then in my code I use to get the user from session.userId. Something like

def books = {
   User user = User.get(session.userId)
   Project project = Project.get(session.projectId)
   ......
   .....
}

The above code fails when user directly hits this action because there is no check to verify the user is not null. The simple answer for this problem is either use beforeInterceptor or filters. So we started checking the session.userId in filters. But again there are cases where you dont want to check this session value or you can say there are public urls as well. Now we have to put few if else statements in filter.

Here I got my use case to implement an annotation for controllers and actions which checks for the required fields in the session before getting into the action. So I created an annotation in src/groovy folder

import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target

@Target([ElementType.FIELD, ElementType.TYPE]) // Annotation is for actions as well as controller so target is field and for class
@Retention(RetentionPolicy.RUNTIME) // We need it at run time to identify the annotated controller and action
@interface RequiredSession {
    String[] exclude() default [] // To exclude some of the actions of controller

    String[] fields() default ["userId","projectId"] // The default value is set to userId and projectId that can be overridden while using the   annotation on controller or action.

    String onFailController() default "home" // Default controller when the field not in session is set to index page

    String onFailAction() default "index" // Default action when the field not in session is set to index page
}

Now I created a ApplicationFilters and before redirected the request to any action I check for the condition of session fields if the requested action or controller is annotated. The code in the filter is something like

class ApplicationFilters {
    def filters = {
        validateSession(controller: '*', action: '*') {
            before = {
                if (controllerName) {

//Get the instance of controller class from string value i.e; controllerName
                    def controllerClass = grailsApplication.controllerClasses.find {it.logicalPropertyName == controllerName}

//Read the RequiredSession annotation from controller class
                    def annotation = controllerClass.clazz.getAnnotation(RequiredSession)

//Get the current action from actionName otherwise read default action of controller
                    String currentAction = actionName ?: controllerClass.defaultActionName

//Look for the annotation on action if controller is not annotated or the action name is excluded
                    if (!annotation || currentAction in annotation.exclude()) {

//Get the action field from string value i.e; currentAction
                        def action = applicationContext.getBean(controllerClass.fullName).class.declaredFields.find { field -> field.name == currentAction }
//If action is found get the annotation else set it to null
                        annotation = action ? action.getAnnotation(RequiredSession) : null
                    }

//Check for the field in session whether the are null or not if any of the field is null loginFailed is true
                    boolean loginFailed = annotation ? (annotation.fields().any {session[it] == null}) : false

                    if (loginFailed) {

// If login is failed user redirected to on fail action and controller
                        redirect(action: annotation.onFailAction() , controller: annotation.onFailController())
                        return false;
                    }
                }
            }

        }
    }
}


And its all done. Now we just annotate our controller and actions accordingly.

@RequiredSession(exclude = ["registration", "joinProject"])
class UserController {
      def edit ={}
      def update = {}
      def list ={}
      def save ={}

      def registration ={}
      def joinProject = {}
}

In above example registration and joinProject action will bypass the session fields check.

@RequiredSession
class ItemController {
        def index={}
        def buy ={}
        def save ={}
}

All the action of above examples can be accessed only when user is logged in.

class HomeController {
      def index ={}
      def aboutUs={}
      @RequiredSession
      def dashboard = {}
}

Actions other than dashboard are public actions which can be accessed without login.

class UserController {
  @RequiredSession(fields = ["loggedInUserId"])
   def updatePassword = {

   }
}

For updating password user dont need to have some project into session so we specified the fields to be checked in session.

Hope it helps



## Uday Pratap Singh ##
uday@intelligrape.com
http://www.IntelliGrape.com/
http://in.linkedin.com/in/meudaypratap

  • Share/Bookmark

Grails Transactions using @Transactional annotations and Propagation.REQUIRES_NEW

Posted by Abhishek Tejpaul on July 30th, 2010

Hi All,

Here is how you can implement a new transaction in an already executing transaction in Grails which uses nothing but Spring framework’s Transaction mechanism as an underlying implementation. Spring provides @Transactional annotations to provide declarative transactions. We can use the same in our Grails project to achieve the transactional behavior.

Here is the scenario: You have two domain classes named SecuredAccount and AccountCreationAttempt. You try to transactionally save the SecuredAccount object which in turn creates a AccountCreationAttempt object which writes to the database stating: “There is an attempt to create a new SecuredAccount at this time: <current date and time>”. Point to note here is that even if the creation of the new SecuredAccount object fails, the record must still be written to the database so that the Administrator can validate whether the attempt at the specific time was by a legitimate user or an attacker.

Here is the code:

import org.springframework.transaction.annotation.*
Class MyService {
 
static transactional = false
def anotherService
 
@Transactional
def createSecuredAccount() {
def securedAccount = new SecuredAccount(userId:"John")
securedAccount.save(flush:true)
anotherService.createAccountCreationAttempt()
throw new RuntimeException("Error thrown in createSecuredAccount()")
}
}
import org.springframework.transaction.annotation.*
class AnotherService {
 
static transactional = false
 
@Transactional(propagation = Propagation.REQUIRES_NEW)
def createAccountCreationAttempt() {
def accountCreationAttempt = new AccountCreationAttempt(logRemarks: "There is an attempt to create a new SecuredAccount at this time: {new Date()}")
accountCreationAttempt.save(flush:true)
}
}

Now in this scenario, AccountCreationAttempt object always gets persisted whether or not the transaction for creating SecuredAccount object fails.

Here are few gotchas regarding the above transactions:

1.) First of all, for Propagation.REQUIRES_NEW to work as intended, it has to be inside a new object i.e. a new service in our example. If we had put the createAccountCreationAttempt() method in the MyService there would be no new transaction spawned and even no log entry would be made. This is Spring’s proxy object implementation of transactions and you can read more about it here:

http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html#transaction-declarative-annotations.

Please pay special attention to the “NOTE” sub-section.This is what it states:

“In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional.”

2.) Secondly, all the @Transactional methods should have a public visibility i.e. createSecuredAccount() and createAccountCreationAttempt() methods should be public methods, and not private or protected. This again is Spring’s @Transactional annotations implementation and you can read about it at the same link as provided above. Note the right side-bar titled “Method visibility and @Transactional“.

Well, once you keep note of these gotchas I guess you are all set to make good use of @Transactional annotations and its full power.

Cheers !!!

- Abhishek Tejpaul
abhishek@intelligrape.com
[IntelliGrape Software Pvt. Ltd.]

  • Share/Bookmark
Posted in Database, Grails, Groovy