reset password

Thoughts on CSNS2 Security

The main goal of CSNS2 is to support multiple departments. From the security perspective, this requires that access control decisions be made based on department-level roles instead of systemwide roles. For example, a CS department administrator should be able to manage all CS department resources like classes and surveys, but none of the Tech department resources. The security implementation should be clean (which means separating security code from application code), simple (by taking full advantage of what Spring Security provides), and of course, efficient.

Roles

Name Level Description
ROLE_ADMIN Systemwide A user with this role can create new departments and perform other system maintenance operations.
ROLE_USER Systemwide This role is assigned to users who already logged in.
ROLE_GUEST Systemwide This role is assigned to users who are not logged in.
DEPT_ROLE_ADMIN Department Department administrators. Actual roles names have the format of DEPT_ROLE_ADMIN_<department_symbol>, e.g. DEPT_ROLE_ADMIN_cs.
DEPT_ROLE_FACULTY Department Department full-time faculty. Actual roles names have the format of DEPT_ROLE_FACULTY_<department_symbol>.
DEPT_ROLE_INSTRUCTOR Department Department part-time instructors and TAs. Actual roles names have the format of DEPT_ROLE_INSTRUCTOR_<department_symbol>.
DEPT_ROLE_REVIEWER Department Program reviewers (e.g. ABET) of the department. Actual roles names have the format of DEPT_ROLE_REVIEWER_<department_symbol>.

Note that we no longer have ROLE_STUDENT as it would have the same privileges as ROLE_USER.

And ROLE_USER and ROLE_GUEST are no longer necessary because we can use Spring EL like anonymous and authenticated to check. In theory we don't really need the department roles either as they kind of duplicate the information stored in Department, but having them as roles makes security checks easier to implement and much more efficient.

Role Management

There's no user interface for managing systemwide roles:

  • ROLE_ADMIN is manually added in the database.
  • ROLE_USER is automatically added when a user is created or imported.
  • ROLE_GUEST is assigned automatically by the system.

Department roles are managed using the department management interface. In particular, each department keeps tracks of its own lists of administrators, faculty, instructors, and reviewers (see the Department class). When a user is added to (or removed from) a list, the system will automatically add (or remove) the corresponding role to the user. Only department administrators can access the department management interface.

User Management

The isAccountNoneExpired() method in UserDetails can be used replace the ROLE_NEWUSER mechanism used in CSNS. In particular, for any auto-created account will be set as expired, and when a user tries to log in, an AccountExpiredException will be raised. In Spring Security configuration, this exception can be mapped to a registration page where the user can enter username, password etc.

URL Access Control

Approach 1: same configuration as regular role-based url access control but use a custom role voter that recognizes department roles.

Replace the default access decision manager with a custom one as shown in the example below:

<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
    <property name="decisionVoters">
        <list>
            <bean class="org.springframework.security.access.vote.RoleVoter" />
            <bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
            <bean class="securitytest.security.vote.DepartmentRoleVoter" />
        </list>
    </property>
</bean>

<security:http auto-config="true" access-decision-manager-ref="accessDecisionManager">
    <security:intercept-url pattern="/users.html" access="ROLE_ADMIN" />
    <security:intercept-url pattern="/dept/*/users.html" access="DEPT_ROLE_ADMIN" />
</security:http>

RoleVoter and AuthenticatedVoter are the two voters used by the default access decision manager. DepartmentRoleVoter is the custom voter. When a request needs to be authorized based on URL, a FilterInvocation object will be passed to DepartmentRoleVoter. In theory, DepartmentRoleVoter can extract the department code from the URL, and make an access decision based on the department, the required role (obtained from ConfigAttribute) and the user's roles (obtained from Authentication). The problem with this approach is that the department code must always appear at a fixed location of the URL (e.g. /dept/{dept}/**); otherwise we won't be able to extract the department code.

Approach 2: secure controller methods using @PreAuthorize.

First of all, add the CGLIB2 dependency to the project:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2</version>
</dependency>

Add <global-method-security> to <servlet-name>-servlet.xml as follows:

<security:global-method-security pre-post-annotations="enabled" />

Note that to secure controllers, <global-method-security> has to be added to <servlet-name>-servlet.xml, not applicationContext.xml as these two files are not merged (and AOP has to be declared in each or something like that). The difference between pre-post-annotations and secured-annotations is that @PreAuthorize supports SpEL while @Secure can only check simple roles.

Secure the controller method as shown in the following example:

@PreAuthorize("hasRole('DEPT_ROLE_ADMIN_' + #dept)")
@RequestMapping("/dept/{dept}/admin/home.html")
public String admin( @PathVariable("dept") String dept, ModelMap models )
{
    models.addAttribute( "dept", dept );
    return "department/admin/home";
}

This approach has some obvious advantages over the previous approach: no custom access decision manager required, much easier coding (SpEL vs. Voter, and department code extraction with @PathVariable). The only downside is that the performance may not be as good.

Method Access Control

Object Access Control

This page has been viewed 4321 times.