Skip to content

Commit

Permalink
feat: PolicyEngineExpressionLanguage - PEEL support added
Browse files Browse the repository at this point in the history
  • Loading branch information
ivsokol committed Sep 21, 2024
1 parent c304f95 commit 272c349
Show file tree
Hide file tree
Showing 87 changed files with 7,809 additions and 35 deletions.
46 changes: 32 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,26 @@
<br />
<br />

PoE is a catalog driven policy engine written in Kotlin. It provides a flexible and extensible framework for defining and evaluating
PolicyEngine - PoE is a catalog driven policy engine written in Kotlin. It provides a flexible framework for defining and evaluating
policies and conditions based on the provided context.

PoE is designed to be used as a library in various enterprise applications, for scenarios as [access control](https://ivsokol.github.io/poe/docs/examples/access-control.md), authorization, credit scoring, price calculation or any other use
PoE is designed to be used as a library in various enterprise applications, for scenarios as [access control](https://ivsokol.github.io/policy-engine/docs/examples/access-control.md), authorization, credit scoring, price calculation or any other use
case where policies and conditions need to be evaluated. Catalog definition is defined in JSON format.

It is written in Kotlin and can be used in any Java or Kotlin JVM project. As library deals with potentially complex definitions, all parts of the engine are extensively tested.
It is written in Kotlin and can be used in any Java or Kotlin JVM project. As library deals with potentially complex definitions, all parts of the engine are extensively tested (code is covered with more than 2000 tests).

[PolicyEngine](https://ivsokol.github.io/poe/docs/policy-engine.md) is a catalog driven policy engine, so in order to use it, you need to define a catalog. [PolicyCatalog](https://ivsokol.github.io/poe/docs/policy-catalog.md) can be defined as a Class or as a JSON string. JSON string can be pulled from a remote location or from a local file (not part of the engine). One engine instance can only contain one catalog, but it is possible to create multiple PolicyEngine instances at the same time.
[PolicyEngine](https://ivsokol.github.io/policy-engine/docs/policy-engine.md) is a catalog driven policy engine, so in order to use it, you need to define a catalog. [PolicyCatalog](https://ivsokol.github.io/policy-engine/docs/policy-catalog.md) can be defined as a Class or as a JSON string. JSON string can be pulled from a remote location or from a local file (not part of the engine). One engine instance can only contain one catalog, but it is possible to create multiple PolicyEngine instances at the same time.

One instance of PolicyEngine can be used to evaluate multiple policies or conditions in parallel (it can be used as a singleton).
PolicyEngine is doing variable extraction over a provided [Context](https://ivsokol.github.io/poe/docs/context.md). Engine cannot communicate with external systems, so
PolicyEngine is doing variable extraction over a provided [Context](https://ivsokol.github.io/policy-engine/docs/context.md). Engine cannot communicate with external systems, so
it is up to the client to provide all necessary data to the engine as part of the Context stores.

Engine also ships with expression language support (PEEL - [PolicyEngine Expression Language](https://ivsokol.github.io/policy-engine/docs/expression-language.md)). It allows users to define custom conditions and policies through strings that can be evaluated by the engine.


## Documentation

Full documentation can be found on [ivsokol.github.io/poe](https://ivsokol.github.io/poe) page.
Full documentation can be found on [ivsokol.github.io/policy-engine](https://ivsokol.github.io/policy-engine) page.

### Inspiration

Expand All @@ -39,18 +42,18 @@ is not a direct implementation of XACML, it shares the same principles and conce
### Engine entities

PolicyEngine is based on following entities:
* **[PolicyCatalog](https://ivsokol.github.io/poe/docs/policy-catalog.md)** - a collection of all engine entities.
* **[Policy](https://ivsokol.github.io/poe/docs/policy.md)** - entity that evaluates a condition or other Policies and returns result with possible side effects.
* **[PolicyCondition](https://ivsokol.github.io/poe/docs/policy-condition.md)** - entity that evaluates a condition and returns boolean result.
* **[PolicyVariable](https://ivsokol.github.io/poe/docs/policy-variable.md)** - entity that represents a dynamic or static variable that can be used in a Policy or PolicyCondition.
* **[PolicyAction](https://ivsokol.github.io/poe/docs/policy-action.md)** - entity that executes a side effect of a Policy evaluation.
* **[PolicyCatalog](https://ivsokol.github.io/policy-engine/docs/policy-catalog.md)** - a collection of all engine entities.
* **[Policy](https://ivsokol.github.io/policy-engine/docs/policy.md)** - entity that evaluates a condition or other Policies and returns result with possible side effects.
* **[PolicyCondition](https://ivsokol.github.io/policy-engine/docs/policy-condition.md)** - entity that evaluates a condition and returns boolean result.
* **[PolicyVariable](https://ivsokol.github.io/policy-engine/docs/policy-variable.md)** - entity that represents a dynamic or static variable that can be used in a Policy or PolicyCondition.
* **[PolicyAction](https://ivsokol.github.io/policy-engine/docs/policy-action.md)** - entity that executes a side effect of a Policy evaluation.

## Getting Started

In order to use PoE in your project, you need to add the following dependency to your project:

```kotlin
implementation("io.github.ivsokol:poe:1.0.0")
implementation("io.github.ivsokol:poe:1.1.0")
```

After that you need to define a PolicyCatalog and instantiate a PolicyEngine.
Expand All @@ -62,7 +65,7 @@ val engine = PolicyEngine(catalog)

Once PolicyEngine is instantiated, you can start evaluating policies and conditions by defining a Context. What can be provided in Context is up to the client. Usually request data is put in request store (it can contain body of a request, headers, metadata, ...),
security data (username, roles, ...) is put in subject store and server related data is put in the environment store.
Context stores are maps, as explained in [Context stores](https://ivsokol.github.io/poe/docs/context.md#stores) page.
Context stores are maps, as explained in [Context stores](https://ivsokol.github.io/policy-engine/docs/context.md#stores) page.
One context should be created for each evaluation.

```kotlin
Expand All @@ -73,7 +76,7 @@ val context = Context(
```

Then you can evaluate a policy by calling `evaluate` method or condition by calling `check` method on PolicyEngine. There are different methods defined, where you can execute method by id, Reference, label, list of ids or references. You can also execute all Conditions
or Policies in the catalog. All possible methods are described in [PolicyEngine](https://ivsokol.github.io/poe/docs/policy-engine.md) page.
or Policies in the catalog. All possible methods are described in [PolicyEngine](https://ivsokol.github.io/policy-engine/docs/policy-engine.md) page.

**PolicyCondition check by id**
```kotlin
Expand Down Expand Up @@ -114,6 +117,21 @@ val result =
// result value will always be true as condition argument is always in the past
```

**Custom Condition check by expression language**
```kotlin
val policyConditionStr = """
*past(
#dTime(15.05.2023 14:30:00.123 +01:00,
#opts(dateTimeFormat="dd.MM.yyyy HH:mm:ss.SSS XXX")
)
)
""".trimIndent()
val context = Context()
val result =
PolicyEngine().checkCondition(PEELParser(policyConditionStr).parseCondition(), context)
// result value will always be true as condition argument is always in the past
```

**Custom Policy evaluation**

```kotlin
Expand Down
12 changes: 6 additions & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ plugins {

group = "io.github.ivsokol"

version = "1.0.0"
version = "1.1.0"

repositories {
mavenLocal()
Expand Down Expand Up @@ -82,7 +82,7 @@ jreleaser {
license = "Apache-2.0"
maintainer("Ivan Sokol")
links {
homepage = "https://ivsokol.github.io/poe"
homepage = "https://ivsokol.github.io/policy-engine"
license = "https://opensource.org/licenses/Apache-2.0"
}
java {
Expand Down Expand Up @@ -126,8 +126,8 @@ publishing {
pom {
name = "PolicyEngine"
description =
"PolicyEngine - PoE is a catalog driven policy engine written in Kotlin. It provides a flexible and extensible framework for defining and evaluating policies and conditions based on the provided context."
url = "https://ivsokol.github.io/poe"
"PolicyEngine - PoE is a catalog driven policy engine written in Kotlin. It provides a flexible framework for defining and evaluating policies and conditions based on the provided context with expression language support."
url = "https://ivsokol.github.io/policy-engine"
licenses {
license {
name = "The Apache License, Version 2.0"
Expand All @@ -142,8 +142,8 @@ publishing {
}
}
scm {
connection = "scm:git:https://ivsokol.github.io/poe.git"
url = "https://ivsokol.github.io/poe"
connection = "scm:git:https://ivsokol.github.io/policy-engine.git"
url = "https://ivsokol.github.io/policy-engine"
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions examples/policy-action/policy-action-clear-managed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"id": "polAct1",
"version": "1.2.3",
"description": "This is a managed PolicyActionClear",
"labels": [
"label1"
],
"key": "foo",
"failOnMissingKey": true,
"type": "clear"
}
5 changes: 5 additions & 0 deletions examples/policy-action/policy-action-clear.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"key": "foo",
"failOnMissingKey": true,
"type": "clear"
}
20 changes: 20 additions & 0 deletions examples/policy-action/policy-action-json-merge-managed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"id": "polAct1",
"version": "1.2.3",
"description": "This is a managed PolicyActionJsonMerge",
"labels": [
"label1"
],
"key": "someJson",
"source": {
"id": "polVar1",
"refType": "PolicyVariableRef"
},
"merge": {
"type": "string",
"value": "{\"foo\":\"baz\"}"
},
"failOnMissingKey": true,
"failOnNullSource": true,
"type": "jsonMerge"
}
12 changes: 12 additions & 0 deletions examples/policy-action/policy-action-json-merge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"key": "someJson",
"source": {
"type": "string",
"value": "{\"foo\":\"bar\"}"
},
"merge": {
"type": "string",
"value": "{\"foo\":\"baz\"}"
},
"type": "jsonMerge"
}
20 changes: 20 additions & 0 deletions examples/policy-action/policy-action-json-patch-managed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"id": "polAct1",
"version": "1.2.3",
"description": "This is a managed PolicyActionJsonPatch",
"labels": [
"label1"
],
"key": "someJson",
"source": {
"id": "polVar1",
"refType": "PolicyVariableRef"
},
"patch": {
"type": "string",
"value": "[{\"op\":\"replace\",\"path\":\"/foo\",\"value\":[\"bar\"]}]"
},
"failOnMissingKey": true,
"failOnNullSource": true,
"type": "jsonPatch"
}
12 changes: 12 additions & 0 deletions examples/policy-action/policy-action-json-patch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"key": "someJson",
"source": {
"type": "string",
"value": "{\"foo\":[\"bar\",\"baz\"],\"foo2\":\"baz\"}"
},
"patch": {
"type": "string",
"value": "[{\"op\":\"replace\",\"path\":\"/foo\",\"value\":[\"bar\"]}]"
},
"type": "jsonPatch"
}
14 changes: 14 additions & 0 deletions examples/policy-action/policy-action-save-dynamic.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"key": "foo",
"value": {
"resolvers": [
{
"id": "birthdayResolver",
"refType": "PolicyVariableResolverRef"
}
]
},
"failOnMissingKey": true,
"failOnNullSource": true,
"type": "save"
}
20 changes: 20 additions & 0 deletions examples/policy-action/policy-action-save-managed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"id": "polAct1",
"version": "1.2.3",
"description": "This is a managed PolicyActionSave",
"labels": [
"label1"
],
"key": "foo",
"value": {
"resolvers": [
{
"id": "birthdayResolver",
"refType": "PolicyVariableResolverRef"
}
]
},
"failOnMissingKey": true,
"failOnNullSource": true,
"type": "save"
}
9 changes: 9 additions & 0 deletions examples/policy-action/policy-action-save-static.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"key": "foo",
"value": {
"type": "string",
"value": "bar"
},
"failOnExistingKey": true,
"type": "save"
}
Loading

0 comments on commit 272c349

Please sign in to comment.