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.
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).
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.
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.