Working with Rust Types
Oso’s Rust authorization library allows you to write policy rules over Rust types directly. This document explains how different Rust types can be used in Oso policies.
More detailed examples of working with application objects can be found in our Guides.
Structs + Enums
Rust structs and enums can be registered with Oso, which lets you pass them in and access their methods and fields in your policy (see Application Types).
Rust structs can also be constructed from inside an Oso policy using
the new
operator if a type constructor is provided at registration.
Numbers and Booleans
Polar supports integer and floating point real numbers, as well as booleans (see Primitive Types).
Strings
Rust Strings are mapped to Polar strings. Many of Rust’s String methods may be called in policies:
allow(actor, _action, _resource) if actor.username.ends_with("example.com");
#[derive(Clone, PolarClass)]
struct User {
#[polar(attribute)]
pub username: String
}
oso.register_class(User::get_polar_class())?;
let user = User{username: "alice@example.com".to_owned()};
assert!(oso.is_allowed(user, "foo", "bar")?);
Polar does not support methods that mutate strings in place.
Vec
Vec<T>
maps to a Polar
list, given that T: ToPolar
.
Implementations also exist to convert LinkedList
, VecDeque
,
BinaryHeap
, HashSet
, and BTreeSet
to and from Polar lists,
but lists are treated as Vec<T>
when calling methods.
Currently, no methods on Vec
are exposed to Polar.
allow(actor, _action, _resource) if "HR" in actor.groups;
#[derive(Clone, PolarClass)]
struct User {
#[polar(attribute)]
pub groups: Vec<String>,
}
oso.register_class(User::get_polar_class())?;
let user = User { groups: vec!["HR".to_string(), "payroll".to_string()] };
assert!(oso.is_allowed(user, "foo", "bar")?);
Polar does not support methods that mutate lists in place unless the list is also returned from the method.
Rust methods like
Vec::get
may be used for random access to
list elements, but there is currently no Polar syntax that is equivalent to the
Rust 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.
HashMap
A Rust
HashMap
maps to a Polar
dictionary but requires that the HashMap
key is
a String
.
Implementations also exist to convert BTreeMap
s to and
from Polar dictionaries, but dictionaries are treated as HashMap
when calling methods.
allow(actor, _action, _resource) if actor.roles.project1 = "admin";
#[derive(Clone, PolarClass)]
struct User {
#[polar(attribute)]
pub roles: HashMap<String, String>,
}
oso.register_class(User::get_polar_class())?;
let user = User { roles: maplit::hashmap!{
"project1".to_string() => "admin".to_string()
}};
assert!(oso.is_allowed(user, "foo", "bar")?);
Likewise, dictionaries constructed in Polar may be passed into Rust methods.
Iterators
You may iterate over a Rust
iterator using Polar’s
in
operator:
allow(actor, _action, _resource) if "payroll" in actor.get_groups();
#[derive(Clone, PolarClass)]
struct User {
groups: Vec<String>,
}
oso.register_class(
User::get_polar_class_builder()
.add_iterator_method("get_groups", |u: &User| u.groups.clone().into_iter())
.build(),
)
.unwrap();
let user = User {
groups: vec!["HR".to_string(), "payroll".to_string()],
};
assert!(oso.is_allowed(user, "foo", "bar")?);
Options
The Rust type Option<T>
is registered as a class. You can use unwrap()
on
an option in a policy, but it’s safer to use the in
operator, which will
return 0 or 1 values depending on whether the value is None
or Some(T)
respectively.
The Option
variant None
is registered as the Polar constant
nil
. If a Rust method can return
None
, you may want to compare the result to nil
:
allow(actor, _action, _resource) if
"Jimmy" in actor.nickname or
actor.get_optional() != nil;
#[derive(Clone, PolarClass)]
struct User {
#[polar(attribute)]
nickname: Option<String>,
}
oso.register_class(
User::get_polar_class_builder()
.add_method("get_optional", |u: &User| None)
.build(),
)
.unwrap();
let user = User { nickname: Some("Jimmy".to_string()), };
assert!(oso.is_allowed(user, "foo", "bar")?);
UUIDs via the uuid
crate
Oso supports UUIDs via the
uuid
crate behind
a feature flag. To enable support, you’ll need to add a feature flag to your
Cargo.toml
file and make sure you have the uuid
crate as a separate
dependency. In Cargo.toml
, an Oso dependency that supports UUIDs looks as
follows:
oso = { version = "X.Y.Z", features = ["uuid-10"] }
Note that the numbers in the feature flags do not refer to
the UUID
version but to the version of the uuid
crate. Most people will want
the uuid-10
feature flag, as it supports recent versions of the uuid
crate.
uuid Crate Version |
Feature Flag |
---|---|
0.6.5 - 0.6.x |
uuid-06 |
0.7.0 - 0.8.x |
uuid-07 |
1.0.0 - 2.0.0 |
uuid-10 |
Rust → Polar Types Summary
Rust type | Polar type |
---|---|
i32 , i64 , usize |
Integer |
f32 , f64 |
Float |
bool |
Boolean |
String , &'static str , str |
String |
HashMap , BTreeMap |
Dictionary |
Vec , LinkedList , VecDeque BinaryHeap , HashSet , BTreeSet |
List |
UUID (behind a feature flag) | Uuid |
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.