From 31cde1a977d7d58bbf4cb3034d38be45867a4598 Mon Sep 17 00:00:00 2001 From: "J.R. Hill" Date: Fri, 29 Dec 2023 16:50:15 -0800 Subject: [PATCH] chore: finish up example project --- example/Makefile | 7 +- example/example1/auth-model.json | 73 ++++++++++++++++++ example/example1/example1.py | 123 ++++++++++++++++++++++++++++--- 3 files changed, 187 insertions(+), 16 deletions(-) create mode 100644 example/example1/auth-model.json diff --git a/example/Makefile b/example/Makefile index 826c0340..161efbae 100644 --- a/example/Makefile +++ b/example/Makefile @@ -3,11 +3,8 @@ all: run project_name=example1 openfga_version=latest -restore: - # TODO - -run: restore - # TODO +run: + python example1/example1.py run-openfga: docker pull docker.io/openfga/openfga:${openfga_version} && \ diff --git a/example/example1/auth-model.json b/example/example1/auth-model.json new file mode 100644 index 00000000..e1361656 --- /dev/null +++ b/example/example1/auth-model.json @@ -0,0 +1,73 @@ +{ + "schema_version": "1.1", + "type_definitions": [ + { + "type": "user" + }, + { + "type": "document", + "relations": { + "reader": { + "this": {} + }, + "writer": { + "this": {} + }, + "owner": { + "this": {} + } + }, + "metadata": { + "relations": { + "reader": { + "directly_related_user_types": [ + { + "type": "user" + } + ] + }, + "writer": { + "directly_related_user_types": [ + { + "type": "user" + } + ] + }, + "owner": { + "directly_related_user_types": [ + { + "type": "user" + } + ] + }, + "conditional_reader": { + "directly_related_user_types": [ + { + "condition": "name_starts_with_a", + "type": "user" + } + ] + } + } + } + } + ], + "conditions": { + "ViewCountLessThan200": { + "name": "ViewCountLessThan200", + "expression": "ViewCount < 200", + "parameters": { + "ViewCount": { + "type_name": "TYPE_NAME_INT" + }, + "Type": { + "type_name": "TYPE_NAME_STRING" + }, + "Name": { + "type_name": "TYPE_NAME_STRING" + } + } + } + } + } + \ No newline at end of file diff --git a/example/example1/example1.py b/example/example1/example1.py index 54c37945..3687c806 100644 --- a/example/example1/example1.py +++ b/example/example1/example1.py @@ -1,10 +1,14 @@ import asyncio +import json + import openfga_sdk -from openfga_sdk.models import CreateStoreRequest +from openfga_sdk.client.models import ClientAssertion, ClientCheckRequest, ClientReadChangesRequest, ClientTuple, ClientWriteRequest +from openfga_sdk.models import CreateStoreRequest, Metadata, ObjectRelation, RelationMetadata, TupleKey, TypeDefinition, Userset, Usersets, WriteAuthorizationModelRequest from openfga_sdk import ClientConfiguration, OpenFgaClient from openfga_sdk.credentials import CredentialConfiguration, Credentials import os + async def main(): credentials = Credentials() if os.getenv("FGA_CLIENT_ID") is not None: @@ -31,36 +35,38 @@ async def main(): ) async with OpenFgaClient(configuration) as fga_client: - # ListStores + # ListStores (before create) print('Listing Stores') response = await fga_client.list_stores() print(f"Stores Count: {len(response.stores)}") - # // CreateStore + store_name = 'Test Store' + + # CreateStore (before create) print('Creating Test Store') - body = CreateStoreRequest(name='Test Store') + body = CreateStoreRequest(name=store_name) response = await fga_client.create_store(body) print(f"Test Store ID: {response.id}") - # // Set the store id + # Set the store ID fga_client.set_store_id(response.id) - # // ListStores after Create + # ListStores (after create) print('Listing Stores') response = await fga_client.list_stores() print(f"Stores Count: {len(response.stores)}") - # // GetStore + # GetStore (after create) print('Getting Current Store') response = await fga_client.get_store() print(f"Current Store Name: {response.name}") - # // ReadAuthorizationModels + # ReadAuthorizationModels (before write) print('Reading Authorization Models') response = await fga_client.read_authorization_models() print(f"Models Count: {len(response.authorization_models)}") - # // ReadLatestAuthorizationModel + # ReadLatestAuthorizationModel (before write) try: response = await fga_client.read_latest_authorization_model() if response.authorization_model is not None: @@ -68,7 +74,102 @@ async def main(): except: print('Latest Authorization Model not found') - # TODO: Continue fleshing this out - print('Survived!') + # WriteAuthorizationModel + print('Writing an Authorization Model') + with open(os.path.join(os.path.dirname(__file__), 'auth-model.json')) as f: + auth_model_request = json.load(f) + response = await fga_client.write_authorization_model(auth_model_request) + print(f"Authorization Model ID: {response.authorization_model_id}") + + # ReadAuthorizationModels (after write) + print('Reading Authorization Models') + response = await fga_client.read_authorization_models() + print(f"Models Count: {len(response.authorization_models)}") + + # ReadLatestAuthorizationModel (after write) + response = await fga_client.read_latest_authorization_model() + if response.authorization_model is not None: + print(f"Latest Authorization Model ID: {response.authorization_model.id}") + + auth_model_id = response.authorization_model.id + + # Write + print('Writing Tuples') + body = ClientWriteRequest( + writes=[ + ClientTuple( + user='user:anne', + relation='writer', + object='document:roadmap', + # condition=RelationshipCondition( + # name='ViewCountLessThan200', + # context=dict( + # Name='Roadmap', + # Type='Document', + # ), + # ), + ), + ], + ) + options = { + # You can rely on the model id set in the configuration or override it for this specific request + "authorization_model_id": auth_model_id + } + await fga_client.write(body, options) + print('Done Writing Tuples') + + # Set the model ID + fga_client.set_authorization_model_id(auth_model_id) + + # Read + print('Reading Tuples') + response = await fga_client.read(TupleKey(user='user:anne', object='document:')) + print(f"Read Tuples: {response.tuples}") + + # ReadChanges + print('Reading Tuple Changes') + body = ClientReadChangesRequest('document') + response = await fga_client.read_changes(body) + print(f"Read Changes Tuples: {response.changes}") + + # Check + print('Checking for access') + response = await fga_client.check(ClientCheckRequest( + user='user:anne', + relation='reader', + object='document:roadmap', + )) + print(f"Allowed: {response.allowed}") + + # Checking for access with context + # TODO + + # WriteAssertions + await fga_client.write_assertions([ + ClientAssertion( + user='user:carl', + relation='writer', + object='document:budget', + expectation=True, + ), + ClientAssertion( + user='user:anne', + relation='reader', + object='document:roadmap', + expectation=False, + ), + ]) + print('Assertions updated') + + # ReadAssertions + print('Reading Assertions') + response = await fga_client.read_assertions() + print(f"Assertions: {response.assertions}") + + # DeleteStore + print('Deleting Current Store') + await fga_client.delete_store() + print(f"Deleted Store: {store_name}") + asyncio.run(main())