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 +4 -4
- data/.travis.yml +9 -1
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +1 -1
- data/README.md +149 -13
- data/Rakefile +8 -6
- data/lib/kan.rb +1 -0
- data/lib/kan/abilities.rb +27 -1
- data/lib/kan/abilities_list.rb +14 -0
- data/lib/kan/application.rb +11 -1
- data/lib/kan/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b05a0ee35f96b6d7cf1860821bbb9377ce6865d
|
4
|
+
data.tar.gz: 2c95160455b028aeddc5888a39659f61a2fef75e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e2d8dc36dfdfebf3da5f453824cc94b46f478346963d10d1d92c23873045cb2efb94debd260d5192e3233931236d09e80ede00b60e75e61b88b189e34fa55dc1
|
7
|
+
data.tar.gz: bc672426e146ad132e7339dd780c171e8da22ce350b70d9fc54a6bd1c1f9a5505faa64f5009c01d20036fab86ab37f19396b034314c2f4bb92d7f98aa76702c9
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
data/Gemfile.lock
CHANGED
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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)
|
70
|
-
admin_abilities['post.delete'].call(admin_user, post)
|
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)
|
80
|
-
global_abilities['post.edit'].call(admin_user, post)
|
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/
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
end
|
4
|
+
begin
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
9
8
|
|
10
|
-
task :
|
9
|
+
task default: :spec
|
10
|
+
rescue LoadError
|
11
|
+
# no rspec available
|
12
|
+
end
|
data/lib/kan.rb
CHANGED
data/lib/kan/abilities.rb
CHANGED
@@ -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
|
data/lib/kan/application.rb
CHANGED
@@ -6,7 +6,17 @@ module Kan
|
|
6
6
|
|
7
7
|
def [](ability)
|
8
8
|
scope, ability_name = ability.split('.')
|
9
|
-
|
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
|
data/lib/kan/version.rb
CHANGED
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.
|
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-
|
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
|