Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(jans-cedarling): Implement python bindings for the authorize method #9731

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions jans-cedarling/bindings/cedarling_python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ crate-type = ["cdylib"]
[dependencies]
pyo3 = { version = "0.22.0", features = ["extension-module"] }
cedarling = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
serde-pyobject = "0.4.0"
125 changes: 108 additions & 17 deletions jans-cedarling/bindings/cedarling_python/PYTHON_TYPES.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,54 +155,145 @@ Methods

:param config: A `BootstrapConfig` object with startup settings.

.. method:: pop_logs(self)
.. method:: pop_logs(self) -> List[dict]

Retrieves and removes all logs from storage.

:returns: A list of log entries as Python objects.
:rtype: List[PyObject]

:raises ValueError: If an error occurs while fetching logs.

.. method:: get_log_by_id(self, id)
.. method:: get_log_by_id(self, id: str) -> dict|None

Gets a log entry by its ID.

:param id: The log entry ID.
:type id: str

:returns: The log entry as a Python object or None if not found.
:rtype: Optional[PyObject]

:raises ValueError: If an error occurs while fetching the log.

.. method:: get_log_ids(self)
.. method:: get_log_ids(self) -> List[str]

Retrieves all stored log IDs.

:returns: A list of log entry IDs.
:rtype: List[str]
.. method:: authorize(self, request: Request) -> AuthorizeResult

Execute authorize request
:param request: Request struct for authorize.

___

.. method:: authorize(self, Request)
ResourceData
============

Evaluate Authorization Request.
A Python wrapper for the Rust `cedarling::ResourceData` struct. This class represents
a resource entity with a type, ID, and attributes. Attributes are stored as a payload
in a dictionary format.

Attributes
----------
:param resource_type: Type of the resource entity.
:param id: ID of the resource entity.
:param payload: Optional dictionary of attributes.

Methods
-------
.. method:: __init__(self, resource_type: str, id: str, **kwargs: dict)
Initialize a new ResourceData. In kwargs the payload is a dictionary of entity attributes.

.. method:: from_dict(cls, value: dict) -> ResourceData
Initialize a new ResourceData from a dictionary.
To pass `resource_type` you need to use `type` key.
___

Request
=======

Python wrapper for the Rust `cedarling::Request` struct.
Stores authorization data
A Python wrapper for the Rust `cedarling::Request` struct. Represents
authorization data with access token, action, resource, and context.

Attributes
----------
:param access_token: A string containing the access token.
:param access_token: The access token string.
:param action: The action to be authorized.
:param resource: Resource data (wrapped `ResourceData` object).
:param context: Python dictionary with additional context.

Example
-------
```python
# Create a request for authorization
request = Request(access_token="token123", action="read", resource=resource, context={})
```
req = Request(access_token="your_token")
```
___

AuthorizeResult
===============

A Python wrapper for the Rust `cedarling::AuthorizeResult` struct.
Represents the result of an authorization request.

Methods
-------
.. method:: is_allowed(self) -> bool
Returns whether the request is allowed.

.. method:: workload(self) -> AuthorizeResultResponse
Returns the detailed response as an `AuthorizeResultResponse` object.

___

AuthorizeResultResponse
=======================

A Python wrapper for the Rust `cedar_policy::Response` struct.
Represents the result of an authorization request.

Attributes
----------
:param decision: The authorization decision (wrapped `Decision` object).
:param diagnostics: Additional information on the decision (wrapped `Diagnostics` object).
___

Decision
========

Represents the decision result of a Cedar policy authorization.

Methods
-------
value() -> str
Returns the string value of the decision.
__str__() -> str
Returns the string representation of the decision.
__repr__() -> str
Returns the detailed type representation of the decision.
__eq__(other: Decision) -> bool
Compares two `Decision` objects for equality.
___

Diagnostics
===========

Provides detailed information about how a policy decision was made, including policies that contributed to the decision and any errors encountered during evaluation.

Attributes
----------
reason : set of str
A set of `PolicyId`s for the policies that contributed to the decision. If no policies applied, this set is empty.
errors : list of PolicyEvaluationError
A list of errors that occurred during the authorization process. These are unordered as policies may be evaluated in any order.
___

PolicyEvaluationError
=====================

Represents an error that occurred when evaluating a Cedar policy.

Attributes
----------
id : str
The ID of the policy that caused the error.
error : str
The error message describing the evaluation failure.
___

47 changes: 45 additions & 2 deletions jans-cedarling/bindings/cedarling_python/example.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from cedarling_python import MemoryLogConfig, DisabledLoggingConfig, StdOutLogConfig
from cedarling_python import PolicyStoreSource, PolicyStoreConfig, BootstrapConfig, JwtConfig
from cedarling_python import Cedarling

from cedarling_python import ResourceData, Request

# use log config to store logs in memory with a time-to-live of 120 seconds
# by default it is 60 seconds
Expand All @@ -13,7 +13,7 @@
# log_config = DisabledLoggingConfig()

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

# Create policy source configuration
with open("example_files/policy-store.json",
Expand Down Expand Up @@ -53,3 +53,46 @@
# show logs
print("Logs stored in memory:")
print(*instance.pop_logs(), sep="\n\n")


# //// Execute authentication request ////

# 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")
# or we can init resource using dict
resource = ResourceData.from_dict({
"type": "Jans::Issue",
"id": "random_id",
"org_id": 1 # "some_long_id"
})

action_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJib0c4ZGZjNU1LVG4zN283Z3NkQ2V5cUw4THBXUXRnb080MW0xS1p3ZHEwIiwiY29kZSI6ImJmMTkzNGY2LTM5MDUtNDIwYS04Mjk5LTZiMmUzZmZkZGQ2ZSIsImlzcyI6Imh0dHBzOi8vYWRtaW4tdWktdGVzdC5nbHV1Lm9yZyIsInRva2VuX3R5cGUiOiJCZWFyZXIiLCJjbGllbnRfaWQiOiI1YjQ0ODdjNC04ZGIxLTQwOWQtYTY1My1mOTA3YjgwOTQwMzkiLCJhdWQiOiI1YjQ0ODdjNC04ZGIxLTQwOWQtYTY1My1mOTA3YjgwOTQwMzkiLCJhY3IiOiJiYXNpYyIsIng1dCNTMjU2IjoiIiwic2NvcGUiOlsib3BlbmlkIiwicHJvZmlsZSJdLCJvcmdfaWQiOiJzb21lX2xvbmdfaWQiLCJhdXRoX3RpbWUiOjE3MjQ4MzA3NDYsImV4cCI6MTcyNDk0NTk3OCwiaWF0IjoxNzI0ODMyMjU5LCJqdGkiOiJseFRtQ1ZSRlR4T2pKZ3ZFRXBvek1RIiwibmFtZSI6IkRlZmF1bHQgQWRtaW4gVXNlciIsInN0YXR1cyI6eyJzdGF0dXNfbGlzdCI6eyJpZHgiOjIwMSwidXJpIjoiaHR0cHM6Ly9hZG1pbi11aS10ZXN0LmdsdXUub3JnL2phbnMtYXV0aC9yZXN0djEvc3RhdHVzX2xpc3QifX19._eQT-DsfE_kgdhA0YOyFxxPEMNw44iwoelWa5iU1n9s"

request = Request(
action_token,
action='Jans::Action::"Update"',
context={}, resource=resource)

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)
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,12 @@
"description": "gluu",
"policies": {
"840da5d85403f35ea76519ed1a18a33989f855bf1cf8": {
"description": "admin access",
"description": "simple policy example",
"creation_date": "2024-09-20T17:22:39.996050",
"policy_content": "QGlkKCJhZG1pbiBhY2Nlc3MiKQpwZXJtaXQKKAogcHJpbmNpcGFsID09IEphbnM6OlJvbGU6OiJBZG1pbiIsCiBhY3Rpb24gaW4gW0phbnM6OkFjdGlvbjo6IkNvbXBhcmUiLEphbnM6OkFjdGlvbjo6IkV4ZWN1dGUiXSwKIHJlc291cmNlID09IEphbnM6OkFwcGxpY2F0aW9uOjoiQWRtaW4iCikKd2hlbgp7CiBKYW5zOjpBY2Nlc3NfdG9rZW46OiJhYmMiLnNjb3BlPT0iYWJjIiAmJiBKYW5zOjppZF90b2tlbjo6ImlkeHh4Ii5hbXI9PSJpZHh4eCIgIAp9Ow=="
},
"b6313811924c9e67f898257cbf017674e08203779ae9": {
"description": "manager access",
"creation_date": "2024-09-20T18:11:26.442574",
"policy_content": "QGlkKCJtYW5hZ2VyIGFjY2VzcyIpCnBlcm1pdAooCiBwcmluY2lwYWwsCiBhY3Rpb24sCiByZXNvdXJjZQopCndoZW4KewogSmFuczo6QWNjZXNzX3Rva2VuOjoieHh4Ii5zY29wZT09Inh4eCIgfHwgSmFuczo6aWRfdG9rZW46OiJpZHh4eCIuYW1yPT0iaWR4eHgiICYmIGNvbnRleHQubmV0d29yay5pc0luUmFuZ2UoaXAoIjIyMi4yMjIuMjIyLjAvMjQiKSkgIAp9Ow=="
},
"f2b38413cad977ab21616bd4a63c233548491cf25b72": {
"description": "manager access",
"creation_date": "2024-09-20T18:11:37.774401",
"policy_content": "QGlkKCJtYW5hZ2VyIGFjY2VzcyIpCnBlcm1pdAooCiBwcmluY2lwYWwsCiBhY3Rpb24sCiByZXNvdXJjZQopCndoZW4KewogSmFuczo6QWNjZXNzX3Rva2VuOjoieHh4Ii5zY29wZT09Inh4eCIgfHwgSmFuczo6aWRfdG9rZW46OiJpZHh4eCIuYW1yPT0iaWR4eHgiICYmIGNvbnRleHQubmV0d29yay5pc0luUmFuZ2UoaXAoIjIyMi4yMjIuMjIyLjAvMjQiKSkgIAp9Ow=="
},
"fa6a3f46ab5f741e806deff0f81d0f848af37604500f": {
"description": "without condition",
"creation_date": "2024-09-22T18:18:35.801566",
"policy_content": "QGlkKCJ3aXRob3V0IGNvbmRpdGlvbiIpCnBlcm1pdAooCiBwcmluY2lwYWwgPT0gSmFuczo6Um9sZTo6IkFkbWluIiwKIGFjdGlvbiwKIHJlc291cmNlCikKOw=="
},
"96deb02f8ce44c46d497d44dbfec80b3b6a64fe22994": {
"description": "forbid",
"creation_date": "2024-09-23T14:51:21.480763",
"policy_content": "QGlkKCJmb3JiaWQiKQpmb3JiaWQKKAogcHJpbmNpcGFsIGluIEphbnM6OlJvbGU6OiJBZG1pbiIsCiBhY3Rpb24gaW4gW0phbnM6OkFjdGlvbjo6IlNlYXJjaCIsSmFuczo6QWN0aW9uOjoiVGFnIl0sCiByZXNvdXJjZSBpbiBKYW5zOjpBcHBsaWNhdGlvbjo6IkFkbWluUG9ydGFsIgopCndoZW4KewogSmFuczo6QWNjZXNzX3Rva2VuOjoieHh4Ii5leHA+MTIzICYmIEphbnM6OkFjY2Vzc190b2tlbjo6ImFhYSIuZXhwPDMyMSB8fCBKYW5zOjpBY2Nlc3NfdG9rZW46OiJhYWEiLmlhdD49MTExICAKfTs="
"policy_content": "cGVybWl0KAogICAgcHJpbmNpcGFsIGlzIEphbnM6Oldvcmtsb2FkLAogICAgYWN0aW9uIGluIFtKYW5zOjpBY3Rpb246OiJVcGRhdGUiXSwKICAgIHJlc291cmNlIGlzIEphbnM6Oklzc3VlCil3aGVuewogICAgcHJpbmNpcGFsLm9yZ19pZCA9PSByZXNvdXJjZS5vcmdfaWQKfTs="
}
},
"identity_source": {},
"schema": "eyJKYW5zIjp7ImNvbW1vblR5cGVzIjp7IlVybCI6eyJ0eXBlIjoiUmVjb3JkIiwiYXR0cmlidXRlcyI6eyJob3N0Ijp7InR5cGUiOiJTdHJpbmcifSwicGF0aCI6eyJ0eXBlIjoiU3RyaW5nIn0sInByb3RvY29sIjp7InR5cGUiOiJTdHJpbmcifX19LCJDb250ZXh0Ijp7InR5cGUiOiJSZWNvcmQiLCJhdHRyaWJ1dGVzIjp7ImJyb3dzZXIiOnsidHlwZSI6IlN0cmluZyJ9LCJjdXJyZW50X3RpbWUiOnsidHlwZSI6IkxvbmcifSwiZGV2aWNlX2hlYWx0aCI6eyJ0eXBlIjoiU2V0IiwiZWxlbWVudCI6eyJ0eXBlIjoiU3RyaW5nIn19LCJmcmF1ZF9pbmRpY2F0b3JzIjp7InR5cGUiOiJTZXQiLCJlbGVtZW50Ijp7InR5cGUiOiJTdHJpbmcifX0sImdlb2xvY2F0aW9uIjp7InR5cGUiOiJTZXQiLCJlbGVtZW50Ijp7InR5cGUiOiJTdHJpbmcifX0sIm5ldHdvcmsiOnsidHlwZSI6IkV4dGVuc2lvbiIsIm5hbWUiOiJpcGFkZHIifSwibmV0d29ya190eXBlIjp7InR5cGUiOiJTdHJpbmcifSwib3BlcmF0aW5nX3N5c3RlbSI6eyJ0eXBlIjoiU3RyaW5nIn19fSwiZW1haWxfYWRkcmVzcyI6eyJ0eXBlIjoiUmVjb3JkIiwiYXR0cmlidXRlcyI6eyJkb21haW4iOnsidHlwZSI6IlN0cmluZyJ9LCJpZCI6eyJ0eXBlIjoiU3RyaW5nIn19fX0sImVudGl0eVR5cGVzIjp7IlVzZXJpbmZvX3Rva2VuIjp7InNoYXBlIjp7InR5cGUiOiJSZWNvcmQiLCJhdHRyaWJ1dGVzIjp7ImF1ZCI6eyJ0eXBlIjoiU3RyaW5nIn0sImJpcnRoZGF0ZSI6eyJ0eXBlIjoiU3RyaW5nIn0sImVtYWlsIjp7InR5cGUiOiJlbWFpbF9hZGRyZXNzIn0sImV4cCI6eyJ0eXBlIjoiTG9uZyJ9LCJpYXQiOnsidHlwZSI6IkxvbmcifSwiaXNzIjp7InR5cGUiOiJFbnRpdHkiLCJuYW1lIjoiVHJ1c3RlZElzc3VlciJ9LCJqdGkiOnsidHlwZSI6IlN0cmluZyJ9LCJuYW1lIjp7InR5cGUiOiJTdHJpbmcifSwicGhvbmVfbnVtYmVyIjp7InR5cGUiOiJTdHJpbmcifSwicm9sZSI6eyJ0eXBlIjoiU2V0IiwiZWxlbWVudCI6eyJ0eXBlIjoiU3RyaW5nIn19LCJzdWIiOnsidHlwZSI6IlN0cmluZyJ9fX19LCJBY2Nlc3NfdG9rZW4iOnsic2hhcGUiOnsidHlwZSI6IlJlY29yZCIsImF0dHJpYnV0ZXMiOnsiYXVkIjp7InR5cGUiOiJTdHJpbmcifSwiZXhwIjp7InR5cGUiOiJMb25nIn0sImlhdCI6eyJ0eXBlIjoiTG9uZyJ9LCJpc3MiOnsidHlwZSI6IkVudGl0eSIsIm5hbWUiOiJUcnVzdGVkSXNzdWVyIn0sImp0aSI6eyJ0eXBlIjoiU3RyaW5nIn0sIm5iZiI6eyJ0eXBlIjoiTG9uZyJ9LCJzY29wZSI6eyJ0eXBlIjoiU3RyaW5nIn19fX0sIkFwcGxpY2F0aW9uIjp7InNoYXBlIjp7InR5cGUiOiJSZWNvcmQiLCJhdHRyaWJ1dGVzIjp7ImNsaWVudCI6eyJ0eXBlIjoiRW50aXR5IiwibmFtZSI6IkNsaWVudCJ9LCJuYW1lIjp7InR5cGUiOiJTdHJpbmcifX19fSwiUm9sZSI6e30sIlRydXN0ZWRJc3N1ZXIiOnsic2hhcGUiOnsidHlwZSI6IlJlY29yZCIsImF0dHJpYnV0ZXMiOnsiaXNzdWVyX2VudGl0eV9pZCI6eyJ0eXBlIjoiVXJsIn19fX0sIkNsaWVudCI6eyJzaGFwZSI6eyJ0eXBlIjoiUmVjb3JkIiwiYXR0cmlidXRlcyI6eyJjbGllbnRfaWQiOnsidHlwZSI6IlN0cmluZyJ9LCJpc3MiOnsidHlwZSI6IkVudGl0eSIsIm5hbWUiOiJUcnVzdGVkSXNzdWVyIn19fX0sImlkX3Rva2VuIjp7InNoYXBlIjp7InR5cGUiOiJSZWNvcmQiLCJhdHRyaWJ1dGVzIjp7ImFjciI6eyJ0eXBlIjoiU2V0IiwiZWxlbWVudCI6eyJ0eXBlIjoiU3RyaW5nIn19LCJhbXIiOnsidHlwZSI6IlN0cmluZyJ9LCJhdWQiOnsidHlwZSI6IlN0cmluZyJ9LCJhenAiOnsidHlwZSI6IlN0cmluZyJ9LCJiaXJ0aGRhdGUiOnsidHlwZSI6IlN0cmluZyJ9LCJlbWFpbCI6eyJ0eXBlIjoiZW1haWxfYWRkcmVzcyJ9LCJleHAiOnsidHlwZSI6IkxvbmcifSwiaWF0Ijp7InR5cGUiOiJMb25nIn0sImlzcyI6eyJ0eXBlIjoiRW50aXR5IiwibmFtZSI6IlRydXN0ZWRJc3N1ZXIifSwianRpIjp7InR5cGUiOiJTdHJpbmcifSwibmFtZSI6eyJ0eXBlIjoiU3RyaW5nIn0sInBob25lX251bWJlciI6eyJ0eXBlIjoiU3RyaW5nIn0sInJvbGUiOnsidHlwZSI6IlNldCIsImVsZW1lbnQiOnsidHlwZSI6IlN0cmluZyJ9fSwic3ViIjp7InR5cGUiOiJTdHJpbmcifX19fSwiVXNlciI6eyJtZW1iZXJPZlR5cGVzIjpbIlJvbGUiXSwic2hhcGUiOnsidHlwZSI6IlJlY29yZCIsImF0dHJpYnV0ZXMiOnsiZW1haWwiOnsidHlwZSI6ImVtYWlsX2FkZHJlc3MifSwicGhvbmVfbnVtYmVyIjp7InR5cGUiOiJTdHJpbmcifSwicm9sZSI6eyJ0eXBlIjoiU2V0IiwiZWxlbWVudCI6eyJ0eXBlIjoiU3RyaW5nIn19LCJzdWIiOnsidHlwZSI6IlN0cmluZyJ9LCJ1c2VybmFtZSI6eyJ0eXBlIjoiU3RyaW5nIn19fX0sIkhUVFBfUmVxdWVzdCI6eyJzaGFwZSI6eyJ0eXBlIjoiUmVjb3JkIiwiYXR0cmlidXRlcyI6eyJhY2NlcHQiOnsidHlwZSI6IlNldCIsImVsZW1lbnQiOnsidHlwZSI6IlN0cmluZyJ9fSwiaGVhZGVyIjp7InR5cGUiOiJTZXQiLCJlbGVtZW50Ijp7InR5cGUiOiJTdHJpbmcifX0sInVybCI6eyJ0eXBlIjoiVXJsIn19fX19LCJhY3Rpb25zIjp7IkdFVCI6eyJhcHBsaWVzVG8iOnsicmVzb3VyY2VUeXBlcyI6WyJIVFRQX1JlcXVlc3QiXSwicHJpbmNpcGFsVHlwZXMiOlsiQ2xpZW50Il0sImNvbnRleHQiOnsidHlwZSI6IkNvbnRleHQifX19LCJERUxFVEUiOnsiYXBwbGllc1RvIjp7InJlc291cmNlVHlwZXMiOlsiSFRUUF9SZXF1ZXN0Il0sInByaW5jaXBhbFR5cGVzIjpbIkNsaWVudCJdLCJjb250ZXh0Ijp7InR5cGUiOiJDb250ZXh0In19fSwiUE9TVCI6eyJhcHBsaWVzVG8iOnsicmVzb3VyY2VUeXBlcyI6WyJIVFRQX1JlcXVlc3QiXSwicHJpbmNpcGFsVHlwZXMiOlsiQ2xpZW50Il0sImNvbnRleHQiOnsidHlwZSI6IkNvbnRleHQifX19LCJIRUFEIjp7ImFwcGxpZXNUbyI6eyJyZXNvdXJjZVR5cGVzIjpbIkhUVFBfUmVxdWVzdCJdLCJwcmluY2lwYWxUeXBlcyI6WyJDbGllbnQiXSwiY29udGV4dCI6eyJ0eXBlIjoiQ29udGV4dCJ9fX0sIlBVVCI6eyJhcHBsaWVzVG8iOnsicmVzb3VyY2VUeXBlcyI6WyJIVFRQX1JlcXVlc3QiXSwicHJpbmNpcGFsVHlwZXMiOlsiQ2xpZW50Il0sImNvbnRleHQiOnsidHlwZSI6IkNvbnRleHQifX19LCJQQVRDSCI6eyJhcHBsaWVzVG8iOnsicmVzb3VyY2VUeXBlcyI6WyJIVFRQX1JlcXVlc3QiXSwicHJpbmNpcGFsVHlwZXMiOlsiQ2xpZW50Il0sImNvbnRleHQiOnsidHlwZSI6IkNvbnRleHQifX19LCJBY2Nlc3MiOnsiYXBwbGllc1RvIjp7InJlc291cmNlVHlwZXMiOlsiQXBwbGljYXRpb24iXSwicHJpbmNpcGFsVHlwZXMiOlsiVXNlciIsIlJvbGUiXSwiY29udGV4dCI6eyJ0eXBlIjoiQ29udGV4dCJ9fX19fX0="
"schema": "ewogICAgIkphbnMiOiB7CiAgICAgICAgImNvbW1vblR5cGVzIjogewogICAgICAgICAgICAiVXJsIjogewogICAgICAgICAgICAgICAgInR5cGUiOiAiUmVjb3JkIiwKICAgICAgICAgICAgICAgICJhdHRyaWJ1dGVzIjogewogICAgICAgICAgICAgICAgICAgICJob3N0IjogewogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJFbnRpdHlPckNvbW1vbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIjogIlN0cmluZyIKICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICJwYXRoIjogewogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJFbnRpdHlPckNvbW1vbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIjogIlN0cmluZyIKICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICJwcm90b2NvbCI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiOiAiRW50aXR5T3JDb21tb24iLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSI6ICJTdHJpbmciCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICAiZW50aXR5VHlwZXMiOiB7CiAgICAgICAgICAgICJBY2Nlc3NfdG9rZW4iOiB7CiAgICAgICAgICAgICAgICAic2hhcGUiOiB7CiAgICAgICAgICAgICAgICAgICAgInR5cGUiOiAiUmVjb3JkIiwKICAgICAgICAgICAgICAgICAgICAiYXR0cmlidXRlcyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImF1ZCI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIjogIkVudGl0eU9yQ29tbW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIjogIlN0cmluZyIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImV4cCI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIjogIkVudGl0eU9yQ29tbW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIjogIkxvbmciCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJpYXQiOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJFbnRpdHlPckNvbW1vbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSI6ICJMb25nIgogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiaXNzIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiOiAiRW50aXR5T3JDb21tb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiOiAiVHJ1c3RlZElzc3VlciIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImp0aSI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIjogIkVudGl0eU9yQ29tbW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIjogIlN0cmluZyIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIklzc3VlIjogewogICAgICAgICAgICAgICAgInNoYXBlIjogewogICAgICAgICAgICAgICAgICAgICJ0eXBlIjogIlJlY29yZCIsCiAgICAgICAgICAgICAgICAgICAgImF0dHJpYnV0ZXMiOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJvcmdfaWQiOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJFbnRpdHlPckNvbW1vbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSI6ICJTdHJpbmciCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJUcnVzdGVkSXNzdWVyIjogewogICAgICAgICAgICAgICAgInNoYXBlIjogewogICAgICAgICAgICAgICAgICAgICJ0eXBlIjogIlJlY29yZCIsCiAgICAgICAgICAgICAgICAgICAgImF0dHJpYnV0ZXMiOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJpc3N1ZXJfZW50aXR5X2lkIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiOiAiRW50aXR5T3JDb21tb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiOiAiVXJsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAiV29ya2xvYWQiOiB7CiAgICAgICAgICAgICAgICAic2hhcGUiOiB7CiAgICAgICAgICAgICAgICAgICAgInR5cGUiOiAiUmVjb3JkIiwKICAgICAgICAgICAgICAgICAgICAiYXR0cmlidXRlcyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImNsaWVudF9pZCI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIjogIkVudGl0eU9yQ29tbW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIjogIlN0cmluZyIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImlzcyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIjogIkVudGl0eU9yQ29tbW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIjogIlRydXN0ZWRJc3N1ZXIiCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiOiAiRW50aXR5T3JDb21tb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiOiAiU3RyaW5nIgogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAib3JnX2lkIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiOiAiRW50aXR5T3JDb21tb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiOiAiU3RyaW5nIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICAiYWN0aW9ucyI6IHsKICAgICAgICAgICAgIlVwZGF0ZSI6IHsKICAgICAgICAgICAgICAgICJhcHBsaWVzVG8iOiB7CiAgICAgICAgICAgICAgICAgICAgInJlc291cmNlVHlwZXMiOiBbCiAgICAgICAgICAgICAgICAgICAgICAgICJJc3N1ZSIKICAgICAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgICAgICJwcmluY2lwYWxUeXBlcyI6IFsKICAgICAgICAgICAgICAgICAgICAgICAgIldvcmtsb2FkIgogICAgICAgICAgICAgICAgICAgIF0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KfQ=="
}
}
Loading