Supporting reference · Working design and evidence, not a promise of complete support.

On this page

Rails authorization

Status: Implemented for the admitted public Policy algebra and its Scaffold consumers. Other Policy shapes are listed gaps.

Admitted Policy roots lower to Action Policy record rules, and admitted protected consumers demand relation scopes; the Policies owner (repository-only) lists both. Public, item, and gate authorization remain distinct; protected member lookup starts from an authorized relation, and generated controls repeat the request authorization decision. The Policies owner (repository-only) also states which destroy? aliases a complete exact-source operation census permits. Authentication supplies the principal through current_account; generated Policies, controllers, and views consume it with Action Policy.

On this page

Direction

Lower explicit Policy meaning with Action Policy and generated denial tests in the first profile. Generalized Web Scaffold output consumes exact public, item, gate, and profile decisions in requests and presentation. Other record sources and Expressions, attachments, native clients, and broader generated proof remain open. The Scaffold lowering includes the admitted indirect collection's destination Policy scope.

The first Rails profile selects Action Policy as the fixed lowering. The cumulative public candidate generates:

A complete application-wide authorization lowering still needs:

Policy record lowering

Ordinary CompilationInput derives an immutable PolicyRecordLowering from the exact same-generation Domain, relationship, Account, Policy-resolution, Policy-type, and Scaffold-definition evidence. It admits no Policy root unless the exact Field-only public Web Account realization succeeds. The closed public algebra admits record identity against current_account, one direct Reference-to-Account comparison, and a singular matches_policy delegation whose dependency is also admitted. It omits and lists unsupported expression shapes, missing or unemitted dependencies, generated Policy names that collide with Project or pinned runtime claims, and rule methods that collide with the pinned Policy-instance method census.

Relation demand comes only from admitted Scaffold consumers. Bookmark manage_by_user supplies the protected Bookmark index and member consumers, Rating manage_by_user supplies its update/destroy member lookup, and Notification read_by_recipient supplies the protected collection. Case, Conversation, ConversationThread, and Message participant-read rules supply the joined relation scopes needed by their admitted consumers. Only demanded scopes are emitted; Policy rules with no relation consumer do not mint an unused scope. A gate over current_account, a profile action resolved directly from current_account, and an omitted projection likewise do not mint one.

The lowering derives target demand directly from captured Scaffold definitions and authorization rows plus the typed Policy evidence. Each demanded relation scope filters the exact emitted Account foreign key and returns relation.none for a guest.

The complete exact-source operation census records every Entity even when it owns zero Policies and independently checks every Policy UUID and pointer against the submitted document. It permits destroy? to alias to a selected standard manage? rule only when no exact authored destroy exists. Bookmark and Rating use that alias. An explicit authored destroy would be direct; Oscar's custom manage_movie_catalog? remains the direct selected rule for Movie, Person, and Credit destroy instead of acquiring an alias. Missing, stale, forged, or incomplete census evidence omits the consumer.

PolicyRecordFiles emits one ApplicationPolicy with default_rule nil, one class and RSpec file per admitted owner Entity, required relation scopes, and complete source provenance. Examples exercise authored operations and relation visibility with actual records. The Policy owner (repository-only) distinguishes emitted application cases from upstream identity edge cases. Setup limitations retain guest and missing-ID guards and disclose remaining coverage in the README. The application-wide dependency plan adds the qualified Action Policy block and projects the complete pair; application spec discovery runs Policy examples after schema loading.

Policy rules

Lower the admitted allow_when algebra with Action Policy. Admitted protected consumers demand relation scopes beside the record rules; the Policies owner (repository-only) lists both. Every request and displayed Association selects explicit public access or one exact Policy record. Current output omits a consumer when its record or relation projection cannot preserve the authored Expression, and lists the target gap. This strict current slice is not a target-wide pre-alpha requirement: later breadth may retain a conventional public scaffold consumer as disclosed starter code without claiming Policy coverage. Generated method-name collisions or paths with no useful bootable projection may still select omission. Contradictory duplicate Entity operations remain semantic errors. Emit default_rule nil, so unknown operations in the realized Policy layer raise ActionPolicy::UnknownRule.

Public record rules and relation scopes use user&.id.present? as their identity boundary. Ownership then compares record&.user_id == user.id; self rules compare record == user; membership rules inspect the preloaded members; and delegation calls allowed_to? with the explicit dependency Policy class and nullable related record. A conjunction joins its ordinary child expressions with &&, retaining their authored order without blanket parentheses. Safe navigation handles expected absence without swallowing a wrong method interface. Guests, ID-less Accounts, missing records, and missing delegated relationships remain denied. The Action Policy 0.7.6 ownership examples use ordinary identity comparisons, and its nested authorization API accepts a nil record with an explicit with: class.

Authentication supplies the principal through current_account; Policies do not repeat that responsibility. The generated seam calls Rodauth's rails_account only for a logged-in session, and rodauth-rails 2.2.0 builds the model from the resolved authentication account. Dropping persisted? intentionally permits explicitly supplied unsaved or destroyed Accounts whose IDs match. Rails 8.1.3.1 defines it as object lifecycle flags, not authentication or current row existence. This replaces the redundant guards introduced in the initial public Policy renderer without changing admission, the reviewed GapSet, preloads, Action Policy verification, or the authorization consumers.

Exact authored destroy is direct. A selected standard manage may supply conventional destroy? only when the complete exact-source operation census proves no authored destroy; generated code aliases destroy? to manage?. Bookmark and Rating meet that condition. An explicitly selected custom operation, including Oscar's manage_movie_catalog, remains the direct rule and creates no alias. The Scaffold still owns route selection and return behavior. Missing census evidence omits the alias and every dependent consumer. The target-unlowered message.notifications relationship omits only that child. The admitted Message implicit Ordering emits independently. Neither suppresses the six admitted Case participant Policy roots or their other consumers.

Scaffold consumption

The Scaffold renderer consumes those decisions directly. Member lookup starts from the authorized relation when required, and views repeat allowed_to? for protected content and controls. Relations preload the nested Policy, control, and presentation graph under strict_loading; associated records may enter server memory even when their display is denied. Action and collection-wide gates authorize before presentation queries. Public indexes, item authorizations, environment gates, projection gates, and current-account profile authorization keep their distinct records and preload ownership. The Rails profile owns the preload lowering. Account is one authenticated /account destination. Its custom profile details and queries retain their read Policy; editing retains the update Policy. Ordinary native clients admit neither Account nor protected Scaffold navigation.

The cumulative Scaffold semantics emit Policy record projections plus demanded relation scopes. Oscar Party emits User, Bookmark, and Rating rules; Bookmark and Rating each gain manage from admitted consumers. Case Chat emits User, Case, Conversation, Participant, ConversationThread, Message, and Notification rules. Notification gains read, while the four participant-read Policies gain the joined relation scopes demanded by their consumers. Generated Web controllers, relations, projections, controls, mutations, and custom Account details consume those exact decisions. Photogram remains the neutral control and emits no Policy or Scaffold files.

The target-unlowered message.notifications relationship still omits that nested child. The delivery import skip remains a separate disclosure; Message's exact sent_at, id implicit Ordering now emits. None withholds the six Case participant Policy roots or their other admitted consumers.

Friendly HTML and Turbo Policy denial responses remain in the generated application.

Authorization coverage

For requests claimed as authorized, configure Action Policy verification from authored semantics rather than controller-action names alone. An item-authorized collection uses verify_authorized_scoped; a detail, create, update, gate-authorized collection, or destroy request uses verify_authorized. An explicitly public request, including a public index, joins the controller's skip_verify_authorized only: list. This keeps realized public access intentional without weakening coverage for neighboring realized actions. Extra unguarded pre-alpha boilerplate remains a Policy gap.

Private Policy gate

PolicyGateQualificationInput is a second internal type. It reconstructs that same Account and registration prerequisite, requires web-only output, and admits only the Movie index targeted by the Account's admitted favorite_movie Reference, linked to one Account-owned manage_movie_catalog Policy. Pure same-generation Policy resolution and typing must prove the exact comparison current_record == current_account; the authorization row must independently select environment/current_account as its gate record. Target and Project constant collisions for Action Policy, ruby-next-core, ApplicationPolicy, and the Entity Policy fail before rendering. RSpec examples add no named test class claim. This path also qualifies the same fixed Account self surface against the Policy entry's generated claims. Its shared ApplicationNavigation contains Account self, while Web separately composes the authorized Movie entry. iOS is declined because this qualifier is Web-only. Public CompilationInput for this exact Association-registration Plan emits no Policy record and retains its Policy omission in the reviewed GapSet. Only the private qualifier emits this controller, view, navigation, and request gate; it does not enter the durable Compilation lifecycle.

The Policy path contributes Action Policy ~> 0.7.6 to the application-wide dependency plan. Projection from the maximal universe pins Action Policy 0.7.6 and ruby-next-core 1.2.1. Five Policy-specific renderers emit ApplicationPolicy, the same-Account UserPolicy, its generated RSpec example, the protected index controller, and the read-only index view. Shared web tasks add the route, locale, pagination, and conditional navigation. The application controller authorizes through current_account, rescues only ActionPolicy::Unauthorized through the shared Policy denial response (repository-only), and leaves all other exceptions visible. The index controller verifies authorization, authorizes current_account before it constructs the Movie relation, and then paginates. No relation scope, member lookup, mutation, Association authorization, native output, or broader Policy Expression is admitted by that private gate.

The Policy renderers likewise select their five paths only for the exact private gate.

A distinct private Policy-gate qualifier accepts optional private Association registration only when the gated Movie has a direct required short_text Primary Descriptor; its one-hop descriptor shape omits that input. It requires the Movie index targeted by favorite_movie and one Account-owned manage_movie_catalog Policy typed as current_record == current_account and explicit environment/current_account gate-record evidence. It pins Action Policy 0.7.6 and ruby-next-core 1.2.1, emits ApplicationPolicy, a same-Account UserPolicy, its generated same-account RSpec example, and controller authorization before the Movie relation. Denial rescues only ActionPolicy::Unauthorized through the shared HTML 403 page, retaining empty responses for other formats; navigation uses the same decision for visibility. This private record-only slice has no relation scope, member lookup, mutation, Association authorization, native client, or broader Expression support. Public record projection is separate and does not reuse this controller/view gate.

References marked “repository-only” name implementation or internal material outside this public guide. They are intentionally not links. Publishing a design does not prove its implementation.