Module: CurrentScope
- Defined in:
- lib/current_scope.rb,
lib/current_scope/guard.rb,
lib/current_scope/engine.rb,
lib/current_scope/context.rb,
lib/current_scope/version.rb,
lib/current_scope/resolver.rb,
lib/current_scope/scopeable.rb,
app/models/current_scope/role.rb,
lib/current_scope/permissions.rb,
app/models/current_scope/event.rb,
lib/current_scope/test_helpers.rb,
lib/current_scope/configuration.rb,
app/models/current_scope/current.rb,
lib/current_scope/mutation_guard.rb,
lib/current_scope/gating_tripwire.rb,
lib/current_scope/permission_grid.rb,
lib/current_scope/permission_catalog.rb,
app/models/current_scope/role_assignment.rb,
app/models/current_scope/role_permission.rb,
app/models/current_scope/application_record.rb,
app/helpers/current_scope/application_helper.rb,
app/controllers/current_scope/roles_controller.rb,
app/controllers/current_scope/events_controller.rb,
app/models/current_scope/scoped_role_assignment.rb,
app/controllers/current_scope/subjects_controller.rb,
app/controllers/current_scope/application_controller.rb,
lib/generators/current_scope/install/install_generator.rb,
app/controllers/current_scope/role_assignments_controller.rb,
app/controllers/current_scope/scoped_role_assignments_controller.rb
Defined Under Namespace
Modules: ApplicationHelper, Context, GatingTripwire, Generators, Guard, MutationGuard, Permissions, Scopeable, TestHelpers
Classes: AccessDenied, ApplicationController, ApplicationRecord, Configuration, ConfigurationError, Current, Engine, Event, EventsController, PermissionCatalog, PermissionGrid, Resolver, Role, RoleAssignment, RoleAssignmentsController, RolePermission, RolesController, ScopedRoleAssignment, ScopedRoleAssignmentsController, SubjectsController
Constant Summary
collapse
- VERSION =
"0.2.0"
Class Method Summary
collapse
Class Method Details
.allowed?(action, subject:, record: nil, controller_path: nil, actor: nil) ⇒ Boolean
The single entry point behind every allowed_to? call.
action is either a full permission key ("admin/reports#approve") or a
bare action name resolved against record's route key, falling back to
controller_path.
77
78
79
80
81
82
83
84
|
# File 'lib/current_scope.rb', line 77
def allowed?(action, subject:, record: nil, controller_path: nil, actor: nil)
resolver.allow?(
subject: subject,
permission: permission_key(action, record: record, controller_path: controller_path),
record: record,
actor: actor
)
end
|
.catalog ⇒ Object
45
46
47
|
# File 'lib/current_scope.rb', line 45
def catalog
@catalog ||= PermissionCatalog.new
end
|
.config ⇒ Object
33
34
35
|
# File 'lib/current_scope.rb', line 33
def config
@config ||= Configuration.new
end
|
37
38
39
|
# File 'lib/current_scope.rb', line 37
def configure
yield config
end
|
.grant!(subject, role: nil) ⇒ Object
Bootstrap the first admin: assign a role (default: the full_access Owner)
to subject as its one org-wide role. Idempotent — re-running sets the
same subject's org role to role rather than creating a duplicate (which
the one-role-per-subject uniqueness would reject anyway). Backs the
current_scope:grant rake task, so a fresh install doesn't need a console.
142
143
144
145
146
|
# File 'lib/current_scope.rb', line 142
def grant!(subject, role: nil)
seed_defaults!
role ||= Role.find_by!(name: "Owner")
RoleAssignment.find_or_initialize_by(subject: subject).tap { |a| a.update!(role: role) }
end
|
.permission_key(action, record: nil, controller_path: nil) ⇒ Object
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
# File 'lib/current_scope.rb', line 94
def permission_key(action, record: nil, controller_path: nil)
action = action.to_s
return action if action.include?("#")
if record.respond_to?(:model_name)
route_key = record.model_name.route_key
return "#{controller_path}##{action}" if controller_path&.split("/")&.last == route_key
return "#{route_key}##{action}"
end
return "#{controller_path}##{action}" if controller_path
raise ArgumentError,
"cannot derive a permission key for #{action.inspect} — pass a record, " \
"a full \"controller#action\" string, or call from a controller/view"
end
|
.record_impersonation_started!(subject) ⇒ Object
Impersonation boundary events. The impersonated identity is an EXPLICIT
argument (not read from the ambient pair): at act-as START the ambient
actor still equals the effective user — Current re-resolves next request —
so an ambient-only recorder would lose who was impersonated. Call these
from the host's start/stop-impersonation endpoints.
119
120
121
122
|
# File 'lib/current_scope.rb', line 119
def record_impersonation_started!(subject)
require_actor_method!
Event.record!(event: "impersonation.started", target: subject)
end
|
.record_impersonation_stopped!(subject) ⇒ Object
124
125
126
127
|
# File 'lib/current_scope.rb', line 124
def record_impersonation_stopped!(subject)
require_actor_method!
Event.record!(event: "impersonation.stopped", target: subject)
end
|
.register_scopeable(model_name) ⇒ Object
61
62
63
|
# File 'lib/current_scope.rb', line 61
def register_scopeable(model_name)
scopeable_registry << model_name.to_s
end
|
.reset_catalog! ⇒ Object
49
50
51
|
# File 'lib/current_scope.rb', line 49
def reset_catalog!
@catalog = nil
end
|
.reset_scopeable_registry! ⇒ Object
69
70
71
|
# File 'lib/current_scope.rb', line 69
def reset_scopeable_registry!
@scopeable_registry = Set.new
end
|
.resolver ⇒ Object
41
42
43
|
# File 'lib/current_scope.rb', line 41
def resolver
@resolver ||= Resolver.new
end
|
.scope_for(subject:, model:, permission:) ⇒ Object
The list-side companion to allowed?. Returns a chainable relation of the
records of model the subject may act on under permission — same
grants, same fail-closed rules as the per-record gate. permission is a
resolved key ("projects#index"); the mixin derives the default.
90
91
92
|
# File 'lib/current_scope.rb', line 90
def scope_for(subject:, model:, permission:)
resolver.scope_for(subject: subject, model: model, permission: permission)
end
|
.scopeable_registry ⇒ Object
Models that opted into the scoped-role picker via CurrentScope::Scopeable.
Stored as class-name strings and resolved lazily so dev-mode reloading
never pins a stale constant. Rebuilt from scratch on every engine
to_prepare (see reset_scopeable_registry!).
57
58
59
|
# File 'lib/current_scope.rb', line 57
def scopeable_registry
@scopeable_registry ||= Set.new
end
|
.scopeable_resources ⇒ Object
65
66
67
|
# File 'lib/current_scope.rb', line 65
def scopeable_resources
scopeable_registry.map(&:constantize).sort_by(&:name)
end
|
.seed_defaults! ⇒ Object
Creates the two baseline roles every install needs: an Owner with
full_access (present and future permissions) and a Member baseline.
Call from db/seeds.rb.
132
133
134
135
|
# File 'lib/current_scope.rb', line 132
def seed_defaults!
Role.find_or_create_by!(name: "Owner") { |r| r.full_access = true }
Role.find_or_create_by!(name: "Member")
end
|