Class: Karst::Access::ResourceEvidence
- Inherits:
-
Object
- Object
- Karst::Access::ResourceEvidence
- Defined in:
- lib/karst/access/resource_evidence.rb
Overview
Given one exact resource (the specific record a route addresses by id) and one specific principal (typically the successful outcome from an Access::Sweep), reports simple, directly observed foreign-key relationships between those two records. This is evidence, not an authorization claim: it never states or implies why an outcome occurred, only which foreign-key columns, if any, point from one given record to the other's id.
Deliberately narrow, matching the v1 scope this class was built for:
- only foreign-key-shaped columns (ending in "_id") on the two given records are ever inspected -- never an arbitrary attribute, so no other column value (name, email, token, ...) is ever read or shown;
- only a direct column-value comparison between the two given records, never a join, a has_many traversal, or any multi-hop graph walk;
- resource resolution from a route path is attempted only through Rails' own route recognition plus its controller-to-model naming convention, and only trusted when every step succeeds unambiguously (a recognized route with an :id segment, a controller name that classifies to a real loaded Active Record class, and a record that actually exists for that id). Anything softer -- an unrecognized route, a controller with no conventional model, a missing record -- is reported as a limitation rather than guessed at. rubocop:disable Metrics/ClassLength
Defined Under Namespace
Classes: Error
Constant Summary collapse
- ResourceDescriptor =
The resource side never gets Identity::PrincipalDescriptor's configurable display_label hook -- there is no equivalent concept for "the current route's resource" -- so it gets its own minimal, equally attribute-free descriptor.
Value.define(:model_name, :id)
- Relationship =
from_model/from_id is whichever of the resource/principal actually holds the foreign-key column; to_model/to_id is the other side.
Value.define(:column, :from_model, :from_id, :to_model, :to_id)
- Result =
Value.define(:principal, :resource, :relationships, :observed_status, :observed_redirect, :limitation) do # Plain-text rendering deliberately kept free of causal wording # ("owns", "is authorized", "grants") -- see class comment above. def to_text lines = [principal.display_label] lines << observed_line if observed_status || observed_redirect lines << "" << "Related state:" lines.concat() lines.join("\n") end private def observed_line observed_redirect ? "Observed #{observed_status} → #{observed_redirect}" : "Observed #{observed_status}" end def return [" Unavailable: #{limitation}"] if limitation return [no_relationship_line] if relationships.empty? grouped_relationship_lines end def no_relationship_line " No observed foreign-key relationship to #{resource.model_name} ##{resource.id}." end def grouped_relationship_lines relationships.group_by { |rel| [rel.from_model, rel.from_id] }.flat_map do |(model, id), grouped| ["#{model} ##{id}"] + grouped.map { |rel| " #{rel.column} → #{rel.to_model} ##{rel.to_id}" } end end end
Class Method Summary collapse
-
.for_outcome(outcome:, path:, http_method: "GET", application: nil) ⇒ Object
Resolves the resource, resolves the principal (from a Sweep::Outcome's PrincipalDescriptor), and reports relationships in one call.
-
.resolve_principal(descriptor) ⇒ Object
Resolves the actual record behind a Karst::Identity::PrincipalDescriptor only through the configured principal source.
-
.resolve_resource(path:, http_method: "GET", application: nil) ⇒ Object
Attempts to resolve the exact record a route addresses, trusting only Rails' own route recognition and controller naming convention, and only when every step is unambiguous.
Instance Method Summary collapse
- #call(observed_status: nil, observed_redirect: nil) ⇒ Object
-
#initialize(resource:, principal:) ⇒ ResourceEvidence
constructor
A new instance of ResourceEvidence.
Constructor Details
#initialize(resource:, principal:) ⇒ ResourceEvidence
Returns a new instance of ResourceEvidence.
166 167 168 169 |
# File 'lib/karst/access/resource_evidence.rb', line 166 def initialize(resource:, principal:) @resource = resource @principal = principal end |
Class Method Details
.for_outcome(outcome:, path:, http_method: "GET", application: nil) ⇒ Object
Resolves the resource, resolves the principal (from a Sweep::Outcome's PrincipalDescriptor), and reports relationships in one call. Either resolution step may fail safely -- see #resolve_resource and #resolve_principal -- in which case the Result carries a limitation instead of relationships.
87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/karst/access/resource_evidence.rb', line 87 def for_outcome(outcome:, path:, http_method: "GET", application: nil) resource, resource_limitation = resolve_resource(path: path, http_method: http_method, application: application) principal, principal_limitation = resolve_principal(outcome.principal) limitation = [resource_limitation, principal_limitation].compact.join("; ") return unresolved_result(outcome, limitation) if resource.nil? || principal.nil? new(resource: resource, principal: principal).call( observed_status: outcome.status, observed_redirect: outcome.redirect ) end |
.resolve_principal(descriptor) ⇒ Object
Resolves the actual record behind a Karst::Identity::PrincipalDescriptor only through the configured principal source. A valid model/id outside that source is deliberately unresolved.
121 122 123 124 125 126 127 128 |
# File 'lib/karst/access/resource_evidence.rb', line 121 def resolve_principal(descriptor) record = Identity.resolve(model_name: descriptor.model_name, id: descriptor.id) return [nil, "principal is not available from the configured principal source"] unless record [record, nil] rescue Identity::Error [nil, "the configured principal source is unavailable"] end |
.resolve_resource(path:, http_method: "GET", application: nil) ⇒ Object
Attempts to resolve the exact record a route addresses, trusting only Rails' own route recognition and controller naming convention, and only when every step is unambiguous. Returns [record, nil] on success or [nil, reason] when any step is not reliable -- never a guessed record.
105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/karst/access/resource_evidence.rb', line 105 def resolve_resource(path:, http_method: "GET", application: nil) app = application || rails_application return [nil, "no Rails application is available to recognize the route"] unless app params = recognize(app, path, http_method) return [nil, "the route could not be recognized"] unless params id = params[:id] return [nil, "the recognized route has no :id segment addressing one specific resource"] unless id find_by_controller(params[:controller], id) end |
Instance Method Details
#call(observed_status: nil, observed_redirect: nil) ⇒ Object
171 172 173 174 175 176 177 178 179 180 |
# File 'lib/karst/access/resource_evidence.rb', line 171 def call(observed_status: nil, observed_redirect: nil) Result.new( principal: Identity.describe(@principal), resource: ResourceDescriptor.new(model_name: model_name(@resource), id: primary_key_value(@resource)), relationships: relationships.freeze, observed_status: observed_status, observed_redirect: observed_redirect, limitation: nil ) end |