The core components of the NIST Policy Machine, a reference implementation of the Next Generation Access Control (NGAC) standard. For complete documentation and detailed examples visit the Wiki.
git clone https://github.com/usnistgov/policy-machine-core.git
cd policy-machine-core
mvn clean install
<dependency>
<groupId>gov.nist.csd.pm</groupId>
<artifactId>policy-machine-core</artifactId>
<version>x.y.z</version>
</dependency>
Policy Machine Core uses JitPack to compile and build the artifact to import with maven.
First, add jitpack as a repository
<project>
--
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
--
</project>Then, add the maven dependency
<dependency>
<groupId>com.github.usnistgov</groupId>
<artifactId>policy-machine-core</artifactId>
<version>x.y.z</version>
</dependency>common- Objects common to other packages.pap- Policy Administration Point. Provides the Policy Machine implementation of the NGAC PAP interfaces for modifying and querying policy.pdp- Policy Decision Point. Implementation of an administrative PDP that controls access to admin operations on the PAP.epp- Event Processing Point. The epp attaches to a PDP to listen to administrative events while exposing an interface for a PEP to send events.impl- Policy Machine supported implementations of the PAP interfaces. Included are in memory and embedded Neo4j.
package gov.nist.csd.pm.core.example;
import gov.nist.csd.pm.core.common.exception.PMException;
import gov.nist.csd.pm.core.common.graph.relationship.AccessRightSet;
import gov.nist.csd.pm.core.common.prohibition.ContainerCondition;
import gov.nist.csd.pm.core.common.prohibition.ProhibitionSubject;
import gov.nist.csd.pm.core.epp.EPP;
import gov.nist.csd.pm.core.impl.memory.pap.MemoryPAP;
import gov.nist.csd.pm.core.pap.PAP;
import gov.nist.csd.pm.core.pap.query.model.context.TargetContext;
import gov.nist.csd.pm.core.pap.query.model.context.UserContext;
import gov.nist.csd.pm.core.pdp.PDP;
import java.util.List;
public class Main {
public static void main(String[] args) throws PMException {
// create a new memory PAP
PAP pap = new MemoryPAP();
// set the resource operations the policy will support
pap.modify().operations().setResourceOperations(new AccessRightSet("read", "write"));
// create a simple configuration with one of each node type, granting u1 read access to o1.
long pc1 = pap.modify().graph().createPolicyClass("pc1");
long ua1 = pap.modify().graph().createUserAttribute("ua1", List.of(pc1));
long ua2 = pap.modify().graph().createUserAttribute("ua2", List.of(pc1));
long u1 = pap.modify().graph().createUser("u1", List.of(ua1, ua2));
long oa1 = pap.modify().graph().createObjectAttribute("oa1", List.of(pc1));
long o1 = pap.modify().graph().createObject("o1", List.of(oa1));
pap.modify().graph().associate(ua1, oa1, new AccessRightSet("read", "write", "create_object_attribute", "associate", "associate_to"));
pap.modify().graph().associate(ua2, ua1, new AccessRightSet("associate"));
// create a prohibition
pap.modify().prohibitions().createProhibition(
"deny u1 write on oa1",
new ProhibitionSubject(u1),
new AccessRightSet("write"),
false,
List.of(new ContainerCondition(oa1, false)));
// create an obligation that associates ua1 with any OA
String obligationPML = """
create obligation "sample_obligation" {
create rule "rule1"
when any user
performs "create_object_attribute"
on {
descendants: "oa1"
}
do(ctx) {
associate "ua1" and ctx.args.name with ["read", "write"]
}
}""";
// when creating an obligation a user is required
// this is the user the obligation response will be executed on behalf of
pap.executePML(new UserContext(u1), obligationPML);
/*
Alternatively, create the above policy using PML:
String pml = """
set resource operations ["read", "write"]
create pc "pc1"
create oa "oa1" in ["pc1"]
create ua "ua1" in ["pc1"]
create u "u1" in ["ua1"]
create o "o1" in ["oa1"]
associate "ua1" and "oa1" with ["read", "write", "create_object_attribute", "associate", "associate_to"]
associate "ua2" and "ua1" with ["associate"]
create prohibition "deny u1 write on oa1"
deny U "u1"
access rights ["write"]
on union of {"oa1": false}
create obligation "sample_obligation" {
create rule "rule1"
when any user
performs "create_object_attribute"
on {
descendants: "oa1"
}
do(ctx) {
associate "ua1" and ctx.args.name with ["read", "write"]
}
}
""";
pap.executePML(new UserContext("u1")), pml);
*/
AccessRightSet privileges = pap.query().access().computePrivileges(new UserContext(u1), new TargetContext(o1));
System.out.println(privileges);
// expected output: {associate, read, create_object_attribute, associate_to}
// create a PDP instance
PDP pdp = new PDP(pap);
// create a new EPP instance to listen to admin operations emitted by the PDP
EPP epp = new EPP(pdp, pap);
epp.subscribeTo(pdp);
// run a tx as u1 that will trigger the above obligation
pdp.runTx(new UserContext(u1), policy -> {
policy.modify().graph().createObjectAttribute("oa2", List.of(oa1));
return null;
});
System.out.println(pap.query().graph().getAssociationsWithSource(ua1));
// expected output: {ua1->oa1{associate, read, create_object_attribute, write, associate_to}, ua1->oa2{read, write}}
}
}Prohibitions can be created by specifying:
- name: or unique identifier
- subject: (either a user ID, user attribute ID, or process)
- set: of access rights to be denied
- boolean: flag indicating whether to take the union of the following container conditions
- container conditions:
- attribute ID: the ID of an attribute node
- complement (boolean): if true, the condition applies to everything outside the specified containers subgraph
pap.modify().prohibitions().createProhibition(
"deny u1 write on oa1",
new ProhibitionSubject(u1),
new AccessRightSet("write"),
false,
List.of(new ContainerCondition(oa1, false))
);Obligations should be defined using PML because obligation responses are defined using PML statements. See the full Obligation PML specification for more details
String pml = """
create obligation "sample_obligation" {
create rule "rule1"
when any user
performs "create_object_attribute"
on {
descendants: "oa1"
}
do(ctx) {
associate "ua1" and ctx.args.name with ["read", "write"]
}
}""";
pap.executePML(u1, pml);PML is a domain-specific language for defining NGAC access control policies. It provides a declarative syntax that is easier to use and maintainable than using the Java API. See the full PML specification.
Policies can be exported and imported using the Policy Machine JSON schema.
String json = pap.serialize(new JSONSerializer());
// Create new PAP and import policy
PAP newPap = new MemoryPAP();
newPap.deserialize(json, new JSONDeserializer());The Policy Machine supports custom, parameterized administrative functions for packaging policy modifications into single transactions. Administrative functions can be defined in Java or PML.
Operations are administrative functions with a single privilege check. Once the check is passed, the policy modifications in the body of the operation are executed without privilege checks.
String pml = """
operation op1(string a, string[] b) {
check "read" on [a]
check "write" on b
} {
// PML statements
}
""";
pap.executePML(userContext, pml);
// Call the operation
pap.adjudicateAdminOperation(userContext, "op1", Map.of("a", "arg1", "b", List.of("arg2", "arg3")));Routines are administrative functions with privilege checks for each modification.
String pml = """
routine routine1(string a, string[] b) {
// PML Statements
}
""";
pap.executePML(userContext, pml);
// Call the routine
pap.adjudicateAdminRoutine(userContext, "routine1", Map.of("a", "arg1", "b", List.of("arg2", "arg3")));In memory and Neo4j embedded implementations of the PAP and PolicyStore are provided.
To implement a custom policy store:
- Implement the PolicyStore Interface
public class CustomPolicyStore implements PolicyStore {
// Implementation details...
}- Implement Required Store Interfaces
GraphStoreProhibitionsStoreObligationsStoreOperationsStoreRoutinesStore
- Create a Custom PAP that extends PAP.
See the MemoryPAP implementation for a complete example.