Skip to content

Commit

Permalink
chore(jans-cedarling): update python example and docs (#10183)
Browse files Browse the repository at this point in the history
* chore(jans-cedarling): update examples

Signed-off-by: SafinWasi <[email protected]>

* docs(jans-cedarling): update doc for python

Signed-off-by: SafinWasi <[email protected]>

* docs(jans-cedarling): fix typos and adjust schema

Signed-off-by: SafinWasi <[email protected]>

---------

Signed-off-by: SafinWasi <[email protected]>
Co-authored-by: Mohammad Abudayyeh <[email protected]>
  • Loading branch information
SafinWasi and moabu authored Nov 19, 2024
1 parent 1ea9b26 commit 78c705f
Show file tree
Hide file tree
Showing 7 changed files with 617 additions and 552 deletions.
265 changes: 68 additions & 197 deletions docs/cedarling/python/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,232 +7,103 @@ tags:

# Python usage

In this example, we will show an example Python script that calls the `cedarling_python` module and calls the `authz` function. Before beginning, ensure that you have completed the [building steps](./README.md#building) and are currently in a virtual Python environment that has the `cedarling_python` module installed.
In this example, we will show an example Python script that calls the `cedarling_python` module and calls the `authorize()` function.

## Demo script
- Before beginning, ensure that you have completed the [building steps](./README.md#building) and are currently in a virtual Python environment that has the `cedarling_python` module installed. You can confirm this with `pip list`.
- Run the script `jans/jans-cedarling/bindings/cedarling_python/example.py` from within the virtual environment.

```python
from cedarling_python import MemoryLogConfig, DisabledLoggingConfig, StdOutLogConfig
from cedarling_python import PolicyStoreSource, PolicyStoreConfig, BootstrapConfig, JwtConfig
from cedarling_python import Cedarling, ResourceData, Request

# use MemoryLogConfig to store logs in memory with a time-to-live of 120 seconds
# by default it is 60 seconds
log_config = MemoryLogConfig(log_ttl=100)
# we can also set value to as property
# log_config.log_ttl = 120

# use DisabledLoggingConfig to ignore all logging
# log_config = DisabledLoggingConfig()

# use StdOutLogConfig to print logs to stdout
log_config = StdOutLogConfig()

# Create policy source configuration
with open("policy-store.json",
mode="r", encoding="utf8") as f:
policy_raw_json = f.read()
"""
This policy store contains 2 policy as such:
## Output
```
(venv) $ python example.py
Policy store location not provided, use 'CEDARLING_LOCAL_POLICY_STORE' environment variable
Used default policy store path: example_files/policy-store.json
{"id":"0193414e-9672-786a-986c-57f48d41c4e4","time":1731967489,"log_kind":"System","pdp_id":"c0ec33ff-9482-4bdc-83f6-2925a41a3280","msg":"configuration parsed successfully"}
{"id":"0193414e-9672-786a-986c-57f5379086c3","time":1731967489,"log_kind":"System","pdp_id":"c0ec33ff-9482-4bdc-83f6-2925a41a3280","msg":"Cedarling Authz initialized successfully","application_id":"TestApp"}
{"id":"0193414e-9676-7d8a-b55b-3f0097355851","time":1731967489,"log_kind":"Decision","pdp_id":"c0ec33ff-9482-4bdc-83f6-2925a41a3280","msg":"Result of authorize.","application_id":"TestApp","action":"Jans::Action::\"Read\"","resource":"Jans::Application::\"some_id\"","context":{"user_agent":"Linux","operating_system":"Linux","network_type":"Local","network":"127.0.0.1","geolocation":["America"],"fraud_indicators":["Allowed"],"device_health":["Healthy"],"current_time":1731967489},"person_principal":"Jans::User::\"qzxn1Scrb9lWtGxVedMCky-Ql_ILspZaQA6fyuYktw0\"","person_diagnostics":{"reason":["840da5d85403f35ea76519ed1a18a33989f855bf1cf8"],"errors":[]},"person_decision":"ALLOW","workload_principal":"Jans::Workload::\"d7f71bea-c38d-4caf-a1ba-e43c74a11a62\"","workload_diagnostics":{"reason":["444da5d85403f35ea76519ed1a18a33989f855bf1cf8"],"errors":[]},"workload_decision":"ALLOW","role_authorize_info":[{"role_principal":"Jans::Role::\"CasaAdmin\"","role_diagnostics":{"reason":[],"errors":[]},"role_decision":"DENY"}],"authorized":true}
Result of workload authorization: ALLOW
Policy ID used:
444da5d85403f35ea76519ed1a18a33989f855bf1cf8
Errors during authorization: 0
Result of person authorization: ALLOW
Policy ID used:
840da5d85403f35ea76519ed1a18a33989f855bf1cf8
Errors during authorization: 0
Role authorization present
Role authorization result: DENY
Errors during authorization: 0
```

## Explanation
Cedarling creates principal entities from the access, ID and userinfo tokens. The action, resource and context entities are declared in code. These four entities together form the `PARC` format that cedarling evaluates against policies provided in the policy store. The principal entities can be either User, Workload or Role. After forming the entities, cedarling evaluates them against the policies provided in the policy store. If entity is explicitly permitted by a policy, the result of the evaluation is `ALLOW`, otherwise it is `DENY`.

In this case there are two policies in the store, one for User entities and one for Workload entities:

```
@444da5d85403f35ea76519ed1a18a33989f855bf1cf8
permit(
principal is Jans::Workload,
action in [Jans::Action::"Update"],
resource is Jans::Issue
action in [Jans::Action::"Read"],
resource is Jans::Application
)when{
principal.org_id == resource.org_id
resource.name == "Some Application"
};
@840da5d85403f35ea76519ed1a18a33989f855bf1cf8
permit(
principal is Jans::User,
action in [Jans::Action::"Update"],
resource is Jans::Issue
action in [Jans::Action::"Read"],
resource is Jans::Application
)when{
principal.country == resource.country
resource.name == "Some Application"
};
"""
# for now we support only json source
policy_source = PolicyStoreSource(json=policy_raw_json)

policy_store_config = PolicyStoreConfig(source=policy_source)


# Create jwt configuration
# do not validate JWT tokens
jwt_config = JwtConfig(enabled=False)

# collect all in the BootstrapConfig
bootstrap_config = BootstrapConfig(
application_name="TestApp",
log_config=log_config,
policy_store_config=policy_store_config,
jwt_config=jwt_config
)

# initialize cedarling instance
# all values in the bootstrap_config is parsed and validated at this step.
instance = Cedarling(bootstrap_config)

# returns a list of all active log ids
# active_log_ids = instance.get_log_ids()

# get log entry by id
# log_entry = instance.get_log_by_id(active_log_ids[0])
```

These two policies say that a Principal entity (User or Workload) is allowed to execute a `Read` action on an Application resource when the resource is named "Some Application". As there are no policies for Role entities, the result of the evaluation for the Role entity is `DENY`.

# show logs
print("Logs stored in memory:")
print(*instance.pop_logs(), sep="\n\n")
In the script, the action, resource and context entities are used to create the request and execute the `authorize()` call:

```python

# //// Execute authentication request ////
action = 'Jans::Action::"Read"'

# Creating cedar resource object.
# field resource_type and id is mandatory
# other fields are attributes of the resource.
resource = ResourceData(resource_type="Jans::Issue",
id="random_id", org_id="some_long_id", country="US")
# or we can init resource using dict
resource = ResourceData.from_dict({
"type": "Jans::Issue",
"id": "random_id",
"org_id": "some_long_id",
"country": "US"
"type": "Jans::Application",
"id": "some_id",
"app_id": "application_id",
"name": "Some Application",
"url": {
"host": "jans.test",
"path": "/protected-endpoint",
"protocol": "http"
}
})


action_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJib0c4ZGZjNU1LVG4zN283Z3NkQ2V5cUw4THBXUXRnb080MW0xS1p3ZHEwIiwiY29kZSI6ImJmMTkzNGY2LTM5MDUtNDIwYS04Mjk5LTZiMmUzZmZkZGQ2ZSIsImlzcyI6Imh0dHBzOi8vYWRtaW4tdWktdGVzdC5nbHV1Lm9yZyIsInRva2VuX3R5cGUiOiJCZWFyZXIiLCJjbGllbnRfaWQiOiI1YjQ0ODdjNC04ZGIxLTQwOWQtYTY1My1mOTA3YjgwOTQwMzkiLCJhdWQiOiI1YjQ0ODdjNC04ZGIxLTQwOWQtYTY1My1mOTA3YjgwOTQwMzkiLCJhY3IiOiJiYXNpYyIsIng1dCNTMjU2IjoiIiwic2NvcGUiOlsib3BlbmlkIiwicHJvZmlsZSJdLCJvcmdfaWQiOiJzb21lX2xvbmdfaWQiLCJhdXRoX3RpbWUiOjE3MjQ4MzA3NDYsImV4cCI6MTcyNDk0NTk3OCwiaWF0IjoxNzI0ODMyMjU5LCJqdGkiOiJseFRtQ1ZSRlR4T2pKZ3ZFRXBvek1RIiwibmFtZSI6IkRlZmF1bHQgQWRtaW4gVXNlciIsInN0YXR1cyI6eyJzdGF0dXNfbGlzdCI6eyJpZHgiOjIwMSwidXJpIjoiaHR0cHM6Ly9hZG1pbi11aS10ZXN0LmdsdXUub3JnL2phbnMtYXV0aC9yZXN0djEvc3RhdHVzX2xpc3QifX19._eQT-DsfE_kgdhA0YOyFxxPEMNw44iwoelWa5iU1n9s"
"""
JSON payload of access token
{
"sub": "boG8dfc5MKTn37o7gsdCeyqL8LpWQtgoO41m1KZwdq0",
"code": "bf1934f6-3905-420a-8299-6b2e3ffddd6e",
"iss": "https://admin-ui-test.gluu.org",
"token_type": "Bearer",
"client_id": "5b4487c4-8db1-409d-a653-f907b8094039",
"aud": "5b4487c4-8db1-409d-a653-f907b8094039",
"acr": "basic",
"x5t#S256": "",
"scope": [
"openid",
"profile"
],
"org_id": "some_long_id",
"auth_time": 1724830746,
"exp": 1724945978,
"iat": 1724832259,
"jti": "lxTmCVRFTxOjJgvEEpozMQ",
"name": "Default Admin User",
"status": {
"status_list": {
"idx": 201,
"uri": "https://admin-ui-test.gluu.org/jans-auth/restv1/status_list"
}
}
context = {
"current_time": int(time.time()),
"device_health": ["Healthy"],
"fraud_indicators": ["Allowed"],
"geolocation": ["America"],
"network": "127.0.0.1",
"network_type": "Local",
"operating_system": "Linux",
"user_agent": "Linux"
}
"""

id_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY3IiOiJiYXNpYyIsImFtciI6IjEwIiwiYXVkIjoiNWI0NDg3YzQtOGRiMS00MDlkLWE2NTMtZjkwN2I4MDk0MDM5IiwiZXhwIjoxNzI0ODM1ODU5LCJpYXQiOjE3MjQ4MzIyNTksInN1YiI6ImJvRzhkZmM1TUtUbjM3bzdnc2RDZXlxTDhMcFdRdGdvTzQxbTFLWndkcTAiLCJpc3MiOiJodHRwczovL2FkbWluLXVpLXRlc3QuZ2x1dS5vcmciLCJqdGkiOiJzazNUNDBOWVNZdWs1c2FIWk5wa1p3Iiwibm9uY2UiOiJjMzg3MmFmOS1hMGY1LTRjM2YtYTFhZi1mOWQwZTg4NDZlODEiLCJzaWQiOiI2YTdmZTUwYS1kODEwLTQ1NGQtYmU1ZC01NDlkMjk1OTVhMDkiLCJqYW5zT3BlbklEQ29ubmVjdFZlcnNpb24iOiJvcGVuaWRjb25uZWN0LTEuMCIsImNfaGFzaCI6InBHb0s2WV9SS2NXSGtVZWNNOXV3NlEiLCJhdXRoX3RpbWUiOjE3MjQ4MzA3NDYsImdyYW50IjoiYXV0aG9yaXphdGlvbl9jb2RlIiwic3RhdHVzIjp7InN0YXR1c19saXN0Ijp7ImlkeCI6MjAyLCJ1cmkiOiJodHRwczovL2FkbWluLXVpLXRlc3QuZ2x1dS5vcmcvamFucy1hdXRoL3Jlc3R2MS9zdGF0dXNfbGlzdCJ9fX0.8BwLLGkFpWGx8wGpvVmNk_Ao8nZrP_WT-zoo-MY4zqY"
"""
JSON payload of id token
{
"acr": "basic",
"amr": "10",
"aud": "5b4487c4-8db1-409d-a653-f907b8094039",
"exp": 1724835859,
"iat": 1724832259,
"sub": "boG8dfc5MKTn37o7gsdCeyqL8LpWQtgoO41m1KZwdq0",
"iss": "https://admin-ui-test.gluu.org",
"jti": "sk3T40NYSYuk5saHZNpkZw",
"nonce": "c3872af9-a0f5-4c3f-a1af-f9d0e8846e81",
"sid": "6a7fe50a-d810-454d-be5d-549d29595a09",
"jansOpenIDConnectVersion": "openidconnect-1.0",
"c_hash": "pGoK6Y_RKcWHkUecM9uw6Q",
"auth_time": 1724830746,
"grant": "authorization_code",
"status": {
"status_list": {
"idx": 202,
"uri": "https://admin-ui-test.gluu.org/jans-auth/restv1/status_list"
}
}
}
"""

userinfo_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb3VudHJ5IjoiVVMiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJ1c2VybmFtZSI6IlVzZXJOYW1lRXhhbXBsZSIsInN1YiI6ImJvRzhkZmM1TUtUbjM3bzdnc2RDZXlxTDhMcFdRdGdvTzQxbTFLWndkcTAiLCJpc3MiOiJodHRwczovL2FkbWluLXVpLXRlc3QuZ2x1dS5vcmciLCJnaXZlbl9uYW1lIjoiQWRtaW4iLCJtaWRkbGVfbmFtZSI6IkFkbWluIiwiaW51bSI6IjhkMWNkZTZhLTE0NDctNDc2Ni1iM2M4LTE2NjYzZTEzYjQ1OCIsImNsaWVudF9pZCI6IjViNDQ4N2M0LThkYjEtNDA5ZC1hNjUzLWY5MDdiODA5NDAzOSIsImF1ZCI6IjViNDQ4N2M0LThkYjEtNDA5ZC1hNjUzLWY5MDdiODA5NDAzOSIsInVwZGF0ZWRfYXQiOjE3MjQ3Nzg1OTEsIm5hbWUiOiJEZWZhdWx0IEFkbWluIFVzZXIiLCJuaWNrbmFtZSI6IkFkbWluIiwiZmFtaWx5X25hbWUiOiJVc2VyIiwianRpIjoiZmFpWXZhWUlUMGNEQVQ3Rm93MHBRdyIsImphbnNBZG1pblVJUm9sZSI6WyJhcGktYWRtaW4iXSwiZXhwIjoxNzI0OTQ1OTc4fQ.3LTc8YLvEeb7ONZp_FKA7yPP7S6e_VTzwhvAWUJrL4M"
"""
JSON payload of userinfo token
{
"country": "US",
"email": "[email protected]",
"username": "UserNameExample",
"sub": "boG8dfc5MKTn37o7gsdCeyqL8LpWQtgoO41m1KZwdq0",
"iss": "https://admin-ui-test.gluu.org",
"given_name": "Admin",
"middle_name": "Admin",
"inum": "8d1cde6a-1447-4766-b3c8-16663e13b458",
"client_id": "5b4487c4-8db1-409d-a653-f907b8094039",
"aud": "5b4487c4-8db1-409d-a653-f907b8094039",
"updated_at": 1724778591,
"name": "Default Admin User",
"nickname": "Admin",
"family_name": "User",
"jti": "faiYvaYIT0cDAT7Fow0pQw",
"jansAdminUIRole": [
"api-admin"
],
"exp": 1724945978
}
"""


# Creating cedarling request
request = Request(
action_token,
access_token,
id_token,
userinfo_token,
action='Jans::Action::"Update"',
context={}, resource=resource)
action=action,
resource=resource, context=context)

# Authorize call
authorize_result = instance.authorize(request)
print(*instance.pop_logs(), sep="\n\n")

# if you change org_id result will be false
assert authorize_result.is_allowed()

# watch on the decision for workload
workload_result = authorize_result.workload()
print(workload_result.decision)

# show diagnostic information
workload_diagnostic = workload_result.diagnostics
for i, reason in enumerate(workload_diagnostic.reason):
if i == 0:
print("reason policies:")
print(reason)

for i, error in enumerate(workload_diagnostic.errors):
if i == 0:
print("errors:")
print("id:", error.id, "error:", error.error)
```

- Save the script in a file called `example.py`
- Save [this](https://github.com/JanssenProject/jans/blob/main/jans-cedarling/bindings/cedarling_python/example_files/policy-store.json) demo policy store file to `policy-store.json` in the same location
- Run the example script:

```
(venv) $ python example.py
Logs stored in memory:
{"id":"01929b39-b7d9-7e58-8abb-329c19c3d821","time":1729181104,"log_kind":"System","pdp_id":"1039a067-88de-4e8b-8d5f-421fc912b94f","msg":"PolicyStore loaded successfully","application_id":"TestApp"}
{"id":"01929b39-b7d9-7e58-8abb-329dfb797de1","time":1729181104,"log_kind":"System","pdp_id":"1039a067-88de-4e8b-8d5f-421fc912b94f","msg":"JWT service loaded successfully","application_id":"TestApp"}
{"id":"01929b39-b7d9-7e58-8abb-329e91d2b37f","time":1729181104,"log_kind":"System","pdp_id":"1039a067-88de-4e8b-8d5f-421fc912b94f","msg":"Cedarling Authz initialized successfully","application_id":"TestApp"}
Logs stored in memory:
{"id":"01929b39-b7db-766c-95a8-4790ff90532d","time":1729181104,"log_kind":"Decision","pdp_id":"1039a067-88de-4e8b-8d5f-421fc912b94f","msg":"Result of authorize with resource as workload entity","application_id":"TestApp","principal":"Jans::Workload::\"5b4487c4-8db1-409d-a653-f907b8094039\"","action":"Jans::Action::\"Update\"","resource":"Jans::Issue::\"random_id\"","context":{},"decision":"ALLOW","diagnostics":{"reason":["840da5d85403f35ea76519ed1a18a33989f855bf1cf8"],"errors":[]}}
```
Cedarling will return `is_allowed()` as `True` only if both the User and Workload entity evaluations are `ALLOW`.

As you can see, the logs from cedarling's initialization and the result of the authorize call is printed to the console.

## Exposed functions

Expand Down
2 changes: 1 addition & 1 deletion jans-cedarling/bindings/cedarling_python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ If you only want to build the library without installing it in the Python enviro

To run the tests, with `pytest`:

1. Make sure that you have installed the `cedarling_python` package in your virtual enviroment or system.
1. Make sure that you have installed the `cedarling_python` package in your virtual environment or system.
1. Install `pytest`:

```bash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,10 @@ class Request:
resource: ResourceData
context: Dict[str, Any]

def __init__(self, access_token: str,
def __init__(self,
access_token: str,
id_token: str,
userinfo_token: str,
action: str,
resource: ResourceData,
context: Dict[str, Any]) -> None: ...
Expand Down
Loading

0 comments on commit 78c705f

Please sign in to comment.