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_tokenscopes
(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 checkvalue
(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 pathclaim_name
(string): The name of the claim to checksegment_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
)
}