trax_controller 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Rakefile +0 -1
- data/lib/trax/controller/actions.rb +1 -1
- data/lib/trax/controller/authorization/pundit/actions/create.rb +18 -0
- data/lib/trax/controller/authorization/pundit/actions/destroy.rb +18 -0
- data/lib/trax/controller/authorization/pundit/actions/index.rb +18 -0
- data/lib/trax/controller/authorization/pundit/actions/new.rb +18 -0
- data/lib/trax/controller/authorization/pundit/actions/read.rb +16 -0
- data/lib/trax/controller/authorization/pundit/actions/search.rb +18 -0
- data/lib/trax/controller/authorization/pundit/actions/show.rb +18 -0
- data/lib/trax/controller/authorization/pundit/actions/update.rb +19 -0
- data/lib/trax/controller/authorization/pundit/actions.rb +20 -0
- data/lib/trax/controller/authorization/pundit/adapter.rb +43 -0
- data/lib/trax/controller/authorization/pundit/base_policy.rb +18 -0
- data/lib/trax/controller/authorization/pundit.rb +13 -0
- data/lib/trax/controller/authorization.rb +9 -0
- data/lib/trax/controller/authorize.rb +24 -0
- data/lib/trax/controller/config.rb +13 -0
- data/lib/trax/controller/serialization/adapters/json.rb +14 -14
- data/lib/trax/controller.rb +14 -1
- data/lib/trax_controller/version.rb +1 -1
- data/spec/internal/app/controllers/widgets_controller.rb +22 -0
- data/spec/internal/app/models/category.rb +3 -0
- data/spec/internal/app/models/user.rb +3 -0
- data/spec/internal/app/policies/widget_policy.rb +26 -0
- data/spec/internal/config/routes.rb +1 -0
- data/spec/internal/db/combustion_test.sqlite +0 -0
- data/spec/internal/db/schema.rb +8 -1
- data/spec/spec_helper.rb +7 -0
- data/spec/trax/controller/authorization/pundit/base_policy_spec.rb +43 -0
- data/spec/trax/controller/authorization/pundit_spec.rb +179 -0
- data/spec/trax/controllers/products_controller_spec.rb +0 -2
- data/spec/trax/serialization/adapters/json_spec.rb +55 -0
- data/trax_controller.gemspec +2 -3
- metadata +44 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e4adb200e79b39ccad802b45128fa422f496406
|
4
|
+
data.tar.gz: fb0dcf577b599ad791f87fc840875c94a156d2a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33bc80d31708005d6e42b77a0a6b4805b986e147dfee728db9e63a6b80fd735cfcb64123d41876b6c38b7addb9aa87e716f695ee7bcf54532c701eeb90a8a04b
|
7
|
+
data.tar.gz: ae5aa8a93e9aa0408b6e6a6d2c10da94500123f209ddd9f40108427489bc649c2a344d53e338fdb984f3257fe74d6f10d2660a3185c84c537fdcfdff3e226da7
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -99,7 +99,7 @@ module Trax
|
|
99
99
|
end
|
100
100
|
|
101
101
|
def render_errors(status = :unprocessable_entity, error_messages_hash:{}, **options)
|
102
|
-
errors = error_messages_hash.merge(resource_error_messages(**options))
|
102
|
+
errors = error_messages_hash.merge(resource_error_messages(**options) || {})
|
103
103
|
render json: { meta: { errors: errors } }, status: status, serializer: nil
|
104
104
|
end
|
105
105
|
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Trax
|
2
|
+
module Controller
|
3
|
+
module Authorization
|
4
|
+
module Pundit
|
5
|
+
module Actions
|
6
|
+
module Create
|
7
|
+
extend ::ActiveSupport::Concern
|
8
|
+
|
9
|
+
def create_resource(object)
|
10
|
+
authorize(object)
|
11
|
+
super(object)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Trax
|
2
|
+
module Controller
|
3
|
+
module Authorization
|
4
|
+
module Pundit
|
5
|
+
module Actions
|
6
|
+
module Destroy
|
7
|
+
extend ::ActiveSupport::Concern
|
8
|
+
|
9
|
+
def destroy_resource(object)
|
10
|
+
authorize(object)
|
11
|
+
super(object)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Trax
|
2
|
+
module Controller
|
3
|
+
module Authorization
|
4
|
+
module Pundit
|
5
|
+
module Actions
|
6
|
+
module Index
|
7
|
+
extend ::ActiveSupport::Concern
|
8
|
+
|
9
|
+
def index(*args, **options)
|
10
|
+
authorize(collection)
|
11
|
+
super(*args, **options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Trax
|
2
|
+
module Controller
|
3
|
+
module Authorization
|
4
|
+
module Pundit
|
5
|
+
module Actions
|
6
|
+
module New
|
7
|
+
extend ::ActiveSupport::Concern
|
8
|
+
|
9
|
+
def new(*args, *options)
|
10
|
+
authorize(build_resource)
|
11
|
+
super(*args, *options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Trax
|
2
|
+
module Controller
|
3
|
+
module Authorization
|
4
|
+
module Pundit
|
5
|
+
module Actions
|
6
|
+
module Read
|
7
|
+
extend ::ActiveSupport::Concern
|
8
|
+
|
9
|
+
include ::Trax::Controller::Authorization::Pundit::Actions::Index
|
10
|
+
include ::Trax::Controller::Authorization::Pundit::Actions::Show
|
11
|
+
include ::Trax::Controller::Authorization::Pundit::Actions::Search
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Trax
|
2
|
+
module Controller
|
3
|
+
module Authorization
|
4
|
+
module Pundit
|
5
|
+
module Actions
|
6
|
+
module Search
|
7
|
+
extend ::ActiveSupport::Concern
|
8
|
+
|
9
|
+
def search(*args, **options)
|
10
|
+
authorize(collection)
|
11
|
+
super(*args, **options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Trax
|
2
|
+
module Controller
|
3
|
+
module Authorization
|
4
|
+
module Pundit
|
5
|
+
module Actions
|
6
|
+
module Show
|
7
|
+
extend ::ActiveSupport::Concern
|
8
|
+
|
9
|
+
def show(*args, **options)
|
10
|
+
authorize(resource)
|
11
|
+
super(*args, **options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Trax
|
2
|
+
module Controller
|
3
|
+
module Authorization
|
4
|
+
module Pundit
|
5
|
+
module Actions
|
6
|
+
module Update
|
7
|
+
extend ::ActiveSupport::Concern
|
8
|
+
|
9
|
+
def update_resource(object, attributes={})
|
10
|
+
object.assign_attributes(*attributes)
|
11
|
+
authorize(object)
|
12
|
+
super(object, attributes)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Trax
|
2
|
+
module Controller
|
3
|
+
module Authorization
|
4
|
+
module Pundit
|
5
|
+
module Actions
|
6
|
+
extend ::ActiveSupport::Autoload
|
7
|
+
|
8
|
+
autoload :Create
|
9
|
+
autoload :Destroy
|
10
|
+
autoload :Index
|
11
|
+
autoload :New
|
12
|
+
autoload :Read
|
13
|
+
autoload :Search
|
14
|
+
autoload :Show
|
15
|
+
autoload :Update
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'pundit'
|
2
|
+
|
3
|
+
module Trax
|
4
|
+
module Controller
|
5
|
+
module Authorization
|
6
|
+
module Pundit
|
7
|
+
module Adapter
|
8
|
+
extend ::ActiveSupport::Concern
|
9
|
+
|
10
|
+
include ::Pundit
|
11
|
+
|
12
|
+
included do
|
13
|
+
rescue_from ::Pundit::NotAuthorizedError, :with => :render_pundit_errors
|
14
|
+
class_attribute :_policy_class
|
15
|
+
end
|
16
|
+
|
17
|
+
def render_pundit_errors
|
18
|
+
render_errors(:forbidden, error_messages_hash: { :not_authorized => 'You are not authorized to perform this action' } )
|
19
|
+
end
|
20
|
+
|
21
|
+
def policy(record)
|
22
|
+
self.class._policy_class.new(current_user, record)
|
23
|
+
end
|
24
|
+
|
25
|
+
#not included into controller
|
26
|
+
def self.authorization_action_for(action_name)
|
27
|
+
"::Trax::Controller::Authorization::Pundit::Actions::#{action_name.to_s.camelize}".safe_constantize
|
28
|
+
end
|
29
|
+
|
30
|
+
module ClassMethods
|
31
|
+
def policy_class(klass)
|
32
|
+
self._policy_class = klass
|
33
|
+
end
|
34
|
+
|
35
|
+
def policy!(user, record)
|
36
|
+
_policy_class.new(user, record)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Trax
|
2
|
+
module Controller
|
3
|
+
module Authorization
|
4
|
+
module Pundit
|
5
|
+
class BasePolicy
|
6
|
+
attr_accessor :user, :resource, :result
|
7
|
+
|
8
|
+
#Result is set to true by default because it makes chaining easy with &&=
|
9
|
+
def initialize(user, resource)
|
10
|
+
@user = user
|
11
|
+
@resource = resource
|
12
|
+
@result = true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Trax
|
2
|
+
module Controller
|
3
|
+
module Authorize
|
4
|
+
extend ::ActiveSupport::Concern
|
5
|
+
|
6
|
+
include ::Trax::Controller.config.authorization_adapter
|
7
|
+
|
8
|
+
STANDARD_ACTIONS = [ :index, :search, :show, :destroy, :update, :create, :new ]
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def actions_to_authorize(*_actions, all:false, except:false, only:false)
|
12
|
+
_actions += (all || except) ? STANDARD_ACTIONS.dup : []
|
13
|
+
_actions += only if only
|
14
|
+
_actions.reject!{ |_action| except.include?(_action) } if except
|
15
|
+
|
16
|
+
_actions.each do |action|
|
17
|
+
action_module = ::Trax::Controller.config.authorization_adapter.authorization_action_for(action)
|
18
|
+
include action_module if action_module
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -8,35 +8,33 @@ module Trax
|
|
8
8
|
module Adapters
|
9
9
|
class Json < ActiveModel::Serializer::Adapter::Json
|
10
10
|
def serializable_hash(options={})
|
11
|
-
is_collection? ? collection_serializable_hash(options) : resource_serializable_hash(options)
|
11
|
+
result = is_collection? ? collection_serializable_hash(options) : resource_serializable_hash(options)
|
12
|
+
@options[:nested] ? result : { root => result }
|
12
13
|
end
|
13
14
|
|
14
15
|
private
|
15
16
|
|
16
|
-
def
|
17
|
-
|
17
|
+
def get_hash(serializer, options={})
|
18
|
+
self.class.new(serializer, :nested => true).serializable_hash(options)
|
18
19
|
end
|
19
20
|
|
20
21
|
def collection_serializable_hash(options={})
|
21
|
-
|
22
|
-
{ root => _result }
|
22
|
+
serializer.map { |s| get_hash(s, options) }
|
23
23
|
end
|
24
24
|
|
25
25
|
def is_collection?
|
26
26
|
serializer.respond_to?(:each)
|
27
27
|
end
|
28
28
|
|
29
|
-
def resource_result
|
30
|
-
@resource_result ||= {}
|
31
|
-
end
|
32
|
-
|
33
29
|
def resource_serializable_hash(options={})
|
34
|
-
cache_check(serializer){
|
35
|
-
serialize_resource_associations
|
36
|
-
|
30
|
+
cached = cache_check(serializer){ serializer.attributes(options) }
|
31
|
+
resource_result = serialize_resource_associations
|
32
|
+
cached.merge(resource_result)
|
37
33
|
end
|
38
34
|
|
39
35
|
def serialize_resource_associations
|
36
|
+
resource_result = {}
|
37
|
+
|
40
38
|
serializer.associations.each do |association|
|
41
39
|
association_serializer = association.serializer
|
42
40
|
association_options = association.options
|
@@ -45,16 +43,18 @@ module Trax
|
|
45
43
|
array_serializer = association_serializer
|
46
44
|
|
47
45
|
resource_result[association.key] = array_serializer.map do |item|
|
48
|
-
cache_check(item) {
|
46
|
+
cache_check(item) { get_hash(item) }
|
49
47
|
end
|
50
48
|
else
|
51
49
|
resource_result[association.key] = if association_serializer && association_serializer.object
|
52
|
-
cache_check(association_serializer) {
|
50
|
+
cache_check(association_serializer) { get_hash(association_serializer) }
|
53
51
|
elsif association_options[:virtual_value]
|
54
52
|
association_options[:virtual_value]
|
55
53
|
end
|
56
54
|
end
|
57
55
|
end
|
56
|
+
|
57
|
+
resource_result
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
data/lib/trax/controller.rb
CHANGED
@@ -16,19 +16,32 @@ module Trax
|
|
16
16
|
end
|
17
17
|
|
18
18
|
autoload :Actions
|
19
|
+
autoload :Authorize
|
20
|
+
autoload :Authorization
|
21
|
+
autoload :Config
|
19
22
|
autoload :Collection
|
20
23
|
autoload :Engine
|
21
24
|
autoload :Resource
|
22
25
|
autoload :InheritResources
|
23
26
|
autoload :ActionTypes
|
24
|
-
autoload :Engine
|
25
27
|
autoload :Serialization
|
26
28
|
|
29
|
+
@configuration ||= ::Trax::Controller::Config.new
|
30
|
+
|
31
|
+
def self.configure(&block)
|
32
|
+
block.call(config)
|
33
|
+
end
|
34
|
+
|
27
35
|
include ::Trax::Controller::InheritResources
|
28
36
|
include ::Trax::Controller::Collection::Base
|
29
37
|
include ::Trax::Controller::Resource::Base
|
30
38
|
include ::Trax::Controller::Actions
|
31
39
|
include ::Trax::Controller::ActionTypes
|
40
|
+
|
41
|
+
class << self
|
42
|
+
attr_accessor :configuration
|
43
|
+
alias_method :config, :configuration
|
44
|
+
end
|
32
45
|
end
|
33
46
|
end
|
34
47
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class WidgetsController < ::ApplicationController
|
2
|
+
include ::Trax::Controller
|
3
|
+
include ::Trax::Controller::Collection::Pageable
|
4
|
+
|
5
|
+
defaults :resource_class => ::Widget
|
6
|
+
|
7
|
+
has_scope :by_id
|
8
|
+
|
9
|
+
actions :index, :show, :create, :update, :destroy
|
10
|
+
|
11
|
+
def widget_params
|
12
|
+
params.require(:widget).permit(:name, :quantity)
|
13
|
+
end
|
14
|
+
|
15
|
+
def user_email
|
16
|
+
params[:user_email]
|
17
|
+
end
|
18
|
+
|
19
|
+
def current_user
|
20
|
+
@current_user ||= ::User.find_by(:email => user_email)
|
21
|
+
end
|
22
|
+
end
|
@@ -2,4 +2,7 @@ class Category < ::ActiveRecord::Base
|
|
2
2
|
validates :name, :length => { :minimum => 5 }
|
3
3
|
|
4
4
|
has_many :products
|
5
|
+
|
6
|
+
belongs_to :parent, :class_name => self.name, :foreign_key => :parent_id
|
7
|
+
has_many :subcategories, :class_name => self.name, :foreign_key => :parent_id
|
5
8
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class WidgetPolicy < Trax::Controller::Authorization::Pundit::BasePolicy
|
2
|
+
def initialize(*args)
|
3
|
+
super(*args)
|
4
|
+
end
|
5
|
+
|
6
|
+
def index?
|
7
|
+
true
|
8
|
+
end
|
9
|
+
|
10
|
+
def create?
|
11
|
+
@result &&= user.can_create_widgets
|
12
|
+
end
|
13
|
+
|
14
|
+
def show?
|
15
|
+
@result &&= user.can_read_widgets
|
16
|
+
end
|
17
|
+
|
18
|
+
def update?
|
19
|
+
@result &&= user.can_update_widgets
|
20
|
+
end
|
21
|
+
|
22
|
+
def destroy?
|
23
|
+
@result &&= user.can_destroy_widgets
|
24
|
+
@result &&= user.is_admin
|
25
|
+
end
|
26
|
+
end
|
Binary file
|
data/spec/internal/db/schema.rb
CHANGED
@@ -13,6 +13,7 @@
|
|
13
13
|
|
14
14
|
create_table "categories", :force => true do |t|
|
15
15
|
t.string "name"
|
16
|
+
t.integer "parent_id"
|
16
17
|
t.datetime "created_at", :null => false
|
17
18
|
t.datetime "updated_at", :null => false
|
18
19
|
end
|
@@ -21,6 +22,11 @@
|
|
21
22
|
t.string "first_name"
|
22
23
|
t.string "last_name"
|
23
24
|
t.string "email"
|
25
|
+
t.boolean "can_destroy_widgets"
|
26
|
+
t.boolean "can_create_widgets"
|
27
|
+
t.boolean "can_read_widgets"
|
28
|
+
t.boolean "can_update_widgets"
|
29
|
+
t.boolean "is_admin"
|
24
30
|
t.datetime "created_at", :null => false
|
25
31
|
t.datetime "updated_at", :null => false
|
26
32
|
end
|
@@ -31,7 +37,8 @@
|
|
31
37
|
t.string "subdomain"
|
32
38
|
t.string "website"
|
33
39
|
t.integer "status"
|
34
|
-
t.integer "quantity"
|
40
|
+
t.integer "quantity"
|
41
|
+
t.boolean "is_read_only"
|
35
42
|
t.datetime "created_at", :null => false
|
36
43
|
t.datetime "updated_at", :null => false
|
37
44
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -18,6 +18,13 @@ end
|
|
18
18
|
|
19
19
|
::RSpec.configure do |config|
|
20
20
|
config.include RSpec::Rails::RequestExampleGroup, type: :feature
|
21
|
+
|
22
|
+
config.before(:all) do
|
23
|
+
::Trax::Controller.config.authorization_adapter = ::Trax::Controller::Authorization::Pundit::Adapter
|
24
|
+
end
|
25
|
+
|
26
|
+
config.filter_run :focus => true
|
27
|
+
config.run_all_when_everything_filtered = true
|
21
28
|
end
|
22
29
|
|
23
30
|
::Bundler.require(:default, :development, :test)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
::RSpec.describe ::Trax::Controller::Authorization::Pundit::BasePolicy do
|
4
|
+
let(:widget_policy_class) { ::WidgetPolicy }
|
5
|
+
|
6
|
+
let!(:widget) { ::Widget.create(:name => "Haterade", :is_read_only => true) }
|
7
|
+
subject { WidgetPolicy.new(user, widget) }
|
8
|
+
|
9
|
+
context "user with permissions" do
|
10
|
+
let!(:user) {
|
11
|
+
::User.create(
|
12
|
+
:can_read_widgets => true,
|
13
|
+
:can_update_widgets => true,
|
14
|
+
:can_create_widgets => true,
|
15
|
+
:can_destroy_widgets => true,
|
16
|
+
:is_admin => false
|
17
|
+
)
|
18
|
+
}
|
19
|
+
|
20
|
+
it { expect(subject.show?).to be true }
|
21
|
+
it { expect(subject.update?).to be true }
|
22
|
+
it { expect(subject.create?).to be true }
|
23
|
+
it { expect(subject.destroy?).to be false }
|
24
|
+
end
|
25
|
+
|
26
|
+
context "user without permissions" do
|
27
|
+
let!(:user) {
|
28
|
+
::User.create(
|
29
|
+
:can_read_widgets => false,
|
30
|
+
:can_update_widgets => false,
|
31
|
+
:can_create_widgets => false,
|
32
|
+
:can_destroy_widgets => false,
|
33
|
+
:is_admin => false
|
34
|
+
)
|
35
|
+
}
|
36
|
+
subject { WidgetPolicy.new(user, widget) }
|
37
|
+
|
38
|
+
it { expect(subject.show?).to be false }
|
39
|
+
it { expect(subject.update?).to be false }
|
40
|
+
it { expect(subject.create?).to be false }
|
41
|
+
it { expect(subject.destroy?).to be false }
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
::RSpec.describe WidgetsController do
|
4
|
+
before(:all) do
|
5
|
+
::Trax::Controller.config.authorization_adapter = ::Trax::Controller::Authorization::Pundit::Adapter
|
6
|
+
|
7
|
+
::WidgetsController.class_eval do
|
8
|
+
include ::Trax::Controller::Authorize
|
9
|
+
actions_to_authorize :index, :show, :create, :update, :destroy
|
10
|
+
policy_class ::WidgetPolicy
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "WidgetsController" do
|
15
|
+
context "Unit Test" do
|
16
|
+
context ".actions_to_authorize" do
|
17
|
+
{
|
18
|
+
:index => Trax::Controller::Authorization::Pundit::Actions::Index,
|
19
|
+
:create => Trax::Controller::Authorization::Pundit::Actions::Create,
|
20
|
+
:show => Trax::Controller::Authorization::Pundit::Actions::Show,
|
21
|
+
:destroy => Trax::Controller::Authorization::Pundit::Actions::Destroy,
|
22
|
+
:update => Trax::Controller::Authorization::Pundit::Actions::Update
|
23
|
+
}.each_pair do |k,v|
|
24
|
+
it "includes the pundit action for #{k}" do
|
25
|
+
expect(subject.class.ancestors).to include(v)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "WidgetsController", :type => :controller do
|
32
|
+
let!(:user1) {
|
33
|
+
::User.create(
|
34
|
+
:email => 'kanyewest@theleatherjoggingpants.com',
|
35
|
+
:can_read_widgets => true,
|
36
|
+
:can_update_widgets => true,
|
37
|
+
:can_create_widgets => true,
|
38
|
+
:can_destroy_widgets => true,
|
39
|
+
:is_admin => false
|
40
|
+
)
|
41
|
+
}
|
42
|
+
|
43
|
+
let!(:user2) {
|
44
|
+
::User.create(
|
45
|
+
:email => 'kanyewest@waterbottledesign.com',
|
46
|
+
:can_read_widgets => false,
|
47
|
+
:can_update_widgets => false,
|
48
|
+
:can_create_widgets => false,
|
49
|
+
:can_destroy_widgets => false,
|
50
|
+
:is_admin => true
|
51
|
+
)
|
52
|
+
}
|
53
|
+
|
54
|
+
let(:user_who_can) {
|
55
|
+
{
|
56
|
+
:read_widgets => user1,
|
57
|
+
:update_widgets => user1,
|
58
|
+
:create_widgets => user1,
|
59
|
+
:destroy_widgets => user1
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
let(:user_who_cannot) {
|
64
|
+
{
|
65
|
+
:read_widgets => user2,
|
66
|
+
:update_widgets => user2,
|
67
|
+
:create_widgets => user2,
|
68
|
+
:destroy_widgets => user2
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
let(:common_params) { {:user_email => current_user.email} }
|
73
|
+
let!(:widget) { ::Widget.create(:name => "Water Bottle", :is_read_only => true, :quantity => 5) }
|
74
|
+
|
75
|
+
let(:unauthorized_result_expectations) {
|
76
|
+
lambda { |json|
|
77
|
+
expect(json).to have_key("meta")
|
78
|
+
expect(json["meta"]).to have_key("errors")
|
79
|
+
expect(json["meta"]["errors"]).to have_key("not_authorized")
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
describe '#show' do
|
84
|
+
context 'user can read widgets' do
|
85
|
+
let(:current_user) { user_who_can[:read_widgets] }
|
86
|
+
|
87
|
+
it 'is authorized' do
|
88
|
+
get :show, common_params.merge!(id: widget.id)
|
89
|
+
expect(response).to be_ok
|
90
|
+
json = JSON.parse response.body
|
91
|
+
expect(json).to have_key('widget')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'user cannot read widgets' do
|
96
|
+
let(:current_user) { user_who_cannot[:read_widgets] }
|
97
|
+
|
98
|
+
it do
|
99
|
+
get :show, common_params.merge!(id: widget.id)
|
100
|
+
expect(response).to_not be_ok
|
101
|
+
json = JSON.parse response.body
|
102
|
+
unauthorized_result_expectations.call(json)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#create' do
|
108
|
+
context "user can create widgets" do
|
109
|
+
let(:current_user) { user_who_can[:create_widgets] }
|
110
|
+
let(:widget_name) { 'Kanyes water bottle' }
|
111
|
+
let(:params){ {
|
112
|
+
:format => :json,
|
113
|
+
:widget => widget_params
|
114
|
+
}}
|
115
|
+
let(:widget_params) { { :name => 'Marble Conference Table', :quantity => 1 } }
|
116
|
+
before { post :create, common_params.merge(params) }
|
117
|
+
|
118
|
+
it { expect(response).to be_successful }
|
119
|
+
end
|
120
|
+
|
121
|
+
context "user cannot create widgets" do
|
122
|
+
let(:current_user) { user_who_cannot[:create_widgets] }
|
123
|
+
let(:params){ {
|
124
|
+
:format => :json,
|
125
|
+
:widget => widget_params
|
126
|
+
}}
|
127
|
+
let(:widget_params) { { :name => 'Marble Conference Table', :quantity => 1 } }
|
128
|
+
before { post :create, common_params.merge(params) }
|
129
|
+
|
130
|
+
it {
|
131
|
+
expect(response).to_not be_ok
|
132
|
+
json = JSON.parse response.body
|
133
|
+
unauthorized_result_expectations.call(json)
|
134
|
+
}
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '#update' do
|
139
|
+
context "user cannot update widgets" do
|
140
|
+
let(:current_user) { user_who_cannot[:update_widgets] }
|
141
|
+
let(:updated_widget_name) { 'Kanyes water bottle' }
|
142
|
+
let(:params){ {
|
143
|
+
:format => :json,
|
144
|
+
:id => widget.id,
|
145
|
+
:widget => widget_params
|
146
|
+
}}
|
147
|
+
let(:widget_params) { { :name => updated_widget_name, :quantity => 1 } }
|
148
|
+
|
149
|
+
before { put :update, common_params.merge(params) }
|
150
|
+
|
151
|
+
it { expect(response).to_not be_successful }
|
152
|
+
it { expect(response.status).to eq 403 }
|
153
|
+
it {
|
154
|
+
expect(response).to_not be_ok
|
155
|
+
json = JSON.parse response.body
|
156
|
+
unauthorized_result_expectations.call(json)
|
157
|
+
}
|
158
|
+
end
|
159
|
+
|
160
|
+
context "user can update widgets" do
|
161
|
+
let(:current_user) { user_who_can[:update_widgets] }
|
162
|
+
let(:updated_widget_name) { 'Kanyes water bottle' }
|
163
|
+
let(:params){ {
|
164
|
+
:format => :json,
|
165
|
+
:id => widget.id,
|
166
|
+
:widget => widget_params
|
167
|
+
}}
|
168
|
+
let(:widget_params) { { :name => updated_widget_name, :quantity => 1 } }
|
169
|
+
let(:request_params) { common_params.merge(params) }
|
170
|
+
|
171
|
+
before { put :update, request_params }
|
172
|
+
|
173
|
+
it { expect(response).to be_successful }
|
174
|
+
it { expect(response).to be_ok }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class NameCategorySerializer < ::ActiveModel::Serializer
|
4
|
+
attributes :name
|
5
|
+
has_many :subcategories, :serializer => NameCategorySerializer
|
6
|
+
end
|
7
|
+
|
8
|
+
class NameProductSerializer < ::ActiveModel::Serializer
|
9
|
+
attributes :name
|
10
|
+
has_one :category, :serializer => NameCategorySerializer
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Trax::Controller::Serialization::Adapters::Json do
|
14
|
+
let(:category_flat_heads){ ::Category.new(:name => "flat heads") }
|
15
|
+
let(:category_screwdrivers){ ::Category.new(:name => "screwdrivers", :subcategories => [category_flat_heads]) }
|
16
|
+
let(:category_knives){ ::Category.new(:name => "knives") }
|
17
|
+
let(:category_tools){ ::Category.new(:name => "tools", :subcategories => [category_screwdrivers, category_knives]) }
|
18
|
+
let(:product_tool){ ::Product.new(:name => "Generics Multi-Purpose Tool", :category => category_tools) }
|
19
|
+
let(:root_key){ 'root_key' }
|
20
|
+
let(:serializer){ ::NameProductSerializer.new(product_tool, :root => root_key) }
|
21
|
+
let!(:expected_root_key){ root_key.to_sym }
|
22
|
+
let!(:expected_tool_hash){ {
|
23
|
+
:name => product_tool.name,
|
24
|
+
:category => {
|
25
|
+
:name => category_tools.name,
|
26
|
+
:subcategories => [
|
27
|
+
{ :name => category_screwdrivers.name,
|
28
|
+
:subcategories => [{:name => category_flat_heads.name, :subcategories => []}]
|
29
|
+
},
|
30
|
+
{:name => category_knives.name, :subcategories => []}
|
31
|
+
]
|
32
|
+
}
|
33
|
+
}}
|
34
|
+
|
35
|
+
subject(:serialized){ described_class.new(serializer).serializable_hash }
|
36
|
+
|
37
|
+
it { expect(serialized).to eq(expected_root_key => expected_tool_hash) }
|
38
|
+
|
39
|
+
context "collection" do
|
40
|
+
let(:category_cookware){ ::Category.new(:name => "cookware") }
|
41
|
+
let(:product_pot){ ::Product.new(:name => "Pot", :category => category_cookware) }
|
42
|
+
let(:collection){ [product_tool, product_pot] }
|
43
|
+
let!(:expected_root_key){ root_key.pluralize.to_sym }
|
44
|
+
let!(:expected_pot_hash){ {
|
45
|
+
:name => product_pot.name,
|
46
|
+
:category => {
|
47
|
+
:name => category_cookware.name,
|
48
|
+
:subcategories => []
|
49
|
+
}
|
50
|
+
}}
|
51
|
+
let(:serializer){ ::ActiveModel::Serializer::ArraySerializer.new(collection, :root => root_key, :serializer => ::NameProductSerializer) }
|
52
|
+
|
53
|
+
it { expect(serialized).to eq(expected_root_key => [expected_tool_hash, expected_pot_hash]) }
|
54
|
+
end
|
55
|
+
end
|
data/trax_controller.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_dependency "will_paginate"
|
23
23
|
spec.add_dependency "has_scope"
|
24
24
|
spec.add_development_dependency "rails"
|
25
|
-
spec.add_development_dependency "inherited_resources"
|
25
|
+
spec.add_development_dependency "inherited_resources", "~> 1.5.1"
|
26
26
|
spec.add_development_dependency 'combustion', '~> 0.5.3'
|
27
27
|
spec.add_development_dependency "bundler", "~> 1.6"
|
28
28
|
spec.add_development_dependency "rake"
|
@@ -34,9 +34,8 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.add_development_dependency 'rspec-its', '~> 1'
|
35
35
|
spec.add_development_dependency 'rspec-collection_matchers', '~> 1'
|
36
36
|
spec.add_development_dependency 'guard', '~> 2'
|
37
|
-
spec.add_development_dependency 'guard-rspec', '~> 4'
|
38
37
|
spec.add_development_dependency 'guard-bundler', '~> 2'
|
39
38
|
spec.add_development_dependency 'rb-fsevent'
|
40
39
|
spec.add_development_dependency 'terminal-notifier-guard'
|
41
|
-
|
40
|
+
spec.add_development_dependency 'pundit'
|
42
41
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trax_controller
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Ayre
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-04-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: trax_core
|
@@ -70,16 +70,16 @@ dependencies:
|
|
70
70
|
name: inherited_resources
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 1.5.1
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 1.5.1
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: combustion
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -235,35 +235,35 @@ dependencies:
|
|
235
235
|
- !ruby/object:Gem::Version
|
236
236
|
version: '2'
|
237
237
|
- !ruby/object:Gem::Dependency
|
238
|
-
name: guard-
|
238
|
+
name: guard-bundler
|
239
239
|
requirement: !ruby/object:Gem::Requirement
|
240
240
|
requirements:
|
241
241
|
- - "~>"
|
242
242
|
- !ruby/object:Gem::Version
|
243
|
-
version: '
|
243
|
+
version: '2'
|
244
244
|
type: :development
|
245
245
|
prerelease: false
|
246
246
|
version_requirements: !ruby/object:Gem::Requirement
|
247
247
|
requirements:
|
248
248
|
- - "~>"
|
249
249
|
- !ruby/object:Gem::Version
|
250
|
-
version: '
|
250
|
+
version: '2'
|
251
251
|
- !ruby/object:Gem::Dependency
|
252
|
-
name:
|
252
|
+
name: rb-fsevent
|
253
253
|
requirement: !ruby/object:Gem::Requirement
|
254
254
|
requirements:
|
255
|
-
- - "
|
255
|
+
- - ">="
|
256
256
|
- !ruby/object:Gem::Version
|
257
|
-
version: '
|
257
|
+
version: '0'
|
258
258
|
type: :development
|
259
259
|
prerelease: false
|
260
260
|
version_requirements: !ruby/object:Gem::Requirement
|
261
261
|
requirements:
|
262
|
-
- - "
|
262
|
+
- - ">="
|
263
263
|
- !ruby/object:Gem::Version
|
264
|
-
version: '
|
264
|
+
version: '0'
|
265
265
|
- !ruby/object:Gem::Dependency
|
266
|
-
name:
|
266
|
+
name: terminal-notifier-guard
|
267
267
|
requirement: !ruby/object:Gem::Requirement
|
268
268
|
requirements:
|
269
269
|
- - ">="
|
@@ -277,7 +277,7 @@ dependencies:
|
|
277
277
|
- !ruby/object:Gem::Version
|
278
278
|
version: '0'
|
279
279
|
- !ruby/object:Gem::Dependency
|
280
|
-
name:
|
280
|
+
name: pundit
|
281
281
|
requirement: !ruby/object:Gem::Requirement
|
282
282
|
requirements:
|
283
283
|
- - ">="
|
@@ -311,11 +311,26 @@ files:
|
|
311
311
|
- lib/trax/controller.rb
|
312
312
|
- lib/trax/controller/action_types.rb
|
313
313
|
- lib/trax/controller/actions.rb
|
314
|
+
- lib/trax/controller/authorization.rb
|
315
|
+
- lib/trax/controller/authorization/pundit.rb
|
316
|
+
- lib/trax/controller/authorization/pundit/actions.rb
|
317
|
+
- lib/trax/controller/authorization/pundit/actions/create.rb
|
318
|
+
- lib/trax/controller/authorization/pundit/actions/destroy.rb
|
319
|
+
- lib/trax/controller/authorization/pundit/actions/index.rb
|
320
|
+
- lib/trax/controller/authorization/pundit/actions/new.rb
|
321
|
+
- lib/trax/controller/authorization/pundit/actions/read.rb
|
322
|
+
- lib/trax/controller/authorization/pundit/actions/search.rb
|
323
|
+
- lib/trax/controller/authorization/pundit/actions/show.rb
|
324
|
+
- lib/trax/controller/authorization/pundit/actions/update.rb
|
325
|
+
- lib/trax/controller/authorization/pundit/adapter.rb
|
326
|
+
- lib/trax/controller/authorization/pundit/base_policy.rb
|
327
|
+
- lib/trax/controller/authorize.rb
|
314
328
|
- lib/trax/controller/collection.rb
|
315
329
|
- lib/trax/controller/collection/base.rb
|
316
330
|
- lib/trax/controller/collection/pageable.rb
|
317
331
|
- lib/trax/controller/collection/response_meta.rb
|
318
332
|
- lib/trax/controller/collection/searchable.rb
|
333
|
+
- lib/trax/controller/config.rb
|
319
334
|
- lib/trax/controller/engine.rb
|
320
335
|
- lib/trax/controller/inherit_resources.rb
|
321
336
|
- lib/trax/controller/resource.rb
|
@@ -329,9 +344,12 @@ files:
|
|
329
344
|
- spec/internal/app/controllers/application_controller.rb
|
330
345
|
- spec/internal/app/controllers/categories_controller.rb
|
331
346
|
- spec/internal/app/controllers/products_controller.rb
|
347
|
+
- spec/internal/app/controllers/widgets_controller.rb
|
332
348
|
- spec/internal/app/models/category.rb
|
333
349
|
- spec/internal/app/models/product.rb
|
350
|
+
- spec/internal/app/models/user.rb
|
334
351
|
- spec/internal/app/models/widget.rb
|
352
|
+
- spec/internal/app/policies/widget_policy.rb
|
335
353
|
- spec/internal/app/serializers/categories_serializer.rb
|
336
354
|
- spec/internal/app/serializers/category_serializer.rb
|
337
355
|
- spec/internal/app/serializers/product_serializer.rb
|
@@ -343,9 +361,12 @@ files:
|
|
343
361
|
- spec/internal/log/.gitignore
|
344
362
|
- spec/internal/public/favicon.ico
|
345
363
|
- spec/spec_helper.rb
|
364
|
+
- spec/trax/controller/authorization/pundit/base_policy_spec.rb
|
365
|
+
- spec/trax/controller/authorization/pundit_spec.rb
|
346
366
|
- spec/trax/controller_spec.rb
|
347
367
|
- spec/trax/controllers/categories_controller_spec.rb
|
348
368
|
- spec/trax/controllers/products_controller_spec.rb
|
369
|
+
- spec/trax/serialization/adapters/json_spec.rb
|
349
370
|
- trax_controller.gemspec
|
350
371
|
homepage: http://www.github.com/jasonayre
|
351
372
|
licenses:
|
@@ -367,7 +388,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
367
388
|
version: '0'
|
368
389
|
requirements: []
|
369
390
|
rubyforge_project:
|
370
|
-
rubygems_version: 2.
|
391
|
+
rubygems_version: 2.5.1
|
371
392
|
signing_key:
|
372
393
|
specification_version: 4
|
373
394
|
summary: Resourceful, standardized controllers
|
@@ -375,9 +396,12 @@ test_files:
|
|
375
396
|
- spec/internal/app/controllers/application_controller.rb
|
376
397
|
- spec/internal/app/controllers/categories_controller.rb
|
377
398
|
- spec/internal/app/controllers/products_controller.rb
|
399
|
+
- spec/internal/app/controllers/widgets_controller.rb
|
378
400
|
- spec/internal/app/models/category.rb
|
379
401
|
- spec/internal/app/models/product.rb
|
402
|
+
- spec/internal/app/models/user.rb
|
380
403
|
- spec/internal/app/models/widget.rb
|
404
|
+
- spec/internal/app/policies/widget_policy.rb
|
381
405
|
- spec/internal/app/serializers/categories_serializer.rb
|
382
406
|
- spec/internal/app/serializers/category_serializer.rb
|
383
407
|
- spec/internal/app/serializers/product_serializer.rb
|
@@ -389,6 +413,9 @@ test_files:
|
|
389
413
|
- spec/internal/log/.gitignore
|
390
414
|
- spec/internal/public/favicon.ico
|
391
415
|
- spec/spec_helper.rb
|
416
|
+
- spec/trax/controller/authorization/pundit/base_policy_spec.rb
|
417
|
+
- spec/trax/controller/authorization/pundit_spec.rb
|
392
418
|
- spec/trax/controller_spec.rb
|
393
419
|
- spec/trax/controllers/categories_controller_spec.rb
|
394
420
|
- spec/trax/controllers/products_controller_spec.rb
|
421
|
+
- spec/trax/serialization/adapters/json_spec.rb
|