diff --git a/README.md b/README.md index cbc65b31a3dabfab8cded3682f1cbca4c2032932..17346b4f0382c0ec3b55aa917c11179dbd6bb46e 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,151 @@ # Policy Service -The policy service exposes HTTP API to execute policies. +The policy service provides HTTP API to evaluate/execute +[OPA](https://www.openpolicyagent.org/) policies. + +It is developed using the [Goa v3](https://goa.design/) framework +and uses the [Go OPA framework](https://github.com/open-policy-agent/opa) +as a library. + +While the service is up and running, you can see a live Swagger API +description at `servicehost:serviceport/swagger-ui`. In the local docker-compose +environment, the Swagger URL is available at http://localhost:8081/swagger-ui/ + +### High-level Overview + +```mermaid +flowchart LR + A([client]) -- HTTP --> B[Policy API] + subgraph policy + B --> C[(policies DB)] + end + C --sync--- D[Git Server] +``` + +### Policy Evaluation + +The policy service exposes HTTP endpoints to evaluate/execute policies. +The endpoint interface is conformant to the TSA requirements document. + +To evaluate a policy a POST request is sent to the evaluation URL. +The example URL below is given for the local docker-compose environment. +The `host` and `port` parts will be different for the different environments. + +``` +# URL with example policy group, name and version +http://localhost:8081/policy/gaiax/didresolve/1.0/evaluation + +# URL with parameter placeholders +http://localhost:8081/policy/{group}/{policy}/{version}/evaluation +``` + +There are three parameters in the URL specifying which exact policy +should be evaluated - `group`, `policy` and `version`. These parameters +are also important during policy development (see below) as `group` +and `policy` **must** be used as package name inside the policy +source code file. + +The body of the POST request **must** be JSON and it is passed directly +to the policy execution runtime. Inside the policy it is accessed with +the global variable name `input`. For example, if you pass to the evaluation +endpoint the following JSON, it will be accessible by `input.message`: +```json +{ + "message": "hello world" +} +``` + +Here is a complete example CURL request: +```shell +curl -X POST http://localhost:8081/policy/gaiax/didresolve/1.0/evaluation -d '{"message":"hello world"}' +``` + +### Policy Locking + +The service exposes HTTP endpoints to lock and unlock policies. Locking a policy +means that it's not allowed for evaluation (execution). Unlocking a policy allows +its evaluation/execution to proceed normally. + +Lock a policy with POST request: +```shell +curl -X POST http://localhost:8081/policy/gaiax/didresolve/1.0/lock +``` + +Unlock a policy with DELETE request: +```shell +curl -X DELETE http://localhost:8081/policy/gaiax/didresolve/1.0/lock +``` + +### Policy Storage + +Policies (rego source code and metadata) are stored in a MongoDB collection `policies`, +with one collection document representing one policy. A document contains additional +policy state un-related to OPA and Rego, but necessary for implementing the TSA +requirements (e.g. policy lock/unlock). + +The database is used as read-only source of truth for the current policy state when +policies need to be evaluated. Policy storage is updated externally from a separate +component. The update process is automatically triggered by updating policy source +code files in an external Git server. + +```mermaid +flowchart LR + A[Policy\nDeveloper] --git push/merge--> B[Git branch] + subgraph Git Server + B --> C[example_1.0.rego] + B --> D[example_2.0.rego] + B --> G[example_3.0.rego] + end + C --> E[Sync] + D --> E[Sync] + G --> E[Sync] + E --update--> F + subgraph policy service + F[(policies DB)] + end +``` + +### Policy Development + +Policies are written in the [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/) +language. Please refer to the [OPA documentation](https://www.openpolicyagent.org/docs/latest/) +for detailed overview of Rego and OPA capabilities. + +**Some conventions *must* be followed when writing policies.** + +1. The filename of the policy *must* follow rules for the naming and directory structure: +the `group` is a directory inside the Git repo, while the policy name and version are used +for naming the file. For example: `/gaiax/example_1.0.rego`. +2. The policy package name inside the policy source code file *must* exactly match +the `group` and `policy` (name) of the policy. + +*What does all this mean?* + +Let's see an example for the 1st convention. +``` +package gaiax.example + +allow { + input.message == "hello world" +} +``` + +Next, the filename must be `/gaiax/example_1.0.rego`. When such file is synchronized +with the policy service (storage), the naming convention allows the service to understand +which part is the policy group, which part is policy name and which part is version. + +If we create the above policy and store it in the Git repo as `/gaiax/example_1.0.rego`, +after the Git server is synchronized with the Policy Storage, the policy service will +automatically expose URLs for working with the policy at: +``` +http://localhost:8081/policy/gaiax/example/1.0/evaluation +http://localhost:8081/policy/gaiax/example/1.0/lock +``` + +The 2nd rule for package naming is needed so that a generic evaluation function +can be mapped and used for evaluating all kinds of different policies. Without a +package naming rule, there's no way the service can automatically generate HTTP +endpoints for working with arbitrary dynamically uploaded policies. + +