Working with Java Types

Oso’s Java authorization library lets you write policy rules over Java objects directly. This document explains how different types of Java objects can be used in Oso policies.

Note

More detailed examples of working with application classes can be found in our Guides.

Class Instances

You may pass an instance of any Java class into Oso and access its methods and fields from your policy (see Application Types).

Java instances can be constructed from within an Oso policy using the new operator:

new User("alice@example.com")

To construct instances of a Java class, the class must be registered using the registerClass() method:

oso.registerClass(User.class)

If you want to refer to the class using another name from within a policy, you may supply an alias:

oso.registerClass(Person.class, "User")

At instantiation time, Oso will search the list returned by Class.getConstructors() for a constructor that is applicable to the supplied positional constructor arguments. For example, given the Polar expression new User("alice@example.com"), Oso will search for a Constructor with one parameter compatible with String.class, e.g.:

public User(String username) { ... }

Applicability is determined using Class.isAssignableFrom(Class<?> cls), which allows arguments that are instances of subclasses or implementations of interfaces to properly match the constructor’s parameter types.

Numbers and Booleans

Polar supports integer and floating point real numbers, as well as booleans (see Primitive Types).

Note

Java primitives may be passed into Oso, but numbers and booleans created in an Oso policy will be converted to autoboxed Integer, Float, and Boolean types respectively.

This means that methods called from Oso must have autoboxed argument types. E.g.:

class Foo {
    public static unboxed(int a, int b) {
        // ...
    }
    public static boxed(Integer a, Integer b) {
        // ...
    }
}

The boxed() method may be called from a policy, but attempting to call unboxed() will fail.

Strings

Java Strings are mapped to Polar strings. Java’s String methods may be accessed from policies:

allow(actor, _action, _resource) if actor.username.endsWith("example.com");
public class User {
    public String username;

    public User(String username) {
        this.username = username;
    }

    public static void main(String[] args) {
        User user = new User("alice@example.com");
        assert oso.isAllowed(user, "foo", "bar");
    }
}

Lists and Arrays

Java Arrays and objects that implement the List interface are mapped to Polar lists. Java’s List methods may be accessed from policies:

allow(actor, _action, _resource) if actor.groups.contains("HR");
public class User {
    public List<String> groups;

    public User(List<String> groups) {
        this.groups = groups;
    }

    public static void main(String[] args) {
        User user = new User(List.of("HR", "payroll"));
        assert oso.isAllowed(user, "foo", "bar");
    }
}

Note that the isAllowed() call would also succeed if groups were an Array.

Warning

Polar does not support methods that mutate lists in place. E.g., add() will have no effect on a list in Polar.

Likewise, lists constructed in Polar may be passed into Java methods:

allow(actor, _action, _resource) if actor.has_groups(["HR", "payroll"]);
public class User {
    ...

    public boolean hasGroups(List<String> groups) {
        for(String g : groups) {
            if (!this.groups.contains(g))
                return false;
        }
        return true;
    }

    public static void main(String[] args) {
        User user = new User(List.of("HR", "payroll"));
        assert oso.isAllowed(user, "foo", "bar");
    }
}

Java methods like List.get may be used for random access to list elements, but there is currently no Polar syntax for that is equivalent to the Java expression user.groups[1]. To access the elements of a list without using a method, you may iterate over it with the in operator or destructure it with pattern matching.

Maps

Java objects that implement the Map interface are mapped to Polar dictionaries:

allow(actor, _action, _resource) if actor.roles.project1 = "admin";
public class User {
    public Map<String, String> roles;

    public User(Map<String, String> roles) {
        this.roles = roles;
    }

    public static void main(String[] args) {
        User user = new User(Map.of("project1", "admin"));
        assert oso.isAllowed(user, "foo", "bar");
    }
}

Likewise, dictionaries constructed in Polar may be passed into Java methods.

Enumerations

You may iterate over a Java Enumeration (or anything that can be converted to one, such as a Collection or Iterable) using Polar’s in operator:

allow(actor, _action, _resource) if "payroll" in actor.getGroups();
public class User {
    public List<String> getGroups() {
        return List.of("HR", "payroll");
    }

    public static void main(String[] args) {
        User user = new User(Map.of("project1", "admin"));
        assert oso.isAllowed(user, "foo", "bar");
    }
}

null

The Java null reference is registered as the Polar constant nil. If a Java method can return null, you may want to compare the result to nil:

allow(actor, _action, _resource) if actor.getOptional() != nil;
public class User {
    ...

    public Thing getOptional() {
        if someCondition() {
            return new Thing();
        } else {
            return null;
        }
    }
}

Java → Polar Types Summary

Java type Polar type
int/Integer Integer
float/Float Float
double/Double Float
boolean/Boolean Boolean
List List
Array List
Map Dictionary
String String

Connect with us on Slack

If you have any questions, or just want to talk something through, jump into Slack. An Oso engineer or one of the thousands of developers in the growing community will be happy to help.