Authorization Setup Overview
Authorization in projects generated by Castlecraft Architect is built upon the flexible and extensible framework provided by the castlecraft-engineer library. This document provides an overview of how this system works and how Architect helps you integrate with it.
Core Concepts from castlecraft-engineer
The authorization system in castlecraft-engineer revolves around a few key abstractions:
-
AuthorizationService(Interface): This is the central interface (defined incastlecraft_engineer.abstractions.authorization.base_service.AuthorizationService) that all authorization engines must implement. Its primary method ischeck_permission.# Excerpt from castlecraft_engineer.abstractions.authorization.base_service.py
class AuthorizationService(abc.ABC):
@abc.abstractmethod
async def check_permission(
self,
subject_id: Optional[str],
required_permissions: List[Permission],
provided_permissions: Optional[List[str]] = None,
context: Optional[Dict[str, Any]] = None,
) -> bool:
# ... (implementation raises AuthorizationError on failure)
raise NotImplementedErrorImplementations connect to engines like Casbin, OPA, etc. The
check_permissionmethod raises anAuthorizationErrorif the subject is not authorized. -
PermissionDataclass: Represents a single permission required for an operation (defined incastlecraft_engineer.authorization.permission.Permission).# Excerpt from castlecraft_engineer.authorization.permission.py
@dataclass(frozen=True)
class Permission:
action: BaseStringEnum # e.g., Action.READ, Action.UPDATE
resource: BaseStringEnum # e.g., Resource.WIDGET, 'product'
scope: Optional[BaseStringEnum] = None # e.g., Scope.OWN, 'tenant:123'The
action,resource, andscopefields often use string-based enums for clarity. -
AuthorizationErrorException: A custom exception raised bycheck_permissionupon authorization failure. -
@ctxDecorator: Used on command/query handlerexecutemethods to declare the permissions required for that operation. It injects arequired_permissionslist into the method'skwargs.from castlecraft_engineer.authorization.permission import Permission, ctx
# ...
class MyCommandHandler(CommandHandler[MyCommand]):
@ctx(Permission(action=Action.CREATE, resource=Resource.ORDER))
async def execute(self, command: MyCommand, **kwargs):
# required_permissions = kwargs.get('required_permissions')
# ... authorization check ...
pass
Performing Authorization Checks in Handlers
Within your command or query handlers (generated by Architect or custom-written), you'll typically:
- Inject
AuthorizationService: Receive an instance via dependency injection. - Retrieve
required_permissions: Access the list ofPermissionobjects fromkwargs['required_permissions'](populated by the@ctxdecorator). - Gather Context:
subject_id: The ID of the user/service making the request (often fromget_current_user).provided_permissions: A list of permissions/roles the subject possesses (e.g., from JWT claims).context: A dictionary of additional data for policy evaluation (e.g., resource ID from the command).
- Call
check_permission: Invokeawait self._auth_service.check_permission(...). AnAuthorizationErrorwill halt execution if unauthorized.
Architect's Role and Contributions
While the core authorization mechanism is from castlecraft-engineer, Castlecraft Architect assists in several ways:
-
Scaffolding Permission Components: When you define operations that require authorization, Architect can scaffold "permission" components. These components represent the
action,resource, andscopethat will be used in your@ctxdecorators and policy definitions. -
Generating Authorization Model & Policy: Architect has knowledge of all
Permissioncomponents defined in your application. This allows it to help generate:- Authorization Models: For engines like Casbin, Architect can generate the model configuration file (e.g.,
model.conf) based on the structure of your defined permissions.architect generate authorization-model --engine casbin -o ./auth_model.conf - Bootstrap Authorization Policies: Architect can generate initial policy data (e.g., a Casbin CSV file) that grants permissions to roles or users. This is particularly useful for setting up default roles like "admin".
- Syncing and Exporting Policies: Architect provides commands to sync these generated policy files to a live database and export existing policies.
# Generate the policy file
architect generate authorization-policy --engine casbin --engine-config ./casbin_bootstrap_config.yaml -o ./bootstrap_policy.csv
# Sync the generated file to the database
architect authorization sync-policies --policy-file ./bootstrap_policy.csv --engine casbin
Refer to the Casbin Engine Configuration guide for details on the
--engine-configfile. - Authorization Models: For engines like Casbin, Architect can generate the model configuration file (e.g.,
-
Providing Authorization Engine Adapters: Architect aims to provide adapters for common authorization engines. An adapter implements the logic for how Architect should generate model and policy files specific to that engine, and optionally, how to persist them.
- Casbin Adapter: Architect includes a
CasbinEngineAdapter. This adapter understands how to translate your application's permission components into a Casbin model and policy rules.
- Casbin Adapter: Architect includes a
-
Default
AuthorizationServiceSetup: Architect projects are typically configured to use one ofcastlecraft-engineer's defaultAuthorizationServiceimplementations based on theAUTHORIZATION_ENGINEenvironment variable:allow_all: UsesAllowAllAuthorizationService(bypasses checks - for development/testing only).deny_all(or if unset): UsesDenyAllAuthorizationService(secure default).casbin(or other custom value): Expects a specificAuthorizationServiceimplementation (like Architect'sCasbinAuthorizationService) to be registered in the DI container. Architect's default setup for "collaboration mode" often includes theCasbinAuthorizationService.
Implementing Your Authorization Logic
- Define Permissions: Use Architect to define "permission" components for the actions and resources in your application.
- Decorate Handlers: Apply the
@ctx(...)decorator to your command and query handlers with the appropriatePermissionobjects. - Choose an Engine: Decide on an authorization engine (e.g., Casbin).
- Configure the Engine:
- Set the
AUTHORIZATION_ENGINEenvironment variable (e.g.,casbin). - Provide the necessary configuration for your chosen engine (e.g., Casbin model and policy files).
- Set the
- Generate Artifacts (Optional but Recommended):
- Use
architect generate authorization-modelto create the model file for your engine. - Use
architect generate authorization-policywith an--engine-configYAML to bootstrap your policy data. - Use
architect authorization sync-policiesto load the bootstrap policies into your database.
- Use
- Ensure Runtime
AuthorizationServiceis Registered:- If using a common engine like Casbin with Architect's adapter, Architect's DI setup usually handles registering the appropriate
AuthorizationService(e.g.,CasbinAuthorizationService). - For a fully custom engine, you would implement your own
AuthorizationServiceand register it in your application's DI container.
- If using a common engine like Casbin with Architect's adapter, Architect's DI setup usually handles registering the appropriate
Extensibility
- Custom
AuthorizationService: You can implement theAuthorizationServiceinterface to integrate any authorization engine or custom logic. - Custom Authorization Adapters for Architect: If you want Architect's
generatecommands to support a new engine, you can create a custom "Authorization Adapter" plugin. This adapter would teach Architect how to produce model and policy files for that specific engine. See the "Extending Architect" guides for more details.
By leveraging castlecraft-engineer's authorization primitives and Architect's generation capabilities, you can establish a robust and maintainable authorization layer for your applications.