Quarkus Zanzibar

Overview

The Quarkus Zanzibar extension provides Zanzibar style Fine Grain Authorization (FGA) capabilities for Quarkus' HTTP subsystems. An authorization filter and dedicated annotations are provided to provide easy integration of Zanzibar style FGA into applications.

The extension only provides the framework and relies on dedicated connectors to communicate with specific Zanzibar style servers.

Supported APIs and platforms:

Installation

Adding the quarkus-zanzibar extension to your project only provides access to the authorization and the annotations needed to configure authorization on your resource classes and methods.

To communicate with your selected server API you will need to add the Zanzibar connector for your selected server.

Basic Usage

Authorization Model

Given a model object Thing, and the following simple authorization model that give every owner both read and write permissions.

## OpenFGA Syntax
model
    schema 1.1
type user
type thing
    relations
        define owner: [user]
        ## permissions
        define read as owner
        define write as owner

Creating and Accessing Objects

With a suitable authorization model initialized in your selected server, Zanzibar uses JAX-RS annotations to define the exact authorization details for resource methods.

Using the authorization model from above. The following resource methods allow any user to create new things but only users with read access to fetch them; in this example this will be the owner of the Thing.

import java.security.Principal;
import java.util.List;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;

import io.quarkiverse.zanzibar.Relationship;
import io.quarkiverse.zanzibar.RelationshipManager;
import io.quarkiverse.zanzibar.annotations.FGAPathObject;
import io.quarkiverse.zanzibar.annotations.FGARelation;
import io.quarkiverse.zanzibar.annotations.FGAUserType;
import io.smallrye.mutiny.Uni;

@Path("/things")
class ThingResource {

    @Inject
    ThingRepository thingRepository;
    @Inject (1)
    RelationshipManager relationshipManager;
    @Inject (2)
    Principal principal;

    @POST
    @FGARelation(FGARelation.ANY) (3)
    public Uni<Thing> createThing(@QueryParam("name") String name) {
        return thingRepository.createThing(name)
                .flatMap((thing) -> {
                    (4)
                    var relationship = new Relationship("thing", thing.getId(), "owner", "user:" + principal.getName());
                    (5)
                    return relationshipManager.add(List.of(relationship))
                            .map((unused) -> thing);
                });
    }

    @GET
    @Path("{id}")
    @FGAPathObject(param = "id", type = "thing") (6)
    @FGARelation("owner") (7)
    @FGAUserType("user") (8)
    public Uni<Thing> getThing(String id) {
        return thingRepository.fetchThing(id);
    }
}
1 Inject RelationshipManager to allow adding & removing relationships.
2 Inject Principal for access to the current user id.
3 FGARelation.ANY allows access for any user, disregarding the authorization model.
4 Create a Relationship that defines the current user as the owner of the newly created Thing.
5 Add the new thing’s owner relationship to the authorization model.
6 Dynamically determine the id of the Thing to authorize from the id path parameter of the current HTTP request. Additionally, set the object type to thing as defined by the authorization model.
7 Check if the current user has the owner relation for the thing dynamically determined by the @FGAPathObject.
8 For implementations that require it, provide the user object type.

More Details

Zanzibar provides a number of annotations to determine the current object id and object type. The annotations are detailed here.

Extension Configuration Reference

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

Type

Default

Whether the filter is enabled.

When enabled all endpoints must have a resolvable FGADynamicObject and FGARelation otherwise a FORBIDDEN will be returns to clients.

Environment variable: QUARKUS_ZANZIBAR_FILTER_ENABLED

boolean

true

Should access to resource methods without FGA annotations be denied.

Environment variable: QUARKUS_ZANZIBAR_FILTER_DENY_UNANNOTATED_RESOURCE_METHODS

boolean

true

User-id used for authorization when the request is unauthenticated.

Environment variable: QUARKUS_ZANZIBAR_FILTER_UNAUTHENTICATED_USER

string

Maximum time an authorization check is allowed to take.

Environment variable: QUARKUS_ZANZIBAR_FILTER_TIMEOUT

Duration

5S

About the Duration format

To write duration values, use the standard java.time.Duration format. See the Duration#parse() Java API documentation for more information.

You can also use a simplified format, starting with a number:

  • If the value is only a number, it represents time in seconds.

  • If the value is a number followed by ms, it represents time in milliseconds.

In other cases, the simplified format is translated to the java.time.Duration format for parsing:

  • If the value is a number followed by h, m, or s, it is prefixed with PT.

  • If the value is a number followed by d, it is prefixed with P.