3

I'm developing an Application using Spring. In Access Control Access section I want to use Spring Security Acl (I'm new at Acl). I want to implement ACL in my application base on 2 points:

  1. Application should has five permissions, read, create, modify, delete and Administrator.
  2. Permissions be hierarchy, When a user has create permission, it should be able to read, Or when it has modify permission, it should be able to read, create and modify and etc.

Is it possible? How?

update:
My application is base on Spring MVC RESTFUL. When a user wants to modify own information, he send some json data with Ajax. An example of json data is as follow:

{
  "id": 1,//user Id
  "name": "my name",
  "password": "my password",
  "email": "email@email.com",
   ...
}

Now, a malicious user can login to own account. This user can modify its data like all other users. Before he send data, change his id, and modify another account user information. I want to use ACL to prevent this Subversive work. And user can get some access to other that others can modify his info.

Morteza Malvandi
  • 1,656
  • 7
  • 30
  • 73
  • You don't need to create separate anything. Role management is provided by Spring-security. You can simple create roles `ROLE_READ, ROLE_CREATE`, etc and then use annotations `@Secured("condition")` or `@PreAuthorize("condition")`. Also, what have you already tried? – We are Borg Apr 18 '16 at 14:15
  • I want to secure methods along with **objects** – Morteza Malvandi Apr 18 '16 at 14:18
  • What do you mean by secure objects? Methods call getters for objects...if a method is not allowed, then objects in turn are also not allowed. Also, I asked what have you already tried? – We are Borg Apr 18 '16 at 14:19
  • An example: Users wants to edit own info, it allow to call `update` method, but it only allow to call `update` method, if it send his info to mehod. – Morteza Malvandi Apr 18 '16 at 14:22
  • What do you mean by that? If the user calls the update method and has no role to perform the update, the method will simply be not executed. What does the information has to do with it? And you have still avoided the question, what have you tried. I am unfortunately forced to downvote given your lack of an example and the actual code. – We are Borg Apr 18 '16 at 14:29
  • I updated my question – Morteza Malvandi Apr 18 '16 at 14:37
  • If the user is transmitting sensitive information over net, then use https. Also, can you explain me how the user can change other users information if he has access to only one account. If he can, then your security infrastructure is broken, as you must add checks for this very reason. With spring-security you can simply retrieve the logged-in user, who has an active session and only modify that user. What is this got to do with roles is what I don't understand. Adding a JSON is not describing your project or your current security infrastructure, please keep that in mind. – We are Borg Apr 18 '16 at 14:43
  • Thank you for taking your time to mine – Morteza Malvandi Apr 18 '16 at 14:46
  • Good luck. Read docs first is what I would recommend. :-) – We are Borg Apr 18 '16 at 14:47

2 Answers2

9

You can implement a simple solution with spring security. The idea is to create a class that implements org.springframework.security.access.PermissionEvaluator and override the method hasPermission. Look the next example:

@Component("permissionEvaluator")
public class PermissionEvaluator implements org.springframework.security.access.PermissionEvaluator {

    /**
     * @param authentication     represents the user in question. Should not be null.
     * @param targetDomainObject the domain object for which permissions should be
     *                           checked. May be null in which case implementations should return false, as the null
     *                           condition can be checked explicitly in the expression.
     * @param permission         a representation of the permission object as supplied by the
     *                           expression system. Not null.
     * @return true if the permission is granted, false otherwise
     */
    @Override
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
        if (authentication != null && permission instanceof String) {
            User loggedUser = (User) authentication.getPrincipal();
            String permissionToCheck = (String) permission;
            // in this part of the code you need to check if the loggedUser has the "permission" over the
            // targetDomainObject. In this implementation the "permission" is a string, for example "read", or "update"
            // The targetDomainObject is an actual object, for example a object of UserProfile class (a class that
            // has the profile information for a User)

            // You can implement the permission to check over the targetDomainObject in the way that suits you best
            // A naive approach:
            if (targetDomainObject.getClass().getSimpleName().compareTo("UserProfile") == 0) {
                if ((UserProfile) targetDomainObject.getId() == loggedUser.getId())
                    return true;
            }
            // A more robust approach: you can have a table in your database holding permissions to each user over
            // certain targetDomainObjects
            List<Permission> userPermissions = permissionRepository.findByUserAndObject(loggedUser,
                targetDomainObject.getClass().getSimpleName());
            // now check if in userPermissions list we have the "permission" permission.

            // ETC...
        }
        //access denied
        return false;
    }

}

Now, with this implementation you can use in for example your service layer the @PreAuthorize annotation like this:

@PreAuthorize("hasPermission(#profile, 'update')")
public void updateUserProfileInASecureWay(UserProfile profile) {
    //code to update user profile
}

The "hasPermission" inside the @PreAuthorize annotation receives the targetDomainObject #profile from the params of the updateUserProfileInASecureWay method and also we pass the required permission (in this case 'update').

This solution avoids all the complexity of the ACL by implementing a "small" ACL. Maybe it can work for you.

dacuna
  • 1,096
  • 1
  • 11
  • 22
  • I was looking for a similar solution using `PermissionEvaluator`. But my use case is more than just checking if a user has permission. Basically, the user is authenticated externally and I get a token that also contains list of client_ids that the user has access. For each subsequent request, the list of client_ids will be used to query the database. So my repositories will contain something like `findOrder(listOfClientIds, orderId)` or `findOrderHistory(listOfClientIds, orderId)`. – alltej Jun 09 '20 at 20:43
0

You can use the role hierarchy combination as used in the following examples.Spring acl has base permissions,If you want to implement Custom permissions you need to create a custom Permission class extending Base Permissions class in the api. we can define roles for each kind of permission and define role hierarchy as below.

<bean id="expressionHandler"
    class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
    <property name="permissionEvaluator" ref="permissionEvaluator" />
    <property name="roleHierarchy" ref="roleHierarchy" />
</bean>

<!-- A customized PermissionEvaluator that evaluates permissions via the ACL module -->
<bean class="org.springframework.security.acls.AclPermissionEvaluator" id="permissionEvaluator">
    <!-- Reference to the ACL service which performs JDBC calls to an ACL database -->
    <constructor-arg ref="aclService"/>
     <property name="permissionFactory" ref="customPermissionFactory" /> 
</bean>
<bean id="customPermissionFactory" class="org.krams.tutorial.security.CustomPermissionFactory"></bean>

<!-- A customized ACL service which provides default JDBC implementation -->
<bean class="org.springframework.security.acls.jdbc.JdbcMutableAclService" id="aclService">
    <constructor-arg ref="dataSource"/>
    <constructor-arg ref="lookupStrategy"/>
    <constructor-arg ref="aclCache"/>
</bean>

<!-- A lookup strategy for optimizing database queries -->
<bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
    <constructor-arg ref="dataSource"/>
    <constructor-arg ref="aclCache"/>
    <constructor-arg ref="aclAuthorizationStrategy"/>
    <constructor-arg ref="auditLogger"/>
    <property name="permissionFactory" ref="customPermissionFactory"/>
</bean>

<!-- A MySQL datasource with pooling capabalities for the ACL module -->
<!-- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
     destroy-method="close"
     p:driverClass="com.mysql.jdbc.Driver"
     p:jdbcUrl="jdbc:mysql://localhost/acl"
     p:user="root"
     p:password=""
     p:acquireIncrement="5"
     p:idleConnectionTestPeriod="60"
     p:maxPoolSize="100"
     p:maxStatements="50"
     p:minPoolSize="10" /> -->




<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.h2.Driver" />
    <property name="url" value="jdbc:h2:tcp://localhost/~/springsecurity" />

    <!--<property name="url" value="jdbc:h2:tcp://ggk-wrl-win1/~/dev" /> -->
    <property name="username" value="sa" />
    <property name="password" value="" />
</bean>

<!-- An ACL cache to minimize calls to the ACL database -->   
<bean id="aclCache" class="org.springframework.security.acls.domain.EhCacheBasedAclCache">
    <constructor-arg>
        <bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
            <property name="cacheManager">
                <bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
            </property>
            <property name="cacheName" value="aclCache"/>
        </bean>
    </constructor-arg>
</bean>

<!-- An ACL authorization strategy to determine whether a principal is permitted to call administrative methods -->
<bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
    <constructor-arg>
        <list>
            <bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
                <constructor-arg value="ROLE_USER"/>
            </bean>
            <bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
                <constructor-arg value="ROLE_USER"/>
            </bean>
            <bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
                <constructor-arg value="ROLE_USER"/>
            </bean>
        </list>
    </constructor-arg>
</bean>

<!-- An audit logger used to log audit events -->
<bean id="auditLogger" class="org.springframework.security.acls.domain.ConsoleAuditLogger"/>

<!-- Defines the role order -->
<!-- http://static.springsource.org/spring-security/site/docs/3.0.x/apidocs/org/springframework/security/access/hierarchicalroles/RoleHierarchyImpl.html -->
<bean id="roleHierarchy"  class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
    <property name="hierarchy">
        <value>
            ROLE_ADMIN > ROLE_USER
            ROLE_USER > ROLE_VISITOR
        </value>
    </property>
</bean>
anu
  • 31
  • 6