/abac-spring-security

Attribute Based Access Control for Spring Security

Primary LanguageJavaApache License 2.0Apache-2.0

Attribute Based Access Control for Spring Security

alt text

What is Attribute Based Access Control (ABAC)

Here is an article to get started with Attribute Based Access Control.
and the XACML reference architecture here.

About this Project

The Attribute Based Access Control (ABAC) for Spring Security is a Policy Enforcment Point (PEP) implementation which provides both method and web expressions to secure Spring Boot applications based on attributes evaluated against a policy from a Policy Decision Point (PDP) server. The expressions are called #abac.evaluate(Category... categories) and #abac.evaluateAttributes(String... attributes) which send authorization request based on Json Profile of XACML 3.0 Specification

When using #abac.evaluateAttributes(String... attributes), the array of Strings must follow the format below:
access-subject:<attribute id>:<attribute values>
resource:<attribute id>:<attribute values>
action:<attribute id>:<attribute values>
environment:<attribute id>:<attribute values>

When using #abac.evaluate(Category... categories) where the arguments is an array of Category objects, the following expressions may be used as arguments:
#abac.accessSubjectAttribute(<attribute id>, {<list of values>})
#abac.resourceAttribute(<attribute id>, {<list of values>})
#abac.actionAttribute(<attribute id>, {<list of values>})
#abac.environmentAttribute(<attribute id>, {<list of values>})

How to use

  1. Build and publish this project to maven local: $ ./gradlew clean build publishToMavenLocal
  2. Add the two published artifacts from maven local together with the Spring Security to your Spring Boot project dependency. Example for gradle project:
    compile('org.springframework.boot:spring-boot-starter-security')
    compile('com.github.joffryferrater:abac-pep-spring-security:0.5.1')
    compile('com.github.joffryferrater:xacml-resource-models:0.5.1')
  3. Add the PDP server information in the application.properties file using the properties below:
    pdp.server.authorize-endpoint=http://localhost:8083/authorize
    pdp.server.username=pdp-user
    pdp.server.password=password
    pdp.server.print-authorization-request=true
    The pdp.server.print-authorization-request property is useful for debugging purposes. It prints the authorization request on the console.
    01-01-2019 17:46:56.120 [http-nio-8888-exec-6] INFO  com.github.joffryferrater.pep.client.PdpClient.printAuthorizationRequest - Authorization Request --> {"Request":{"Resource":[{"Attribute":[{"AttributeId":"Attributes.resource.endpoint","Value":["helloWorld/someId"]}]}]}}
  4. Include org.github.joffryferrater.pep in the scanBasePackages of your Spring Boot app. See example below:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(
	scanBasePackages={
		"com.github.joffryferrater.sampleappwithxacmlpepspringsecurity",
		"com.github.joffryferrater.pep" //Scans the abac-spring-security configurations
	})
public class SampleAppWithXacmlPepSpringSecurityApplication {

	public static void main(String[] args) {
		SpringApplication.run(SampleAppWithXacmlPepSpringSecurityApplication.class, args);
	}
}


5. Create a global method security and web security configurations.

An example of using Method Security expression:
import com.github.joffryferrater.pep.security.AbacMethodSecurityExpressionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return new AbacMethodSecurityExpressionHandler();
    }
}

Here we created a GlobalMethodSecurityConfiguration where we use AbacMethodSecurityExpressionHandler() which is provided by this project in order to use the expression #abac.evaluate and #abac.evaluateAttributes in @PreAuthorize annotation.

Annotate the resource to be protected by @PreAuthorize(#abac.evaluate({<array of attributes})) or @PreAuthorize(#abac.evaluateAttributes(<string formatted attributes separated by :>)). Example below:

import java.security.Principal;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SampleResource {
    private static final String HELLOWORLD_ACCESS = "#abac.evaluate("
        + "{#abac.resourceAttribute('Attributes.resource.endpoint', {'helloWorld'}), "
        + "#abac.accessSubjectAttribute('urn:oasis:names:tc:xacml:1.0:subject:subject-id', {#principal.name})})";
    private static final String HELLOWORLD_ID_ACCESS = "#abac.evaluateAttributes({'resource:Attributes.resource.endpoint:helloWorld/'+#id})";
    
    @GetMapping("/helloWorld")
    @PreAuthorize(HELLOWORLD_ACCESS)
    public String printHelloWorld(Principal principal) {
        return "hello world";
    }

    @GetMapping("/helloWorld/{id}")
    @PreAuthorize(HELLOWORLD_ID_ACCESS)
    public String getHelloWorldId(@PathVariable String id) {
        return "hello world id is: " + id;
    }
    
    /**
     *
     * Secured by Web Security expression, see @link{#WebSecurityConfig}
     */
    @GetMapping("/securedPath")
    public String getSecuredPath() {
        return "This is the /securedPath ";
    }
}

In the example above, the /helloWorld resource is protected with @PreAuthorize annotation with the abac expression. The #abac.evaluate send the following authorization request to a PDP server.

{
	"Request": {
		"AccessSubject": [{
			"Attribute": [{
				"AttributeId": "urn:oasis:names:tc:xacml:1.0:subject:subject-id",
				"Value": ["Alice"]
			}]
		}],
		"Resource": [{
			"Attribute": [{
				"AttributeId": "Attributes.resource.endpoint",
				"Value": ["helloWorld"]
			}]
		}]
	}
}

where the value Alice is the current user name and the value helloWorld is the protected resource.

An example of using Web Security expression:
import com.github.joffryferrater.pep.security.AbacWebSecurityExpressionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private static final String SECURED_PATH_ACCESS = "#abac.evaluateAttributes('resource:Attributes.resource.endpoint:securedPath', 'action:Attributes.action-id:read')";
    
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .formLogin()
            .and()
            .authorizeRequests()
            .expressionHandler(webSecurityExpressionHandler())
            .antMatchers(HttpMethod.GET, "/securedPath").access(SECURED_PATH_ACCESS)
            .anyRequest().authenticated();

    }

    private AbacWebSecurityExpressionHandler webSecurityExpressionHandler() {
        return this.applicationContext.getBean(AbacWebSecurityExpressionHandler.class);
    }

}

Here we created a Web Security Configuration and we set the expression handler as AbacWebSecurityExpressionHandler which is also provided by this project in order to use #abac.evaluate and #abac.evaluateAttributes expressions in access() as access expressions for securing the resource /securedPath.