Java Client API
These are the docs for the v1 release. For v0, see the docs and the migration guide.
For changes to the Java Client API, see Changelog.
See the Oso Cloud Java client's Maven Central page (opens in a new tab) for instructions on how to add it to your project.
Before going through this guide, make sure you follow the Oso Cloud Quickstart to get your Oso Cloud API Key properly set in your environment.
Instantiating an Oso Cloud client
The Oso Cloud client provides an Oso
class that takes your API key:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.Fact;// ...Oso oso = new Oso(YOUR_API_KEY);// Later:Value user = new Value("User", "1");Value role = new Value("member");Value resource = new Value("Repository", "anvils");oso.insert(new Fact("has_role", user, role, resource));// Wherever authorization needs to be performed:String action = "read";boolean allowed = oso.authorize(user, action, resource);if (allowed) { // Action is allowed.}
You should instantiate one client and share it across your application. Under the hood, it reuses connections to avoid paying the cost of negotiating a new connection on every request.
Specifying an Oso Fallback host
If you have deployed Oso Fallback nodes to your infrastructure, you may specify the host when instantiating the Oso Cloud client.
import com.osohq.oso_cloud.Oso;import java.net.URI;// ...// Assumes Oso Fallback is hosted at http://localhost:8080Oso oso = new Oso(YOUR_API_KEY, null, URI.create("http://localhost:8080"));
Passing application entities into the client
Under the hood, Oso Cloud represents an entity in your application as a combination of a type and an ID, which together uniquely identify the entity.
The Java client represents these entities as Value
objects with both type
and id
fields.
For example:
import com.osohq.oso_cloud.Value;// ...Value alice = new Value("User", "alice");Value anvilsRepository = new Value("Repository", "anvils");
You will pass objects like these into nearly every method call you make to the Java client.
Centralized Authorization Data API
Oso Cloud clients provide an API to manage authorization data stored directly in Oso Cloud.
Add fact: oso.insert(Fact)
Adds a single fact. Example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.Fact;// ...Oso oso = new Oso(YOUR_API_KEY);oso.insert( new Fact( "has_role", new Value("User", "bob"), new Value("owner"), new Value("Organization", "acme") ));
Delete fact: oso.delete(Fact)
/ oso.delete(FactPattern)
Deletes facts. Does not throw an error if the fact(s) are not found.
To delete a single, specific fact, pass a Fact
object:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.Fact;// ...Oso oso = new Oso(YOUR_API_KEY);oso.delete( new Fact( "has_role", new Value("User", "bob"), new Value("maintainer"), new Value("Repository", "anvil") ));
To delete multiple facts matching a pattern, pass a FactPattern
using ValuePattern.ANY
or ValuePattern.ValueOfType
as wildcards:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.FactPattern;import com.osohq.oso_cloud.ValuePattern;// ...Oso oso = new Oso(YOUR_API_KEY);Value user = new Value("User", "bob");// Remove all of bob's roles across all resourcesoso.delete(new FactPattern("has_role", user, ValuePattern.ANY, ValuePattern.ANY));// Remove bob's "member" role from all Organizationsoso.delete(new FactPattern( "has_role", user, new Value("member"), new ValuePattern.ValueOfType("Organization")));
Transactionally delete and add facts: oso.batch(Consumer<BatchTransaction>)
For Oso Cloud developer accounts, batch
calls are limited to 20 total
operations (inserts + deletes). If you attempt to send more than 20
operations, the API will return an error.
The batch command has a 10MB body limit, an exception will be raised for exceeding this limit.
Allows deleting and inserting many facts in one atomic transaction. Operations are performed in the order they appear in the consumer lambda. Example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.Fact;import com.osohq.oso_cloud.FactPattern;import com.osohq.oso_cloud.ValuePattern;import java.util.Arrays;import java.util.List;// ...Oso oso = new Oso(YOUR_API_KEY);Value user = new Value("User", "bob");Value org = new Value("Organization", "acme");Value repo = new Value("Repository", "anvil");List<Fact> factsToInsert = Arrays.asList( new Fact("has_role", user, new Value("owner"), org), new Fact("has_role", user, new Value("maintainer"), repo));List<Fact> factsToDelete = Arrays.asList( new Fact("has_role", user, new Value("member"), org));FactPattern patternToDelete = new FactPattern("has_role", user, new Value("viewer"), ValuePattern.ANY);oso.batch(tx -> { // Insert multiple facts factsToInsert.forEach(tx::insert); // Delete multiple specific facts factsToDelete.forEach(tx::delete); // Delete facts matching a pattern tx.delete(patternToDelete);});
Get facts: oso.get(FactPattern)
For Oso Cloud developer accounts, get
calls are limited to 1000 results. If
you have more than 1000 facts matching the pattern, the function will return
an error.
Lists facts stored in Oso Cloud that match the provided FactPattern
.
Use ValuePattern.ANY
or ValuePattern.ValueOfType
as wildcards in the pattern.
Returns a Fact[]
array.
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.Fact;import com.osohq.oso_cloud.FactPattern;import com.osohq.oso_cloud.ValuePattern;// ...Oso oso = new Oso(YOUR_API_KEY);Value user = new Value("User", "bob");Value repo = new Value("Repository", "anvils");// Get one specific fact:Fact[] specificFact = oso.get( new FactPattern("has_role", user, new Value("admin"), repo));// List all roles for User:bob on any resourceFact[] userRoles = oso.get( new FactPattern("has_role", user, ValuePattern.ANY, ValuePattern.ANY));// List all roles on the `anvils` repo for any UserFact[] repoRoles = oso.get(new FactPattern( "has_role", new ValuePattern.ValueOfType("User"), ValuePattern.ANY, repo));
oso.get()
only returns facts you've explicitly added. If you want to return
a list of authorized resources, use the Check API. For example,
to answer "on which resources can a given user perform a given action", use
oso.list()
. If you want to query for arbitrary
information that can be derived from your facts and policy, use the Query
Builder API.
Check API
For Oso Cloud developer accounts, the number of context facts per request is limited to 20; and the number of records returned is limited to 1000.
Context facts
The Check API lets you provide context facts with each request. When Oso Cloud performs a check, it considers the request's context facts in addition to any other centralized authorization data. Context facts are only used in the API call in which they're provided-- they do not persist across requests.
For more details, see Context Facts.
Check a permission: oso.authorize(actor, action, resource)
Determines whether or not an action is allowed, based on a combination of authorization data and policy logic. Example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import java.util.Arrays;import java.util.List;// ...Oso oso = new Oso(YOUR_API_KEY);Value user = new Value("User", "alice");Value anvilsRepository = new Value("Repository", "anvils");String action = "read";boolean allowed = oso.authorize(user, action, anvilsRepository);if (allowed) { // Action is allowed.}
You may provide a list of context facts as an optional fourth argument to this method. Example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.Fact;import java.util.Arrays;import java.util.List;// ...Oso oso = new Oso(YOUR_API_KEY);Value user = new Value("User", "alice");Value anvilsRepository = new Value("Repository", "anvils");Value issueOnAnvilsRepository = new Value("Issue", "anvils-1");String action = "read";List<Fact> contextFacts = Arrays.asList( new Fact("has_relation", issueOnAnvilsRepository, new Value("parent"), anvilsRepository));boolean allowed = oso.authorize(user, action, issueOnAnvilsRepository, contextFacts);if (allowed) { // Action is allowed}
List authorized resources: oso.list(actor, action, resourceType)
Fetches a list of resource IDs (String[]
) on which an actor can perform a particular action.
Example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;// ...Oso oso = new Oso(YOUR_API_KEY);Value user = new Value("User", "alice");String action = "read";String resourceType = "Repository";String[] repositoryIds = oso.list(user, action, resourceType);// => ["acme", "anvil"]
You may provide a list of context facts as an optional fourth argument to this method. Example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.Fact;import java.util.Arrays;import java.util.List;// ...Oso oso = new Oso(YOUR_API_KEY);Value user = new Value("User", "alice");Value anvilsRepository = new Value("Repository", "anvils");Value acmeRepository = new Value("Repository", "acme");Value issueOnAcmeRepository = new Value("Issue", "acme-1");Value issueOnAnvilsRepository = new Value("Issue", "anvils-2");String action = "read";String resourceType = "Issue";List<Fact> contextFacts = Arrays.asList( new Fact("has_relation", issueOnAnvilsRepository, new Value("parent"), anvilsRepository), new Fact("has_relation", issueOnAcmeRepository, new Value("parent"), acmeRepository));String[] issueIds = oso.list(user, action, resourceType, contextFacts);// => ["acme-1"]
List authorized actions: oso.actions(actor, resource)
Fetches a list of actions (String[]
) which an actor can perform on a particular resource.
Example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;// ...Oso oso = new Oso(YOUR_API_KEY);Value user = new Value("User", "alice");Value anvilsRepository = new Value("Repository", "anvils");String[] actions = oso.actions(user, anvilsRepository);// => ["read", "write"]
You may provide a list of context facts as an optional third argument to this method. Example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.Fact;import java.util.Arrays;import java.util.List;// ...Oso oso = new Oso(YOUR_API_KEY);Value user = new Value("User", "alice");Value anvilsRepository = new Value("Repository", "anvils");Value issueOnAnvilsRepository = new Value("Issue", "anvils-1");List<Fact> contextFacts = Arrays.asList( new Fact("has_relation", issueOnAnvilsRepository, new Value("parent"), anvilsRepository));String[] actions = oso.actions(user, issueOnAnvilsRepository, contextFacts);// => ["read"]
Query for any rule: oso.buildQuery(predicate, ...args)
Query Oso Cloud for any predicate and any combination of concrete values and typed variables.
Unlike oso.get
, which only lists facts you've added, you can use oso.buildQuery
to list derived
information about any rule in your policy. Example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import com.osohq.oso_cloud.QueryBuilder;import com.osohq.oso_cloud.QueryBuilder.EvaluateArgs;import java.util.List;// ...Oso oso = new Oso(YOUR_API_KEY);Value actor = new Value("User", "bob");Value action = new Value("read");TypedVar repository = new TypedVar("Repository");// Query for all the repos `User:bob` can `read`List<String> results = oso.buildQuery("allow", actor, action, repository) .evaluate(EvaluateArgs.values(repository));// => ["acme", "anvils"]
Query Builder API
The oso.buildQuery()
API returns a builder-style API where you chain methods to
construct a query and then execute it.
oso.buildQuery(predicate, ...args)
The oso.buildQuery()
function takes the name of the rule you want to query and
a list of arguments. The arguments can be concrete values (e.g.,
new Value("read")
or new Value("User", "bob")
) or type-constrained variables
constructed via the TypedVar
class:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import com.osohq.oso_cloud.QueryBuilder;// ...Oso oso = new Oso(YOUR_API_KEY);Value actor = new Value("User", "bob");Value action = new Value("read");TypedVar repository = new TypedVar("Repository");// Query for all the repositories bob can readQueryBuilder qb = oso.buildQuery("allow", actor, action, repository);// => QueryBuilder { ... }
Note: Once you've finished building up your query, you must call evaluate
to
run it and get the results.
QueryBuilder.and(predicate, ...args)
This function adds another condition that must be true of the query results.
For example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import com.osohq.oso_cloud.QueryBuilder;// ...Oso oso = new Oso(YOUR_API_KEY);Value actor = new Value("User", "bob");Value action = new Value("read");TypedVar repository = new TypedVar("Repository");Value folder = new Value("Folder", "folder-1");// Query for all the repositories this user can read...QueryBuilder qb = oso .buildQuery("allow", actor, action, repository) // ...and require the repositories to belong to the given folder. .and("has_relation", repository, new Value("folder"), folder);// => QueryBuilder { ... }
Note: Once you've finished building up your query, you must call evaluate
to
run it and get the results.
QueryBuilder.in(variable, values)
This function requires a given TypedVar
query variable to be included in a
given set of values (List<String>
). You can only call in
once per variable per query. Calling
in
a second time with the same variable on the same query builder will throw
an error.
For example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import com.osohq.oso_cloud.QueryBuilder;import java.util.Arrays;import java.util.List;// ...Oso oso = new Oso(YOUR_API_KEY);Value actor = new Value("User", "bob");List<String> repositoryIds = Arrays.asList("acme", "anvil");TypedVar actionVar = new TypedVar("String");TypedVar repositoryVar = new TypedVar("Repository");// Query for all the actions this user can perform on any repository...QueryBuilder qb = oso .buildQuery("allow", actor, actionVar, repositoryVar) // ...given that the repository's ID is in the given list of IDs. .in(repositoryVar, repositoryIds);// => QueryBuilder { ... }
Note: Once you've finished building up your query, you must call evaluate
to
run it and get the results.
QueryBuilder.withContextFacts(contextFacts)
This function adds the given context facts (List<Fact>
) to the query. For example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import com.osohq.oso_cloud.QueryBuilder;import com.osohq.oso_cloud.Fact;import java.util.Arrays;import java.util.List;// ...Oso oso = new Oso(YOUR_API_KEY);Value actor = new Value("User", "bob");Value action = new Value("read");TypedVar repository = new TypedVar("Repository");List<Fact> contextFacts = Arrays.asList( new Fact("has_role", actor, new Value("owner"), new Value("Repository", "acme")));// Query for all the repositories bob can read...QueryBuilder qb = oso .buildQuery("allow", actor, action, repository) // ...while including the fact that bob owns acme .withContextFacts(contextFacts);// => QueryBuilder { ... }
For more information on context facts, see this section.
Note: Once you've finished building up your query, you must call evaluate
to
run it and get the results.
QueryBuilder.evaluate(EvaluateArg)
This function evaluates the built query, fetching the results from Oso Cloud.
The shape of the return value is determined by the EvaluateArg
you provide.
Use the static factory methods in EvaluateArgs
to construct the desired argument structure.
-
EvaluateArgs.exists()
: Returns aBoolean
indicating if the query is satisfiable.import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.QueryBuilder.EvaluateArgs;// ...Oso oso = new Oso(YOUR_API_KEY);Value actor = new Value("User", "bob");Value action = new Value("read");Value resource = new Value("Repository", "acme");Boolean allowed = oso.buildQuery("allow", actor, action, resource).evaluate(EvaluateArgs.exists());// => true if the given actor can perform the given action on the given resource -
EvaluateArgs.values(TypedVar variable)
: Returns aList<String>
of values for that variable.import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import com.osohq.oso_cloud.QueryBuilder.EvaluateArgs;import java.util.List;// ...Oso oso = new Oso(YOUR_API_KEY);Value actor = new Value("User", "bob");Value resource = new Value("Repository", "acme");TypedVar actionVar = new TypedVar("String");List<String> actions = oso.buildQuery("allow", actor, actionVar, resource).evaluate(EvaluateArgs.values(actionVar));// => all the actions the actor can perform on the given resource- eg. ["read", "write"] -
EvaluateArgs.map(TypedVar keyVariable, EvaluateArg valueArg)
: Returns aMap<String, SubT>
where keys are unique values of the key variable, and values are the results of recursively evaluating the nested value argument (SubT
). This allows arbitrarily nested structures.import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import com.osohq.oso_cloud.QueryBuilder.EvaluateArgs;import java.util.List;import java.util.Map;// ...Oso oso = new Oso(YOUR_API_KEY);Value actor = new Value("User", "bob");TypedVar actionVar = new TypedVar("String");TypedVar repositoryVar = new TypedVar("Repository");Map<String, List<String>> repoToActions = oso.buildQuery("allow", actor, actionVar, repositoryVar).evaluate(EvaluateArgs.map(repositoryVar, EvaluateArgs.values(actionVar)));// => a map of repo IDs to allowed actions-// eg. {"acme"=["read"], "anvil"=["read", "write"]}// Example of deeply nested map: User -> Repo -> List<Action>TypedVar userVar = new TypedVar("User");Map<String, Map<String, List<String>>> userRepoActions = oso.buildQuery("allow", userVar, actionVar, repositoryVar).evaluate(EvaluateArgs.map(userVar, EvaluateArgs.map(repositoryVar, EvaluateArgs.values(actionVar))));// => {"alice"={"acme"=["read"]}, "bob"={"anvil"=["read", "write"]}}
Some queries have unconstrained results. For instance, maybe users with the
admin
role can read all Repository
entities in your application. In this
case, rather than returning a list containing the ID of every repository,
evaluate
will return a list containing the string "*"
. For example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import com.osohq.oso_cloud.QueryBuilder.EvaluateArgs;import java.util.List;// ...Oso oso = new Oso(YOUR_API_KEY);Value adminUser = new Value("User", "admin");Value action = new Value("read");TypedVar repos = new TypedVar("Repository");List<String> results = oso .buildQuery("allow", adminUser, action, repos) .evaluate(EvaluateArgs.values(repos)); // Return just the IDs of the repos admin can read// => ["*"] // admin can read anything
Query Builder examples
Field-level access control
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import com.osohq.oso_cloud.QueryBuilder.EvaluateArgs;import java.util.List;// ...Oso oso = new Oso(YOUR_API_KEY);Value actor = new Value("User", "alice");Value resource = new Value("Repository", "anvil");Value action = new Value("read");TypedVar field = new TypedVar("Field");List<String> results = oso .buildQuery("allow_field", actor, action, resource, field) .evaluate(EvaluateArgs.values(field));// => Returns a list of the fields alice can read on the given repo- eg.// ["name", "stars"]
Checking a global permission
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.QueryBuilder.EvaluateArgs;// ...Oso oso = new Oso(YOUR_API_KEY);Value actor = new Value("User", "alice");Value permission = new Value("create_repository");Boolean result = oso .buildQuery("has_permission", actor, permission) .evaluate(EvaluateArgs.exists());// => true if alice has the global "create_repository" permission
Fetching authorized actions for a collection of resources
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import com.osohq.oso_cloud.QueryBuilder.EvaluateArgs;import java.util.Arrays;import java.util.List;import java.util.Map;// ...Oso oso = new Oso(YOUR_API_KEY);List<String> repoIds = Arrays.asList("anvil", "acme");Value actor = new Value("User", "alice");TypedVar actionVar = new TypedVar("String");TypedVar repoVar = new TypedVar("Repository");Map<String, List<String>> results = oso .buildQuery("allow", actor, actionVar, repoVar) .in(repoVar, repoIds) .evaluate(EvaluateArgs.map(repoVar, EvaluateArgs.values(actionVar)));// => Returns a map of the given repos to the actions alice can perform on those repos- eg.// {"anvil"=["read"], "acme"=["read", "write"]}
Filtering out unauthorized resources from a collection
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import com.osohq.oso_cloud.QueryBuilder.EvaluateArgs;import java.util.Arrays;import java.util.List;// ...Oso oso = new Oso(YOUR_API_KEY);List<String> repoIds = Arrays.asList("anvil", "acme", "bob-repo");Value actor = new Value("User", "bob");Value action = new Value("read");TypedVar repoVar = new TypedVar("Repository");List<String> results = oso .buildQuery("allow", actor, action, repoVar) .in(repoVar, repoIds) .evaluate(EvaluateArgs.values(repoVar));// => Returns the subset of `repoIds` that bob can read- eg.// ["anvil", "bob-repo"]
Filtering an authorize()
query based on a relation
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import com.osohq.oso_cloud.QueryBuilder.EvaluateArgs;import java.util.List;// ...Oso oso = new Oso(YOUR_API_KEY);Value actor = new Value("User", "bob");Value action = new Value("read");TypedVar repoVar = new TypedVar("Repository");Value org = new Value("Org", "coolguys");Value relation = new Value("parent");List<String> results = oso .buildQuery("allow", actor, action, repoVar) .and("has_relation", repoVar, relation, org) .evaluate(EvaluateArgs.values(repoVar));// => Returns the IDs of the repos in the coolguys org that bob can read- eg.// ["acme", "anvil"]
Learn more about how to query Oso Cloud.
Local Check API
The local check API lets you perform authorization using data that's across Oso Cloud and your own database.
After creating your Local Authorization configuration, provide the path to the YAML file that specifies how to resolve facts in your database.
import com.osohq.oso_cloud.Oso;import java.net.URI;// ...Oso oso = new Oso(YOUR_API_KEY, null, null, "path/to/local_authorization_config.yaml");
For more information, see Local Authorization.
List authorized resources with local data: oso.listLocal(actor, action, resourceType, column)
Fetches a SQL query fragment (String
) that can be applied to a database query's WHERE
clause to return just the resources
on which an actor can perform an action. Example with JPA:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import javax.persistence.EntityManager;import java.util.List;// ...Oso oso = new Oso(/*...*/);EntityManager entityManager = // ... get your entity managerValue alice = new Value("User", "alice");String action = "read";String resourceType = "Issue";String idColumn = "id";String filterSql = oso.listLocal(alice, action, resourceType, idColumn);List<Issue> authorizedIssues = entityManager.createNativeQuery( "SELECT * FROM issues WHERE " + filterSql, Issue.class ).getResultList();
You may provide a list of context facts as an optional fifth argument to this method. Example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.Fact;import javax.persistence.EntityManager;import java.util.Arrays;import java.util.List;import your.app.Issue;// ...Oso oso = new Oso(/*...*/);EntityManager entityManager = // ... get your entity managerValue carol = new Value("User", "carol");Value organization = new Value("Organization", "acme");String action = "read";String resourceType = "Issue";String idColumn = "id";Fact contextFact = new Fact("has_role", carol, new Value("member"), organization);List<Fact> contextFacts = Arrays.asList(contextFact);String filterSql = oso.listLocal(carol, action, resourceType, idColumn, contextFacts);List<Issue> authorizedIssues = entityManager.createNativeQuery( "SELECT * FROM issues WHERE " + filterSql, Issue.class ).getResultList();
Check a permission with local data: oso.authorizeLocal(actor, action, resource)
Fetches a complete SQL query (String
) that can be run against your database to determine whether an actor can perform an action on a resource. The query returns a single row with a single boolean column named allowed
.
Example with JPA:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import javax.persistence.EntityManager;// ...Oso oso = new Oso(/*...*/);EntityManager entityManager = // ... get your entity managerValue alice = new Value("User", "alice");Value swageIssue = new Value("Issue", "swage");String action = "read";String sqlQuery = oso.authorizeLocal(alice, action, swageIssue);boolean allowed = (Boolean) entityManager.createNativeQuery(sqlQuery).getSingleResult();if (allowed) { // Action is allowed.}
You may provide a list of context facts as an optional fourth argument to this method. Example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.Fact;import javax.persistence.EntityManager;import java.util.Arrays;import java.util.List;// ...Oso oso = new Oso(/*...*/);EntityManager entityManager = // ... get your entity managerValue carol = new Value("User", "carol");Value organization = new Value("Organization", "acme");Value swageIssue = new Value("Issue", "swage");String action = "read";Fact contextFact = new Fact("has_role", carol, new Value("member"), organization);List<Fact> contextFacts = Arrays.asList(contextFact);String sqlQuery = oso.authorizeLocal(carol, action, swageIssue, contextFacts);boolean allowed = (Boolean) entityManager.createNativeQuery(sqlQuery).getSingleResult();if (allowed) { // Action is allowed.}
List authorized actions with local data: oso.actionsLocal(actor, resource)
Fetches a complete SQL query (String
) that can be run against your database to fetch the actions (List<String>
) an actor can perform on a resource. The query returns multiple rows, each with a single string column named action
.
Example with JPA:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import javax.persistence.EntityManager;import java.util.List;// ...Oso oso = new Oso(/*...*/);EntityManager entityManager = // ... get your entity managerValue bob = new Value("User", "bob");Value swageIssue = new Value("Issue", "swage");String sqlQuery = oso.actionsLocal(bob, swageIssue);List<String> actions = entityManager.createNativeQuery(sqlQuery).getResultList();// => ["read", "comment"]
You may provide a list of context facts as an optional third argument to this method. Example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.Fact;import javax.persistence.EntityManager;import java.util.Arrays;import java.util.List;// ...Oso oso = new Oso(/*...*/);EntityManager entityManager = // ... get your entity managerValue carol = new Value("User", "carol");Value organization = new Value("Organization", "acme");Value swageIssue = new Value("Issue", "swage");Fact contextFact = new Fact("has_role", carol, new Value("member"), organization);List<Fact> contextFacts = Arrays.asList(contextFact);String sqlQuery = oso.actionsLocal(carol, swageIssue, contextFacts);List<String> actions = entityManager.createNativeQuery(sqlQuery).getResultList();// => ["read"]
Query for any rule with local data: QueryBuilder
You can use the Query Builder to construct SQL queries that you can run against your database to answer arbitrary questions about authorization.
See the Query Builder API documentation for information on how to construct queries. Once you've
built your query, instead of calling evaluate
(which would evaluate your query exclusively against data
stored in Oso Cloud), call either evaluateLocalSelect
or
evaluateLocalFilter
to construct a SQL query which you can run against
your database.
Local Query API
QueryBuilder.evaluateLocalSelect(columnNamesToQueryVars)
Fetches a complete SQL query (String
) representing the given Query Builder query.
(See the Query Builder API documentation for information on how to construct queries.)
The argument to this function is a Map<String, TypedVar>
where keys are the desired column names that will appear in the SELECT
clause of the SQL query, and values are the Query Builder variables whose values should be selected. For example,
query.evaluateLocalSelect(Map.of("user_id", userVar))
will select all the authorized values of userVar
(that is, all the values of userVar
that satisfy the Query Builder query) into a column called "user_id"
.
If you pass in an empty map or call the no-argument overload evaluateLocalSelect()
, the SQL query will return a single row selecting a boolean column called result
.
This column will be true
when there's some combination of data in your database that can satisfy the given Query Builder query and false
otherwise.
Note that each query variable can appear at most once in the columnNamesToQueryVars
mapping. For example,
query.evaluateLocalSelect(Map.of("user_id", userVar, "another_user_id", userVar))
will throw an IllegalArgumentException
, because
userVar
appears twice in the mapping parameter.
Note that column names will be double-quoted and are thus case-sensitive in the generated SQL.
Limitations
evaluateLocalSelect
has the following limitations:
-
Queries that would return a wildcard (
"*"
) for one of the selected query variables are currently unsupported.Example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import com.osohq.oso_cloud.ApiException;import java.util.Map;// ...Oso oso = new Oso(/*...*/);// Assume alice is a global admin, and can read *any* repooso.insert(new Fact("is_global_admin", new Value("User", "alice")));// UNSUPPORTED: Attempt to query for each authorized user / repo pairTypedVar userVar = new TypedVar("User");TypedVar repoVar = new TypedVar("Repo");try {String sql = oso.buildQuery("allow", userVar, new Value("read"), repoVar).evaluateLocalSelect(Map.of("user_id", userVar, "repo_id", repoVar));// => throws ApiException, because alice can read any repo, so this query would return// a wildcard for the repos alice can read} catch (ApiException e) {// Handle error}
QueryBuilder.evaluateLocalFilter(columnName, queryVar)
Fetches a SQL query fragment (String
) representing the given Query Builder query that you can embed
in the WHERE
clause of some other SQL query. Use this to filter out unauthorized results from
your SQL query.
(See the Query Builder API documentation for information on how to construct queries.)
columnName
is the name of the column you want to filter in your SQL query, and queryVar
is the
query variable to filter against.
For example, query.evaluateLocalFilter("user_id", userVar)
will return a SQL fragment of the form
"user_id" IN (...)
, constraining the column "user_id"
to the authorized values of userVar
.
Note that the column name will be double-quoted and is thus case-sensitive in the generated SQL.
Local query examples
These examples all use JPA's createNativeQuery
to execute the returned SQL queries, but you
can use any database client you prefer.
Field-level access control
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import javax.persistence.EntityManager;import java.util.List;import java.util.Map;// ...Oso oso = new Oso(/*...*/);EntityManager entityManager = // ... get your entity managerValue actor = new Value("User", "alice");Value resource = new Value("Repository", "anvil");Value action = new Value("read");TypedVar field = new TypedVar("Field");String sqlQuery = oso .buildQuery("allow_field", actor, action, resource, field) .evaluateLocalSelect(Map.of("field_name", field));// => 'SELECT "field_name" FROM (... /* only the fields alice can read on anvil */)'List<String> fields = entityManager.createNativeQuery(sqlQuery).getResultList();// => ["name", "stars"]
Checking a global permission
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import javax.persistence.EntityManager;// ...Oso oso = new Oso(/*...*/);EntityManager entityManager = // ... get your entity managerValue actor = new Value("User", "alice");Value permission = new Value("create_repository");String sqlQuery = oso .buildQuery("has_permission", actor, permission) .evaluateLocalSelect(); // No args -> selects boolean 'result' column// => 'SELECT EXISTS (... /* alice has the create_repository permission */) AS result'Boolean hasPermission = (Boolean) entityManager.createNativeQuery(sqlQuery).getSingleResult();// => true
Fetching authorized actions for a paginated collection of resources
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import javax.persistence.EntityManager;import javax.persistence.SqlResultSetMapping;import javax.persistence.ConstructorResult;import javax.persistence.ColumnResult;import java.util.List;import java.util.Map;// ...Oso oso = new Oso(/*...*/);EntityManager entityManager = // ... get your entity managerValue actor = new Value("User", "alice");TypedVar actionVar = new TypedVar("String");TypedVar repoVar = new TypedVar("Repository");String authorizedActionsAndReposSql = oso .buildQuery("allow", actor, actionVar, repoVar) .evaluateLocalSelect(Map.of("action", actionVar, "repo_id", repoVar));// => 'SELECT "action", "repo_id" FROM (... /* pairs of alice's authorized actions / repos */)'String finalSqlQuery = String.format( "WITH authorized_actions AS (%s)\n" + "SELECT r.id AS id, aa.action AS action FROM repos r\n" + "INNER JOIN authorized_actions aa ON r.id = aa.repo_id\n" + "ORDER BY r.id ASC LIMIT 10", authorizedActionsAndReposSql);// Define a mapping for the result tuple@SqlResultSetMapping( name = "RepoActionMapping", classes = @ConstructorResult( targetClass = RepoActionPair.class, columns = { @ColumnResult(name = "id", type = String.class), @ColumnResult(name = "action", type = String.class) } ))// Assuming RepoActionPair is a simple class with constructor (String id, String action) and gettersList<RepoActionPair> results = entityManager.createNativeQuery(finalSqlQuery, "RepoActionMapping").getResultList();// => [RepoActionPair{id='1', action='read'}, RepoActionPair{id='1', action='write'}, ...]
Filtering an authorize()
query based on a relation
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import javax.persistence.EntityManager;import java.util.List;// ...Oso oso = new Oso(/*...*/);EntityManager entityManager = // ... get your entity managerValue actor = new Value("User", "bob");Value action = new Value("read");TypedVar repoVar = new TypedVar("Repository");Value org = new Value("Org", "coolguys");Value relation = new Value("parent");String sqlQuery = oso .buildQuery("allow", actor, action, repoVar) .and("has_relation", repoVar, relation, org) .evaluateLocalSelect(Map.of("repo_id", repoVar));// => 'SELECT "repo_id" FROM (... /* only repos bob can read which belong to coolguys */)'List<String> repoIds = entityManager.createNativeQuery(sqlQuery).getResultList();// => ["acme", "anvil"]
Filtering on users who can read a certain resource
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.Value;import com.osohq.oso_cloud.TypedVar;import javax.persistence.EntityManager;import java.util.List;// ...Oso oso = new Oso(/*...*/);EntityManager entityManager = // ... get your entity managerTypedVar actorVar = new TypedVar("User");Value resource = new Value("Repository", "anvil");Value action = new Value("read");String idColumn = "user_id"; // Assuming user table has a "user_id" columnString authorizedUserFragment = oso .buildQuery("allow", actorVar, action, resource) .evaluateLocalFilter(idColumn, actorVar);// => '"user_id" IN (... /* only users who can read anvil */)'String sqlQuery = "SELECT email FROM users WHERE " + authorizedUserFragment;List<String> emails = entityManager.createNativeQuery(sqlQuery).getResultList();// => ["[email protected]", "[email protected]"]
Policy API
Update the active policy: oso.policy(policy)
Updates the policy in Oso Cloud. The string passed into this method should be written in Polar. Example:
import com.osohq.oso_cloud.Oso;// ...Oso oso = new Oso(YOUR_API_KEY);oso.policy( "allow(actor, action, resource) if\\n" + " has_permission(actor, action, resource);\\n" + "actor User {}");
This command will run any tests defined in your policy. If one or more of these tests fail, an exception will be thrown and your policy will not be updated.
Get policy metadata: oso.policyMetadata()
Returns metadata about the currently active policy. Example:
import com.osohq.oso_cloud.Oso;import com.osohq.oso_cloud.PolicyMetadata;import java.util.Arrays;// ...Oso oso = new Oso(YOUR_API_KEY);PolicyMetadata metadata = oso.policyMetadata();System.out.println(Arrays.toString(metadata.resources.keySet().toArray()));// prints [Organization, Repository, User, global]PolicyMetadata.ResourceMetadata org = metadata.resources.get("Organization");System.out.println(Arrays.toString(org.roles));// prints [admin, member]
See the Policy Metadata guide for more information on use cases.
Talk to an Oso engineer
If you'd like to learn more about using Oso Cloud in your app or have any questions about this guide, schedule a 1x1 with an Oso engineer. We're happy to help.