admission 0.2.8 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Gemfile +2 -1
  4. data/README.md +24 -2
  5. data/examples/rails5_feudalism/.gitignore +19 -0
  6. data/examples/rails5_feudalism/.ruby-version +1 -0
  7. data/examples/rails5_feudalism/Gemfile +52 -0
  8. data/examples/rails5_feudalism/README.md +17 -0
  9. data/examples/rails5_feudalism/Rakefile +6 -0
  10. data/examples/rails5_feudalism/app/assets/config/manifest.js +3 -0
  11. data/examples/rails5_feudalism/app/assets/images/.keep +0 -0
  12. data/examples/rails5_feudalism/app/assets/javascripts/application.js +15 -0
  13. data/examples/rails5_feudalism/app/assets/stylesheets/application.css +15 -0
  14. data/examples/rails5_feudalism/app/assets/stylesheets/scaffolds.scss +84 -0
  15. data/examples/rails5_feudalism/app/controllers/application_controller.rb +13 -0
  16. data/examples/rails5_feudalism/app/controllers/concerns/.keep +0 -0
  17. data/examples/rails5_feudalism/app/controllers/users_controller.rb +75 -0
  18. data/examples/rails5_feudalism/app/helpers/application_helper.rb +2 -0
  19. data/examples/rails5_feudalism/app/jobs/application_job.rb +2 -0
  20. data/examples/rails5_feudalism/app/models/application_record.rb +3 -0
  21. data/examples/rails5_feudalism/app/models/concerns/.keep +0 -0
  22. data/examples/rails5_feudalism/app/models/person.rb +11 -0
  23. data/examples/rails5_feudalism/app/models/possession.rb +7 -0
  24. data/examples/rails5_feudalism/app/models/trait.rb +7 -0
  25. data/examples/rails5_feudalism/app/models/user.rb +17 -0
  26. data/examples/rails5_feudalism/app/models/user_status.rb +140 -0
  27. data/examples/rails5_feudalism/app/views/admission/forbidden.html.erb +14 -0
  28. data/examples/rails5_feudalism/app/views/layouts/application.html.erb +15 -0
  29. data/examples/rails5_feudalism/app/views/users/_form.html.erb +19 -0
  30. data/examples/rails5_feudalism/app/views/users/_privilege_row.html.erb +24 -0
  31. data/examples/rails5_feudalism/app/views/users/_privileges_list.html.erb +51 -0
  32. data/examples/rails5_feudalism/app/views/users/edit.html.erb +6 -0
  33. data/examples/rails5_feudalism/app/views/users/index.html.erb +25 -0
  34. data/examples/rails5_feudalism/app/views/users/new.html.erb +5 -0
  35. data/examples/rails5_feudalism/app/views/users/show.html.erb +4 -0
  36. data/examples/rails5_feudalism/bin/bundle +3 -0
  37. data/examples/rails5_feudalism/bin/rails +9 -0
  38. data/examples/rails5_feudalism/bin/rake +9 -0
  39. data/examples/rails5_feudalism/bin/setup +38 -0
  40. data/examples/rails5_feudalism/bin/spring +17 -0
  41. data/examples/rails5_feudalism/bin/update +29 -0
  42. data/examples/rails5_feudalism/bin/yarn +11 -0
  43. data/examples/rails5_feudalism/config.ru +5 -0
  44. data/examples/rails5_feudalism/config/application.rb +28 -0
  45. data/examples/rails5_feudalism/config/boot.rb +3 -0
  46. data/examples/rails5_feudalism/config/database.yml +85 -0
  47. data/examples/rails5_feudalism/config/environment.rb +5 -0
  48. data/examples/rails5_feudalism/config/environments/development.rb +49 -0
  49. data/examples/rails5_feudalism/config/environments/production.rb +82 -0
  50. data/examples/rails5_feudalism/config/environments/test.rb +36 -0
  51. data/examples/rails5_feudalism/config/initializers/application_controller_renderer.rb +8 -0
  52. data/examples/rails5_feudalism/config/initializers/assets.rb +14 -0
  53. data/examples/rails5_feudalism/config/initializers/backtrace_silencers.rb +7 -0
  54. data/examples/rails5_feudalism/config/initializers/cookies_serializer.rb +5 -0
  55. data/examples/rails5_feudalism/config/initializers/filter_parameter_logging.rb +4 -0
  56. data/examples/rails5_feudalism/config/initializers/inflections.rb +16 -0
  57. data/examples/rails5_feudalism/config/initializers/mime_types.rb +4 -0
  58. data/examples/rails5_feudalism/config/initializers/wrap_parameters.rb +14 -0
  59. data/examples/rails5_feudalism/config/locales/en.yml +33 -0
  60. data/examples/rails5_feudalism/config/puma.rb +56 -0
  61. data/examples/rails5_feudalism/config/routes.rb +8 -0
  62. data/examples/rails5_feudalism/config/secrets.yml +32 -0
  63. data/examples/rails5_feudalism/config/spring.rb +6 -0
  64. data/examples/rails5_feudalism/db/migrate/20171018085629_create_users.rb +9 -0
  65. data/examples/rails5_feudalism/db/migrate/20171019082044_create_people.rb +9 -0
  66. data/examples/rails5_feudalism/db/migrate/20171019082055_create_possessions.rb +9 -0
  67. data/examples/rails5_feudalism/db/migrate/20171019082059_create_traits.rb +9 -0
  68. data/examples/rails5_feudalism/db/schema.rb +49 -0
  69. data/examples/rails5_feudalism/db/seeds.rb +7 -0
  70. data/examples/rails5_feudalism/lib/assets/.keep +0 -0
  71. data/examples/rails5_feudalism/lib/tasks/.keep +0 -0
  72. data/examples/rails5_feudalism/log/.keep +0 -0
  73. data/examples/rails5_feudalism/package.json +7 -0
  74. data/examples/rails5_feudalism/public/404.html +67 -0
  75. data/examples/rails5_feudalism/public/422.html +67 -0
  76. data/examples/rails5_feudalism/public/500.html +66 -0
  77. data/examples/rails5_feudalism/public/apple-touch-icon-precomposed.png +0 -0
  78. data/examples/rails5_feudalism/public/apple-touch-icon.png +0 -0
  79. data/examples/rails5_feudalism/public/favicon.ico +0 -0
  80. data/examples/rails5_feudalism/public/robots.txt +1 -0
  81. data/examples/rails5_feudalism/test/application_system_test_case.rb +5 -0
  82. data/examples/rails5_feudalism/test/controllers/.keep +0 -0
  83. data/examples/rails5_feudalism/test/fixtures/.keep +0 -0
  84. data/examples/rails5_feudalism/test/fixtures/files/.keep +0 -0
  85. data/examples/rails5_feudalism/test/fixtures/people.yml +36 -0
  86. data/examples/rails5_feudalism/test/fixtures/possessions.yml +0 -0
  87. data/examples/rails5_feudalism/test/fixtures/traits.yml +0 -0
  88. data/examples/rails5_feudalism/test/fixtures/users.yml +0 -0
  89. data/examples/rails5_feudalism/test/helpers/.keep +0 -0
  90. data/examples/rails5_feudalism/test/models/.keep +0 -0
  91. data/examples/rails5_feudalism/test/models/user_status/rules_test.rb +152 -0
  92. data/examples/rails5_feudalism/test/models/user_status/user_status_test.rb +50 -0
  93. data/examples/rails5_feudalism/test/system/.keep +0 -0
  94. data/examples/rails5_feudalism/test/test_helper.rb +9 -0
  95. data/examples/rails5_feudalism/tmp/.keep +0 -0
  96. data/examples/rails5_feudalism/vendor/.keep +0 -0
  97. data/examples/rails5_feudalism/yarn.lock +7 -0
  98. data/lib/admission.rb +2 -0
  99. data/lib/admission/admission.rb +8 -2
  100. data/lib/admission/arbitration.rb +5 -0
  101. data/lib/admission/denied.rb +4 -5
  102. data/lib/admission/index.rb +59 -0
  103. data/lib/admission/minitest.rb +41 -0
  104. data/lib/admission/privilege.rb +13 -3
  105. data/lib/admission/rails.rb +8 -94
  106. data/lib/admission/rails/action_admission.rb +73 -0
  107. data/lib/admission/rails/controller_addon.rb +40 -0
  108. data/lib/admission/rails/scope_not_defined.rb +21 -0
  109. data/lib/admission/rails/scope_resolver.rb +46 -0
  110. data/lib/admission/resource_arbitration.rb +14 -9
  111. data/lib/admission/status.rb +11 -2
  112. data/lib/admission/tests.rb +59 -0
  113. data/lib/admission/version.rb +1 -1
  114. data/spec/integration/action_arbitrating_spec.rb +1 -1
  115. data/spec/integration/resource_arbitrating_spec.rb +1 -1
  116. data/spec/rspec_config.rb +13 -13
  117. data/spec/spec_helper.rb +10 -2
  118. data/spec/test_context/index.rb +0 -2
  119. data/spec/unit/arbitration_spec.rb +33 -0
  120. data/spec/unit/index_spec.rb +144 -0
  121. data/spec/unit/privilege/order_definer_spec.rb +1 -1
  122. data/spec/unit/privilege_spec.rb +22 -5
  123. data/spec/unit/rails/action_admission_spec.rb +188 -0
  124. data/spec/unit/rails/controller_addon_spec.rb +68 -0
  125. data/spec/unit/rails/scope_resolver_spec.rb +72 -0
  126. data/spec/unit/resource_arbitration_spec.rb +36 -1
  127. data/spec/unit/status_spec.rb +1 -1
  128. metadata +108 -5
  129. data/spec/integration/_helper.rb +0 -2
  130. data/spec/unit/_helper.rb +0 -1
@@ -0,0 +1,140 @@
1
+ Admission::Privilege.class_exec do
2
+ alias country context
3
+ end
4
+
5
+ class UserStatus < Admission::Status
6
+
7
+ def self.for_user user
8
+ new user, parse_privileges(user.privileges), rules, Admission::ResourceArbitration
9
+ end
10
+
11
+ def self.privilege_for_country name, level, country
12
+ Admission::Privilege.get_from_order(privileges_index, name, level).dup_with_context country
13
+ end
14
+
15
+ def self.parse_privileges privileges
16
+ list = []
17
+ return list unless privileges && privileges.is_a?(Hash)
18
+ privileges = privileges.stringify_keys
19
+
20
+ (privileges.keys & Person::COUNTRIES).each do |country|
21
+ records = privileges[country.to_s].presence || next
22
+ records.uniq.each do |record|
23
+ name, level = record.split '-'
24
+ list << privilege_for_country(name, level, country)
25
+ end
26
+ end
27
+
28
+ list.compact
29
+ end
30
+
31
+ def self.dump_privileges list
32
+ return if list.blank?
33
+
34
+ hash = list.inject Hash.new do |hash, privilege|
35
+ (hash[privilege.country] ||= []) << privilege.text_key
36
+ hash
37
+ end
38
+
39
+ hash['_all'] = list.map(&:text_key).uniq
40
+
41
+ hash
42
+ end
43
+
44
+ def self.privilege_key_to_text text_key
45
+ case text_key
46
+ when 42 then 'the very answer'
47
+ else text_key
48
+ end
49
+ end
50
+
51
+ def self.privileges_index
52
+ @privileges_index ||= Admission::Privilege.define_order do
53
+ privilege :human, levels: %i[adult adult_white_male]
54
+ privilege :lord, inherits: %i[human]
55
+ privilege :duke, inherits: %i[human]
56
+ end
57
+ end
58
+
59
+ def self.privileges_list
60
+ @privileges_list ||= Admission::Privilege.order_to_array(privileges_index)
61
+ end
62
+
63
+ def self.rules
64
+ @rules ||= Admission::ResourceArbitration.define_rules privileges_index do
65
+
66
+ get_object_person = -> (object) {
67
+ if object.is_a? Person
68
+ object
69
+ elsif object.respond_to? :person
70
+ object.person
71
+ end
72
+ }
73
+
74
+ same_person = -> (object, _) {
75
+ object_person = get_object_person[object]
76
+ return :forbidden unless object_person
77
+ object_person == self.person
78
+ }
79
+
80
+ same_country = -> (object, country) {
81
+ object_person = get_object_person[object]
82
+ return :forbidden unless object_person
83
+ object_person.country == country
84
+ }
85
+
86
+ ###############
87
+
88
+ privilege :human do
89
+
90
+ # can have possessions, can try to make new
91
+ allow nested_scope(Person, :possessions), %i[index new]
92
+
93
+ # is aware of own traits
94
+ allow type_to_scope(Trait), :index
95
+
96
+ end
97
+
98
+ privilege :human, :adult do
99
+
100
+ # can do anything with his own possessions
101
+ allow nested_scope(Person, :possessions), %i[create]
102
+ allow_resource [Person, :possessions], %i[edit update destroy], &same_person
103
+
104
+ # can work only on self
105
+ allow_resource Trait, %i[edit update destroy], &same_person
106
+
107
+ end
108
+
109
+ privilege :human, :adult_white_male do
110
+
111
+ # can desire to work on self to achieve new traits
112
+ allow type_to_scope(Trait), %i[new create]
113
+
114
+ end
115
+
116
+ privilege :lord do
117
+
118
+ # is entitled to know what possessions exists in his country, and impound them
119
+ allow_resource [Person, :possessions], :impound, &same_country
120
+
121
+ # is entitled to command his people
122
+ allow type_to_scope(Trait), %i[index new create]
123
+ allow_resource Trait, %i[edit update destroy], &same_country
124
+
125
+ end
126
+
127
+ privilege :duke do
128
+
129
+ # as a sovereign can impose ownership changes
130
+ allow_resource [Person, :possessions], %i[hand_over_to destroy], &same_country
131
+
132
+ # is entitled to command his people - also?
133
+ allow_resource :traits, %i[edit update destroy], &same_country
134
+
135
+ end
136
+
137
+ end
138
+ end
139
+
140
+ end
@@ -0,0 +1,14 @@
1
+
2
+ <h1>Forbidden.</h1>
3
+
4
+ <%
5
+ back_link = if referer_url
6
+ %>
7
+ <%= link_to 'back', referer_url %>
8
+ <%
9
+ else
10
+ 'back'
11
+ end
12
+ %>
13
+
14
+ return <%= back_link %> or go to <%= link_to 'homepage', root_path %>
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Admission Integration into Rails: Feudalism</title>
5
+ <%= csrf_meta_tags %>
6
+
7
+ <%= stylesheet_link_tag 'application', media: 'all' %>
8
+ <%= javascript_include_tag 'application' %>
9
+ </head>
10
+
11
+ <body>
12
+ <%= yield %>
13
+ <%= console %>
14
+ </body>
15
+ </html>
@@ -0,0 +1,19 @@
1
+ <%= form_with(model: user, local: true) do |form| %>
2
+ <% if user.errors.any? %>
3
+ <div id="error_explanation">
4
+ <h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
5
+
6
+ <ul>
7
+ <% user.errors.full_messages.each do |message| %>
8
+ <li><%= message %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+
14
+ <%= render partial: 'privileges_list', locals: {user: @user} %>
15
+
16
+ <div class="actions">
17
+ <%= form.submit %>
18
+ </div>
19
+ <% end %>
@@ -0,0 +1,24 @@
1
+ <% country = local_assigns[:country] || nil %>
2
+ <% name = local_assigns[:name] || nil %>
3
+
4
+ <div class="form-group" data-privilege-row>
5
+ <div class="row">
6
+
7
+ <div class="col-xs-5">
8
+ <%= select_tag 'user[privileges][country][]',
9
+ options_for_select(countries_list, country), class: 'form-control' %>
10
+ </div>
11
+
12
+ <div class="col-xs-5">
13
+ <%= select_tag 'user[privileges][name][]',
14
+ options_for_select(names_list, name), class: 'form-control' %>
15
+ </div>
16
+
17
+ <div class="col-xs-2">
18
+ <button type="button" class="btn btn-danger" data-action="remove">
19
+ Remove
20
+ </button>
21
+ </div>
22
+
23
+ </div>
24
+ </div>
@@ -0,0 +1,51 @@
1
+ <%
2
+ countries_list = Person::COUNTRIES
3
+ privileges_list = UserStatus.privileges_list.map &:text_key
4
+ privileges_list.map!{|name| [UserStatus.privilege_key_to_text(name), name]}
5
+ %>
6
+
7
+ <div id="user_privileges_list">
8
+ <%= content_tag :label, User.human_attribute_name(:privileges) %>
9
+
10
+ <div data-privs-list>
11
+ <% if user.status.privileges %>
12
+ <% user.status.privileges.reject{|p| p.name == :super}.each do |privilege| %>
13
+ <%= render partial: 'privilege_row', locals: {
14
+ name: privilege.text_key, country: privilege.country,
15
+ countries_list: countries_list, names_list: privileges_list
16
+ } %>
17
+ <% end %>
18
+ <% end %>
19
+ </div>
20
+
21
+ <div class="form-group">
22
+ <div class="row">
23
+ <div class="col-xs-2">
24
+ <button type="button" class="btn btn-default" data-action="add">
25
+ Add privilege
26
+ </button>
27
+ </div>
28
+ </div>
29
+ </div>
30
+
31
+ <script type="text/html" charset="utf-8" data-privilege-template>
32
+ <%= render partial: 'privilege_row', locals: {countries_list: countries_list, names_list: privileges_list} %>
33
+ </script>
34
+
35
+ <script type="text/javascript" charset="utf-8">
36
+ $(function () {
37
+ 'use strict';
38
+ var $container = $('#user_privileges_list');
39
+ var $list = $container.find('[data-privs-list]');
40
+ var $row = $container.find('[data-privilege-template]');
41
+
42
+ $container.on('click', 'button[data-action=add]', function () {
43
+ $list.append($row.text());
44
+ });
45
+
46
+ $container.on('click', 'button[data-action=remove]', function () {
47
+ $(this).closest('[data-privilege-row]').remove();
48
+ });
49
+ });
50
+ </script>
51
+ </div>
@@ -0,0 +1,6 @@
1
+ <h1>Editing User</h1>
2
+
3
+ <%= render 'form', user: @user %>
4
+
5
+ <%= link_to 'Show', @user %> |
6
+ <%= link_to 'Back', users_path %>
@@ -0,0 +1,25 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <h1>Users</h1>
4
+
5
+ <table>
6
+ <thead>
7
+ <tr>
8
+ <th colspan="3"></th>
9
+ </tr>
10
+ </thead>
11
+
12
+ <tbody>
13
+ <% @users.each do |user| %>
14
+ <tr>
15
+ <td><%= link_to 'Show', user %></td>
16
+ <td><%= link_to 'Edit', edit_user_path(user) %></td>
17
+ <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
18
+ </tr>
19
+ <% end %>
20
+ </tbody>
21
+ </table>
22
+
23
+ <br>
24
+
25
+ <%= link_to 'New User', new_user_path %>
@@ -0,0 +1,5 @@
1
+ <h1>New User</h1>
2
+
3
+ <%= render 'form', user: @user %>
4
+
5
+ <%= link_to 'Back', users_path %>
@@ -0,0 +1,4 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <%= link_to 'Edit', edit_user_path(@user) %> |
4
+ <%= link_to 'Back', users_path %>
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3
+ load Gem.bin_path('bundler', 'bundle')
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ begin
3
+ load File.expand_path('../spring', __FILE__)
4
+ rescue LoadError => e
5
+ raise unless e.message.include?('spring')
6
+ end
7
+ APP_PATH = File.expand_path('../config/application', __dir__)
8
+ require_relative '../config/boot'
9
+ require 'rails/commands'
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ begin
3
+ load File.expand_path('../spring', __FILE__)
4
+ rescue LoadError => e
5
+ raise unless e.message.include?('spring')
6
+ end
7
+ require_relative '../config/boot'
8
+ require 'rake'
9
+ Rake.application.run
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ require 'pathname'
3
+ require 'fileutils'
4
+ include FileUtils
5
+
6
+ # path to your application root.
7
+ APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
8
+
9
+ def system!(*args)
10
+ system(*args) || abort("\n== Command #{args} failed ==")
11
+ end
12
+
13
+ chdir APP_ROOT do
14
+ # This script is a starting point to setup your application.
15
+ # Add necessary setup steps to this file.
16
+
17
+ puts '== Installing dependencies =='
18
+ system! 'gem install bundler --conservative'
19
+ system('bundle check') || system!('bundle install')
20
+
21
+ # Install JavaScript dependencies if using Yarn
22
+ # system('bin/yarn')
23
+
24
+
25
+ # puts "\n== Copying sample files =="
26
+ # unless File.exist?('config/database.yml')
27
+ # cp 'config/database.yml.sample', 'config/database.yml'
28
+ # end
29
+
30
+ puts "\n== Preparing database =="
31
+ system! 'bin/rails db:setup'
32
+
33
+ puts "\n== Removing old logs and tempfiles =="
34
+ system! 'bin/rails log:clear tmp:clear'
35
+
36
+ puts "\n== Restarting application server =="
37
+ system! 'bin/rails restart'
38
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This file loads spring without using Bundler, in order to be fast.
4
+ # It gets overwritten when you run the `spring binstub` command.
5
+
6
+ unless defined?(Spring)
7
+ require 'rubygems'
8
+ require 'bundler'
9
+
10
+ lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
11
+ spring = lockfile.specs.detect { |spec| spec.name == "spring" }
12
+ if spring
13
+ Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
14
+ gem 'spring', spring.version
15
+ require 'spring/binstub'
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ require 'pathname'
3
+ require 'fileutils'
4
+ include FileUtils
5
+
6
+ # path to your application root.
7
+ APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
8
+
9
+ def system!(*args)
10
+ system(*args) || abort("\n== Command #{args} failed ==")
11
+ end
12
+
13
+ chdir APP_ROOT do
14
+ # This script is a way to update your development environment automatically.
15
+ # Add necessary update steps to this file.
16
+
17
+ puts '== Installing dependencies =='
18
+ system! 'gem install bundler --conservative'
19
+ system('bundle check') || system!('bundle install')
20
+
21
+ puts "\n== Updating database =="
22
+ system! 'bin/rails db:migrate'
23
+
24
+ puts "\n== Removing old logs and tempfiles =="
25
+ system! 'bin/rails log:clear tmp:clear'
26
+
27
+ puts "\n== Restarting application server =="
28
+ system! 'bin/rails restart'
29
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ VENDOR_PATH = File.expand_path('..', __dir__)
3
+ Dir.chdir(VENDOR_PATH) do
4
+ begin
5
+ exec "yarnpkg #{ARGV.join(" ")}"
6
+ rescue Errno::ENOENT
7
+ $stderr.puts "Yarn executable was not detected in the system."
8
+ $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
9
+ exit 1
10
+ end
11
+ end