module

KemalIdentity::Authz::Authorizable

Something an authorization question can be about.

Two methods, and never a third

An authorizer needs to know what kind of thing this is and which one it is. From "invoice" and "42" it can key a relationship lookup, consult an ownership table, or serialise the question to a remote policy engine. An authorizer that wants the application's actual object asks for it by type, and a wrong guess is nil rather than an exception:

invoice = context.resource.as?(Invoice)
return Forbidden.not_permitted(permission) if invoice.nil?

This module is frozen at these two methods. Adding a third abstract def after 1.0 would stop every implementor compiling — abstract def Authorizable#x must be implemented by Invoice — and adding a concrete def would be worse: it compiles, but it injects a name into every including class, and where that class already defines the name its own definition wins. The build would stay green while the authorizer quietly read the application's meaning of a name this shard chose.

So nothing is added here. Everything an authorizer might later need travels on Authz::Context, which this shard constructs and which injects nothing into anybody's types. spec/unit/authorizable_spec.cr holds a fixture implementing exactly these two methods, so a third one does not fail a test — it fails the suite's compilation.

Why a module and not an abstract class

Crystal has single inheritance, and an application's Invoice normally already descends from an ORM model. A base class would make this unadoptable for exactly the applications docs/03-data-model.md treats as the normal case. See blueprints/0022-authorization-context-and-denials.md.

Instance methods

authz_id

Which one, as a string. The application's own identifier.

Source
authz_type

What kind of thing this is: "invoice", "document", "repository".

Source