Package: authzee
authzeeAuthzee is a highly expressive grant-based authorization framework that is async, extensible, and scalable.Authzee was originally developed with a focus on authorization for existing infrastructure
like AD users, AD roles, AWS roles etc. Users, roles, groups and other identities are not stored in authzee.Authzee supportsABAC(Attribute Based Access Control) andACL(Access Control List) style authorization.
It does not support self-containedRBAC(Role Based Access Control) since roles are not stored in authzee.
The relationships between users, roles, and groups is deferred to external systems.
Authzee is notReBACbased, but supports many of the ideas.  Tying users to specific actions on specific resources,
and expressions to authorize. Note that this can be done on a user by user basis but authzee is more compute intense than
other options.For prod ready, large scaleReBACcheck outauthzed.InstallationTutorialIdentityResourceResource ActionsResource AuthzGrantauthzeeAppauthzeeSync AppauthzeeApp Grant ManagementauthzeeApp Authorization MethodsauthzeeApp Helper MethodsFull Tutorial ExampleDefinitionsInstallationInstall from pip$ pip install authzeeFor different compute or storage backends you may need to install extra deps.$ pip install authzee[sql]Extra dependencies:sql- ForSQLStorage.TutorialLet's start with a simple example.  An authorization request where an entity needs to perform an action on a resource,
and authzee should tell us if it is allowed to or not.NOTE- The tutorial here covers a basic setup, but for more details please see the Documentation(Link TBD).You can go straight to thefull code example, or follow along with the tutorial to get all of the definitions and smaller examples.IdentityAuthzee expects the calling entity to be described by its identities.
An entity could be a person, a service user, a role etc.
An identity could be anything used to describe who or what an entity is.  The calling entity can have many identities.
Common identity types could be AD user, AD groups, AWS User, AWS Role.  A single identity could have all of these and multiples of them.In Authzee actions can be limited based on identities.
Identity models are made with pydantic.frompydanticimportBaseModelclassADUser(BaseModel):cn:strResourceResources in Authzee represent resources that authorization is needed for.
The resource type and fields can be used in authorization.
Resource Models are made with pydantic.frompydanticimportBaseModelclassBalloon(BaseModel):color:strsize:strResource ActionsResource actions are used to enumerate operations that can be performed on a resources.
You can define resource actions as enums that are based onauthzee.ResourceAction.
Each resource type must have it's own set of resource actions.fromenumimportautofromauthzeeimportResourceActionclassBalloonAction(ResourceAction):CreateBalloon:str=auto()DeleteBalloon:str=auto()ListBalloons:str=auto()Resource Authz"Resource Authz" is a metadata wrapper for resources, resource actions, and their relationships.Create them as a child class ofauthzee.ResourceAuthz, and fill in the default values to declare the resource type, resource action type, as well as parent and child relationships.Authzee does not keep a a hierarchy of relationships, and defining these is purely up to the user, and how they would like to authorize their resources.If you create a resource authz then you can set up the parent and child relationships however you want.  What Authzee will do with the defined relationships is:check parent and child resource types against the authznormalize the parent and child resources so you can query them in authorization requestsfromenumimportautofromtypingimportSet,TypefrompydanticimportBaseModel,FieldfromauthzeeimportResourceAction,ResourceAuthzclassBalloon(BaseModel):color:strsize:floatclassBalloonAction(ResourceAction):CreateBalloon:str=auto()DeleteBalloon:str=auto()ListBalloons:str=auto()classBalloonString(BaseModel):color:strlength:floatclassBalloonStringActions(ResourceAction):CreateBalloonString:str=auto()DeleteBalloonString:str=auto()ListBalloonsString:str=auto()BalloonAuthz=ResourceAuthz(resource_type=Balloon,resource_action_type=BalloonAction,parent_resource_types=set(),child_resource_types={BalloonString})BalloonStringAuthz=ResourceAuthz(resource_type=BalloonString,resource_action_type=BalloonStringAction,parent_resource_types={Balloon},child_resource_types=set())GrantBy default everything in Authzee is unauthorized/not allowed.In order to allow anything, grants must be created.There are two types of grants denoted by their effect.Allow - Grants that authorize/allow matching requests.Deny - Grants that deny matching requests.  Requests with matching deny grants are unauthorized. Even if there are matching allow grants.Grants are created with theauthzee.Grantmodel, then added to the authzee app.frompydanticimportBaseModelfromauthzeeimportGrant,GrantEffectclassBalloon(BaseModel):color:strsize:floatclassBalloonAction(ResourceAction):CreateBalloon:str=auto()DeleteBalloon:str=auto()ListBalloons:str=auto()new_grant=Grant(name="Human friendly name",description="human friendly description",resource_type=Balloon,# The class resource typeresource_actions={# the set of resource actionsBalloonAction.CreateBalloon,BalloonAction.DeleteBalloon},# JMESpath is the JSON query language for verifying identities and# resources, as well as their relationshipsjmespath_expression="""contains(identities.ADUser[].cn, 'authzee_user_1')&& resource.color == 'blue'""",result_match=True# If the result of the jmespath search query matches this, then the grant is considered a match!)# add to an authzee app. See more in authzee app section# new_grant = authzee_app.add_grant(#     effect=GrantEffect.ALLOW,#     grant=new_grant# )The grant above will match with:resources of theBalloontyperesource actions ofCreateBalloonorDeleteBalloonBut what arejmespath_expressionandresult_matchfor?JMESPathis a JSON query language with a complete specification. Authzee uses it as the query tool for authorizations.The request data is normalized into a JSON object. The JMESPath query fromjmespath_expressionis evaluated and then checked if it is an exact match ofresult_match.  If so, then the grant
is considered a match.Example of normalized request data:{"identities":{"ADUser":[{"cn":"authzee_user_1"}],"ADGroup":[]},"resource_type":"Balloon","resource":{"color":"green","size":12.27},"resource_action":"BalloonAction.CreateBalloon","parent_resources":{},"child_resources":{"BalloonString":[{"color":"purple","length":27}]}}The data for this is normalized as follows:identitiesis a JSON object whose keys include all identity types, and the value of each is an array.Any identities passed will be serialized and added to the array of their respective identity types.resource_typeis the class name of the resource type model for the request.resourceis the serialized resource model for the requestresource_actionis the full name of the action for the request.<class name>.<enum member>parent_resourcesandchild_resourcesare JSON objects that include all of the parent and child resource types class names as keys, and the value of each is an array.Any child or parent resources will be serialized and added to the array of their respective parent or child resource types.The above json is used as the data injmespath.search(), along with the jmespath expression from the grant used as the expression.authzeeAppThe central interface of authzee is with the creation of an authzee app.
An Authzee app requires a storage backend and a compute backend.Available Storage Backends:SQLStorage- Store data in a SQL database - async enabledAvailable Compute Backends:MainProcessCompute- process authorization requests synchronously in the main thread - not asyncMultiprocessCompute- process authorization requests asynchronously.  Distributes work to a process poolThreadedCompute- Process authorization requests asynchronously.  Distributes work to a thread pool.  Note that because of the GIL, using multiple threads may actually be slightly slower thanMainProcessCompute.  While it's not truly parallel processing, it will not block the main thread.fromauthzeeimport(Authzee,MultiprocessCompute,SQLStorage)compute=MultiprocessCompute()storage=SQLStorage(sqlalchemy_async_engine_kwargs={"url":"sqlite+aiosqlite:///test.sqlite","echo":True})authzee_app=Authzee(compute_backend=compute,storage_backend=storage)authzeeSync AppThere is also a synchronous wrapper for the Authzee app.
It has all of the same methods but they are synchronous.fromauthzeeimport(Authzee,AuthzeeSync,MultiprocessCompute,SQLStorage)compute=MultiprocessCompute()storage=SQLStorage(sqlalchemy_async_engine_kwargs={"url":"sqlite+aiosqlite:///test.sqlite","echo":True})authzee_app=Authzee(compute_backend=compute,storage_backend=storage)authzee_sync_app=AuthzeeSync(authzee_app=authzee_app)authzeeApp Grant ManagementAfter initialization of the Authzee app you can manage grants with these async methods:list_grants- List grants with an iterator.add_grant- Add a new grant.delete_grant- Delete a grant.get_grants_page- Retrieve a single page of grants.authzeeApp Authorization MethodsOf course you can also use the async authorization methods!authorize- Determine if the request is authorized.authorize_many- Determine if several of the same resource type are authorized for the request.list_matching_grants- List matching grants with an iterator.get_matching_grants_page- Retrieve a single page of matching grants.authzeeApp Helper Methodsgrant_matches- Check if the request matches the given grant.Full Tutorial ExampleimportasynciofromenumimportautofromtypingimportSet,TypefrompydanticimportBaseModel,Fieldfromauthzeeimport(Authzee,AuthzeeSync,Grant,GrantEffect,MultiprocessCompute,ResourceAction,ResourceAuthz,SQLStorage)# Identity Models# Create identity models that represent the calling entities identitiesclassADUser(BaseModel):cn:strclassADGroup(BaseModel):cn:str# Resource Models# Used to authorize actions on resources# Can use authorization specific resource modelsclassBalloon(BaseModel):color:strsize:floatclassBalloonString(BaseModel):color:strlength:float# Resource Actions# One resource action per resource type to represent the actions that can be taken on the resourceclassBalloonAction(ResourceAction):CreateBalloon:str=auto()DeleteBalloon:str=auto()ListBalloons:str=auto()classBalloonStringAction(ResourceAction):CreateBalloonString:str=auto()DeleteBalloonString:str=auto()ListBalloonsString:str=auto()# Resource Authzs# Tie resource types, resource actions, as well as child and parent relationships togetherBalloonAuthz=ResourceAuthz(resource_type=Balloon,resource_action_type=BalloonAction,parent_resource_types=set(),child_resource_types={BalloonString})BalloonStringAuthz=ResourceAuthz(resource_type=BalloonString,resource_action_type=BalloonStringAction,parent_resource_types={Balloon},child_resource_types=set())# Create a compute and storage backendcompute=MultiprocessCompute()storage=SQLStorage(sqlalchemy_async_engine_kwargs={"url":"sqlite+aiosqlite:///test.sqlite","echo":False})# Pass those to the Authzee appauthzee_app=Authzee(compute_backend=compute,storage_backend=storage)# Most methods to authzee are async, but you can use the synchronous wrapper if you aren't using asyncauthzee_sync=AuthzeeSync(authzee_app=authzee_app)# Register Identity typesauthzee_app.register_identity_type(ADUser)authzee_app.register_identity_type(ADGroup)# Then register ResourceAuthzsauthzee_app.register_resource_authz(BalloonAuthz)authzee_app.register_resource_authz(BalloonStringAuthz)if__name__=="__main__":# By default no requests are authorized in authzee# Grants are the base unit for describing how to match an authorization request# Grants are added to the authzee app as either ALLOW or DENY.# Authorization requests that match a DENY grant are not authorized.# Requests that match an ALLOW grant but not any DENY grants are authorized.# Create new grant objectsmy_balloon=Balloon(color="blue",size=27.0)identities=[ADUser(cn="authzee_user_1"),ADGroup(cn="some_group"),ADGroup(cn="another_group")]# as long as the compute and storage backends support is there is also# async versions for all of these besides grant_matches.# Simply append "_async" to the method.asyncdeftutorial()->None:# It's recommended to initialize the authzee app with a __main__ block# or a frameworks startup function.# Some compute backends me actually mandate it to be done like this.awaitauthzee_app.initialize()# Run the one time setup.  This should only be done once per configuration.# Creates DB tables, other storage setup, and other compute setup#await authzee_app.setup()# To tear down, delete everything that did run#await authzee_app.teardown()new_grant=Grant(name="Human friendly name",description="human friendly description",resource_type=Balloon,# The class resource typeresource_actions={# the set of resource actionsBalloonAction.CreateBalloon,BalloonAction.DeleteBalloon},# JMESpath is the JSON query language for verifying identities and# resources, as well as their relationshipsjmespath_expression="""contains(identities.ADUser[].cn, 'authzee_user_1')&& resource.color == 'blue'""",result_match=True# If the result of the jmespath search query matches this, then the grant is considered a match!)match_everything_grant=Grant(name="everything",description="",resource_type=Balloon,resource_actions={BalloonAction.CreateBalloon},jmespath_expression="`true`",result_match=True)# Add the new grant to authzee as an ALLOW Grantnew_grant=awaitauthzee_app.add_grant(effect=GrantEffect.ALLOW,grant=new_grant)match_everything_grant=awaitauthzee_app.add_grant(effect=GrantEffect.ALLOW,grant=match_everything_grant)# Get an iterator for the grantsasyncforgrantinauthzee_app.list_grants(effect=GrantEffect.ALLOW):print(grant)# Delete a grantawaitauthzee_app.delete_grant(effect=GrantEffect.ALLOW,uuid=match_everything_grant.uuid)# Get a single page of grantsgrants_page=awaitauthzee_app.get_grants_page(effect=GrantEffect.ALLOW)forgrantingrants_page.grants:print(grant)# Authorize a requestauthorized=awaitauthzee_app.authorize(resource=my_balloon,resource_action=BalloonAction.CreateBalloon,parent_resources=[],child_resources=[],identities=identities)print(authorized)# True# Authorize many resources in a requestauthorized_many=awaitauthzee_app.authorize_many(resources=[my_balloon,Balloon(color="red",size=100.8)],resource_action=BalloonAction.CreateBalloon,parent_resources=[],child_resources=[],identities=identities)print(authorized_many)# [True, False]# iterator for matching grantsmatching_grants_iter=authzee_app.list_matching_grants(effect=GrantEffect.ALLOW,resource=my_balloon,resource_action=BalloonAction.CreateBalloon,parent_resources=[],child_resources=[],identities=identities)asyncforgrantinmatching_grants_iter:print(grant)# Get a single page of matching grantsmatching_grants_page=awaitauthzee_app.get_matching_grants_page(effect=GrantEffect.ALLOW,resource=my_balloon,resource_action=BalloonAction.CreateBalloon,parent_resources=[],child_resources=[],identities=identities)forgrantinmatching_grants_page.grants:print(grant)asyncio.run(tutorial())DefinitionsAuthorization Request (Request) - A request to see if a the calling entity is authorized to perform a specific resource action on a resource.Calling Entity (Entity) - In an authorization request, the calling entity is essentially "who" is being authorized. The entity could be a person, service account, role etc.Identity - A way to identify an entity. An identity could be AD users, AD groups, AWS roles, AWS users etc.Resource - Resources in Authzee represent resources that authorization is needed for.  Example: My application deals with balloons, balloons are resources we authorize against.Resource Type - The type of a "Resource".  Example: My app needs to authorize around balloons. So, "Balloon" is the resource type.Resource Actions - Actions that can be done to resources.  Example: Balloon resource type could have actions of "InflateBalloon", "PopBalloon", "ListBalloons", "CreateBalloon".Grant - The unit that defines how to query and match against authorization requests.  Grants are added to authzee to allow of explicitly deny authorization requests.  Requests that match any DENY grants are not authorized.  Requests that match any ALLOW grants and does not match any DENY grants are allowed.ChangelogChangelog forauthzee.
All notable changes to this project will be documented in this file.The format is based onKeep a Changelog,
and this project adheres toSemantic Versioning.[Unreleased] - YYYY-MM-DD[0.1.0a3] - 2024-02-DDRemove sync methods and only accepts asyncAuthzeeSyncclass as a sync wrapper for Authzee appLocality checks streamlined. Only specify current backends localityParallel pagination skeletongeneral renamingrework ResourceAuthzs. They are now passed as instances to the Authzee app. Logic is streamlined[0.1.0a2] - 2023-07-23Initial Alpha.[0.1.0a1] - 2023-07-03Initial stub