Lets take a look at sample REST APIs that allows Students to view their grades and professors to add/update grades.
If a student wants to view his/her grade in English, your system perhaps has a REST API like:
GET /student/{student_id}/english/grade
The Professor perhaps has access to the following REST APIs to be able to view and update any one of their students’ grades:
- View a student’s grade
GET /student/{student_id}/english/grade
2. Post a student’s grade
POST /student/{student_id}/english/grade
3. Update a student’s grade
PUT /student/{student_id}/english/grade
Of course there is a portal that the Student logs in to to view their grades and the professor logs in to post and update grades.
A savvy student is able to turn on HttpWatch to view the REST APIs being called by the portal. Lets say, the student A observes that when she clicks on a link to view her English grade, a REST API call is generated that looks like the following:
GET /student/1234/english/grade
What if the student is smart enough to replay this request with a different student id, 6789?
GET /student/6789/english/grade
If there are no proper Authorization checks in the APIs, the student A with id, “1234” is now able to view student B with id “6789” grade.
Even worse if if the student was able to update their own grade. How about the student try the following API?
PUT /student/1234/english/grade with payload “A”
or update someone else’s grade to “F”
PUT /student/6789/english/grade with payload “F”
However, a professor should be able to view, post or update anyone of their students’ grades. All of the above APIs are fair if they are issued on behalf of a professor.
So, every REST API call needs to enforce proper authorization to make sure that the user on whose behalf the API call is being made has access to the Resource on which they are taking action.
Since Java 6, the following Authorization annotations were added to make it easy to decorate methods with Authorization checks:
Annotation Types Summary | |
---|---|
DeclareRoles | Used by application to declare roles. |
DenyAll | Specifies that no security roles are allowed to invoke the specified method(s) – i.e that the methods are to be excluded from execution in the J2EE container. |
PermitAll | Specifies that all security roles are allowed to invoke the specified method(s) i.e that the specified method(s) are “unchecked”. |
RolesAllowed | Specifies the list of roles permitted to access method(s) in an application. |
RunAs | Defines the identity of the application during execution in a J2EE container. |
However, these are annotations do not allow you to check if the user has access to a specific Resource ID. This is where, I find using Spring Security to be very helpful.
Spring Security provides a mechanism to do Data Level Authorizations using “hasPermission” annotation. Below is a reference from the documentation:
hasPermission(Object targetId, String targetType, Object permission) |
Returns true if the user has access to the provided target for the given permission. For example, hasPermission(1, ‘com.example.domain.Message’, ‘read’) |
This makes it a bit easy to centralize logic for authorization checks. In the case of a REST Call such as POST /student/1234/english/grade “A” would translate in to a hasPermissions check:
hasPermission(“1234”, Grade, “update”) The method has access to the Principal on whose behalf the call is being. So, you have access to the Principal and the Target, and you can verify if the Principal has access to act up on the target ID. In the case of a student, this check returns a true only if the Principal and the Target ID are the same and also the permission is “view”. In the case of a professor, the check may be something like does the Target ID belong to a student that is currently enrolled in the Professor’s class.
If you are using Spring Security or some other framework, make sure you provide an easy way to apply authorization checks to ensure that the Principal is able to act on a given Resource.