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.