Architectural approaches to authorization in server applications: Activity-Based Access Control Framework

Background

Approaches to Creating a Framework

Creating a Framework for Authorization

  1. Easy to use — to save users from reading a multi-page manual, settings, etc.
  2. Flexible — so that it can be adapted to different goals and applications
  3. Reliably capable of handling errors

Declarative style

  1. Java annotations are a tool of both the Java language itself and the JVM in particular, which allows you to process annotations both at runtime and at compile time.
  2. Annotations are easy to use because it is easy to see which resource is limited and why.
  3. Annotations are flexible enough in the configuration because they are part of the Java language.

Authorization Implementation Approaches

  • User roles (very convenient in applications with a small granularity of roles).
  • User permissions (convenient in applications with a more granular distribution of rights, i.e. when the usual set of roles is insufficient).
  • The user’s actions — also convenient in cases of granular distribution of rights, i.e. instead of declaratively indicating what rights are needed to access the resource, the action that the user performs with the resource (for example, create, modify, delete) is indicated. The number and type of actions are only limited by your requirements and imagination. Action-based authorization is convenient because there is no need to change access rights later — the rights are declaratively described by the action, and the action with the resource usually is not changed. The rights that are necessary to perform the action can be changed, however.

Configuration and Error Handling

Level of Abstraction

  • Resources which require authorization are classified. This can be an organization, a project, a subproject — any entity.
  • The developer creates actions for each type of resource.
  • A custom annotation is created for each type of resource; the annotation indicates the action(s) performed on the resource.
  • The application developer creates an action handler (validator) for each type of resource (or for all of them at once).
  • We bind user roles and/or user permissions to actions. This remains a task for the application developer, and it can be done in a variety of ways. This is what provides sufficient flexibility for our purposes.

Easy-ABAC Framework

<dependency>
<groupId>com.exadel.security</groupId>
<artifactId>easy-abac</artifactId>
<version>1.1</version>
</dependency>
@SpringBootApplication
@Import(AbacConfiguration.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

1. Description of required actions

  • Admin
  • Developer
  • Project owner
  • View
  • Edit
  • Close
  • Delete
import com.exadel.easyabac.model.core.Action;public enum ProjectAction implements Action {
VIEW,
UPDATE,
CLOSE,
DELETE
}

2. Creating Annotations for Managing Access Control

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface ProjectId {
}
import com.exadel.easyabac.model.annotation.Access;
import com.exadel.easyabac.model.validation.EntityAccessValidator;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Access(identifier = ProjectId.class)
public @interface ProjectAccess {
ProjectAction[] actions(); Class<? extends EntityAccessValidator> validator();
}
Error:(13, 9) java: value() method is missing for @com.example.abac.model.ProjectAccess
Error:(13, 9) java: validator() method is missing for @com.example.abac.model.ProjectAccess
@Target({ElementType.METHOD, ElementType.TYPE})

3. Creating a Validator for Checking Access Rights

import com.exadel.easyabac.model.validation.EntityAccessValidator;
import com.exadel.easyabac.model.validation.ExecutionContext;
import com.example.abac.model.ProjectAction;
import org.springframework.stereotype.Component;
@Component
public class ProjectValidator implements EntityAccessValidator<ProjectAction> {
@Override
public void validate(ExecutionContext<ProjectAction> context) {
// here get current user actions
// and compare them with context.getRequiredActions()
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Access(identifier = ProjectId.class)
public @interface ProjectAccess {
ProjectAction[] value(); Class<? extends EntityAccessValidator> validator() default ProjectValidator.class;
}
@ProjectAccess(value = ProjectAction.VIEW, validator = ProjectValidator.class)

4. Access Restriction

import com.exadel.easyabac.model.annotation.ProtectedResource;
import com.example.abac.Project;
import com.example.abac.model.ProjectAccess;
import com.example.abac.model.ProjectAction;
import com.example.abac.model.ProjectId;
import org.springframework.web.bind.annotation.*;
@RestController
@ProtectedResource
@RequestMapping("/project/{projectId}")
public class ProjectController {
@GetMapping
@ProjectAccess(ProjectAction.VIEW)
public Project getProject(@ProjectId @PathVariable("projectId") Long projectId) {
Project project = ...; // get project here
return project;
}
@PostMapping
@ProjectAccess({ProjectAction.VIEW, ProjectAction.UPDATE})
public Project updateProject(@ProjectId @PathVariable("projectId") Long projectId) {
Project project = ...; // update project here
return project;
}
@PostMapping("/close")
@ProjectAccess(ProjectAction.CLOSE)
public Project updateProject(@ProjectId @PathVariable("projectId") Long projectId) {
Project project = ...; // close project here
return project;
}
@DeleteMapping
@ProjectAccess(ProjectAction.DELETE)
public Project updateProject(@ProjectId @PathVariable("projectId") Long projectId) {
Project project = ...; // delete project here
return project;
}
}

5. Validator Implementation

public void validate(ExecutionContext<Action> context);

Comparative Analysis

Further Framework Development

Areas of Use

  1. Java applications with granular authorizations
  2. Multi-tenant applications
  3. Applications with dynamic access rights
  4. Spring-based applications

Conclusion

  1. Declarative authorization style
  2. Handling configuration errors at compile time
  3. Simple and straightforward configuration
  4. Flexibility

Conclusion

  1. Declarative authorization style
  2. Handling configuration errors at compile time
  3. Simple and straightforward configuration
  4. Flexibility

--

--

--

Software Engineer

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Unity Development — Load Screen

Building a CIS Hardened AMI on AWS for FREE

Checklist Before You Hire a Mobile App Developer for Your Dream Project | Zealous System

Code review of the system with git

Setting up TrueNAS with Crashplan Pro Backup

Sprint #3 Update

Starting My Software Engineering Journey

Confluent KSQL

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Gleb Bondarchuk

Gleb Bondarchuk

Software Engineer

More from Medium

Bug tracking: fixing hibernate heap problems

Spring cloud: introduction to service discovery using Netflix eureka

Add Auth0 authentication to a Java application using Datawiza in 5 mins

Newbie’s intro to kafka