kan 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d21ee95936ea431cd541add761806729598ee02d
4
- data.tar.gz: 3af71fe7b9a1e7b76d1e41955356d7c88fc2220b
3
+ metadata.gz: 4b05a0ee35f96b6d7cf1860821bbb9377ce6865d
4
+ data.tar.gz: 2c95160455b028aeddc5888a39659f61a2fef75e
5
5
  SHA512:
6
- metadata.gz: 51068bae77f496f2c4411540d2a99d5a30dcf12203cec91d5664760fd1e75f52f1c89efe7c65c61f5df428abea926874250a3a68e117a573016572583aa2fe79
7
- data.tar.gz: 168764d5406fceb5806388dff893d52fa5f41ba8c49445540140dfc962afbebe421f0eae5d1c0491654cf0814192346ba0317c941fa105b019dd67244013b14b
6
+ metadata.gz: e2d8dc36dfdfebf3da5f453824cc94b46f478346963d10d1d92c23873045cb2efb94debd260d5192e3233931236d09e80ede00b60e75e61b88b189e34fa55dc1
7
+ data.tar.gz: bc672426e146ad132e7339dd780c171e8da22ce350b70d9fc54a6bd1c1f9a5505faa64f5009c01d20036fab86ab37f19396b034314c2f4bb92d7f98aa76702c9
@@ -1,5 +1,13 @@
1
1
  sudo: false
2
2
  language: ruby
3
+ cache: bundler
3
4
  rvm:
5
+ - 2.1.10
6
+ - 2.2.7
7
+ - 2.3.4
4
8
  - 2.4.1
5
- before_install: gem install bundler -v 1.16.0
9
+ - 2.5.0
10
+
11
+ before_install:
12
+ - gem install bundler -v 1.16.0
13
+ - gem update --system
@@ -0,0 +1,7 @@
1
+ ## HEAD
2
+
3
+ ## v0.2
4
+
5
+ * Add logger support (@valikos) #7
6
+ * Integrate CI (@davydovanton)
7
+ * Implement roles support (@davydovanton)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- kan (0.1.0)
4
+ kan (0.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,6 +1,21 @@
1
1
  # Kan
2
+ [![Build Status](https://travis-ci.org/davydovanton/kan.svg?branch=master)](https://travis-ci.org/davydovanton/kan)
2
3
 
3
- Simple fundctional authorization library for ruby. Inspired by [transproc](https://github.com/solnic/transproc)
4
+ Simple functional authorization library for ruby. Inspired by [transproc](https://github.com/solnic/transproc) and [dry project](http://dry-rb.org)
5
+
6
+ ## Table of context
7
+
8
+ * [Installation](#installation)
9
+ * [Usage](#usage)
10
+ * [Register abilities](#register-abilities)
11
+ * [Check abilities](#check-abilities)
12
+ * [Default ability block](#default-ability-block)
13
+ * [List of abilities](#list-of-abilities)
14
+ * [Roles](#roles)
15
+ * [Dry-auto\_inject](#dry-auto_inject)
16
+ * [Contributing](#contributing)
17
+ * [License](#license)
18
+ * [Code of Conduct](#code-of-conduct)
4
19
 
5
20
  ## Installation
6
21
 
@@ -20,6 +35,8 @@ Or install it yourself as:
20
35
 
21
36
  ## Usage
22
37
 
38
+ ### Register abilities
39
+
23
40
  ```ruby
24
41
  class Post::Abilities
25
42
  include Kan::Abilities
@@ -28,8 +45,11 @@ class Post::Abilities
28
45
  register 'edit' { |user, post| user.id == post.user_id }
29
46
  register 'delete' { |_, _| false }
30
47
  end
48
+ ```
49
+
50
+ Also, you can register more than one ability in one place and use string or symbol keys:
31
51
 
32
- # register more than one ability in one place
52
+ ```ruby
33
53
  class Post::AdminAbilities
34
54
  include Kan::Abilities
35
55
 
@@ -46,38 +66,146 @@ class Comments::Abilities
46
66
  user.id == comment.user_id && comment.created_at < Time.now + TEN_MINUTES
47
67
  end
48
68
  end
69
+ ```
49
70
 
71
+ ### Check abilities
72
+
73
+ ```ruby
50
74
  abilities = Kan::Application.new(
51
75
  post: Post::Abilities.new,
52
- comment: Comments::Abilities.new,
76
+ comment: Comments::Abilities.new
53
77
  )
54
78
 
55
79
  abilities['post.read'].call(current_user, post) # => true
56
80
  abilities['post.delete'].call(current_user, post) # => false
57
-
58
81
  abilities['comment.delete'].call(current_user, post) # => false
82
+ ```
83
+
84
+ #### Default ability block
59
85
 
60
- # Default ability eq `proc { true }`
86
+ By default Kan use `proc { true }` as a default ability block:
87
+
88
+ ```ruby
61
89
  abilities['comment.invalid'].call(current_user, post) # => true
90
+ ```
91
+
92
+ But you can rewrite it
62
93
 
63
- # But you can rewrite default ability block
94
+ ```ruby
64
95
  admin_abilities = Kan::Application.new(
65
- post: Post::AdminAbilities.new(default_ability_block: proc { false}),
96
+ post: Post::AdminAbilities.new(default_ability_block: proc { false }),
66
97
  comment: Comments::Abilities.new,
67
98
  )
68
99
 
69
- admin_abilities['post.delete'].call(current_user, post) # => false
70
- admin_abilities['post.delete'].call(admin_user, post) # => true
100
+ admin_abilities['post.delete'].call(current_user, post) # => false
101
+ admin_abilities['post.delete'].call(admin_user, post) # => true
71
102
  admin_abilities['post.invalid'].call(current_user, post) # => false
103
+ ```
72
104
 
105
+ #### List of abilities
106
+ You can provide array of abilities for each scope and Kan will return `true` if at least one ability return `true`:
107
+
108
+ ```ruby
73
109
  global_abilities = Kan::Application.new(
74
110
  post: [Post::Abilities.new, Post::AdminAbilities.new],
75
- comment: Comments::Abilities.new,
111
+ comment: Comments::Abilities.new
76
112
  )
77
113
 
78
114
  global_abilities['post.edit'].call(current_user, post) # => false
79
- global_abilities['post.edit'].call(owner_user, post) # => true
80
- global_abilities['post.edit'].call(admin_user, post) # => true
115
+ global_abilities['post.edit'].call(owner_user, post) # => true
116
+ global_abilities['post.edit'].call(admin_user, post) # => true
117
+ ```
118
+
119
+ ### Roles
120
+ Kan provide simple role system. For this you need to define role block in each abilities classes:
121
+ ```ruby
122
+ module Post
123
+ class AnonymousAbilities
124
+ include Kan::Abilities
125
+
126
+ role :anonymous do |user, _|
127
+ user.id.nil?
128
+ end
129
+
130
+ register(:read, :edit, :delete) { false }
131
+ end
132
+
133
+ class BaseAbilities
134
+ include Kan::Abilities
135
+
136
+ role :all do |_, _|
137
+ true
138
+ end
139
+
140
+ register(:read) { |_, _| true }
141
+ register(:edit, :delete) { |user, post| false }
142
+ end
143
+
144
+
145
+ class AuthorAbilities
146
+ include Kan::Abilities
147
+
148
+ role :author do |user, post|
149
+ user.id == post.author_id
150
+ end
151
+
152
+ register(:read, :edit) { |_, _| true }
153
+ register(:delete) { |_, _| false }
154
+ end
155
+
156
+ class AdminAbilities
157
+ include Kan::Abilities
158
+
159
+ role :admin do |user, _|
160
+ user.admin?
161
+ end
162
+
163
+ register :read, :edit, :delete { |_, _| true }
164
+ end
165
+ end
166
+ ```
167
+
168
+ After that initialize Kan application object and call it with payload:
169
+ ```ruby
170
+ abilities = Kan::Application.new(
171
+ post: [Post::AnonymousAbilities.new, Post::BaseAbilities.new, Post::AuthorAbilities.new, Post::AdminAbilities.new],
172
+ comment: Comments::Abilities.new
173
+ )
174
+
175
+ abilities['post.read'].call(anonymous, post) # => false
176
+ abilities['post.read'].call(regular, post) # => true
177
+ abilities['post.read'].call(author, post) # => true
178
+ abilities['post.read'].call(admin, post) # => true
179
+
180
+ abilities['post.edit'].call(anonymous, post) # => false
181
+ abilities['post.edit'].call(regular, post) # => false
182
+ abilities['post.edit'].call(author, post) # => true
183
+ abilities['post.edit'].call(admin, post) # => true
184
+
185
+ abilities['post.delete'].call(anonymous, post) # => false
186
+ abilities['post.delete'].call(regular, post) # => false
187
+ abilities['post.delete'].call(author, post) # => false
188
+ abilities['post.delete'].call(admin, post) # => true
189
+ ```
190
+
191
+ ### Logger support
192
+ By default kan support default ruby logger (`Logger.new` class). For setup custom logger you can use `logger` option for each abilities instances:
193
+ ```ruby
194
+ abilities = Kan::Application.new(
195
+ comment: Comments::Abilities.new(logger: MyCustomLogger.new)
196
+ )
197
+ ```
198
+
199
+ And call it from ability block:
200
+ ```ruby
201
+ class AnonymousAbilities
202
+ include Kan::Abilities
203
+
204
+ register(:read, :edit, :delete) do
205
+ logger.info 'Anonymous ability checked'
206
+ false
207
+ end
208
+ end
81
209
  ```
82
210
 
83
211
  ### Dry-auto\_inject
@@ -105,10 +233,18 @@ UpdateOperation.new(ability_checker: ->(*) { false })
105
233
 
106
234
  Bug reports and pull requests are welcome on GitHub at https://github.com/davydovanton/kan. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
107
235
 
236
+ ### How to instal the project
237
+ Just clone repository and call:
238
+
239
+ ```
240
+ $ bundle install
241
+ $ bundle exec rspec
242
+ ```
243
+
108
244
  ## License
109
245
 
110
246
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
111
247
 
112
248
  ## Code of Conduct
113
249
 
114
- Everyone interacting in the Kan project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/kan/blob/master/CODE_OF_CONDUCT.md).
250
+ Everyone interacting in the Kan project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/davydovanton/kan/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile CHANGED
@@ -1,10 +1,12 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
3
 
4
- Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
7
- t.test_files = FileList["test/**/*_test.rb"]
8
- end
4
+ begin
5
+ require 'rspec/core/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
9
8
 
10
- task :default => :test
9
+ task default: :spec
10
+ rescue LoadError
11
+ # no rspec available
12
+ end
data/lib/kan.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'kan/abilities_list'
1
2
  require 'kan/application'
2
3
  require 'kan/abilities'
3
4
  require 'kan/version'
@@ -1,3 +1,5 @@
1
+ require 'logger'
2
+
1
3
  module Kan
2
4
  module Abilities
3
5
  def self.included(base)
@@ -5,11 +7,31 @@ module Kan
5
7
  end
6
8
 
7
9
  module ClassMethods
10
+ DEFAULT_ROLE_NAME = :base
11
+ DEFAULT_ROLE_BLOCK = proc { true }
12
+
8
13
  def register(*abilities, &block)
9
14
  @ability_list ||= {}
10
15
  abilities.each { |ability| @ability_list[ability.to_sym] = block }
11
16
  end
12
17
 
18
+ def role(role_name, &block)
19
+ @role_name = role_name
20
+ @role_block = block
21
+ end
22
+
23
+ def role_name
24
+ @role_name || DEFAULT_ROLE_NAME
25
+ end
26
+
27
+ def role_block
28
+ @role_block || DEFAULT_ROLE_BLOCK
29
+ end
30
+
31
+ def valid_role?(*args)
32
+ role_block.call(*args)
33
+ end
34
+
13
35
  def ability_list
14
36
  @ability_list || {}
15
37
  end
@@ -17,12 +39,16 @@ module Kan
17
39
 
18
40
  DEFAULT_ABILITY_BLOCK = proc { true }
19
41
 
42
+ attr_reader :logger
43
+
20
44
  def initialize(options = {})
21
45
  @options = options
46
+ @logger = @options.fetch(:logger, Logger.new(STDOUT))
22
47
  end
23
48
 
24
49
  def ability(name)
25
- self.class.ability_list[name.to_sym] || @options[:default_ability_block] || DEFAULT_ABILITY_BLOCK
50
+ rule = self.class.ability_list[name.to_sym] || @options[:default_ability_block] || DEFAULT_ABILITY_BLOCK
51
+ lambda { |*args| instance_exec(args, &rule) }
26
52
  end
27
53
  end
28
54
  end
@@ -0,0 +1,14 @@
1
+ module Kan
2
+ class AbilitiesList
3
+ def initialize(name, list)
4
+ @name = name
5
+ @list = list
6
+ end
7
+
8
+ def call(*payload)
9
+ @list
10
+ .select { |abilities| abilities.class.valid_role?(*payload) }
11
+ .any? { |abilities| abilities.ability(@name).call(*payload) }
12
+ end
13
+ end
14
+ end
@@ -6,7 +6,17 @@ module Kan
6
6
 
7
7
  def [](ability)
8
8
  scope, ability_name = ability.split('.')
9
- @scopes[scope.to_sym].ability(ability_name)
9
+
10
+ abilities = Array(@scopes[scope.to_sym])
11
+ raise_scope_error(scope) if abilities.empty?
12
+
13
+ AbilitiesList.new(ability_name, abilities)
14
+ end
15
+
16
+ private
17
+
18
+ def raise_scope_error(scope)
19
+ raise ArgumentError.new("Invalid scope #{scope}")
10
20
  end
11
21
  end
12
22
  end
@@ -1,3 +1,3 @@
1
1
  module Kan
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Davydov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-01-09 00:00:00.000000000 Z
11
+ date: 2018-01-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -76,6 +76,7 @@ files:
76
76
  - ".gitignore"
77
77
  - ".rspec"
78
78
  - ".travis.yml"
79
+ - CHANGELOG.md
79
80
  - CODE_OF_CONDUCT.md
80
81
  - Gemfile
81
82
  - Gemfile.lock
@@ -85,6 +86,7 @@ files:
85
86
  - kan.gemspec
86
87
  - lib/kan.rb
87
88
  - lib/kan/abilities.rb
89
+ - lib/kan/abilities_list.rb
88
90
  - lib/kan/application.rb
89
91
  - lib/kan/version.rb
90
92
  homepage: https://github.com/davydovanton/kan