trax_controller 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|