diff --git a/example/example1/auth-model.json b/example/example1/auth-model.json deleted file mode 100644 index e136165..0000000 --- a/example/example1/auth-model.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "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 3687c80..c06f3c5 100644 --- a/example/example1/example1.py +++ b/example/example1/example1.py @@ -1,10 +1,10 @@ import asyncio -import json -import openfga_sdk -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.client.models import ClientAssertion, ClientCheckRequest, ClientReadChangesRequest, ClientTuple, \ + ClientWriteRequest, ClientListRelationsRequest, ClientListObjectsRequest, WriteTransactionOpts +from openfga_sdk import ClientConfiguration, OpenFgaClient, RelationReference, RelationshipCondition, \ + ConditionParamTypeRef, Condition, ReadRequestTupleKey, CreateStoreRequest, Metadata, ObjectRelation, \ + RelationMetadata, TypeDefinition, Userset, Usersets, WriteAuthorizationModelRequest from openfga_sdk.credentials import CredentialConfiguration, Credentials import os @@ -76,9 +76,65 @@ async def main(): # 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) + response = await fga_client.write_authorization_model(WriteAuthorizationModelRequest( + schema_version="1.1", + type_definitions=[ + TypeDefinition( + type="user" + ), + TypeDefinition( + type="document", + relations=dict( + writer=Userset( + this=dict(), + ), + viewer=Userset( + union=Usersets( + child=[ + Userset(this=dict()), + Userset(computed_userset=ObjectRelation( + object="", + relation="writer", + )), + ], + ), + ), + ), + metadata=Metadata( + relations=dict( + writer=RelationMetadata( + directly_related_user_types=[ + RelationReference(type="user"), + RelationReference(type="user", condition="ViewCountLessThan200"), + ] + ), + viewer=RelationMetadata( + directly_related_user_types=[ + RelationReference(type="user"), + ] + ) + ) + ) + ) + ], + conditions=dict( + ViewCountLessThan200=Condition( + name="ViewCountLessThan200", + expression="ViewCount < 200", + parameters=dict( + ViewCount=ConditionParamTypeRef( + type_name="TYPE_NAME_INT" + ), + Type=ConditionParamTypeRef( + type_name="TYPE_NAME_STRING" + ), + Name=ConditionParamTypeRef( + type_name="TYPE_NAME_STRING" + ), + ) + ) + ) + )) print(f"Authorization Model ID: {response.authorization_model_id}") # ReadAuthorizationModels (after write) @@ -101,13 +157,13 @@ async def main(): user='user:anne', relation='writer', object='document:roadmap', - # condition=RelationshipCondition( - # name='ViewCountLessThan200', - # context=dict( - # Name='Roadmap', - # Type='Document', - # ), - # ), + condition=RelationshipCondition( + name='ViewCountLessThan200', + context=dict( + Name='Roadmap', + Type='Document', + ), + ), ), ], ) @@ -118,31 +174,113 @@ async def main(): await fga_client.write(body, options) print('Done Writing Tuples') + # Write + print('Writing Tuples - non txn') + body = ClientWriteRequest( + writes=[ + ClientTuple( + user='user:beth', + relation='writer', + object='document:1', + condition=RelationshipCondition( + name='ViewCountLessThan200', + context=dict( + Name='Roadmap', + Type='Document', + ), + ), + ), + ClientTuple( + user='user:beth', + relation='viewer', + object='document:2' + ), + ], + ) + 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, + "transaction": WriteTransactionOpts( + max_per_chunk=1 + ) + } + 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:')) + response = await fga_client.read(ReadRequestTupleKey(user='user:anne', object='document:')) print(f"Read Tuples: {response.tuples}") # ReadChanges print('Reading Tuple Changes') - body = ClientReadChangesRequest('document') + body = ClientReadChangesRequest(type='document') response = await fga_client.read_changes(body) print(f"Read Changes Tuples: {response.changes}") # Check - print('Checking for access') + print('Checking for access w/o context') + try: + response = await fga_client.check(ClientCheckRequest( + user='user:anne', + relation='viewer', + object='document:roadmap' + )) + print(f"Allowed: {response.allowed}") + except Exception as err: + print(f"Failed due to: {err}") + + # Checking for access with context + print('Checking for access with context') + response = await fga_client.check(ClientCheckRequest( user='user:anne', - relation='reader', + relation='viewer', object='document:roadmap', + context=dict( + ViewCount=100 + ) )) print(f"Allowed: {response.allowed}") - # Checking for access with context - # TODO + # List objects with context + print('Listing objects for access with context') + + response = await fga_client.list_objects(ClientListObjectsRequest( + user='user:anne', + relation='viewer', + type='document', + context=dict( + ViewCount=100 + ) + )) + print(f"Objects: {response.objects}") + + # List relations w/o context + print('Listing relations for access w/o context') + + response = await fga_client.list_relations(ClientListRelationsRequest( + user='user:anne', + relations=['viewer', 'writer'], + object='document:roadmap' + )) + print(f"Relations: {response}") + + # List relations with context + print('Listing relations for access with context') + + response = await fga_client.list_relations(ClientListRelationsRequest( + user='user:anne', + relations=['viewer', 'writer'], + object='document:roadmap', + context=dict( + ViewCount=100 + ) + )) + print(f"Relations: {response}") # WriteAssertions await fga_client.write_assertions([ @@ -154,7 +292,7 @@ async def main(): ), ClientAssertion( user='user:anne', - relation='reader', + relation='viewer', object='document:roadmap', expectation=False, ), diff --git a/openfga_sdk/client/client.py b/openfga_sdk/client/client.py index e779a78..450f31a 100644 --- a/openfga_sdk/client/client.py +++ b/openfga_sdk/client/client.py @@ -632,7 +632,7 @@ async def list_relations(self, body: ClientListRelationsRequest, options: dict[s options = set_heading_if_not_set(options, CLIENT_BULK_REQUEST_ID_HEADER, str(uuid.uuid4())) request_body = [construct_check_request( - user=body.user, relation=i, object=body.object, contextual_tuples=body.contextual_tuples) for i in body.relations] + user=body.user, relation=i, object=body.object, contextual_tuples=body.contextual_tuples, context=body.context) for i in body.relations] result = await self.batch_check(request_body, options) # need to filter with the allowed response result_iterator = filter(_check_allowed, result) diff --git a/openfga_sdk/client/models/check_request.py b/openfga_sdk/client/models/check_request.py index 26d76a5..b717e81 100644 --- a/openfga_sdk/client/models/check_request.py +++ b/openfga_sdk/client/models/check_request.py @@ -13,14 +13,14 @@ from openfga_sdk.client.models.tuple import ClientTuple -from typing import List, Any +from typing import List -def construct_check_request(user: str, relation: str, object: str, context: Any = None, contextual_tuples: List[ClientTuple] = None): +def construct_check_request(user: str, relation: str, object: str, contextual_tuples: List[ClientTuple] = None, context: object = None): """ helper function to construct the check request body """ - return ClientCheckRequest(user, relation, object, context, contextual_tuples) + return ClientCheckRequest(user, relation, object, contextual_tuples, context) class ClientCheckRequest(): @@ -28,14 +28,14 @@ class ClientCheckRequest(): ClientCheckRequest encapsulates the parameters for check request """ - def __init__(self, user: str, relation: str, object: str, context: Any = None, contextual_tuples: List[ClientTuple] = None): + def __init__(self, user: str, relation: str, object: str, contextual_tuples: List[ClientTuple] = None, context: object = None): self._user = user self._relation = relation self._object = object - self._context = context self._contextual_tuples = None if contextual_tuples: self._contextual_tuples = contextual_tuples + self._context = context @property def user(self): @@ -59,18 +59,18 @@ def object(self): return self._object @property - def context(self): + def contextual_tuples(self): """ - Return context + Return contextual tuples """ - return self._context + return self._contextual_tuples @property - def contextual_tuples(self): + def context(self): """ - Return contextual tuples + Return context """ - return self._contextual_tuples + return self._context @user.setter def user(self, value): @@ -93,16 +93,16 @@ def object(self, value): """ self._object = value - @context.setter - def context(self, value): - """ - Set context - """ - self._context = value - @contextual_tuples.setter def contextual_tuples(self, value): """ Set contextual tuples """ self._contextual_tuples = value + + @context.setter + def context(self, value): + """ + Set context + """ + self._context = value diff --git a/openfga_sdk/client/models/list_objects_request.py b/openfga_sdk/client/models/list_objects_request.py index 8fb89eb..90fb1d3 100644 --- a/openfga_sdk/client/models/list_objects_request.py +++ b/openfga_sdk/client/models/list_objects_request.py @@ -13,7 +13,7 @@ from openfga_sdk.client.models.tuple import ClientTuple -from typing import List, Any +from typing import List class ClientListObjectsRequest(): @@ -21,12 +21,12 @@ class ClientListObjectsRequest(): ClientListObjectsRequest encapsulates the parameters required for list objects """ - def __init__(self, user: str, relation: str, type: str, context: Any = None, contextual_tuples: List[ClientTuple] = None): + def __init__(self, user: str, relation: str, type: str, contextual_tuples: List[ClientTuple] = None, context: object = None): self._user = user self._relation = relation self._type = type - self._context = context self._contextual_tuples = contextual_tuples + self._context = context @property def user(self): @@ -50,18 +50,18 @@ def type(self): return self._type @property - def context(self): + def contextual_tuples(self): """ - Return context + Return contextual_tuples """ - return self._context + return self._contextual_tuples @property - def contextual_tuples(self): + def context(self): """ - Return contextual_tuples + Return context """ - return self._contextual_tuples + return self._context @user.setter def user(self, value): @@ -84,16 +84,16 @@ def type(self, value): """ self._type = value - @context.setter - def context(self, value): - """ - Set context - """ - self._context = value - @contextual_tuples.setter def contextual_tuples(self, value): """ Set contextual tuples """ self._contextual_tuples = value + + @context.setter + def context(self, value): + """ + Set context + """ + self._context = value diff --git a/openfga_sdk/client/models/list_relations_request.py b/openfga_sdk/client/models/list_relations_request.py index 8e86671..1372f8c 100644 --- a/openfga_sdk/client/models/list_relations_request.py +++ b/openfga_sdk/client/models/list_relations_request.py @@ -10,7 +10,6 @@ NOTE: This file was auto generated by OpenAPI Generator (https://openapi-generator.tech). DO NOT EDIT. """ - from openfga_sdk.client.models.tuple import ClientTuple from typing import List @@ -21,11 +20,12 @@ class ClientListRelationsRequest(): ClientListRelationsRequest encapsulates the parameters required for list all relations user have with object """ - def __init__(self, user: str, relations: List[str], object: str, contextual_tuples: List[ClientTuple] = None): + def __init__(self, user: str, relations: List[str], object: str, contextual_tuples: List[ClientTuple] = None, context: object = None): self._user = user self._relations = relations self._object = object self._contextual_tuples = contextual_tuples + self._context = context @property def user(self): @@ -55,6 +55,13 @@ def contextual_tuples(self): """ return self._contextual_tuples + @property + def context(self): + """ + Return context + """ + return self._context + @user.setter def user(self, value): """ @@ -82,3 +89,10 @@ def contextual_tuples(self, value): Set contextual tuples """ self._contextual_tuples = value + + @context.setter + def context(self, value): + """ + Set context + """ + self._context = value