authorizable 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.travis.yml +14 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +178 -0
  6. data/LICENSE +22 -0
  7. data/README.md +80 -0
  8. data/Rakefile +1 -0
  9. data/authorizable.gemspec +40 -0
  10. data/config/locales/en.yml +6 -0
  11. data/lib/authorizable.rb +31 -0
  12. data/lib/authorizable/controller.rb +156 -0
  13. data/lib/authorizable/model.rb +162 -0
  14. data/lib/authorizable/permission_utilities.rb +89 -0
  15. data/lib/authorizable/permissions.rb +112 -0
  16. data/lib/authorizable/version.rb +5 -0
  17. data/spec/integration/controller_spec.rb +127 -0
  18. data/spec/integration/model_spec.rb +169 -0
  19. data/spec/rails_helper.rb +14 -0
  20. data/spec/spec_helper.rb +48 -0
  21. data/spec/support/definitions.rb +16 -0
  22. data/spec/support/factories.rb +17 -0
  23. data/spec/support/factory_girl.rb +7 -0
  24. data/spec/support/rails_app/Rakefile +6 -0
  25. data/spec/support/rails_app/app/assets/images/.keep +0 -0
  26. data/spec/support/rails_app/app/assets/javascripts/application.js +16 -0
  27. data/spec/support/rails_app/app/assets/javascripts/some_resources.js +2 -0
  28. data/spec/support/rails_app/app/assets/javascripts/users.js +2 -0
  29. data/spec/support/rails_app/app/assets/stylesheets/application.css +15 -0
  30. data/spec/support/rails_app/app/assets/stylesheets/scaffold.css +56 -0
  31. data/spec/support/rails_app/app/assets/stylesheets/some_resources.css +4 -0
  32. data/spec/support/rails_app/app/assets/stylesheets/users.css +4 -0
  33. data/spec/support/rails_app/app/controllers/application_controller.rb +14 -0
  34. data/spec/support/rails_app/app/controllers/events_controller.rb +58 -0
  35. data/spec/support/rails_app/app/controllers/users_controller.rb +58 -0
  36. data/spec/support/rails_app/app/helpers/application_helper.rb +2 -0
  37. data/spec/support/rails_app/app/helpers/events_helper.rb +2 -0
  38. data/spec/support/rails_app/app/helpers/users_helper.rb +2 -0
  39. data/spec/support/rails_app/app/mailers/.keep +0 -0
  40. data/spec/support/rails_app/app/models/collaboration.rb +16 -0
  41. data/spec/support/rails_app/app/models/concerns/.keep +0 -0
  42. data/spec/support/rails_app/app/models/discount.rb +3 -0
  43. data/spec/support/rails_app/app/models/event.rb +5 -0
  44. data/spec/support/rails_app/app/models/user.rb +9 -0
  45. data/spec/support/rails_app/app/views/events/_form.html.erb +17 -0
  46. data/spec/support/rails_app/app/views/events/edit.html.erb +6 -0
  47. data/spec/support/rails_app/app/views/events/index.html.erb +25 -0
  48. data/spec/support/rails_app/app/views/events/new.html.erb +5 -0
  49. data/spec/support/rails_app/app/views/events/show.html.erb +4 -0
  50. data/spec/support/rails_app/app/views/layouts/application.html.erb +14 -0
  51. data/spec/support/rails_app/app/views/users/_form.html.erb +17 -0
  52. data/spec/support/rails_app/app/views/users/edit.html.erb +6 -0
  53. data/spec/support/rails_app/app/views/users/index.html.erb +25 -0
  54. data/spec/support/rails_app/app/views/users/new.html.erb +5 -0
  55. data/spec/support/rails_app/app/views/users/show.html.erb +4 -0
  56. data/spec/support/rails_app/bin/bundle +3 -0
  57. data/spec/support/rails_app/bin/rails +8 -0
  58. data/spec/support/rails_app/bin/rake +4 -0
  59. data/spec/support/rails_app/config.ru +0 -0
  60. data/spec/support/rails_app/config/application.rb +29 -0
  61. data/spec/support/rails_app/config/boot.rb +3 -0
  62. data/spec/support/rails_app/config/database.yml +25 -0
  63. data/spec/support/rails_app/config/environment.rb +5 -0
  64. data/spec/support/rails_app/config/environments/development.rb +41 -0
  65. data/spec/support/rails_app/config/environments/production.rb +79 -0
  66. data/spec/support/rails_app/config/environments/test.rb +42 -0
  67. data/spec/support/rails_app/config/initializers/assets.rb +11 -0
  68. data/spec/support/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  69. data/spec/support/rails_app/config/initializers/cookies_serializer.rb +3 -0
  70. data/spec/support/rails_app/config/initializers/filter_parameter_logging.rb +4 -0
  71. data/spec/support/rails_app/config/initializers/inflections.rb +16 -0
  72. data/spec/support/rails_app/config/initializers/mime_types.rb +4 -0
  73. data/spec/support/rails_app/config/initializers/session_store.rb +3 -0
  74. data/spec/support/rails_app/config/initializers/wrap_parameters.rb +14 -0
  75. data/spec/support/rails_app/config/locales/en.yml +23 -0
  76. data/spec/support/rails_app/config/routes.rb +60 -0
  77. data/spec/support/rails_app/config/secrets.yml +22 -0
  78. data/spec/support/rails_app/db/development.sqlite3 +0 -0
  79. data/spec/support/rails_app/db/migrate/20141231134904_create_users.rb +8 -0
  80. data/spec/support/rails_app/db/migrate/20150102221633_create_collaborations.rb +13 -0
  81. data/spec/support/rails_app/db/migrate/20150102225507_create_events.rb +9 -0
  82. data/spec/support/rails_app/db/migrate/20150104171110_create_discounts.rb +11 -0
  83. data/spec/support/rails_app/db/schema.rb +45 -0
  84. data/spec/support/rails_app/db/seeds.rb +7 -0
  85. data/spec/support/rails_app/db/test.sqlite3 +0 -0
  86. data/spec/support/rails_app/log/development.log +26296 -0
  87. data/spec/support/rails_app/public/404.html +67 -0
  88. data/spec/support/rails_app/public/422.html +67 -0
  89. data/spec/support/rails_app/public/500.html +66 -0
  90. data/spec/support/rails_app/public/favicon.ico +0 -0
  91. data/spec/support/rails_app/public/robots.txt +5 -0
  92. data/spec/unit/permission_utilities_spec.rb +157 -0
  93. data/spec/unit/permissions_spec.rb +65 -0
  94. metadata +352 -0
@@ -0,0 +1,169 @@
1
+ require 'rails_helper'
2
+
3
+ describe Authorizable::Model, type: :model do
4
+
5
+ before(:each) do
6
+ @user = create(:user)
7
+ @user.class.send(:include, Authorizable::Model)
8
+ end
9
+
10
+ describe "process_permission" do
11
+ before(:each) do
12
+ @event = create(:event, user: @user)
13
+ @collaborator = create(:user)
14
+
15
+ @event.collaborators << @collaborator
16
+ @event.save
17
+ end
18
+
19
+ it 'allows a permission' do
20
+ result = @user.can_view_collaborators?(@event)
21
+ expect(result).to eq true
22
+ end
23
+
24
+ it 'allows permission short hand' do
25
+ result = @user.can_edit?(@event)
26
+ expect(result).to eq true
27
+ end
28
+
29
+ it 'calls super if method does not match permission pattern' do
30
+ expect{
31
+ @user.do_something
32
+ }.to raise_error(NoMethodError)
33
+ end
34
+
35
+ it 'disallows a permission' do
36
+ result = @collaborator.can_view_collaborators?(@event)
37
+ expect(result).to eq false
38
+ end
39
+
40
+ it 'sets cache' do
41
+ expect(@user).to receive(:set_permission_cache).and_call_original
42
+ @user.can_view_collaborators?(@event)
43
+ cache = @user.instance_variable_get("@permission_cache")
44
+ expect(cache[Authorizable::Model::IS_OWNER]).to have_key(:can_view_collaborators?)
45
+ end
46
+
47
+ it 'returns from cache' do
48
+ # call once - store cache
49
+ @user.can_view_collaborators?(@event)
50
+ cache = @user.instance_variable_get("@permission_cache")
51
+ expect(cache[Authorizable::Model::IS_OWNER]).to have_key(:can_view_collaborators?)
52
+
53
+ # call again, return from cache
54
+ expect(@user).to_not receive(:set_permission_cache)
55
+ @user.can_view_collaborators?(@event)
56
+
57
+ end
58
+ end
59
+
60
+ describe "can?" do
61
+
62
+ it 'gets the default value' do
63
+ result = @user.send(:can?, Authorizable.definitions.keys.first)
64
+ expect(result).to eq true
65
+ end
66
+
67
+ it 'gets the value from a pre-defined set' do
68
+ key = Authorizable.definitions.keys.first
69
+ result = @user.send(:can?, key, Authorizable::Model::IS_OWNER, { key => false } )
70
+ expect(result).to eq false
71
+ end
72
+ end
73
+
74
+ describe "get_role_of" do
75
+ before(:each) do
76
+ @collaborator = create(:user)
77
+ @unrelated = create(:user)
78
+ end
79
+
80
+ context 'event' do
81
+ before(:each) do
82
+ @event = create(:event, user: @user)
83
+ end
84
+
85
+ it 'is owner' do
86
+ result = @user.send(:get_role_of, @event)
87
+ expect(result).to eq Authorizable::Model::IS_OWNER
88
+ end
89
+
90
+ it 'is collaborator' do
91
+ @event.collaborators << @collaborator
92
+ @event.save
93
+ result = @collaborator.send(:get_role_of, @event)
94
+ expect(result).to eq Authorizable::Model::IS_UNRELATED
95
+ end
96
+
97
+ it 'is unrelated' do
98
+ result = @unrelated.send(:get_role_of, @event)
99
+ expect(result).to eq Authorizable::Model::IS_UNRELATED
100
+ end
101
+ end
102
+ end
103
+
104
+ describe "has_role_with" do
105
+
106
+ it 'is the owner' do
107
+ @event = create(:event, user: @user)
108
+ result = @user.send(:has_role_with, @event)
109
+ expect(result).to eq Authorizable::Model::IS_OWNER
110
+ end
111
+
112
+ it 'is not the owner' do
113
+ user = create(:user)
114
+ @event = create(:event, user: @user)
115
+ result = user.send(:has_role_with, @event)
116
+ expect(result).to eq Authorizable::Model::IS_UNRELATED
117
+ end
118
+
119
+ it 'object does not respond to user_id' do
120
+ result = @user.send(:has_role_with, 3)
121
+ expect(result).to eq Authorizable::Model::IS_UNRELATED
122
+ end
123
+
124
+ end
125
+
126
+ context 'cache' do
127
+
128
+ before(:each) do
129
+ @user = create(:user)
130
+ end
131
+
132
+ describe "value_from_permission_cache" do
133
+ before(:each) do
134
+ @user.instance_variable_set(
135
+ '@permission_cache',
136
+ {
137
+ 1 => { "role" => true },
138
+ "roleless" => true
139
+ }
140
+ )
141
+ end
142
+
143
+ it 'gets a value for a non-role-based permission' do
144
+ expect(@user.send(:value_from_permission_cache, "roleless")).to eq true
145
+ end
146
+
147
+ it 'gets a value for a role-based permission' do
148
+ expect(@user.send(:value_from_permission_cache, "role", 1)).to eq true
149
+ end
150
+ end
151
+
152
+ describe "set_permission_cache" do
153
+ it 'sets a role-based permission' do
154
+ @user.send(:set_permission_cache, name: "permission_name", role: 1, value: true)
155
+ cache = @user.instance_variable_get('@permission_cache')
156
+
157
+ expect(cache[1]).to be_present
158
+ expect(cache[1]["permission_name"]).to eq true
159
+ end
160
+
161
+ it 'sets a non-role-based permission' do
162
+ @user.send(:set_permission_cache, name: "permission_name", value: true)
163
+ cache = @user.instance_variable_get('@permission_cache')
164
+
165
+ expect(cache["permission_name"]).to eq true
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ require 'factory_girl_rails'
3
+ require 'rspec/rails'
4
+
5
+ ActiveRecord::Migration.maintain_test_schema!
6
+
7
+ require 'support/rails_app/config/environment'
8
+
9
+ # set up db
10
+ # be sure to update the schema if required by doing
11
+ # - cd spec/rails_app
12
+ # - rake db:migrate
13
+ ActiveRecord::Schema.verbose = false
14
+ load "support/rails_app/db/schema.rb" # use db agnostic schema by default
@@ -0,0 +1,48 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ require "pry-byebug" # binding.pry to debug!
5
+
6
+ # Coverage
7
+ require "codeclimate-test-reporter"
8
+ ENV['CODECLIMATE_REPO_TOKEN'] = "68f28e1d037ca7b58d24f943b59d82cd649e1f9b9f79437d4a5d864140dd4eb0"
9
+ CodeClimate::TestReporter.start
10
+
11
+ require 'rspec/autorun'
12
+ require 'factory_girl'
13
+
14
+ # This Gem
15
+ require "authorizable"
16
+
17
+ Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each {|file|
18
+ # skip the dummy app
19
+ next if file.include?('support/rails_app')
20
+ require file
21
+ }
22
+
23
+ # This file was generated by the `rspec --init` command. Conventionally, all
24
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
25
+ # Require this file using `require "spec_helper"` to ensure that it is only
26
+ # loaded once.
27
+ #
28
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
29
+ RSpec.configure do |config|
30
+ config.treat_symbols_as_metadata_keys_with_true_values = true
31
+ config.run_all_when_everything_filtered = true
32
+ config.filter_run :focus
33
+
34
+ # Run specs in random order to surface order dependencies. If you find an
35
+ # order dependency and want to debug it, you can fix the order by providing
36
+ # the seed, which is printed after each run.
37
+ # --seed 1234
38
+ config.order = 'random'
39
+
40
+ config.before(:each) do |x|
41
+ $example_definitions.call
42
+ end
43
+
44
+ config.after(:each) do |x|
45
+ # reset class variable
46
+ Authorizable::Permissions.definitions = {}
47
+ end
48
+ end
@@ -0,0 +1,16 @@
1
+ $example_definitions = ->{
2
+ Authorizable::Permissions.set(
3
+ edit_organization: [Authorizable::OBJECT, [true, false]],
4
+ delete_organization: [Authorizable::OBJECT, [true, false], nil, ->(e, user){ e.user == user }, ->(e, user){ e.owner == user }],
5
+ create_organization: [Authorizable::ACCESS, true, nil, nil],
6
+
7
+ view_collaborators: [Authorizable::ACCESS, [true, false]]
8
+ )
9
+
10
+
11
+ Authorizable::Permissions.class_eval do
12
+ can :edit_event
13
+ can :delete_event
14
+ can :not_do_this, false
15
+ end
16
+ }
@@ -0,0 +1,17 @@
1
+ FactoryGirl.define do
2
+
3
+ factory :user do
4
+
5
+ end
6
+
7
+ factory :event do
8
+ name "Test Event"
9
+ association :user, factory: :user
10
+ end
11
+
12
+ factory :discount do
13
+ name "Some Discount"
14
+ event
15
+ end
16
+
17
+ end
@@ -0,0 +1,7 @@
1
+ RSpec.configure do |config|
2
+ config.include FactoryGirl::Syntax::Methods
3
+
4
+ config.before(:suite) do
5
+ FactoryGirl.lint
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,16 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require turbolinks
16
+ //= require_tree .
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -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,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,14 @@
1
+ class ApplicationController < ActionController::Base
2
+ # Prevent CSRF attacks by raising an exception.
3
+ # For APIs, you may want to use :null_session instead.
4
+ protect_from_forgery with: :exception
5
+
6
+ # some fake helpers here
7
+ def current_user=(user = User.last)
8
+ @current_user = user
9
+ end
10
+
11
+ def current_user
12
+ @current_user
13
+ end
14
+ end
@@ -0,0 +1,58 @@
1
+ class EventsController < ApplicationController
2
+ before_action :set_event, only: [:show, :edit, :update, :destroy]
3
+
4
+ # GET /events
5
+ def index
6
+ @events = Event.all
7
+ end
8
+
9
+ # GET /events/1
10
+ def show
11
+ end
12
+
13
+ # GET /events/new
14
+ def new
15
+ @event = Event.new
16
+ end
17
+
18
+ # GET /events/1/edit
19
+ def edit
20
+ end
21
+
22
+ # POST /events
23
+ def create
24
+ @event = Event.new(event_params)
25
+
26
+ if @event.save
27
+ redirect_to @event, notice: 'Event was successfully created.'
28
+ else
29
+ render :new
30
+ end
31
+ end
32
+
33
+ # PATCH/PUT /events/1
34
+ def update
35
+ if @event.update(event_params)
36
+ redirect_to @event, notice: 'Event was successfully updated.'
37
+ else
38
+ render :edit
39
+ end
40
+ end
41
+
42
+ # DELETE /events/1
43
+ def destroy
44
+ @event.destroy
45
+ redirect_to events_url, notice: 'Event was successfully destroyed.'
46
+ end
47
+
48
+ private
49
+ # Use callbacks to share common setup or constraints between actions.
50
+ def set_event
51
+ @event = Event.find(params[:id])
52
+ end
53
+
54
+ # Only allow a trusted parameter "white list" through.
55
+ def event_params
56
+ params[:event]
57
+ end
58
+ end