kojac 0.9.1 → 0.11.0

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.
Files changed (95) hide show
  1. checksums.yaml +6 -14
  2. data/.gitignore +4 -0
  3. data/Gemfile +1 -1
  4. data/app/assets/javascripts/kojac.js +393 -125
  5. data/app/assets/javascripts/kojac_canjs.js +34 -34
  6. data/app/assets/javascripts/kojac_ember.js +110 -152
  7. data/app/controllers/{kojac_controller.rb → kojac_base_controller.rb} +18 -11
  8. data/app/policies/kojac_base_policy.rb +114 -0
  9. data/app/serializers/kojac_base_serializer.rb +35 -0
  10. data/kojac.gemspec +12 -10
  11. data/lib/kojac/app_serialize.rb +31 -29
  12. data/lib/kojac/concentric.rb +152 -0
  13. data/lib/kojac/kojac_policy.rb +70 -0
  14. data/lib/kojac/kojac_rails.rb +200 -49
  15. data/lib/kojac/version.rb +1 -1
  16. data/spec/can_cache_spec.js +19 -19
  17. data/spec/demo/.gitignore +16 -0
  18. data/spec/demo/.ruby-gemset +1 -0
  19. data/spec/demo/.ruby-version +1 -0
  20. data/spec/demo/Gemfile +59 -0
  21. data/spec/demo/Gemfile.lock +153 -0
  22. data/spec/demo/README.rdoc +15 -248
  23. data/spec/demo/Rakefile +25 -1
  24. data/spec/demo/app/{mailers/.gitkeep → assets/images/.keep} +0 -0
  25. data/spec/demo/app/assets/javascripts/application.js +3 -3
  26. data/spec/demo/app/controllers/application_controller.rb +6 -1
  27. data/spec/demo/app/{models/.gitkeep → controllers/concerns/.keep} +0 -0
  28. data/spec/demo/app/controllers/users_controller.rb +5 -0
  29. data/spec/demo/{lib/assets/.gitkeep → app/mailers/.keep} +0 -0
  30. data/spec/demo/{log/.gitkeep → app/models/.keep} +0 -0
  31. data/spec/demo/app/models/concerns/.keep +0 -0
  32. data/spec/demo/app/models/user.rb +36 -0
  33. data/spec/demo/app/policies/user_policy.rb +42 -0
  34. data/spec/demo/bin/bundle +3 -0
  35. data/spec/demo/bin/rails +4 -0
  36. data/spec/demo/bin/rake +4 -0
  37. data/spec/demo/config.ru +1 -1
  38. data/spec/demo/config/application.rb +14 -46
  39. data/spec/demo/config/application.yml +4 -0
  40. data/spec/demo/config/boot.rb +3 -9
  41. data/spec/demo/config/database.yml +6 -6
  42. data/spec/demo/config/environment.rb +4 -2
  43. data/spec/demo/config/environments/development.rb +11 -19
  44. data/spec/demo/config/environments/production.rb +40 -27
  45. data/spec/demo/config/environments/test.rb +13 -14
  46. data/spec/demo/config/initializers/concentric_config.rb +9 -0
  47. data/spec/demo/config/initializers/filter_parameter_logging.rb +4 -0
  48. data/spec/demo/config/initializers/inflections.rb +6 -5
  49. data/spec/demo/config/initializers/initialize_kojac.rb +16 -0
  50. data/spec/demo/config/initializers/secret_token.rb +7 -2
  51. data/spec/demo/config/initializers/session_store.rb +0 -5
  52. data/spec/demo/config/initializers/wrap_parameters.rb +6 -6
  53. data/spec/demo/config/locales/en.yml +20 -2
  54. data/spec/demo/config/routes.rb +24 -24
  55. data/spec/demo/db/migrate/20131212034312_add_user.rb +14 -0
  56. data/spec/demo/db/migrate/20140107085351_add_owner_id.rb +5 -0
  57. data/spec/demo/db/schema.rb +28 -0
  58. data/spec/demo/db/seeds.rb +7 -0
  59. data/spec/demo/lib/assets/.keep +0 -0
  60. data/spec/demo/lib/tasks/.keep +0 -0
  61. data/spec/demo/log/.keep +0 -0
  62. data/spec/demo/public/404.html +43 -11
  63. data/spec/demo/public/422.html +43 -11
  64. data/spec/demo/public/500.html +43 -11
  65. data/spec/demo/public/robots.txt +5 -0
  66. data/spec/demo/spec/controllers/allowed_fields_spec.rb +171 -0
  67. data/spec/demo/spec/factories/users.rb +9 -0
  68. data/spec/demo/spec/features/concentric_spec.rb +63 -0
  69. data/spec/demo/spec/features/serialization_spec.rb +86 -0
  70. data/spec/demo/spec/spec_helper.rb +133 -0
  71. data/spec/demo/spec/spec_utils.rb +42 -0
  72. data/spec/demo/vendor/assets/javascripts/.keep +0 -0
  73. data/spec/demo/vendor/assets/stylesheets/.keep +0 -0
  74. data/spec/ember_factory_spec.js +1 -1
  75. data/spec/ember_model_spec.js +13 -3
  76. data/spec/ember_tojsono_spec.js +105 -0
  77. data/spec/error_handling_spec.js +90 -0
  78. data/spec/external/underscore_plus.js +318 -9
  79. data/spec/kojac_caching_spec.js +3 -1
  80. data/spec/kojac_ember_cache_spec.js +9 -0
  81. data/spec/kojac_mock_spec.js +4 -4
  82. data/spec/kojac_operations_spec.js +4 -4
  83. data/spec/local_provider_spec.js +184 -0
  84. data/spec/model_ring_spec.rb +2 -2
  85. data/spec/operation_include_spec.js +2 -2
  86. data/spec/run.html +34 -24
  87. data/spec/type_conversion_spec.js +38 -0
  88. data/vendor/assets/javascripts/jstorage.js +950 -0
  89. metadata +115 -129
  90. data/Gemfile.lock +0 -157
  91. data/app/serializers/default_kojac_serializer.rb +0 -10
  92. data/lib/kojac/ring_strong_parameters.rb +0 -195
  93. data/spec/.DS_Store +0 -0
  94. data/spec/demo/script/rails +0 -6
  95. data/spec/external/.DS_Store +0 -0
@@ -0,0 +1,114 @@
1
+ class KojacBasePolicy
2
+ attr_reader :user, :record, :op
3
+
4
+ def initialize(user, record, op=nil)
5
+ raise Pundit::NotAuthorizedError, "must be logged in" unless user
6
+ @user = user
7
+ @record = record
8
+ @op = op
9
+ end
10
+
11
+ def unauthorized!(aMessage=nil)
12
+ raise Pundit::NotAuthorizedError, aMessage||"You are not authorized to perform this action"
13
+ end
14
+
15
+ #def self.write_op_filter(aCurrentUser,aSafeFields,aSourceFields)
16
+ # ring = aCurrentUser.ring
17
+ # has_owner = !!self.column_names.include?('owner_id')
18
+ # has_dealership = !!self.column_names.include?('dealership_id')
19
+ # # default to user
20
+ # if ring <= SALES_RING
21
+ # aSafeFields['owner_id'] ||= aCurrentUser.owner_id if has_owner
22
+ # aSafeFields['dealership_id'] ||= aCurrentUser.dealership_id if has_dealership
23
+ # end
24
+ # if ring > SYSADMIN_RING
25
+ # unauthorized! if aSafeFields['owner_id'] != aCurrentUser.owner_id if has_owner
26
+ # end
27
+ # if ring > OWNER_ADMIN_RING
28
+ # unauthorized! if aSafeFields['dealership_id'] != aCurrentUser.dealership_id if has_dealership
29
+ # end
30
+ #end
31
+
32
+ def query_ring
33
+ user.ring
34
+ end
35
+
36
+ # kojac methods
37
+ def read?
38
+ record.class.ring_can?(query_ring,:read)
39
+ end
40
+
41
+ def write?
42
+ record.class.ring_can?(query_ring,:write)
43
+ end
44
+
45
+ # rails methods
46
+ def index?
47
+ record.class.ring_can?(query_ring,:read)
48
+ end
49
+
50
+ def show?
51
+ record.class.ring_can?(query_ring,:read)
52
+ end
53
+
54
+ def create?
55
+ record.class.ring_can?(query_ring,:create)
56
+ end
57
+
58
+ def new?
59
+ record.class.ring_can?(query_ring,:create)
60
+ end
61
+
62
+ def update?
63
+ record.class.ring_can?(query_ring,:write)
64
+ end
65
+
66
+ def edit?
67
+ record.class.ring_can?(query_ring,:write)
68
+ end
69
+
70
+ def destroy?
71
+ record.class.ring_can?(query_ring,:destroy)
72
+ end
73
+
74
+ def scope
75
+ Pundit.policy_scope!(user, record.class)
76
+ end
77
+
78
+ def permitted_attributes(aAbility=nil)
79
+ raise "ability not given" unless (@op && @op[:verb]) || aAbility
80
+ if !aAbility && @op
81
+ aAbility = case @op[:verb]
82
+ when 'CREATE'
83
+ when 'UPDATE'
84
+ :write
85
+ when 'READ'
86
+ :read
87
+ when 'ADD'
88
+ :add
89
+ when 'REMOVE'
90
+ :remove
91
+ when 'CREATE_ON'
92
+ :create_on
93
+ end
94
+ end
95
+ cls = record.is_a?(Class) ? record : record.class
96
+ cls.permitted(query_ring,aAbility)
97
+ end
98
+
99
+ def permitted_fields(aAbility=nil)
100
+ result = permitted_attributes(aAbility)
101
+ cls = record.is_a?(Class) ? record : record.class
102
+ result.delete_if { |f| cls.reflections.has_key? f }
103
+ result
104
+ end
105
+
106
+ def permitted_associations(aAbility=nil)
107
+ result = permitted_attributes(aAbility)
108
+ cls = record.is_a?(Class) ? record : record.class
109
+ result.delete_if { |f| !cls.reflections.has_key? f }
110
+ result
111
+ end
112
+
113
+ end
114
+
@@ -0,0 +1,35 @@
1
+ class KojacBaseSerializer < ActiveModel::Serializer
2
+
3
+ self.root = false
4
+
5
+ def initialize(object, options={})
6
+ super(object,options)
7
+ end
8
+
9
+ SERIALIZABLE_TYPES = [NilClass,FalseClass,TrueClass,Fixnum,Bignum,String,Symbol,Hash,Array]
10
+
11
+ def attributes
12
+ attrs = nil
13
+ source = if policy = Kojac::policy(scope,object)
14
+ attrs = policy.permitted_attributes(:read).map(&:to_s)
15
+ object.attributes
16
+ elsif object.respond_to? :attributes
17
+ object.attributes
18
+ elsif object.is_a? Hash
19
+ object
20
+ end
21
+ result = {}
22
+ source.each do |k,v|
23
+ k_s = k.to_s
24
+ next if attrs && !attrs.include?(k_s)
25
+ if SERIALIZABLE_TYPES.include? v.class
26
+ result[k_s] = v
27
+ else
28
+ result[k_s] = KojacUtils.to_jsono(v,scope: scope)
29
+ end
30
+ end
31
+ result
32
+ end
33
+
34
+ alias_method :serializable_object, :serializable_hash
35
+ end
data/kojac.gemspec CHANGED
@@ -21,15 +21,17 @@ Gem::Specification.new do |s|
21
21
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
22
22
  s.require_paths = ["lib"]
23
23
 
24
- s.add_dependency "underscore_plus"
25
- s.add_dependency "jquery-rails"
26
- s.add_dependency 'strong_parameters'
24
+ s.add_runtime_dependency "buzztools", '~> 0.0.5'
25
+ s.add_runtime_dependency "underscore_plus", '~> 0.9.1'
26
+ s.add_runtime_dependency "pundit", '~> 0.2.3'
27
+ s.add_runtime_dependency "active_model_serializers", '= 0.9.0.alpha1'
27
28
 
28
- s.add_development_dependency "rails", "~> 3.2"
29
- s.add_development_dependency "rspec-rails"
30
- s.add_development_dependency "canjs-rails"
31
- s.add_development_dependency "ember-rails"
32
- s.add_development_dependency "jquery-rails"
33
- s.add_development_dependency "capybara"
34
- s.add_development_dependency "sqlite3"
29
+ #s.add_runtime_dependency "jquery-rails"
30
+ #s.add_runtime_dependency "rails", ">= 3.1"
31
+ # s.add_development_dependency "rspec-rails"
32
+ # s.add_development_dependency "canjs-rails"
33
+ # s.add_development_dependency "ember-rails"
34
+ # s.add_development_dependency "jquery-rails"
35
+ # s.add_development_dependency "capybara"
36
+ # s.add_development_dependency "sqlite3"
35
37
  end
@@ -1,29 +1,31 @@
1
- require 'active_record/serializer_override'
2
-
3
- def app_serialize(aObject,aScope)
4
- result = case aObject.class
5
- when Fixnum then aObject.to_s
6
- when Bignum then aObject.to_s
7
- when Array
8
- sz_class = ActiveModel::ArraySerializer
9
- sz_class.new(aObject).to_json(:scope => aScope, :root => false)
10
- when String then aObject
11
- when FalseClass then 'false'
12
- when TrueClass then 'true'
13
- when Symbol then aObject.to_s
14
- #when Hash
15
- #sz_class = ActiveModel::Serializer
16
- #sz_class.new(aObject).to_json(:scope => aScope, :root => false)
17
-
18
- else
19
- sz_class = aObject.respond_to?(:active_model_serializer) && aObject.send(:active_model_serializer)
20
- sz_class = DefaultKojacSerializer if !sz_class && aObject.is_a?(ActiveModel)
21
- if sz_class
22
- sz_class.new(aObject).to_json(:scope => aScope, :root => false)
23
- else
24
- aObject.to_json
25
- end
26
-
27
- end
28
- result
29
- end
1
+ ## http://matthewrobertson.org/blog/2013/08/06/active-record-serializers-from-scratch/
2
+ #
3
+ #if Rails::VERSION::STRING.split('.').first.to_i < 4
4
+ # require 'active_record/serializer_override'
5
+ #end
6
+ #
7
+ #def app_serialize(aObject,aScope)
8
+ # result = case aObject.class
9
+ # when Fixnum then aObject.to_s
10
+ # when Bignum then aObject.to_s
11
+ # when Array
12
+ # sz_class = ActiveModel::ArraySerializer
13
+ # sz_class.new(aObject).to_json(:scope => aScope, :root => false)
14
+ # when String then aObject
15
+ # when FalseClass then 'false'
16
+ # when TrueClass then 'true'
17
+ # when Symbol then aObject.to_s
18
+ # #when Hash
19
+ # #sz_class = ActiveModel::Serializer
20
+ # #sz_class.new(aObject).to_json(:scope => aScope, :root => false)
21
+ # else
22
+ # sz_class = aObject.respond_to?(:active_model_serializer) && aObject.send(:active_model_serializer)
23
+ # sz_class = DefaultKojacSerializer if !sz_class && aObject.is_a?(ActiveModel)
24
+ # if sz_class
25
+ # sz_class.new(aObject).to_json(:scope => aScope, :root => false)
26
+ # else
27
+ # aObject.to_json
28
+ # end
29
+ # end
30
+ # result
31
+ #end
@@ -0,0 +1,152 @@
1
+ #concentric
2
+ #
3
+ #Assists implementation of ring level security (http://en.wikipedia.org/wiki/Ring_(computer_security)) with Rails 4 (or Rails 3 with gem) Strong Parameters.
4
+ #
5
+ #Ring Level Security is a simpler alternative to Role Based Security. Rings are arranged in a concentric hierarchy from most-privileged innermost Ring 0 to the least privileged highest ring number. Users have their own ring level which gives them access to that ring and below.
6
+ #
7
+ #For example, a sysadmin could have Ring 0, a website manager ring 1, a customer ring 2, and anonymous users ring 3. A customer would have all the capabilities of anonymous users, and more. Likewise, a website manager has all the capabilities of a customer, and more etc.
8
+ #
9
+ #This inheritance of capabilities of outer rings, and the simple assigning of users to rings, makes security rules less repetitive and easier to write and maintain, minimising dangerous mistakes.
10
+ #
11
+ #This gem does not affect or replace or prevent the standard strong parameters methods from being used in parallel, it merely generates arguments for the standard strong parameters methods.
12
+ #
13
+ #
14
+ #
15
+ #BASIC_FIELDS = [:name, :address]
16
+ #
17
+ #class Deal
18
+ # ring 1, :write, BASIC_FIELDS
19
+ # ring 1, :write, :phone
20
+ # ring 1, :delete
21
+ # ring 2, :read, BASIC_FIELDS
22
+ #end
23
+ #
24
+ #
25
+ #class DealsController
26
+ #
27
+ # def update
28
+ # ring_fields(:write,model)
29
+ # if ring_can(:write,model,:name)
30
+ # if ring_can(:delete,model)
31
+ # model.update(params.permit( ring_fields(:write,model) ))
32
+ # end
33
+ #
34
+ #end
35
+
36
+
37
+
38
+ class Concentric
39
+
40
+ cattr_accessor :config
41
+
42
+ def self.lookup_ring(aRingName)
43
+ return nil if !aRingName
44
+ return aRingName if aRingName.is_a?(Fixnum)
45
+ if ring_names = Concentric.config[:ring_names]
46
+ return ring_names[aRingName.to_sym]
47
+ else
48
+ return nil
49
+ end
50
+ end
51
+
52
+ def self.ring_name(aRing)
53
+ ring_names = Concentric.config[:ring_names]
54
+ ring_names.key(aRing)
55
+ end
56
+
57
+ def self.ring_text(aRing)
58
+ return 'none' if !aRing
59
+ ring_name(aRing).to_s.humanize
60
+ end
61
+ end
62
+
63
+
64
+ # see http://yehudakatz.com/2009/11/12/better-ruby-idioms/ re class and instance methods and modules
65
+
66
+ module Concentric::Model
67
+
68
+ def self.included(aClass)
69
+ aClass.cattr_accessor :rings_abilities
70
+ aClass.rings_abilities = [] # [1] => {read: [:name,:address], delete: true}
71
+ aClass.send :extend, ClassMethods
72
+ end
73
+
74
+ module ClassMethods
75
+
76
+ # supports different formats :
77
+ # ring :sales, :write => [:name,:address] ie. sales can write the name and address fields
78
+ # ring :sales, :read ie. sales can read this model
79
+ # ring :sales, [:read, :create, :destroy] ie. sales can read, create and destroy this model
80
+ def ring(aRing,aAbilities)
81
+ #aRing.each {|r| ring(r,aAbilities)} and return if aRing.is_a? Array shouldn't need this because of ring system
82
+ aRing = Concentric.lookup_ring(aRing)
83
+ raise "aRing must be a number or a symbol defined in Concentric.config.ring_names" if !aRing.is_a?(Fixnum)
84
+ raise "aAbilities must be a Hash" unless aAbilities.is_a? Hash # eg. :write => [:name,:address]
85
+
86
+ ring_rec = self.rings_abilities[aRing]
87
+ aAbilities.each do |abilities,fields|
88
+ abilities = [abilities] unless abilities.is_a?(Array)
89
+ fields = [fields] unless fields.is_a?(Array)
90
+ next if fields.empty?
91
+ abilities.each do |a|
92
+ a = a.to_sym
93
+ ring_rec ||= {}
94
+ if fields==[:this]
95
+ ring_rec[a] = true unless ring_rec[a].to_nil
96
+ else
97
+ ring_fields = ring_rec[a]
98
+ ring_fields = [] unless ring_fields.is_a? Array
99
+ ring_fields = ring_fields + fields.map(&:to_sym)
100
+ ring_fields.uniq!
101
+ ring_fields.sort!
102
+ ring_rec[a] = ring_fields
103
+ end
104
+ end
105
+ self.rings_abilities[aRing] = ring_rec
106
+ end
107
+ end
108
+
109
+ # returns properties that this ring can use this ability on
110
+ def permitted(aRing,aAbility)
111
+ aRing = Concentric.lookup_ring(aRing)
112
+ raise "aRing must be a number or a symbol defined in Concentric.config.ring_names" if !aRing.is_a?(Fixnum)
113
+ return [] unless aRing and rings_abilities = self.respond_to?(:rings_abilities) && self.rings_abilities.to_nil
114
+
115
+ fields = []
116
+ aRing.upto(rings_abilities.length-1) do |i|
117
+ next unless ring_rec = rings_abilities[i]
118
+ if af = ring_rec[aAbility.to_sym]
119
+ next if af==true
120
+ fields += af if af.is_a?(Array)
121
+ end
122
+ end
123
+ fields.uniq!
124
+ fields.sort!
125
+ fields
126
+ end
127
+
128
+ # Query
129
+ def ring_can?(aRing,aAbility,aFields=nil)
130
+ if aFields
131
+ pf = permitted(aRing,aAbility)
132
+ if aFields.is_a? Array
133
+ return (aFields - pf).empty?
134
+ else
135
+ return pf.include? aFields
136
+ end
137
+ end
138
+
139
+ aRing = Concentric.lookup_ring(aRing)
140
+ return [] unless aRing and rings_abilities = self.respond_to?(:rings_abilities).to_nil && self.rings_abilities
141
+
142
+ aRing.upto(rings_abilities.length-1) do |i|
143
+ next unless ring_rec = rings_abilities[i]
144
+ return true if ring_rec[aAbility.to_sym].to_nil
145
+ end
146
+ return false
147
+ end
148
+
149
+ end
150
+
151
+ end
152
+
@@ -0,0 +1,70 @@
1
+ #require "pundit/version"
2
+ require "pundit/policy_finder"
3
+ #require "active_support/concern"
4
+ #require "active_support/core_ext/string/inflections"
5
+ #require "active_support/core_ext/object/blank"
6
+
7
+ module Kojac
8
+ #class NotAuthorizedError < StandardError; end
9
+ #class NotDefinedError < StandardError; end
10
+
11
+ extend ActiveSupport::Concern
12
+
13
+ class << self
14
+ def policy_scope(user, scope, op=nil)
15
+ policy = Pundit::PolicyFinder.new(scope).scope
16
+ policy.new(user, scope, op).resolve if policy
17
+ end
18
+
19
+ def policy_scope!(user, scope, op=nil)
20
+ Pundit::PolicyFinder.new(scope).scope!.new(user, scope, op).resolve
21
+ end
22
+
23
+ def policy(user, record, op=nil)
24
+ scope = Pundit::PolicyFinder.new(record).policy
25
+ scope.new(user, record, op) if scope
26
+ end
27
+
28
+ def policy!(user, record, op=nil)
29
+ Pundit::PolicyFinder.new(record).policy!.new(user, record, op)
30
+ end
31
+ end
32
+
33
+ included do
34
+ if respond_to?(:helper_method)
35
+ helper_method :policy_scope
36
+ helper_method :policy
37
+ end
38
+ if respond_to?(:hide_action)
39
+ hide_action :authorize
40
+ hide_action :verify_authorized
41
+ hide_action :verify_policy_scoped
42
+ end
43
+ end
44
+
45
+ def verify_authorized
46
+ raise NotAuthorizedError unless @_policy_authorized
47
+ end
48
+
49
+ def verify_policy_scoped
50
+ raise NotAuthorizedError unless @_policy_scoped
51
+ end
52
+
53
+ def authorize(record, query=nil)
54
+ query ||= params[:action].to_s + "?"
55
+ @_policy_authorized = true
56
+ unless policy(record).public_send(query)
57
+ raise NotAuthorizedError, "not allowed to #{query} this #{record}"
58
+ end
59
+ true
60
+ end
61
+
62
+ def policy_scope(scope)
63
+ @_policy_scoped = true
64
+ Pundit::Pundit.policy_scope!(current_user, scope, op)
65
+ end
66
+
67
+ def policy(record)
68
+ Pundit::Pundit.policy!(current_user, record, op)
69
+ end
70
+ end