current_scope 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +577 -0
  4. data/Rakefile +6 -0
  5. data/app/assets/javascripts/current_scope/application.js +225 -0
  6. data/app/assets/stylesheets/current_scope/application.css +707 -0
  7. data/app/controllers/current_scope/application_controller.rb +54 -0
  8. data/app/controllers/current_scope/events_controller.rb +14 -0
  9. data/app/controllers/current_scope/role_assignments_controller.rb +104 -0
  10. data/app/controllers/current_scope/roles_controller.rb +135 -0
  11. data/app/controllers/current_scope/scoped_role_assignments_controller.rb +159 -0
  12. data/app/controllers/current_scope/subjects_controller.rb +57 -0
  13. data/app/helpers/current_scope/application_helper.rb +72 -0
  14. data/app/models/current_scope/application_record.rb +5 -0
  15. data/app/models/current_scope/current.rb +50 -0
  16. data/app/models/current_scope/event.rb +117 -0
  17. data/app/models/current_scope/role.rb +55 -0
  18. data/app/models/current_scope/role_assignment.rb +21 -0
  19. data/app/models/current_scope/role_permission.rb +9 -0
  20. data/app/models/current_scope/scoped_role_assignment.rb +14 -0
  21. data/app/views/current_scope/events/index.html.erb +41 -0
  22. data/app/views/current_scope/roles/edit.html.erb +86 -0
  23. data/app/views/current_scope/roles/index.html.erb +41 -0
  24. data/app/views/current_scope/roles/members.html.erb +96 -0
  25. data/app/views/current_scope/roles/new.html.erb +19 -0
  26. data/app/views/current_scope/scoped_role_assignments/new.html.erb +135 -0
  27. data/app/views/current_scope/subjects/index.html.erb +100 -0
  28. data/app/views/layouts/current_scope/application.html.erb +53 -0
  29. data/config/routes.rb +14 -0
  30. data/db/migrate/20260710000001_create_current_scope_tables.rb +31 -0
  31. data/db/migrate/20260710000002_create_current_scope_events.rb +32 -0
  32. data/db/migrate/20260714000001_add_description_to_current_scope_roles.rb +5 -0
  33. data/lib/current_scope/configuration.rb +181 -0
  34. data/lib/current_scope/context.rb +36 -0
  35. data/lib/current_scope/engine.rb +14 -0
  36. data/lib/current_scope/gating_tripwire.rb +53 -0
  37. data/lib/current_scope/guard.rb +97 -0
  38. data/lib/current_scope/mutation_guard.rb +55 -0
  39. data/lib/current_scope/permission_catalog.rb +33 -0
  40. data/lib/current_scope/permission_grid.rb +90 -0
  41. data/lib/current_scope/permissions.rb +57 -0
  42. data/lib/current_scope/resolver.rb +171 -0
  43. data/lib/current_scope/scopeable.rb +38 -0
  44. data/lib/current_scope/test_helpers.rb +53 -0
  45. data/lib/current_scope/version.rb +3 -0
  46. data/lib/current_scope.rb +168 -0
  47. data/lib/generators/current_scope/install/install_generator.rb +33 -0
  48. data/lib/generators/current_scope/install/templates/initializer.rb +77 -0
  49. data/lib/tasks/current_scope_tasks.rake +15 -0
  50. metadata +113 -0
@@ -0,0 +1,33 @@
1
+ module CurrentScope
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("templates", __dir__)
5
+
6
+ def copy_initializer
7
+ template "initializer.rb", "config/initializers/current_scope.rb"
8
+ end
9
+
10
+ def mount_engine
11
+ route 'mount CurrentScope::Engine => "/current_scope"'
12
+ end
13
+
14
+ def show_next_steps
15
+ say <<~NEXT
16
+
17
+ CurrentScope installed. Next steps:
18
+
19
+ 1. bin/rails current_scope:install:migrations && bin/rails db:migrate
20
+ 2. Include the concerns in ApplicationController (Context first):
21
+ include CurrentScope::Context
22
+ include CurrentScope::Guard
23
+ 3. Seed the baseline roles and give yourself full access, e.g. in db/seeds.rb:
24
+ CurrentScope.seed_defaults!
25
+ CurrentScope::RoleAssignment.create!(
26
+ subject: User.first, role: CurrentScope::Role.find_by!(name: "Owner"))
27
+ 4. Manage roles at /current_scope (full-access subjects only).
28
+
29
+ NEXT
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,77 @@
1
+ CurrentScope.configure do |config|
2
+ # Controller method that returns the authenticated subject.
3
+ # config.user_method = :current_user
4
+
5
+ # Class whose instances hold roles, used by the management UI.
6
+ # config.subject_class = "User"
7
+
8
+ # How a subject is identified in the management UI (an id is meaningless with
9
+ # UUID keys). A Symbol names a method; a Proc takes the subject. Defaults to a
10
+ # best-effort label (current_scope_label, else name/email/title, else id).
11
+ # config.subject_label = :email
12
+ # config.subject_label = ->(u) { "#{u.first_name} #{u.last_name}" }
13
+
14
+ # Fold the role-editor grid's action columns. Default = CRUD (new/edit hide
15
+ # into create/update; index+show read as one). Set to nil to show every raw
16
+ # action as its own column.
17
+ # config.permission_grid_groups = { "read" => %w[index show], "create" => %w[new create],
18
+ # "update" => %w[edit update], "destroy" => %w[destroy] }
19
+
20
+ # --- Impersonation (act-as) ---------------------------------------------
21
+ # These three knobs layer, in this order:
22
+ #
23
+ # 1. actor_method — turns impersonation ON.
24
+ # 2. allow_mutations_while_impersonating — the read-only gate runs FIRST.
25
+ # 3. sod_identity — only observable once a mutation
26
+ # is allowed through (or on a
27
+ # GET-listed sod_action).
28
+ #
29
+ # Controller method returning the REAL actor while impersonating. Leave unset
30
+ # when no one impersonates — actor then equals the subject, so attribution
31
+ # reads current_scope_actor with no nil branch, and sod_identity below has no
32
+ # effect. See the "Impersonation (act-as)" section of the README.
33
+ # config.actor_method = :true_user
34
+
35
+ # false (default): an impersonated session is read-only — every non-GET/HEAD
36
+ # request is denied, INCLUDING the engine's management UI. Your
37
+ # stop-impersonation, sign-out, and sign-in endpoints must opt out with
38
+ # skip_before_action :current_scope_mutation_guard!, or impersonation (and
39
+ # sign-in) can never clear it.
40
+ # config.allow_mutations_while_impersonating = false
41
+
42
+ # Which identities the separation-of-duties veto weighs. :either (default)
43
+ # also vetoes a record the REAL actor initiated while impersonating, so
44
+ # impersonation can never approve your own record. :subject weighs only the
45
+ # effective subject. Identical when not impersonating.
46
+ # config.sod_identity = :either
47
+ # ------------------------------------------------------------------------
48
+
49
+ # Separation of duties is OPT-IN — empty by default. List the actions a
50
+ # record's initiator can never perform on their own record (four-eyes).
51
+ # Deliberately NOT editable in the UI. Records reached by these actions must
52
+ # define current_scope_initiator (return nil to exempt a record type) — the
53
+ # resolver raises if the hook is missing. Leave commented for RBAC-only apps.
54
+ # config.sod_actions = %w[approve]
55
+
56
+ # Audit ledger — tri-state: false | true (default) | :strict.
57
+ # false — no audit rows recorded.
58
+ # true — record every authorization change; if the events table hasn't
59
+ # been migrated yet, degrade gracefully (skip + warn once).
60
+ # :strict — a missing events table RAISES (rolling the mutation back), so an
61
+ # audit-mandatory app never commits an unaudited change.
62
+ # config.audit = true
63
+
64
+ # Dev/test aid: log a nudge when an SoD action is ALLOWED but was gated with a
65
+ # nil record — i.e. the SoD veto was silently skipped because
66
+ # current_scope_record returned nil on a member action. Off by default; never
67
+ # changes behavior.
68
+ # config.warn_on_nil_sod_record = false
69
+
70
+ # Controller paths (regexps) excluded from the permission grid. Excluded
71
+ # controllers can't be granted, so they must also skip the gate with
72
+ # skip_before_action :current_scope_check! — Guard raises otherwise.
73
+ # config.excluded_controllers += [%r{\Awebhooks/}]
74
+
75
+ # Controller the management UI inherits from (for host auth + before_actions).
76
+ # config.parent_controller = "::ApplicationController"
77
+ end
@@ -0,0 +1,15 @@
1
+ namespace :current_scope do
2
+ desc "Grant the full-access Owner role to a subject (bootstrap the first admin). " \
3
+ "Usage: bin/rails current_scope:grant SUBJECT_ID=1"
4
+ task grant: :environment do
5
+ id = ENV["SUBJECT_ID"]
6
+ abort "SUBJECT_ID is required, e.g. bin/rails current_scope:grant SUBJECT_ID=1" if id.blank?
7
+
8
+ klass = CurrentScope.config.subject_class.constantize
9
+ subject = klass.find_by(id: id)
10
+ abort "No #{klass} with id=#{id}" if subject.nil?
11
+
12
+ CurrentScope.grant!(subject)
13
+ puts "Granted the full-access Owner role to #{klass}##{subject.id}."
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: current_scope
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - David Teren
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '8.1'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '9'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '8.1'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '9'
32
+ description: 'A mountable Rails engine for authorization: permissions auto-derived
33
+ from controller actions, roles as editable data, per-record scoped roles, a separation-of-duties
34
+ veto, and an ambient authorization context that makes allowed_to? work identically
35
+ in controllers, views, and components.'
36
+ email:
37
+ - dteren@gmail.com
38
+ executables: []
39
+ extensions: []
40
+ extra_rdoc_files: []
41
+ files:
42
+ - MIT-LICENSE
43
+ - README.md
44
+ - Rakefile
45
+ - app/assets/javascripts/current_scope/application.js
46
+ - app/assets/stylesheets/current_scope/application.css
47
+ - app/controllers/current_scope/application_controller.rb
48
+ - app/controllers/current_scope/events_controller.rb
49
+ - app/controllers/current_scope/role_assignments_controller.rb
50
+ - app/controllers/current_scope/roles_controller.rb
51
+ - app/controllers/current_scope/scoped_role_assignments_controller.rb
52
+ - app/controllers/current_scope/subjects_controller.rb
53
+ - app/helpers/current_scope/application_helper.rb
54
+ - app/models/current_scope/application_record.rb
55
+ - app/models/current_scope/current.rb
56
+ - app/models/current_scope/event.rb
57
+ - app/models/current_scope/role.rb
58
+ - app/models/current_scope/role_assignment.rb
59
+ - app/models/current_scope/role_permission.rb
60
+ - app/models/current_scope/scoped_role_assignment.rb
61
+ - app/views/current_scope/events/index.html.erb
62
+ - app/views/current_scope/roles/edit.html.erb
63
+ - app/views/current_scope/roles/index.html.erb
64
+ - app/views/current_scope/roles/members.html.erb
65
+ - app/views/current_scope/roles/new.html.erb
66
+ - app/views/current_scope/scoped_role_assignments/new.html.erb
67
+ - app/views/current_scope/subjects/index.html.erb
68
+ - app/views/layouts/current_scope/application.html.erb
69
+ - config/routes.rb
70
+ - db/migrate/20260710000001_create_current_scope_tables.rb
71
+ - db/migrate/20260710000002_create_current_scope_events.rb
72
+ - db/migrate/20260714000001_add_description_to_current_scope_roles.rb
73
+ - lib/current_scope.rb
74
+ - lib/current_scope/configuration.rb
75
+ - lib/current_scope/context.rb
76
+ - lib/current_scope/engine.rb
77
+ - lib/current_scope/gating_tripwire.rb
78
+ - lib/current_scope/guard.rb
79
+ - lib/current_scope/mutation_guard.rb
80
+ - lib/current_scope/permission_catalog.rb
81
+ - lib/current_scope/permission_grid.rb
82
+ - lib/current_scope/permissions.rb
83
+ - lib/current_scope/resolver.rb
84
+ - lib/current_scope/scopeable.rb
85
+ - lib/current_scope/test_helpers.rb
86
+ - lib/current_scope/version.rb
87
+ - lib/generators/current_scope/install/install_generator.rb
88
+ - lib/generators/current_scope/install/templates/initializer.rb
89
+ - lib/tasks/current_scope_tasks.rake
90
+ homepage: https://github.com/davidteren/current_scope
91
+ licenses:
92
+ - MIT
93
+ metadata:
94
+ changelog_uri: https://github.com/davidteren/current_scope/blob/main/CHANGELOG.md
95
+ rubygems_mfa_required: 'true'
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '3.2'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubygems_version: 3.6.9
111
+ specification_version: 4
112
+ summary: Data-driven authorization for Rails with an ambient current-user context.
113
+ test_files: []