3

I'm working on a Java EE web application where I use Spring MVC and Spring security. Until now, I succeeded in implementing the common security features using a custom userService (which retrieves the information from my database) but I'm now facing a new security issue and I don't really know what's the best way to tackle it...

Here is what I need to do: Users who are authenticated in my app are allowed to navigate through their projects but should only be able to open the projects they have authorizations to (and open what is inside) but no other project.

My current implementation deals with it with the navigation (user interface) offering only the list of the authorized projects to the user. However if a clever user directly edits the URL to open a project with another id then no authorization test on the project id and the user is done so that it opens the project without trouble.

So, what I would like to add is a control on every requests for opening a project or something inside it. This control will test if the project id requested can be opened by the current user or not. If not, it will return to the user an access denied page. This control is easy to implement in itself but as it is a bit of code which could be in many methods of my app I would like to find the cleanest way to do it !

What do you think is the best way to implement that ? I considered several possibilities but I need advice:

1) Use servlet filters ?

2) Add a special access on Spring security with a custom authenticationManager ? something like :

 <security:intercept-url pattern="/open*" access="canOpen()" /> 

(but I'm not sure how to define this and if I would be able to get the parameters coming from the original request...)

3) Use Aspect Oriented Programming ? (but I read somewhere that it does not work on controller calls)

4) Use Spring interceptors ?

5) other ideas ?

Thanks in advance for you help!

Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
Coralie
  • 107
  • 3
  • 17

2 Answers2

4

I would advise you to use a custom PermissionEvaluator so you could basically use the same implementation in both webpages and controllers:

in the webpage you could use:
<security:authorize access="hasPermission(#project,'read')"></security:authorize>

in your controller and in any of your service methods you could use:
@PreAuthorize("hasPermission(#project,'read')")

or also @PostAuthorize and @PostFilter("hasPermission(filterObject,'read')") (for lists)

all of these function will either restrict access or filter result list according to you own permission evaluator. they will all point to the same implementation, which would look something like this:

@Component
public class MyPermissionEvaluator implements PermissionEvaluator {

    private final Log logger = LogFactory.getLog(getClass());

    @Override
    public boolean hasPermission(Authentication auth, Object arg1, Object arg2) {
        logger.info("hasPermission "+auth+" - "+arg1+" - "+arg2+" ");
        if(arg2 instanceof String && arg1 instanceof MyProject){
            MyProject project = (MyProject)arg1;
            if(((String) arg2).equals("read")){
                boolean result = hasPermissionReadProject(auth, project);                   
                return result;
            }
        }
        return false;
    }

    @Override
    public boolean hasPermission(Authentication arg0, Serializable arg1,
        String arg2, Object arg3) {
        logger.info("hasPermission "+arg0+" - "+arg1+" - "+arg2+" - "+arg3+" ");
        return false;
    }
}

Also, when these methods return false it automatically throws an AccessDeniedException and you can easily configure that to redirect to your own accessDenied page in the http element:

<http auto-config="true">
    <intercept-url pattern="/admin*" access="ROLE_ADMIN" />
    <access-denied-handler error-page="accessDeniedPage"/>
</http>
rptmat57
  • 3,643
  • 1
  • 27
  • 38
  • Thanks a lot for you answer! it looks exactly like what I was looking for! However I have a problem implementing the solution right now... When I run it, it does not go through my ProjectPermissionEvaluator... I'll give you more information about my configuration in another comment. – Coralie Nov 21 '12 at 17:11
  • Here is the configuration I had to add in my security-config.xml: – Coralie Nov 21 '12 at 17:17
  • I annotated the class ProjectPermissionEvaluator with @Component and used @PreAuthorize("hasPermission(#projectID,'read')") above my "openProject" method in the ProjectController. Do you think I'm missing something? – Coralie Nov 21 '12 at 17:18
  • looks good to me, only difference between my config and yours is that I didn't use `@Component` for my PermissionEvaluator but defined it in my xml file – rptmat57 Nov 21 '12 at 19:52
  • I changed my configuration and declared the projectPermissionEvaluator in my xml beans... but it's still not doing anything. The "hasPermission" function is never called... I also added a "" around some code in my jsp and it did not call anything from my projectPermissionEvaluator but hid the things inside the tag. I must be missing something in the config. I read somewhere about enabling debug in order to make this work. Does it ring a bell to you? Or, maybe I don't have the right configuration in my pom.xml?thx – Coralie Nov 23 '12 at 17:04
  • to use it in your jsp you need to set an expressionHandler in your http element – rptmat57 Nov 26 '12 at 00:11
  • For everybody's information, I finally found the problem causing my @PreAuthorize annotation not to be called ! You should put the XML security tags ("...") inside the DispatcherServlet's context (...-servlet.xml) not the spring security application context! See this question : http://stackoverflow.com/questions/3087548/can-spring-security-use-preauthorize-on-spring-controllers-methods – Coralie Jan 03 '13 at 11:07
0

I am not using Spring Security, but I had to face a similar case.

I chose to implement database access validation. Each of my requests has a built in user/data confirmation. Maybe this isn't the simplest way to do it, but this way, users can only request data that they can read/write. I never had any troubles with that.

My requests are adapted for the logged in user role. For example, in my case, if a user is a student, he can only access his own folder, but if it's a teacher, he can access the folders of the students he teaches to.

Request (simplified a lot!) [with IBatis]:

SELECT * 
    FROM FOLDER F 
    <dynamic prepend="WHERE">
        <isNotEmpty property="user">
            <isEqual property="user.role" compareValue="student">
                F.ID_STUDENT = #user.idStudent:NUMERIC#
            </isEqual>
            <isEqual property="user.role" compareValue="teacher">
                F.ID_STUDENT IN 
                    (SELECT ID_STUDENT 
                        FROM CLASSES 
                    WHERE ID_TEACHER = #user.id:NUMERIC#)
            </isNotEmpty>
        </isNotEmpty>
        <isEmpty property="user">
            F.ID IS NULL
        </isEmpty>
    </dynamic>
Christian
  • 56
  • 5