Skip to main content

Java vs Rego for Policy as Code

info

Beta Content If you've found a problem with this documentation or would like to suggest improvements, please join us in the Styra Community Slack!

Java is a general purpose programming language that is commonly used for building web services, APIs and enterprise business applications. Such applications often need to impose strict policies on the operations they perform and the data they process. Java is also commonly used in domains that are highly regulated, such as finance, healthcare, and government where enforcing policies is even more critical.

These domains often come with complex policies to encode and enforce. When doing so, Java code can become verbose and difficult to maintain. This guide shows some examples of policy code in Java and the corresponding Rego code for comparison.

tip

Java Developer? If you want to replace policy functionality in Java with OPA, check out the OPA SDKs to get started.

Check If a User Is an Admin

Admin status is often encoded in a JWT (JSON Web Token). This example shows how to extract information from a JWT and make an authorization decision based on it. The example uses an insecure secret to verify the JWT for brevity.

Java

package com.example.app;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
import java.util.List;
import java.util.Map;

public class Authorization {
public static boolean allow(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey("cGEkJHcwcmQ=") // b64 "pa$$w0rd"
.build()
.parseSignedClaims(token)
.getPayload();
} catch (SignatureException
| ExpiredJwtException
| UnsupportedJwtException
| MalformedJwtException e) {
return false;
} catch (Exception e) {
return false;
}

Object rolesObj = claims.get("roles");
if (rolesObj instanceof List) {
List<String> roles = (List<String>) rolesObj;
return roles.contains("admin");
}

return false;
}
}

Rego

package example

import rego.v1

claims := io.jwt.decode(input.token)[1] if {
io.jwt.verify_hs256(input.token, "pa$$w0rd")
}

default allow := false

allow if "admin" in claims.roles

Note: Verifying JWTs with a hard-coded secret is insecure and is used here for example only, please refer to the OPA documentation on Token Verification for more information on how to securely verify JWTs.

Grant Access Based on Inherited Permissions

Staff roles are used to define permissions that a given user has within an organization. In this example, we show how permissions can be inherited from other roles based on the organization's staff hierarchy. Rego's graph.reachable built-in function not only keeps the policy concise but is also safe from infinite recursion caused by cyclic dependencies in the staff hierarchy.

Java

package com.example.app;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class AuthorizationUtil {
private static Map<String, List<String>> manages =
Map.of("manager", List.of("supervisor", "security"),
"supervisor", List.of("assistant"), "security",
List.of(), "assistant", List.of());

private static Map<String, List<String>>
datasetPermissions = Map.of("manager",
List.of("salaries"), "supervisor",
List.of("rotas"), "security", List.of("cctv"),
"assistant", List.of("product_prices"));

private static List<String> reachableRolesForRole(String role) {
List<String> roles = new ArrayList<>();
roles.add(role);

for (String r : manages.get(role)) {
roles.add(r);
roles.addAll(reachableRolesForRole(r));
}

return roles;
}

public static boolean allow(String role, String dataset) {
List<String> inheritableRoles =
reachableRolesForRole(role);

for (String r : inheritableRoles) {
if (datasetPermissions.containsKey(r)) {
if (datasetPermissions.get(r).contains(dataset)) {
return true;
}
}
}

return false;
}
}

Rego

package example

import rego.v1

manages := {
"manager": {"supervisor", "security"},
"supervisor": {"assistant"},
"security": set(),
"assistant": set(),
}

dataset_permissions := {
"manager": {"salaries"},
"supervisor": {"rotas"},
"security": {"cctv"},
"assistant": {"product_prices"},
}

default allow := false

allow if {
some inherited_role in graph.reachable(
manages, {input.role}
)
input.dataset in dataset_permissions[inherited_role]
}

Validate User-Generated Content

Policy is often used to guide users when they make mistakes. Validation of user-generated resources can be complicated and often needs to be implemented in many applications. This example compares code for validating a user-submitted blog post and shows how Rego rules can be defined incrementally.

Java

package com.example.app;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ValidationUtil {
private static List<String> allowedRoles = List.of("admin", "member", "viewer");

public static List<String> validations(Map<String, Object> input) {
List<String> messages = new ArrayList<>();

// ensure title and content are set
String[] fields = {"title", "content"};
for (String field : fields) {
if (!input.containsKey(field) || ((String) input.get(field)).isEmpty()) {
messages.add("Value missing for field '" + field + "'");
}
}

// ensure title starts with a capital letter
String title = input.containsKey("title") ? (String) input.get("title") : "";
if (!title.isEmpty() && !Character.isUpperCase(title.charAt(0))) {
messages.add("Title must start with a capital");
}

// ensure user identifier is set
if (!input.containsKey("user") || !(input.get("user") instanceof Map)) {
messages.add("User email or id must be set");
} else {
Map<String, Object> user = (Map<String, Object>) input.get("user");
boolean hasEmailOrId = user.containsKey("email") || user.containsKey("id");
if (!hasEmailOrId) {
messages.add("User email or id must be set");
}
// ensure example.com emails are not allowed
if (user.containsKey("email") && ((String) user.get("email")).endsWith("@example.com")) {
messages.add("example.com emails not allowed");
}
}

return messages;
}
}

Rego

package example

import rego.v1

# ensure title and content are set
validations contains message if {
some field in {"title", "content"}
object.get(input, field, "") == ""
message := sprintf("Value missing for field '%s'", [field])
}

# ensure title starts with a capital letter
validations contains "Title must start with a capital" if {
not regex.match(`^[A-Z]`, input.title)
}

# ensure user identifier is set
validations contains "User email or id must be set" if {
not input.user.email
not input.user.id
}

# ensure example.com emails are not allowed
validations contains "example.com emails not allowed" if {
endswith(input.user.email, "@example.com")
}

If you've found a problem with this documentation or would like to suggest improvements, please join us in the Styra Community Slack!