openstax_utilities 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +28 -1
- data/Rakefile +12 -41
- data/lib/openstax/utilities/access_policy.rb +54 -0
- data/lib/openstax/utilities/controller_extensions.rb +47 -0
- data/lib/openstax/utilities/engine.rb +6 -0
- data/lib/openstax/utilities/version.rb +1 -1
- data/lib/openstax_utilities.rb +2 -0
- data/spec/dummy/README.md +1 -0
- data/{test → spec}/dummy/Rakefile +0 -0
- data/spec/dummy/app/access_policies/dummy_access_policy.rb +10 -0
- data/{test → spec}/dummy/app/assets/javascripts/application.js +0 -0
- data/{test → spec}/dummy/app/assets/stylesheets/application.css +0 -0
- data/spec/dummy/app/assets/stylesheets/scaffold.css +56 -0
- data/{test → spec}/dummy/app/controllers/application_controller.rb +2 -0
- data/spec/dummy/app/controllers/users_controller.rb +87 -0
- data/{test → spec}/dummy/app/helpers/application_helper.rb +0 -0
- data/spec/dummy/app/models/user.rb +2 -0
- data/{test → spec}/dummy/app/views/layouts/application.html.erb +0 -0
- data/spec/dummy/app/views/users/_form.html.erb +17 -0
- data/spec/dummy/app/views/users/edit.html.erb +6 -0
- data/spec/dummy/app/views/users/index.html.erb +21 -0
- data/spec/dummy/app/views/users/new.html.erb +5 -0
- data/spec/dummy/app/views/users/show.html.erb +5 -0
- data/{test → spec}/dummy/config/application.rb +0 -0
- data/{test → spec}/dummy/config/boot.rb +0 -0
- data/{test → spec}/dummy/config/database.yml +0 -0
- data/{test → spec}/dummy/config/environment.rb +0 -0
- data/{test → spec}/dummy/config/environments/development.rb +0 -0
- data/{test → spec}/dummy/config/environments/production.rb +0 -0
- data/{test → spec}/dummy/config/environments/test.rb +0 -0
- data/{test → spec}/dummy/config/initializers/backtrace_silencers.rb +0 -0
- data/{test → spec}/dummy/config/initializers/inflections.rb +0 -0
- data/{test → spec}/dummy/config/initializers/mime_types.rb +0 -0
- data/{test → spec}/dummy/config/initializers/secret_token.rb +0 -0
- data/{test → spec}/dummy/config/initializers/session_store.rb +0 -0
- data/{test → spec}/dummy/config/initializers/wrap_parameters.rb +0 -0
- data/{test → spec}/dummy/config/locales/en.yml +0 -0
- data/spec/dummy/config/routes.rb +5 -0
- data/{test → spec}/dummy/config.ru +0 -0
- data/spec/dummy/db/migrate/0_create_users.rb +10 -0
- data/spec/dummy/db/schema.rb +23 -0
- data/{test → spec}/dummy/public/404.html +0 -0
- data/{test → spec}/dummy/public/422.html +0 -0
- data/{test → spec}/dummy/public/500.html +0 -0
- data/{test → spec}/dummy/public/favicon.ico +0 -0
- data/{test → spec}/dummy/script/rails +0 -0
- data/spec/lib/openstax/utilities/access_policy_spec.rb +67 -0
- data/spec/lib/openstax/utilities/controller_extensions_spec.rb +29 -0
- data/spec/lib/openstax/utilities/extended_controller_spec.rb +82 -0
- data/spec/spec_helper.rb +17 -0
- metadata +120 -62
- data/test/dummy/README.rdoc +0 -261
- data/test/dummy/config/routes.rb +0 -4
- data/test/integration/navigation_test.rb +0 -10
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NzdiOTJlYTJhMThlMWQzYjI0MGVkYWI1NzRlNjdlZmUyNmJmMzdlOQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YTQzYTI0YTEzMTQxZDZmZTNkNzQ0OWNkZGI2OWMwZWU5ZjQ3ZjA1ZQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MjBhYTQ2NTc2ODAxNDgxYjhhYzFjYWVhYzFlZWJiOWMyMjBmN2Y5MzExYzgz
|
10
|
+
ZDdmZmI3N2RlMDgzMzMyMmUzMmFhOWVhNjk5OGIyYTdmZGUxNTEzZjY3YjRk
|
11
|
+
OWFlNTMxNmE0MzkzOWU4M2NlODIwMTk3ZDhlNGEzNGE3Njk0NTM=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YzYyY2Y0ZTMxOGQ3NGYxZTg3Y2M0NWEzNjZlNDI0N2RmMmYzYzkxMTg2OTk2
|
14
|
+
YWFjOTc4YjI2NDcxYTJkZTA3ZjgxNDdmMjM1NjhiMDFlZmVkOGYzZjM5Yzlj
|
15
|
+
YWNhYTlmNjM5NjlmODQyYTNjYWU5MGUxODQ2NjI5OTE1NmJhZGY=
|
data/README.md
CHANGED
@@ -9,4 +9,31 @@ To use this utility engine, include it in your Gemfile.
|
|
9
9
|
|
10
10
|
## Helpers
|
11
11
|
|
12
|
-
This engine's helpers are available in the main application by preceding them with `osu.`, e.g. `osu.section_block('Heading') { "guts" }`
|
12
|
+
This engine's helpers are available in the main application by preceding them with `osu.`, e.g. `osu.section_block('Heading') { "guts" }`
|
13
|
+
|
14
|
+
## Access Policies
|
15
|
+
|
16
|
+
As applications grow to include different kinds of users, including signed-in and anonymous human users, as well as other applications, the logic for controlling which user has which accesses to which resources can grow complex. Controllers certainly aren't the place for this logic. In a case with one kind of User, models *may* be the place for this logic but even then this makes models know way too much about other models.
|
17
|
+
|
18
|
+
Access Policies were created to be a dedicated place to store the logic controlling who has access to what. All other code can ask the `AccessPolicy` class for this info, via the `action_allowed?` or the convenience methods `read_allowed?`, `create_allowed?`, etc. `AccessPolicy` then delegates the access decisions off to other policy classes, of which there is normally one per kind of resource (e.g. a `UserAccessPolicy`, `ContactInfoPolicy`, etc).
|
19
|
+
|
20
|
+
These resource-specific policy classes register themselves with the main `AccessPolicy` class, telling `AccessPolicy` what kinds of resources they can handle. E.g. the `UserAccessPolicy` tells `AccessPolicy` it handles permissions for `User` with the following call:
|
21
|
+
|
22
|
+
```rb
|
23
|
+
OSU::AccessPolicy.register(User, UserAccessPolicy)
|
24
|
+
```
|
25
|
+
|
26
|
+
This call is typically made after the policy class' definition so that it is called when the Rails application is loaded. We recommend placing the policy classes under `app/access_policies`, as all files under the `app` directory are autoloaded by Rails.
|
27
|
+
|
28
|
+
## Controller Extensions
|
29
|
+
|
30
|
+
The following methods will be added to all controllers:
|
31
|
+
|
32
|
+
`get_model(id_param, klass)` tries to use the id_param to return the model in question for
|
33
|
+
restful actions, or a new instance of klass if the id could not be found.
|
34
|
+
(e.g. on :new, :create and even :index)
|
35
|
+
By default, id_param is :id and klass is controller_name.classify.constantize.
|
36
|
+
|
37
|
+
`self.require_restful_actions_allowed!(options)` adds a before_filter that calls AccessPolicy.require_action_allowed! for all restful actions except :index. So by default it will add checks to :show, :new, :create, :edit, :update and :destroy.
|
38
|
+
options is a hash that contains any desired before_filter options, plus the optional keys :id_param and :model_class, which are passed to get_model instead.
|
39
|
+
By default, options is an empty hash.
|
data/Rakefile
CHANGED
@@ -1,48 +1,19 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
2
7
|
|
3
|
-
|
8
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
9
|
+
load 'rails/tasks/engine.rake'
|
4
10
|
|
5
|
-
|
6
|
-
# require 'bundler/setup'
|
7
|
-
# rescue LoadError
|
8
|
-
# puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
9
|
-
# end
|
10
|
-
# begin
|
11
|
-
# require 'rdoc/task'
|
12
|
-
# rescue LoadError
|
13
|
-
# require 'rdoc/rdoc'
|
14
|
-
# require 'rake/rdoctask'
|
15
|
-
# RDoc::Task = Rake::RDocTask
|
16
|
-
# end
|
17
|
-
|
18
|
-
# RDoc::Task.new(:rdoc) do |rdoc|
|
19
|
-
# rdoc.rdoc_dir = 'rdoc'
|
20
|
-
# rdoc.title = 'OpenstaxUtilities'
|
21
|
-
# rdoc.options << '--line-numbers'
|
22
|
-
# rdoc.rdoc_files.include('README.rdoc')
|
23
|
-
# rdoc.rdoc_files.include('lib/**/*.rb')
|
24
|
-
# end
|
25
|
-
|
26
|
-
# APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
27
|
-
|
28
|
-
|
29
|
-
# Bundler::GemHelper.install_tasks
|
30
|
-
|
31
|
-
# # require 'rake/testtask'
|
32
|
-
|
33
|
-
# # Rake::TestTask.new(:test) do |t|
|
34
|
-
# # t.libs << 'lib'
|
35
|
-
# # t.libs << 'test'
|
36
|
-
# # t.pattern = 'test/**/*_test.rb'
|
37
|
-
# # t.verbose = false
|
38
|
-
# # end
|
39
|
-
|
40
|
-
|
41
|
-
# # task :default => :test
|
11
|
+
Bundler::GemHelper.install_tasks
|
42
12
|
|
13
|
+
require 'rspec/core'
|
43
14
|
require 'rspec/core/rake_task'
|
44
15
|
|
45
|
-
|
16
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
17
|
+
RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
|
46
18
|
|
47
|
-
|
48
|
-
task :default => :spec
|
19
|
+
task :default => :spec
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# See the README
|
2
|
+
|
3
|
+
module OpenStax
|
4
|
+
module Utilities
|
5
|
+
class AccessPolicy
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
attr_reader :resource_policy_map
|
9
|
+
|
10
|
+
def initialize()
|
11
|
+
@resource_policy_map = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.method_missing(method_name, *arguments, &block)
|
15
|
+
if method_name.to_s =~ /(.*)_allowed?/
|
16
|
+
action_allowed?(*arguments.unshift($1.to_sym), &block)
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.respond_to_missing?(method_name, include_private = false)
|
23
|
+
method_name.to_s.end_with?('_allowed?') || super
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.require_action_allowed!(action, requestor, resource)
|
27
|
+
raise SecurityTransgression unless action_allowed?(action, requestor, resource)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.action_allowed?(action, requestor, resource)
|
31
|
+
|
32
|
+
# If the incoming requestor is an ApiUser, choose to use either its
|
33
|
+
# human_user or its application. If there is a human user involved, it
|
34
|
+
# should always take precedence when testing for access.
|
35
|
+
if defined?(ApiUser) && requestor.is_a?(ApiUser)
|
36
|
+
requestor = requestor.human_user ? requestor.human_user : requestor.application
|
37
|
+
end
|
38
|
+
|
39
|
+
resource_class = resource.is_a?(Class) ? resource : resource.class
|
40
|
+
policy_class = instance.resource_policy_map[resource_class.to_s]
|
41
|
+
|
42
|
+
# If there is no policy registered, we by default deny access
|
43
|
+
return false if policy_class.nil?
|
44
|
+
|
45
|
+
policy_class.action_allowed?(action, requestor, resource)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.register(resource_class, policy_class)
|
49
|
+
self.instance.resource_policy_map[resource_class.to_s] = policy_class
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module OpenStax
|
2
|
+
module Utilities
|
3
|
+
module ControllerExtensions
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
# Tries to find model if id is given, or returns a new model if not
|
9
|
+
def get_model(id_param = nil, klass = nil)
|
10
|
+
id_param ||= :id
|
11
|
+
id = params[id_param]
|
12
|
+
klass ||= controller_name.classify.constantize
|
13
|
+
id.nil? ? klass.new : klass.find(id)
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
# Calls AccessPolicy.require_action_allowed! on all restful actions
|
18
|
+
# For create and update, the new params are not yet set
|
19
|
+
def require_restful_actions_allowed!(options = {})
|
20
|
+
class_eval do
|
21
|
+
before_filter({:only => [:show, :new, :create, :edit, :update,
|
22
|
+
:destroy]}.merge(options.except(:id_param,
|
23
|
+
:model_class))) do |c|
|
24
|
+
|
25
|
+
case c.action_name
|
26
|
+
when 'show'
|
27
|
+
action = :read
|
28
|
+
when 'new'
|
29
|
+
action = :create
|
30
|
+
when 'edit'
|
31
|
+
action = :update
|
32
|
+
else
|
33
|
+
action = c.action_name.to_sym
|
34
|
+
end
|
35
|
+
|
36
|
+
OSU::AccessPolicy.require_action_allowed!(action,
|
37
|
+
c.current_user, c.get_model(options[:id_param],
|
38
|
+
options[:model_class]))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
ActionController::Base.send :include, OpenStax::Utilities::ControllerExtensions
|
data/lib/openstax_utilities.rb
CHANGED
@@ -19,6 +19,8 @@ require "openstax/utilities/action_list"
|
|
19
19
|
require "openstax/utilities/acts_as_numberable"
|
20
20
|
require "openstax/utilities/delegate_access_control"
|
21
21
|
require "openstax/utilities/roar"
|
22
|
+
require "openstax/utilities/access_policy"
|
23
|
+
require "openstax/utilities/controller_extensions"
|
22
24
|
|
23
25
|
require "openstax/utilities/classy_helper"
|
24
26
|
require "openstax/utilities/helpers/misc"
|
@@ -0,0 +1 @@
|
|
1
|
+
Dummy application used to test the openstax_utilities gem.
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,56 @@
|
|
1
|
+
body { background-color: #fff; color: #333; }
|
2
|
+
|
3
|
+
body, p, ol, ul, td {
|
4
|
+
font-family: verdana, arial, helvetica, sans-serif;
|
5
|
+
font-size: 13px;
|
6
|
+
line-height: 18px;
|
7
|
+
}
|
8
|
+
|
9
|
+
pre {
|
10
|
+
background-color: #eee;
|
11
|
+
padding: 10px;
|
12
|
+
font-size: 11px;
|
13
|
+
}
|
14
|
+
|
15
|
+
a { color: #000; }
|
16
|
+
a:visited { color: #666; }
|
17
|
+
a:hover { color: #fff; background-color:#000; }
|
18
|
+
|
19
|
+
div.field, div.actions {
|
20
|
+
margin-bottom: 10px;
|
21
|
+
}
|
22
|
+
|
23
|
+
#notice {
|
24
|
+
color: green;
|
25
|
+
}
|
26
|
+
|
27
|
+
.field_with_errors {
|
28
|
+
padding: 2px;
|
29
|
+
background-color: red;
|
30
|
+
display: table;
|
31
|
+
}
|
32
|
+
|
33
|
+
#error_explanation {
|
34
|
+
width: 450px;
|
35
|
+
border: 2px solid red;
|
36
|
+
padding: 7px;
|
37
|
+
padding-bottom: 0;
|
38
|
+
margin-bottom: 20px;
|
39
|
+
background-color: #f0f0f0;
|
40
|
+
}
|
41
|
+
|
42
|
+
#error_explanation h2 {
|
43
|
+
text-align: left;
|
44
|
+
font-weight: bold;
|
45
|
+
padding: 5px 5px 5px 15px;
|
46
|
+
font-size: 12px;
|
47
|
+
margin: -7px;
|
48
|
+
margin-bottom: 0px;
|
49
|
+
background-color: #c00;
|
50
|
+
color: #fff;
|
51
|
+
}
|
52
|
+
|
53
|
+
#error_explanation ul li {
|
54
|
+
font-size: 12px;
|
55
|
+
list-style: square;
|
56
|
+
}
|
@@ -0,0 +1,87 @@
|
|
1
|
+
class UsersController < ApplicationController
|
2
|
+
# GET /users
|
3
|
+
# GET /users.json
|
4
|
+
def index
|
5
|
+
@users = User.all
|
6
|
+
|
7
|
+
respond_to do |format|
|
8
|
+
format.html # index.html.erb
|
9
|
+
format.json { render json: @users }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# GET /users/1
|
14
|
+
# GET /users/1.json
|
15
|
+
def show
|
16
|
+
@user = User.find(params[:id])
|
17
|
+
|
18
|
+
respond_to do |format|
|
19
|
+
format.html # show.html.erb
|
20
|
+
format.json { render json: @user }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# GET /users/new
|
25
|
+
# GET /users/new.json
|
26
|
+
def new
|
27
|
+
@user = User.new
|
28
|
+
|
29
|
+
respond_to do |format|
|
30
|
+
format.html # new.html.erb
|
31
|
+
format.json { render json: @user }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# GET /users/1/edit
|
36
|
+
def edit
|
37
|
+
@user = User.find(params[:id])
|
38
|
+
end
|
39
|
+
|
40
|
+
# POST /users
|
41
|
+
# POST /users.json
|
42
|
+
def create
|
43
|
+
@user = User.new
|
44
|
+
@user.username = params[:user][:username]
|
45
|
+
@user.password_hash = SecureRandom.hex(32)
|
46
|
+
|
47
|
+
respond_to do |format|
|
48
|
+
if @user.save
|
49
|
+
format.html { redirect_to @user, notice: 'User was successfully created.' }
|
50
|
+
format.json { render json: @user, status: :created, location: @user }
|
51
|
+
else
|
52
|
+
format.html { render action: "new" }
|
53
|
+
format.json { render json: @user.errors, status: :unprocessable_entity }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# PUT /users/1
|
59
|
+
# PUT /users/1.json
|
60
|
+
def update
|
61
|
+
@user = User.find(params[:id])
|
62
|
+
@user.username = params[:user][:username]
|
63
|
+
@user.password_hash = SecureRandom.hex(32)
|
64
|
+
|
65
|
+
respond_to do |format|
|
66
|
+
if @user.save
|
67
|
+
format.html { redirect_to @user, notice: 'User was successfully updated.' }
|
68
|
+
format.json { head :no_content }
|
69
|
+
else
|
70
|
+
format.html { render action: "edit" }
|
71
|
+
format.json { render json: @user.errors, status: :unprocessable_entity }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# DELETE /users/1
|
77
|
+
# DELETE /users/1.json
|
78
|
+
def destroy
|
79
|
+
@user = User.find(params[:id])
|
80
|
+
@user.destroy
|
81
|
+
|
82
|
+
respond_to do |format|
|
83
|
+
format.html { redirect_to users_url }
|
84
|
+
format.json { head :no_content }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<%= form_for(@user) do |f| %>
|
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 |msg| %>
|
8
|
+
<li><%= msg %></li>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
</div>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
<div class="actions">
|
15
|
+
<%= f.submit %>
|
16
|
+
</div>
|
17
|
+
<% end %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<h1>Listing users</h1>
|
2
|
+
|
3
|
+
<table>
|
4
|
+
<tr>
|
5
|
+
<th></th>
|
6
|
+
<th></th>
|
7
|
+
<th></th>
|
8
|
+
</tr>
|
9
|
+
|
10
|
+
<% @users.each do |user| %>
|
11
|
+
<tr>
|
12
|
+
<td><%= link_to 'Show', user %></td>
|
13
|
+
<td><%= link_to 'Edit', edit_user_path(user) %></td>
|
14
|
+
<td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
|
15
|
+
</tr>
|
16
|
+
<% end %>
|
17
|
+
</table>
|
18
|
+
|
19
|
+
<br />
|
20
|
+
|
21
|
+
<%= link_to 'New User', new_user_path %>
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# This file is auto-generated from the current state of the database. Instead
|
3
|
+
# of editing this file, please use the migrations feature of Active Record to
|
4
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
5
|
+
#
|
6
|
+
# Note that this schema.rb definition is the authoritative source for your
|
7
|
+
# database schema. If you need to create the application database on another
|
8
|
+
# system, you should be using db:schema:load, not running all the migrations
|
9
|
+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
10
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
11
|
+
#
|
12
|
+
# It's strongly recommended to check this file into your version control system.
|
13
|
+
|
14
|
+
ActiveRecord::Schema.define(:version => 0) do
|
15
|
+
|
16
|
+
create_table "users", :force => true do |t|
|
17
|
+
t.string "username"
|
18
|
+
t.string "password_hash"
|
19
|
+
t.datetime "created_at", :null => false
|
20
|
+
t.datetime "updated_at", :null => false
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module OpenStax
|
4
|
+
module Utilities
|
5
|
+
|
6
|
+
describe AccessPolicy do
|
7
|
+
|
8
|
+
let!(:user) { User.create }
|
9
|
+
|
10
|
+
it 'responds to any _allowed? calls' do
|
11
|
+
AccessPolicy.register(User, DummyAccessPolicy)
|
12
|
+
|
13
|
+
DummyAccessPolicy.last_action = nil
|
14
|
+
DummyAccessPolicy.last_requestor = nil
|
15
|
+
DummyAccessPolicy.last_resource = nil
|
16
|
+
|
17
|
+
expect(AccessPolicy.respond_to? :wacky_allowed?).to eq(true)
|
18
|
+
expect(AccessPolicy.wacky_allowed?(user, user)).to eq(true)
|
19
|
+
|
20
|
+
expect(DummyAccessPolicy.last_action).to eq(:wacky)
|
21
|
+
expect(DummyAccessPolicy.last_requestor).to eq(user)
|
22
|
+
expect(DummyAccessPolicy.last_resource).to eq(user)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'delegates checks to policy classes based on resource class' do
|
26
|
+
dummy_object = double('Dummy')
|
27
|
+
dummy_policy = double('Dummy Policy', :action_allowed? => true)
|
28
|
+
|
29
|
+
AccessPolicy.register(User, DummyAccessPolicy)
|
30
|
+
AccessPolicy.register(dummy_object.class, dummy_policy)
|
31
|
+
|
32
|
+
DummyAccessPolicy.last_action = nil
|
33
|
+
DummyAccessPolicy.last_requestor = nil
|
34
|
+
DummyAccessPolicy.last_resource = nil
|
35
|
+
|
36
|
+
expect(AccessPolicy.action_allowed?(:read, user, dummy_object)).to(
|
37
|
+
eq(true))
|
38
|
+
|
39
|
+
expect{AccessPolicy.require_action_allowed!(:read, user, dummy_object)
|
40
|
+
}.not_to raise_error
|
41
|
+
|
42
|
+
expect(DummyAccessPolicy.last_action).to be_nil
|
43
|
+
expect(DummyAccessPolicy.last_requestor).to be_nil
|
44
|
+
expect(DummyAccessPolicy.last_resource).to be_nil
|
45
|
+
|
46
|
+
expect(AccessPolicy.action_allowed?(:create, user, User.new)).to(
|
47
|
+
eq(true))
|
48
|
+
|
49
|
+
expect{AccessPolicy.require_action_allowed!(:create, user, User.new)
|
50
|
+
}.not_to raise_error
|
51
|
+
|
52
|
+
expect(DummyAccessPolicy.last_action).to eq(:create)
|
53
|
+
expect(DummyAccessPolicy.last_requestor).to eq(user)
|
54
|
+
expect(DummyAccessPolicy.last_resource).to be_instance_of(User)
|
55
|
+
expect(DummyAccessPolicy.last_resource.id).to be_nil
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'denies permission if the policy class is not registered' do
|
59
|
+
expect(OSU::AccessPolicy.action_allowed?(:destroy, user, Object)).to eq(false)
|
60
|
+
|
61
|
+
expect{OSU::AccessPolicy.require_action_allowed!(:destroy, user, Object)}.to raise_error(SecurityTransgression)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|