skill_tree 0.0.1

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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/lib/generators/skill_tree/install_generator.rb +20 -0
  3. data/lib/generators/skill_tree/orm_helpers.rb +14 -0
  4. data/lib/generators/skill_tree/templates/acl.rb +42 -0
  5. data/lib/generators/skill_tree/templates/create_skill_tree_acl_system.rb +48 -0
  6. data/lib/generators/skill_tree/templates/initializer.rb +1 -0
  7. data/lib/skill_tree.rb +15 -0
  8. data/lib/skill_tree/controller.rb +57 -0
  9. data/lib/skill_tree/errors.rb +5 -0
  10. data/lib/skill_tree/models.rb +10 -0
  11. data/lib/skill_tree/models/acl.rb +13 -0
  12. data/lib/skill_tree/models/acl_mapping.rb +16 -0
  13. data/lib/skill_tree/models/acl_ownership.rb +11 -0
  14. data/lib/skill_tree/models/permission.rb +10 -0
  15. data/lib/skill_tree/models/role.rb +22 -0
  16. data/lib/skill_tree/models/user_role.rb +14 -0
  17. data/lib/skill_tree/parser.rb +44 -0
  18. data/lib/skill_tree/parser/acl_parser.rb +42 -0
  19. data/lib/skill_tree/parser/role_parser.rb +57 -0
  20. data/lib/skill_tree/resource.rb +21 -0
  21. data/lib/skill_tree/resource/callbacks.rb +20 -0
  22. data/lib/skill_tree/resource/class_methods.rb +10 -0
  23. data/lib/skill_tree/resource/instance_methods.rb +52 -0
  24. data/lib/skill_tree/resource/relations.rb +18 -0
  25. data/lib/skill_tree/resource/scopes.rb +38 -0
  26. data/lib/skill_tree/subject.rb +53 -0
  27. data/lib/skill_tree/version.rb +3 -0
  28. data/spec/db/schema.rb +59 -0
  29. data/spec/skill_tree/controller_spec.rb +103 -0
  30. data/spec/skill_tree/generators/install_generator_spec.rb +34 -0
  31. data/spec/skill_tree/models/acl_mapping_spec.rb +46 -0
  32. data/spec/skill_tree/models/acl_ownership_spec.rb +30 -0
  33. data/spec/skill_tree/models/acl_spec.rb +32 -0
  34. data/spec/skill_tree/models/permission_spec.rb +16 -0
  35. data/spec/skill_tree/models/role_spec.rb +41 -0
  36. data/spec/skill_tree/models/user_role_spec.rb +16 -0
  37. data/spec/skill_tree/parser_spec.rb +71 -0
  38. data/spec/skill_tree/resource_spec.rb +137 -0
  39. data/spec/skill_tree/subject_spec.rb +65 -0
  40. data/spec/skill_tree_spec.rb +8 -0
  41. data/spec/spec_helper.rb +37 -0
  42. data/spec/support/acls/acl_example.rb +19 -0
  43. data/spec/support/acls/acl_example2.rb +24 -0
  44. data/spec/support/acls/acl_example3.rb +24 -0
  45. data/spec/support/acls/acl_example4.rb +24 -0
  46. data/spec/support/acls/subject_acl.rb +57 -0
  47. data/spec/support/capture_stdout.rb +12 -0
  48. data/spec/support/controllers.rb +71 -0
  49. data/spec/support/model_builder.rb +15 -0
  50. data/spec/support/models.rb +16 -0
  51. metadata +232 -0
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe SkillTree::Models::AclOwnership, type: :model do
4
+ it 'belongs to an acl' do
5
+ expect(subject).to be_respond_to(:acl)
6
+ end
7
+
8
+ it 'validates presence of acl' do
9
+ expect(subject).to have(1).error_on(:acl)
10
+ subject.acl = build(:acl)
11
+ expect(subject).to have(0).error_on(:acl)
12
+ end
13
+
14
+ it 'belongs to a resource' do
15
+ expect(subject).to be_respond_to(:resource)
16
+ end
17
+
18
+ it 'resource is polymorphic' do
19
+ subject.resource = User.new
20
+ subject.resource = Post.new
21
+ end
22
+
23
+ it 'resource is unique' do
24
+ resource = Post.create!
25
+ acl = create(:acl, name: 'some_name', version: 0)
26
+ described_class.create!(resource: resource, acl: acl)
27
+ subject.resource = User.new
28
+ subject.resource = Post.new
29
+ end
30
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe SkillTree::Models::Acl, type: :model do
4
+ it 'validates name presence' do
5
+ expect(subject).to have(1).error_on(:name)
6
+ subject.name = 'my_name'
7
+ expect(subject).to have(0).error_on(:name)
8
+ end
9
+
10
+ it 'validates name uniqueness' do
11
+ create(:acl, name: 'my_name', version: 0)
12
+ subject.name = 'my_name'
13
+ expect(subject).to have(1).error_on(:name)
14
+ end
15
+
16
+ it 'has many roles' do
17
+ expect(subject).to be_respond_to(:roles)
18
+ expect(subject.roles.model.name).to eq('SkillTree::Models::Role')
19
+ end
20
+
21
+ it 'has many acl_mappings' do
22
+ expect(subject).to be_respond_to(:acl_mappings)
23
+ expect(subject.acl_mappings.model.name).to eq(
24
+ 'SkillTree::Models::AclMapping')
25
+ end
26
+
27
+ it 'has many acl_ownerships' do
28
+ expect(subject).to be_respond_to(:acl_ownerships)
29
+ expect(subject.acl_ownerships.model.name).to eq(
30
+ 'SkillTree::Models::AclOwnership')
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe SkillTree::Models::Permission, type: :model do
4
+ it 'validates name presence' do
5
+ expect(subject).to have(1).error_on(:name)
6
+ subject.name = 'my_name'
7
+ expect(subject).to have(0).error_on(:name)
8
+ end
9
+
10
+ it 'validates name uniqueness' do
11
+ subject.name = 'my_name'
12
+ expect(subject).to have(0).error_on(:name)
13
+ create(:permission, name: 'my_name')
14
+ expect(subject).to have(1).error_on(:name)
15
+ end
16
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe SkillTree::Models::Role, type: :model do
4
+ it 'validates name presence' do
5
+ expect(subject).to have(1).error_on(:name)
6
+ subject.name = 'my_name'
7
+ expect(subject).to have(0).error_on(:name)
8
+ end
9
+
10
+ it 'validates name uniqueness' do
11
+ create(:role, name: 'my_name')
12
+ subject.name = 'my_name'
13
+ expect(subject).to have(1).error_on(:name)
14
+ end
15
+
16
+ describe '#find_for' do
17
+ before(:each) do
18
+ stub_const('Rails', double(root: double))
19
+ allow(Rails.root).to receive(:join)
20
+ .and_return('spec/support/acls/subject_acl.rb')
21
+
22
+ SkillTree::Parser::Initializer.parse!
23
+ end
24
+ let(:user) { User.create! }
25
+ let(:resource) { Post.create! }
26
+ it 'finds the current role of an user given a resource' do
27
+ expect(role_for(nil, resource)).to eq('guest')
28
+ expect(role_for(user, resource)).to eq('user')
29
+ user.role! :editor, resource
30
+ expect(role_for(user, resource)).to eq('editor')
31
+ user.role! :admin, resource
32
+ expect(role_for(user, resource)).to eq('admin')
33
+ user.unrole! :admin, resource
34
+ expect(role_for(user, resource)).to eq('user')
35
+ end
36
+
37
+ def role_for(user, resource)
38
+ described_class.find_for(user, resource).name
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe SkillTree::Models::UserRole, type: :model do
4
+ it 'users can\'t have duplicate roles for the same resource' do
5
+ user = User.create!
6
+ resource = Post.create!
7
+ role = create(:role, name: 'name')
8
+
9
+ create(:user_role, role: role, user: user, resource: resource)
10
+
11
+ subject.assign_attributes(role: role, user: user, resource: resource)
12
+ expect(subject).to have(1).error_on(:user_id)
13
+ subject.user = nil
14
+ expect(subject).to have(0).error_on(:user_id)
15
+ end
16
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe SkillTree::Parser::Initializer do
4
+ before(:each) do
5
+ stub_const('Rails', double(root: double))
6
+ allow(Rails.root).to receive(:join) { |param| param }
7
+ end
8
+
9
+ describe '#parse!' do
10
+ it 'calls setup' do
11
+ expect_any_instance_of(described_class).to receive(:setup)
12
+ described_class.parse!
13
+ end
14
+ end
15
+
16
+ describe '#setup' do
17
+ let(:roles) { %w(guest user editor admin) }
18
+ let(:more_roles) { %w(guest user editor admin master) }
19
+ let(:permissions) { %w(read create write update delete) }
20
+
21
+ it 'parses the acl file updating the model' do
22
+ subject.setup 'spec/support/acls/acl_example.rb'
23
+ acl = SkillTree::Models::Acl.find_by(name: 'desks')
24
+ expect(acl.roles.count).to eq(4)
25
+ expect(SkillTree::Models::Role.where(name: roles).count).to eq(roles.count)
26
+ expect(acl.roles.count).to eq(4)
27
+ expect(permission(acl, 'guest', 'read')).to be_any
28
+ expect(permission(acl, 'guest', 'write')).not_to be_any
29
+ expect(permission(acl, 'admin', 'destroy')).to be_any
30
+ end
31
+
32
+ it 'parses files additively' do
33
+ subject.setup 'spec/support/acls/acl_example.rb'
34
+ subject.setup 'spec/support/acls/acl_example2.rb'
35
+ acl = SkillTree::Models::Acl.find_by(name: 'desks')
36
+ model_roles = SkillTree::Models::Role.where(name: roles)
37
+ expect(model_roles.count).to eq(roles.count)
38
+ expect(acl.roles.count).to eq(5)
39
+
40
+ expect(permission(acl, 'admin', 'read')).to be_any
41
+ expect(permission(acl, 'master', 'destroy')).to be_any
42
+ expect(permission(acl, 'admin', 'destroy')).not_to be_any
43
+ end
44
+
45
+ describe 'versioning:' do
46
+ before { subject.setup 'spec/support/acls/acl_example3.rb' }
47
+ it 'when the version is identical, changes nothing' do
48
+ expect_any_instance_of(SkillTree::Models::Acl).not_to receive(:save!)
49
+ subject.setup 'spec/support/acls/acl_example3.rb'
50
+ end
51
+ it 'when the version changes, recomputes everything' do
52
+ expect_any_instance_of(SkillTree::Models::Acl).to receive(:save!)
53
+ .and_call_original
54
+ subject.setup 'spec/support/acls/acl_example4.rb'
55
+ expect(SkillTree::Models::Acl.first.version).to eq(2)
56
+ end
57
+ end
58
+ end
59
+
60
+ def permission(acl, role, permission)
61
+ acl = SkillTree::Models::Acl.find_by(name: acl) if acl.is_a? String
62
+ role = SkillTree::Models::Role.find_by(name: role) if role.is_a? String
63
+ permission = SkillTree::Models::Permission
64
+ .find_by(name: permission) if permission.is_a? String
65
+ SkillTree::Models::AclMapping.where(
66
+ role: role,
67
+ acl: acl,
68
+ permission: permission
69
+ )
70
+ end
71
+ end
@@ -0,0 +1,137 @@
1
+ require 'spec_helper'
2
+
3
+ describe SkillTree::Resource do
4
+ before(:each) do
5
+ stub_const('Rails', double(root: double))
6
+ allow(Rails.root).to receive(:join)
7
+ .and_return('spec/support/acls/subject_acl.rb')
8
+
9
+ SkillTree::Parser::Initializer.parse!
10
+ end
11
+
12
+ let(:private_acl) { SkillTree::Models::Acl.find_by(name: 'private_post') }
13
+ let(:user) { User.create! }
14
+ let(:any_user) { User.create! }
15
+
16
+ subject { Post.create! }
17
+
18
+ it 'has a default acl' do
19
+ expect(subject.acl).not_to be_nil
20
+ expect(subject.acls.first).to eq(subject.acl)
21
+ end
22
+
23
+ it 'assigns no acl if there\'s none' do
24
+ allow(SkillTree::Parser::Initializer).to receive(:default_acl_for)
25
+ .and_return(nil)
26
+ expect(subject.acl).to be_nil
27
+ expect(subject.acl_ownerships).to be_empty
28
+ end
29
+
30
+ it 'sets up admin automatically if defined' do
31
+ expect do
32
+ subject = PostWithAuthor.create!(user: user)
33
+ expect(user).to have_role :admin, subject
34
+ end.to change(SkillTree::Models::UserRole, :count).by(1)
35
+ end
36
+
37
+ it 'can\'t assign a new acl' do
38
+ expect do
39
+ subject.update(acl: private_acl)
40
+ end.to raise_error SkillTree::AclAlreadySet
41
+ end
42
+
43
+ it '#default_permission?' do
44
+ expect(subject).to have_default_permission(:read)
45
+ expect(subject).to have_default_permission(:create)
46
+ expect(subject).not_to have_default_permission(:write)
47
+ expect(subject).to have_default_permission(:read, :guest)
48
+ expect(subject).not_to have_default_permission(:create, :guest)
49
+ end
50
+
51
+ describe '#acl!' do
52
+ it 'can change his acl, keeping all roles' do
53
+ user.role! :editor, subject
54
+ expect(user).to be_allowed_to :write, subject
55
+ expect(user).not_to be_allowed_to :update, subject
56
+ subject.acl! private_acl
57
+ expect(user).to have_role :editor, subject
58
+ expect(user).to be_allowed_to :update, subject
59
+ expect(user).not_to be_allowed_to :destroy, subject
60
+ end
61
+
62
+ it 'accepts both symbols and strings' do
63
+ subject.acl! :private_post
64
+ subject.acl! 'private_post'
65
+ end
66
+ end
67
+
68
+ describe '#where_user_can' do
69
+ it 'selector for guest' do
70
+ expect(Post.where_user_can(nil, :read)).to include(subject)
71
+ expect(Post.where_user_can(nil, :create)).not_to include(subject)
72
+ expect(Post.where_user_can(nil, :write)).not_to include(subject)
73
+ expect(Post.where_user_can(nil, :destroy)).not_to include(subject)
74
+ end
75
+
76
+ it 'selector for logged user' do
77
+ expect(Post.where_user_can(user, :read)).to include(subject)
78
+
79
+ expect(Post.where_user_can(any_user, :read)).to include(subject)
80
+ expect(Post.where_user_can(any_user, :read)).to include(subject)
81
+ expect(Post.where_user_can(any_user, :create)).to include(subject)
82
+ expect(Post.where_user_can(any_user, :write)).not_to include(subject)
83
+ expect(Post.where_user_can(any_user, :destroy)).not_to include(subject)
84
+ end
85
+
86
+ it 'selector for editor' do
87
+ user.role!(:editor, subject)
88
+
89
+ # Bug fix
90
+ expect(Post.where_user_can(any_user, :read)).to include(subject)
91
+ expect(Post.where_user_can(any_user, :destroy)).not_to include(subject)
92
+
93
+ expect(Post.where_user_can(user, :read)).to include(subject)
94
+ expect(Post.where_user_can(user, :create)).to include(subject)
95
+ expect(Post.where_user_can(user, :write)).to include(subject)
96
+ expect(Post.where_user_can(user, :destroy)).not_to include(subject)
97
+ end
98
+
99
+ it 'selector for admin' do
100
+ user.role!(:admin, subject)
101
+ # Bug fix
102
+ expect(Post.where_user_can(any_user, :read)).to include(subject)
103
+ expect(Post.where_user_can(any_user, :destroy)).not_to include(subject)
104
+
105
+ expect(Post.where_user_can(user, :read)).to include(subject)
106
+ expect(Post.where_user_can(user, :create)).to include(subject)
107
+ expect(Post.where_user_can(user, :write)).to include(subject)
108
+ expect(Post.where_user_can(user, :destroy)).to include(subject)
109
+ expect(Post.where_user_can(user, :foo)).not_to include(subject)
110
+ end
111
+ end
112
+
113
+ describe '#permissions_for' do
114
+ it 'lists all permissions for guests when user is nil' do
115
+ expect(permissions_for(nil)).to contain_exactly(:read)
116
+ end
117
+
118
+ it 'lists all permissions for an user' do
119
+ expect(permissions_for(user)).to contain_exactly(:create, :read)
120
+ end
121
+
122
+ it 'lists all permissions for an editor' do
123
+ user.role! :editor, subject
124
+ expect(permissions_for(user)).to contain_exactly(:create, :read, :write)
125
+ end
126
+
127
+ it 'lists all permissions for an admin' do
128
+ user.role! :admin, subject
129
+ expect(permissions_for(user)).to contain_exactly(
130
+ :read, :create, :write, :update, :destroy)
131
+ end
132
+
133
+ def permissions_for(user)
134
+ subject.permissions_for(user).names
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe SkillTree::Subject do
4
+ before(:each) do
5
+ stub_const('Rails', double(root: double))
6
+ allow(Rails.root).to receive(:join)
7
+ .and_return('spec/support/acls/subject_acl.rb')
8
+ SkillTree::Parser::Initializer.parse!
9
+ end
10
+
11
+ let(:current_acl) { SkillTree::Models::Acl.find_by(name: 'posts') }
12
+
13
+ let(:resource) do
14
+ Post.create!(acl: current_acl)
15
+ end
16
+
17
+ subject { User.create! }
18
+
19
+ it '#can?' do
20
+ expect(subject).to be_allowed_to(:read, resource)
21
+ expect(subject).not_to be_allowed_to(:write, resource)
22
+ end
23
+
24
+ describe '#role!' do
25
+ it 'sets a only one role per resource' do
26
+ subject.role! :editor, resource
27
+ expect(subject).to have_role(:editor, resource)
28
+ subject.role! :admin, resource
29
+ expect(subject).not_to have_role(:editor, resource)
30
+ expect(subject).to have_role(:admin, resource)
31
+ end
32
+
33
+ it 'can\'t set a role that does not exist' do
34
+ expect do
35
+ subject.role! :foo, resource
36
+ end.to raise_error ActiveRecord::RecordNotFound
37
+ end
38
+ end
39
+
40
+ describe '#unrole!' do
41
+ it 'unsets a role' do
42
+ subject.role! :editor, resource
43
+ expect(subject).to have_role(:editor, resource)
44
+ subject.unrole! :editor, resource
45
+ expect(subject).not_to have_role(:editor, resource)
46
+ end
47
+
48
+ it 'unsets also a role you don\'t have' do
49
+ expect(subject).not_to have_role(:editor, resource)
50
+ subject.unrole! :editor, resource
51
+ end
52
+
53
+ it 'can\'t unset a role that does not exist' do
54
+ expect do
55
+ subject.unrole! :foo, resource
56
+ end.to raise_error ActiveRecord::RecordNotFound
57
+ end
58
+ end
59
+
60
+ it '#role?' do
61
+ expect(subject).not_to have_role(:admin, resource)
62
+ subject.role! :admin, resource
63
+ expect(subject).to have_role(:admin, resource)
64
+ end
65
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe SkillTree do
4
+ it '#init! calls Parser' do
5
+ expect(SkillTree::Parser::Initializer).to receive(:parse!)
6
+ described_class.init!
7
+ end
8
+ end
@@ -0,0 +1,37 @@
1
+ require 'codeclimate-test-reporter'
2
+ CodeClimate::TestReporter.start
3
+ require 'active_support'
4
+ require 'active_record'
5
+ require 'action_controller'
6
+ require 'skill_tree'
7
+ require 'generators/skill_tree/install_generator'
8
+ require 'rspec'
9
+ require 'rspec/collection_matchers'
10
+ require 'generator_spec'
11
+
12
+ I18n.enforce_available_locales = false
13
+
14
+ Dir[File.join(File.dirname(__FILE__), '..', 'spec/support/*.rb')].each { |f| require f }
15
+
16
+ RSpec::Expectations.configuration.warn_about_potential_false_positives = false
17
+
18
+ RSpec.configure do |config|
19
+ config.before(:suite) do
20
+ ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
21
+ capture_stdout { load 'db/schema.rb' }
22
+ end
23
+
24
+ config.before(:each) do
25
+ load 'support/models.rb'
26
+ end
27
+
28
+ config.include MockControllerTestHelpers, type: :controller
29
+ config.include ModelBuilder, type: :model
30
+
31
+ config.around(:each) do |example|
32
+ ActiveRecord::Base.transaction do
33
+ example.run
34
+ raise ActiveRecord::Rollback
35
+ end
36
+ end
37
+ end