Creating Policy

Overview

This reference guide covers the available policy functions for implementing authorization policies with Maersk Authorization Service’s OPA solution. It includes base policy examples and detailed function documentation.

Base Policy

The code snippet below shows a sample base level policy, which is the minimum policy that would be expected for any spring microservice.

The policy will deny all request that do not match the specified allow criteria. It will only allow requests that contain a valid ForgeRock or Azure id token or access token, or those to spring actuator endpoints.

Tip

You can use this workflow to generate a base rego policy for your service based on your service’s OAS.

Base Policy Example

package envoy.authz
import rego.v1
import input.attributes.request.http as http_request
import data.com.maersk.global.authz as token

default allow := false

allow if {
    glob.match("/actuator/*", null, http_request.path)
}

allow if {
    token.allow
}

Global Functions

The following functions are available for use in your OPA policies:

Authorization Functions

role(role)

Check the role claim of the token.

Parameters:

  • role (string): The role to check for

Example:

role("BasicCustomer")

any_role(allowed_roles)

Check the role claim for multiple allowed roles.

Parameters:

  • allowed_roles (array): Array of roles to check against

Example:

any_role(["BasicCustomer", "MaerskInternal"])

scope(scope)

Check the scope claim of the access token.

Parameters:

  • scope (string): The scope to check for

Example:

scope("profile")

any_scope(allowed_scopes)

Check the scope claim for multiple allowed scopes.

Parameters:

  • allowed_scopes (array): Array of scopes to check against

Example:

any_scope(["profile", "email"])

Hybrid Functions

any_role_or_scope(roles, scopes)

Checks the roles if the token is id_token and scopes if the token is access_token.

Parameters:

  • roles (array): Array of roles to check for id_token
  • scopes (array): Array of scopes to check for access_token

Example:

any_role_or_scope(["BasicCustomer", "MaerskInternal"], ["customer.read", "user.read"])

check_access(access_list)

Checks the given set of roles/scopes based on the type of token (id_token/access_token).

Parameters:

  • access_list (array): Array of roles or scopes to check

Example:

check_access(["BasicCustomer", "Booking.read"])

Pattern Matching Functions

any_scope_pattern(scope_pattern)

Checks the scope on the access_token against a given regex pattern.

Parameters:

  • scope_pattern (array): Array of regex patterns to match against scopes

Example:

any_scope_pattern(["ec.*.pr.Booking.read", "ten.ocean.cc.*.pr.Booking.read"])

Customer and Carrier Functions

customer_code(cc)

Check customer code claim of the token.

Parameters:

  • cc (string): The customer code to validate

Example:

customer_code("10000007951")

carrier(carr)

Check carrier claim of the token.

Parameters:

  • carr (string): The carrier code to validate

Example:

carrier("MAEU")

Token Validation Functions

issuer(iss)

Check issuer claim of the token.

Parameters:

  • iss (string): The expected issuer URL

Example:

issuer("https://iam-stage.maersk.com/acm/oauth2/mau")

claim(claim, value)

Check for a claim and expected value of the claim in the token.

Parameters:

  • claim (string): The claim name to check
  • value (string): The expected value of the claim

Example:

claim("sub", "tritakov.pres@maersk.com")

claim_http_path_segment(path, claim_name, segment_index)

Check the value for a claim in the token and match based on http path segment.

Parameters:

  • path (string): The HTTP request path
  • claim_name (string): The name of the claim to check
  • segment_index (number): The index of the path segment to match

Example:

claim_http_path_segment(http_request.path, "sub", 2)

Policy Implementation Examples

Basic Role-Based Access

package envoy.authz
import rego.v1
import data.com.maersk.global.authz as global

default allow := false

# Allow users with BasicCustomer role
allow if {
    global.allow
    http_request.method == "GET"
    glob.match("/user", null, http_request.path)
    role("BasicCustomer")
}

Scope-Based Access Control

package envoy.authz
import rego.v1
import data.com.maersk.global.authz as global

default allow := false

# Allow read access to bookings
allow if {
    global.allow
    glob.match("/booking", null, http_request.path)
    any_scope(["booking.read", "booking.write"])
}

# Allow pattern-based scope matching
allow if {
    global.allow
    glob.match("/booking/*", null, http_request.path)
    any_scope_pattern(["*.booking.read"])
}

Path-Based Authorization

package envoy.authz
import rego.v1
import input.attributes.request.http as http_request
import data.com.maersk.global.authz as global

default allow := false

# Allow access to user's own resources
allow if {
    global.allow
    glob.match(http_request.path, "/users/")
    claim_http_path_segment(http_request.path, "sub", 2)
}

# Allow customer-specific access
allow if {
    global.allow
    startswith(http_request.path, "/customers/")
    claim_http_path_segment(http_request.path, "customer_code", 2)
}

Best Practices

1. Always Include Global Authorization in each policy

# Always include this as a fallback
allow if {
    global.allow
    <RULE_1>
    <RULE_2>
}

2. Use Specific Function Names

Choose the most specific function for your use case:

  • Use role() for single role checks
  • Use any_role() for multiple role options
  • Use check_access() for hybrid token support

3. Implement Defense in Depth

# Multiple layers of authorization
allow if {

    # Check the validity of token
    global.allow

    # Check the request method of required
    http_request.method == "POST"

    # Check the API path of the request for path specific policy
    glob.match("/customer/*", null, http_request.path)

    # Check token issuer in case multiple issuers are allowed
    issuer("https://iam-stage.maersk.com/acm/oauth2/mau")
    
    # Check user permissions
    any_role(["BasicCustomer", "MaerskInternal"])
    
    # Check resource access
    customer_code("10000007951")
}

4. Handle Different Token Types

# Use hybrid functions for flexible token support
allow if {
    any_role_or_scope(
        ["BasicCustomer"],           # For ID tokens
        ["customer.read"]            # For access tokens
    )
}