Rest – Role-based REST API

design-patternsprogramming practicesrestweb-api

I'm building a REST API for which several users with different roles will have access to the resources it contains.

To keep the scope simple let's take the "student/teacher/class" domain:

GET /students is the resource to access.

Users might have roles like Student and/or Teacher

Students will only have access to students of their classes. Teachers will have access to students of the classes they teach. Some uses may be a student AND teach other classes too. They must have access to the students of their classes AND the students of the classes they teach.

Ideally I want to implement this as two functions – one per role and then "union" if a user have multiple roles.

My question is: Which pattern should I use for implementing this?

Externally

  • Should I split up my API per role? GET /teacher/students and GET /student/students It doesn't seems right to me.
  • Keep it all I'm one resource (preferred)

Internally

How should it be implemented internally?

  • Should every method start with a BIG switch/if per role?
  • Should I implement a repository per role?
  • Is there a design pattern that will help me in achieving this?

As a side comment: I'm using ASP.NET Web API and Entity Framework 6, but it really doesn't matter for the conceptual implementation.

Best Answer

You should architect the API around resources, not around roles, e.g.:

/rest/students

should be accessible to anyone with a role that allows them to see students.

Internally, you are implementing role-based security. How you go about that depends on the details of your application, but let's say you have a role table, each person has one or more roles, and those roles determine what each person can access. You have already stated the rules for accessing students:

  • students can access students in the classes they take
  • teachers can access students in the the classes they teach

So when a person calls:

/rest/students

you call a method that accesses students, passing in the role of the person. Here is some pseudo code:

roles = person.roles; //array
students = getStudents( roles );
return students;

and in that method, you could get the students for each role with separate calls, e.g.:

factory = getFactory();
classes= [];
students = [];
for( role in roles ){
    service = factory.getService( role );
    // implementation details of how you get classes for student/teacher are hidden in the service
    classes = classes.merge( service.getClasses( person ) );
    // classes[] has class.students[]
    // loop on classes and add each student to students, or send back classes with nested students? depends on use case
  }
}

That's a very rough idea for what you could do and isn't necessarily going to fit your specific needs, but it should give you a sense of the pieces involved. If you want to return the classes with each student listed, this is a good approach. If you just want the students, you could extract them from each class and merge them into a collection of students.

No, you should not have a separate repository per role. All the role does is determine how you get the data, and maybe what you can do with the data (e.g. Teachers can enter Student grades). The data itself is the same.

As for patterns, this approach is using Factory Pattern to abstract away the service that gets data based on role. It may or may not be appropriate to have separate services by role. I like this approach because it minimizes the amount of code at each stage of the program and makes it more readable than a switch or if block.

Related Topic