picky_guard 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +87 -0
- data/.rspec +1 -0
- data/.rubocop.yml +16 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +59 -0
- data/README.md +285 -0
- data/Rakefile +4 -0
- data/bin/console +16 -0
- data/bin/setup +8 -0
- data/db/schema.rb +12 -0
- data/lib/generators/picky_guard/install_generator.rb +18 -0
- data/lib/generators/picky_guard/policy_generator.rb +49 -0
- data/lib/generators/picky_guard/templates/ability.rb +9 -0
- data/lib/generators/picky_guard/templates/policy.rb.erb +22 -0
- data/lib/generators/picky_guard/templates/resource_actions.rb +13 -0
- data/lib/generators/picky_guard/templates/role_policies.rb +10 -0
- data/lib/generators/picky_guard/templates/user_role_checker.rb +9 -0
- data/lib/picky_guard/loader.rb +88 -0
- data/lib/picky_guard/path_helper.rb +17 -0
- data/lib/picky_guard/policy.rb +45 -0
- data/lib/picky_guard/resource_actions.rb +28 -0
- data/lib/picky_guard/role_policies.rb +31 -0
- data/lib/picky_guard/statement.rb +31 -0
- data/lib/picky_guard/statement_proxy.rb +63 -0
- data/lib/picky_guard/user_role_checker.rb +9 -0
- data/lib/picky_guard/validator.rb +89 -0
- data/lib/picky_guard/version.rb +5 -0
- data/lib/picky_guard.rb +7 -0
- data/picky_guard.gemspec +31 -0
- metadata +158 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: dae068b49826c41bc05bdfcc15ddb02cc31fe69c
|
|
4
|
+
data.tar.gz: 828592f4204f3cde8748ac1be0e61a56a4466525
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 68bc8fbca4bd9931c65c2dfd5b5c1a08e83450b6f4974bdd5d132c3e57e7b45b3f8088d0ebc0225a507231dcdb278157d931495a036cee65206d29f7d33bb7fb
|
|
7
|
+
data.tar.gz: 800cba86587b463f310444fe3b15d9ea24bb28ccebc297a57abc1b72dc9c444c7648cf2df97bb3d9da62f032c8bac70eb3e47a87bcdd232cc87c51fe9783ef5b
|
data/.gitignore
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/.bundle/
|
|
2
|
+
/.yardoc
|
|
3
|
+
/_yardoc/
|
|
4
|
+
/coverage/
|
|
5
|
+
/doc/
|
|
6
|
+
/pkg/
|
|
7
|
+
/spec/reports/
|
|
8
|
+
/tmp/
|
|
9
|
+
/.idea/
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Created by https://www.gitignore.io/api/rubymine
|
|
13
|
+
|
|
14
|
+
### RubyMine ###
|
|
15
|
+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
|
16
|
+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
|
17
|
+
|
|
18
|
+
# User-specific stuff:
|
|
19
|
+
.idea/**/workspace.xml
|
|
20
|
+
.idea/**/tasks.xml
|
|
21
|
+
.idea/dictionaries
|
|
22
|
+
|
|
23
|
+
# Sensitive or high-churn files:
|
|
24
|
+
.idea/**/dataSources/
|
|
25
|
+
.idea/**/dataSources.ids
|
|
26
|
+
.idea/**/dataSources.xml
|
|
27
|
+
.idea/**/dataSources.local.xml
|
|
28
|
+
.idea/**/sqlDataSources.xml
|
|
29
|
+
.idea/**/dynamic.xml
|
|
30
|
+
.idea/**/uiDesigner.xml
|
|
31
|
+
|
|
32
|
+
# Gradle:
|
|
33
|
+
.idea/**/gradle.xml
|
|
34
|
+
.idea/**/libraries
|
|
35
|
+
|
|
36
|
+
# CMake
|
|
37
|
+
cmake-build-debug/
|
|
38
|
+
|
|
39
|
+
# Mongo Explorer plugin:
|
|
40
|
+
.idea/**/mongoSettings.xml
|
|
41
|
+
|
|
42
|
+
## File-based project format:
|
|
43
|
+
*.iws
|
|
44
|
+
|
|
45
|
+
## Plugin-specific files:
|
|
46
|
+
|
|
47
|
+
# IntelliJ
|
|
48
|
+
/out/
|
|
49
|
+
|
|
50
|
+
# mpeltonen/sbt-idea plugin
|
|
51
|
+
.idea_modules/
|
|
52
|
+
|
|
53
|
+
# JIRA plugin
|
|
54
|
+
atlassian-ide-plugin.xml
|
|
55
|
+
|
|
56
|
+
# Cursive Clojure plugin
|
|
57
|
+
.idea/replstate.xml
|
|
58
|
+
|
|
59
|
+
# Ruby plugin and RubyMine
|
|
60
|
+
/.rakeTasks
|
|
61
|
+
|
|
62
|
+
# Crashlytics plugin (for Android Studio and IntelliJ)
|
|
63
|
+
com_crashlytics_export_strings.xml
|
|
64
|
+
crashlytics.properties
|
|
65
|
+
crashlytics-build.properties
|
|
66
|
+
fabric.properties
|
|
67
|
+
|
|
68
|
+
### RubyMine Patch ###
|
|
69
|
+
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
|
70
|
+
|
|
71
|
+
# *.iml
|
|
72
|
+
# modules.xml
|
|
73
|
+
# .idea/misc.xml
|
|
74
|
+
# *.ipr
|
|
75
|
+
|
|
76
|
+
# Sonarlint plugin
|
|
77
|
+
.idea/sonarlint
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# End of https://www.gitignore.io/api/rubymine
|
|
81
|
+
|
|
82
|
+
# Created by https://www.gitignore.io/api/idea
|
|
83
|
+
|
|
84
|
+
#!! ERROR: idea is undefined. Use list command to see defined gitignore types !!#
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# End of https://www.gitignore.io/api/idea
|
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Documentation:
|
|
2
|
+
Enabled: false
|
|
3
|
+
Metrics/LineLength:
|
|
4
|
+
Max: 100
|
|
5
|
+
Metrics/BlockLength:
|
|
6
|
+
Exclude:
|
|
7
|
+
- "**/*_spec.rb"
|
|
8
|
+
Metrics/ModuleLength:
|
|
9
|
+
Exclude:
|
|
10
|
+
- "**/*_spec.rb"
|
|
11
|
+
Metrics/MethodLength:
|
|
12
|
+
Max: 5
|
|
13
|
+
Exclude:
|
|
14
|
+
- "**/*_spec.rb"
|
|
15
|
+
Metrics/ParameterLists:
|
|
16
|
+
Max: 4
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.4.1
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
picky_guard (0.1.0)
|
|
5
|
+
activerecord
|
|
6
|
+
cancancan
|
|
7
|
+
|
|
8
|
+
GEM
|
|
9
|
+
remote: https://rubygems.org/
|
|
10
|
+
specs:
|
|
11
|
+
activemodel (5.2.0)
|
|
12
|
+
activesupport (= 5.2.0)
|
|
13
|
+
activerecord (5.2.0)
|
|
14
|
+
activemodel (= 5.2.0)
|
|
15
|
+
activesupport (= 5.2.0)
|
|
16
|
+
arel (>= 9.0)
|
|
17
|
+
activesupport (5.2.0)
|
|
18
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
19
|
+
i18n (>= 0.7, < 2)
|
|
20
|
+
minitest (~> 5.1)
|
|
21
|
+
tzinfo (~> 1.1)
|
|
22
|
+
arel (9.0.0)
|
|
23
|
+
cancancan (2.2.0)
|
|
24
|
+
concurrent-ruby (1.0.5)
|
|
25
|
+
diff-lcs (1.3)
|
|
26
|
+
i18n (1.0.0)
|
|
27
|
+
concurrent-ruby (~> 1.0)
|
|
28
|
+
minitest (5.11.3)
|
|
29
|
+
rake (10.5.0)
|
|
30
|
+
rspec (3.7.0)
|
|
31
|
+
rspec-core (~> 3.7.0)
|
|
32
|
+
rspec-expectations (~> 3.7.0)
|
|
33
|
+
rspec-mocks (~> 3.7.0)
|
|
34
|
+
rspec-core (3.7.1)
|
|
35
|
+
rspec-support (~> 3.7.0)
|
|
36
|
+
rspec-expectations (3.7.0)
|
|
37
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
38
|
+
rspec-support (~> 3.7.0)
|
|
39
|
+
rspec-mocks (3.7.0)
|
|
40
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
41
|
+
rspec-support (~> 3.7.0)
|
|
42
|
+
rspec-support (3.7.1)
|
|
43
|
+
sqlite3 (1.3.13)
|
|
44
|
+
thread_safe (0.3.6)
|
|
45
|
+
tzinfo (1.2.5)
|
|
46
|
+
thread_safe (~> 0.1)
|
|
47
|
+
|
|
48
|
+
PLATFORMS
|
|
49
|
+
ruby
|
|
50
|
+
|
|
51
|
+
DEPENDENCIES
|
|
52
|
+
bundler (~> 1.16)
|
|
53
|
+
picky_guard!
|
|
54
|
+
rake (~> 10.0)
|
|
55
|
+
rspec (~> 3.2)
|
|
56
|
+
sqlite3
|
|
57
|
+
|
|
58
|
+
BUNDLED WITH
|
|
59
|
+
1.16.1
|
data/README.md
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# PickyGuard
|
|
2
|
+
|
|
3
|
+
PickyGuard is an opinionated authorization library which wraps [CanCanCan](https://github.com/CanCanCommunity/cancancan).
|
|
4
|
+
|
|
5
|
+
This library helps to write authorization policies in an opinionated hierarchy.
|
|
6
|
+
|
|
7
|
+
Briefly,
|
|
8
|
+
* **User** has many **roles**.
|
|
9
|
+
* Each **role** has many **policies**.
|
|
10
|
+
* Each **policy** has many **statements** describing which **actions** has what **effect** on which **resources**.
|
|
11
|
+
|
|
12
|
+
For example,
|
|
13
|
+
* User `Paul` has a role named `CampaignManager`.
|
|
14
|
+
* The role `CampaignManager` has a policy named `crud_all_campaigns`.
|
|
15
|
+
* The policy `crud_all_campaigns` means,
|
|
16
|
+
* Actions : `[:read, :update, :create, :delete]` are
|
|
17
|
+
* Effect : `Allow`ed
|
|
18
|
+
* Resources : for `All campaigns under user's company`.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
Add this line to your application's Gemfile:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
gem 'picky_guard'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
And then execute:
|
|
29
|
+
|
|
30
|
+
$ bundle
|
|
31
|
+
|
|
32
|
+
Or install it yourself as:
|
|
33
|
+
|
|
34
|
+
$ gem install picky_guard
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
To generate initial files, execute:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
$ rails generate picky_guard:install
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This will create the following files:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
app/
|
|
48
|
+
- models/
|
|
49
|
+
- ability.rb
|
|
50
|
+
- picky_guard/
|
|
51
|
+
- role_policies.rb
|
|
52
|
+
- resource_actions.rb
|
|
53
|
+
- user_role_checker.rb
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Generated Files
|
|
57
|
+
|
|
58
|
+
### ability.rb
|
|
59
|
+
|
|
60
|
+
The generated file is like this:
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
class Ability < PickyGuard::Loader
|
|
64
|
+
def initialize(user)
|
|
65
|
+
adjust(user, UserRoleChecker, ResourceActions, RolePolicies)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Normally, you don't have to do anything about it.
|
|
71
|
+
|
|
72
|
+
### user_role_checker.rb
|
|
73
|
+
|
|
74
|
+
The generated file is like this:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
class UserRoleChecker < PickyGuard::UserRoleChecker
|
|
78
|
+
def self.check(user, role)
|
|
79
|
+
# ...
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This class defines the way to check if user has specific role. It assumes some roles already have been given to the user somehow.
|
|
85
|
+
|
|
86
|
+
You can implement this on your own, or if you're using a gem like [rolify](https://github.com/RolifyCommunity/rolify), then it should be like this:
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
class UserRoleChecker < PickyGuard::UserRoleChecker
|
|
90
|
+
def self.check(user, role)
|
|
91
|
+
user.has_role? role
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### resource_actions.rb
|
|
97
|
+
|
|
98
|
+
The generated file is like this:
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
class ResourceActions < PickyGuard::ResourceActions
|
|
102
|
+
def initialize
|
|
103
|
+
map(Report, [:create, :read, :update, :delete])
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
This class defines which resource can have which actions. Actions can be an array of either `String` or `Symbol`.
|
|
109
|
+
|
|
110
|
+
By defining this, you can explicitly manage list of actions per resource and filter out unexpected and unknown actions.
|
|
111
|
+
|
|
112
|
+
### role_policies.rb
|
|
113
|
+
|
|
114
|
+
The generated file is like this:
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
class RolePolicies < PickyGuard::RolePolicies
|
|
118
|
+
def initialize
|
|
119
|
+
map(:role_report_manager, [ManageAllReports])
|
|
120
|
+
# map(:role_report_reader, [AnotherPolicy])
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
This class defines which role has which policies. From the example code above, we could assume there is a role named `:role_report_manager` and it has one policy named `ManageAllReports`.
|
|
126
|
+
|
|
127
|
+
Then how do we define policy?
|
|
128
|
+
|
|
129
|
+
## Defining Policies
|
|
130
|
+
|
|
131
|
+
To generate new policy, execute this:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
$ rails generate picky_guard:policy manage_all_reports
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
From the command line, name should be underscored. Otherwise, it will raise an error.
|
|
138
|
+
|
|
139
|
+
Once created, you will find the policy file under `app/picky_guard/policies/`.
|
|
140
|
+
|
|
141
|
+
If you get to have many policies, you can group them into a folder like this:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
$ rails generate picky_guard:policy reports/manage_all_reports
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Then it will generate `app/picky_guard/policies/reports/manage_all_reports.rb`.
|
|
148
|
+
|
|
149
|
+
The generated file is like this:
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
class ManageAllReports < PickyGuard::Policy
|
|
153
|
+
def initialize(current_user)
|
|
154
|
+
statement_for Campaign do
|
|
155
|
+
allow
|
|
156
|
+
actions [:create]
|
|
157
|
+
conditions({})
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
statement_for Campaign do
|
|
161
|
+
allow
|
|
162
|
+
actions [:create]
|
|
163
|
+
class_resource
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`register` method takes a parameter and a block.
|
|
170
|
+
* The parameter is a resource class. It should extend `ActiveRecord::Base`.
|
|
171
|
+
* The block consists of simple DSL, describing the statement.
|
|
172
|
+
|
|
173
|
+
### Building Statement
|
|
174
|
+
|
|
175
|
+
There are two types of resources: `instance resource` and `class resource`.
|
|
176
|
+
|
|
177
|
+
```ruby
|
|
178
|
+
can? :read, Campaign.first # Checking permission against an instance resource
|
|
179
|
+
|
|
180
|
+
can? :create, Campaign # Checking permission against a class resource
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Instance Resource
|
|
184
|
+
|
|
185
|
+
In case of `instance resource`, we need
|
|
186
|
+
* effect(`allow` or `deny`)
|
|
187
|
+
* actions
|
|
188
|
+
* conditions
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
statement_for Campaign do # Instances of `Campaign` are the resources.
|
|
192
|
+
allow # Possibly `deny` instead of `allow`. If omitted, it's `allow` by default.
|
|
193
|
+
actions [:create] # Array of `string` or `symbol`.
|
|
194
|
+
instance_resource # If omitted, it's an instance resource by default.
|
|
195
|
+
conditions({})
|
|
196
|
+
end
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
In a short way,
|
|
200
|
+
|
|
201
|
+
```ruby
|
|
202
|
+
statement_for Campaign do
|
|
203
|
+
actions [:create]
|
|
204
|
+
conditions({})
|
|
205
|
+
end
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Class Resource
|
|
209
|
+
|
|
210
|
+
In case of `class resource`, we need
|
|
211
|
+
* effect
|
|
212
|
+
* actions
|
|
213
|
+
* class_resource
|
|
214
|
+
|
|
215
|
+
```ruby
|
|
216
|
+
statement_for Campaign do # `Campaign` is the resource.
|
|
217
|
+
allow # Possibly `deny` instead of `allow`. If omitted, it's `allow` by default.
|
|
218
|
+
actions [:create] # Array of `string` or `symbol`.
|
|
219
|
+
class_resource # You need this explicit declaration when it comes to a class resource.
|
|
220
|
+
end
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
You cannot specify any conditions on class resource.
|
|
224
|
+
|
|
225
|
+
In a short way,
|
|
226
|
+
|
|
227
|
+
```ruby
|
|
228
|
+
statement_for Campaign do
|
|
229
|
+
actions [:create]
|
|
230
|
+
class_resource
|
|
231
|
+
end
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### `conditions` on instance resource
|
|
235
|
+
|
|
236
|
+
`conditions` is a hash. This is directly used to query database, so it should be real database column names. You can refer to `Hash of Conditions` section from [Defining Abilities - CanCanCan](https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities#hash-of-conditions).
|
|
237
|
+
|
|
238
|
+
When things are too complicated and it's hard to express it a hash, then there's a little detour.
|
|
239
|
+
|
|
240
|
+
```ruby
|
|
241
|
+
ids = extract_campaign_ids_somehow
|
|
242
|
+
statement_for Campaign do
|
|
243
|
+
actions [:create]
|
|
244
|
+
conditions({ id: ids })
|
|
245
|
+
end
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
First, you can extract ids or other values through some complicated business logic of yours. Then, pass it to conditions like the above.
|
|
249
|
+
|
|
250
|
+
However we can make this better by wrapping the conditions with `proc`. This enables lazy-loading.
|
|
251
|
+
|
|
252
|
+
```ruby
|
|
253
|
+
statement_for Campaign do
|
|
254
|
+
actions [:create]
|
|
255
|
+
conditions(proc {
|
|
256
|
+
ids = extract_campaign_ids_somehow
|
|
257
|
+
|
|
258
|
+
{ id: ids }
|
|
259
|
+
})
|
|
260
|
+
end
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
So basically this `conditions` method takes `a hash` or `a proc returning a hash` as a parameter.
|
|
264
|
+
|
|
265
|
+
## Using `Ability`
|
|
266
|
+
|
|
267
|
+
You can use `Ability` class just as you did with `CanCanCan`. The constructor takes one parameter: `user`.
|
|
268
|
+
|
|
269
|
+
With `PickyGuard`, you can pass optional second parameter which is `resource`.
|
|
270
|
+
|
|
271
|
+
```ruby
|
|
272
|
+
Ability.new(user, Campaign).can? :read, Campaign.first
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
This will load only relevant policies.
|
|
276
|
+
|
|
277
|
+
## Development
|
|
278
|
+
|
|
279
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
280
|
+
|
|
281
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
282
|
+
|
|
283
|
+
## Contributing
|
|
284
|
+
|
|
285
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/eunjae-lee/picky_guard.
|
data/Rakefile
ADDED
data/bin/console
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
require 'bundler/setup'
|
|
6
|
+
require 'picky_guard'
|
|
7
|
+
|
|
8
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
9
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
10
|
+
|
|
11
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
12
|
+
# require "pry"
|
|
13
|
+
# Pry.start
|
|
14
|
+
|
|
15
|
+
require 'irb'
|
|
16
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/db/schema.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PickyGuard
|
|
4
|
+
module Generators
|
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
7
|
+
|
|
8
|
+
# rubocop:disable Metrics/LineLength
|
|
9
|
+
def generate_install
|
|
10
|
+
copy_file 'ability.rb', 'app/models/ability.rb'
|
|
11
|
+
copy_file 'role_policies.rb', 'app/picky_guard/role_policies.rb'
|
|
12
|
+
copy_file 'resource_actions.rb', 'app/picky_guard/resource_actions.rb'
|
|
13
|
+
copy_file 'user_role_checker.rb', 'app/picky_guard/user_role_checker.rb'
|
|
14
|
+
end
|
|
15
|
+
# rubocop:enable Metrics/LineLength
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PickyGuard
|
|
4
|
+
module Generators
|
|
5
|
+
class PolicyGenerator < Rails::Generators::NamedBase
|
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
7
|
+
|
|
8
|
+
desc 'Generates a policy with the given NAME'
|
|
9
|
+
|
|
10
|
+
def generate_policy
|
|
11
|
+
return unless validate_name(name)
|
|
12
|
+
create_file dest_path(name), content(name)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def dest_path(name)
|
|
18
|
+
"app/picky_guard/policies/#{name}.rb"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def content(name)
|
|
22
|
+
class_name = class_name(name)
|
|
23
|
+
puts "class_name : #{class_name}"
|
|
24
|
+
path = File.join(File.expand_path('../templates', __FILE__), 'policy.rb.erb')
|
|
25
|
+
ERB.new(File.read(path)).result binding
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def class_name(name)
|
|
29
|
+
name.split('/').last.camelize
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def validate_name(name)
|
|
33
|
+
return true if underscored?(name)
|
|
34
|
+
puts_name_requirement(name)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def puts_name_requirement(name)
|
|
38
|
+
puts ''
|
|
39
|
+
puts 'Name should be underscored'
|
|
40
|
+
puts "Expected : #{name.underscore}"
|
|
41
|
+
puts "Actual : #{name}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def underscored?(name)
|
|
45
|
+
name.underscore == name
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'picky_guard/policy'
|
|
4
|
+
|
|
5
|
+
class <%= class_name %> < PickyGuard::Policy
|
|
6
|
+
def initialize(current_user)
|
|
7
|
+
# register(App, proc {
|
|
8
|
+
# PickyGuard::StatementBuilder.new
|
|
9
|
+
# .allow
|
|
10
|
+
# .actions(%w[Create Read Update Delete])
|
|
11
|
+
# .resource(App)
|
|
12
|
+
# .conditions({})
|
|
13
|
+
# .build
|
|
14
|
+
# })
|
|
15
|
+
|
|
16
|
+
# register(Campaign, PickyGuard::StatementBuilder.new
|
|
17
|
+
# .allow
|
|
18
|
+
# .actions(%w[Create])
|
|
19
|
+
# .class_resource(Campaign)
|
|
20
|
+
# .build)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'picky_guard/resource_actions'
|
|
4
|
+
|
|
5
|
+
class ResourceActions < PickyGuard::ResourceActions
|
|
6
|
+
def initialize
|
|
7
|
+
# map(Report, %w[Create Read Update Delete])
|
|
8
|
+
|
|
9
|
+
# [App, Campaign].each do |resource|
|
|
10
|
+
# map(resource, %w[Create Read Update])
|
|
11
|
+
# end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cancancan'
|
|
4
|
+
|
|
5
|
+
module PickyGuard
|
|
6
|
+
class Loader
|
|
7
|
+
include CanCan::Ability
|
|
8
|
+
|
|
9
|
+
def initialize(user, *resources_whitelist)
|
|
10
|
+
@resources_whitelist = resources_whitelist
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def adjust(user, user_role_checker_class, resource_actions_class, role_policies_class)
|
|
14
|
+
validate_parameters(user_role_checker_class, resource_actions_class, role_policies_class)
|
|
15
|
+
policies = gather_policies(user, user_role_checker_class, role_policies_class.new)
|
|
16
|
+
statements = gather_statements(user, policies, resource_actions_class.new)
|
|
17
|
+
adjust_statements(statements)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def validate_parameters(user_role_checker_class, resource_actions_class, role_policies_class)
|
|
23
|
+
raise ArgumentError unless user_role_checker_class < PickyGuard::UserRoleChecker
|
|
24
|
+
raise ArgumentError unless role_policies_class < PickyGuard::RolePolicies
|
|
25
|
+
raise ArgumentError unless resource_actions_class < PickyGuard::ResourceActions
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def adjust_statements(statements)
|
|
29
|
+
statements.each do |statement|
|
|
30
|
+
adjust_statement(statement)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def adjust_statement(statement)
|
|
35
|
+
statement.actions.each do |action|
|
|
36
|
+
rule = build_rule(action, statement)
|
|
37
|
+
add_rule(rule)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def build_rule(action, statement)
|
|
42
|
+
conditions = eval_conditions_if_needed(statement)
|
|
43
|
+
CanCan::Rule.new(positive?(statement.effect), action, statement.resource, conditions, nil)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def eval_conditions_if_needed(statement)
|
|
47
|
+
if statement.conditions.is_a? Proc
|
|
48
|
+
statement.conditions.call
|
|
49
|
+
else
|
|
50
|
+
statement.conditions
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def positive?(effect)
|
|
55
|
+
effect == Statement::EFFECT_ALLOW
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def gather_policies(user, user_role_checker_class, role_policies)
|
|
59
|
+
role_policies.roles
|
|
60
|
+
.select { |role| user_role_checker_class.check(user, role) }
|
|
61
|
+
.map { |role| role_policies.policies_for(role) }
|
|
62
|
+
.flatten
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def gather_statements(user, policies, resource_actions)
|
|
66
|
+
policies.map { |policy_class| policy_class.new(user) }
|
|
67
|
+
.map do |policy|
|
|
68
|
+
statements = policy.statements(@resources_whitelist)
|
|
69
|
+
validate_statements!(resource_actions, statements)
|
|
70
|
+
end.flatten
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def validate_statements!(resource_actions, statements)
|
|
74
|
+
statements.each do |statement|
|
|
75
|
+
validate_statement!(resource_actions, statement)
|
|
76
|
+
end
|
|
77
|
+
statements
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def validate_statement!(resource_actions, statement)
|
|
81
|
+
statement.actions.each do |action|
|
|
82
|
+
valid = resource_actions.action_exist?(statement.resource, action)
|
|
83
|
+
raise 'Unknown action!' unless valid
|
|
84
|
+
end
|
|
85
|
+
statement
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PickyGuard
|
|
4
|
+
class PathHelper
|
|
5
|
+
def self.user_project_path
|
|
6
|
+
Dir.pwd
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.lib_path
|
|
10
|
+
File.expand_path(File.join(project_root, 'lib'))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.project_root
|
|
14
|
+
File.expand_path(File.join(File.dirname(__FILE__), '../..'))
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'picky_guard/validator'
|
|
4
|
+
require 'picky_guard/statement_proxy'
|
|
5
|
+
|
|
6
|
+
module PickyGuard
|
|
7
|
+
class Policy
|
|
8
|
+
def initialize(current_user)
|
|
9
|
+
# do nothing here
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def statements(resource_whitelist)
|
|
13
|
+
@cached_statements ||= gather_statements(resource_whitelist)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def statement_for(resource, &statement_definition)
|
|
17
|
+
proxy = StatementProxy.new(resource)
|
|
18
|
+
proxy.instance_eval(&statement_definition)
|
|
19
|
+
register(resource, proxy.build)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def register(resource, statement)
|
|
25
|
+
safe_array << [resource, statement]
|
|
26
|
+
@cached_statements = nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def gather_statements(resource_whitelist)
|
|
30
|
+
filtered_array(resource_whitelist).map do |_resource, statement|
|
|
31
|
+
Validator.validate_statement!(statement)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def filtered_array(resource_whitelist)
|
|
36
|
+
return safe_array if resource_whitelist.empty?
|
|
37
|
+
|
|
38
|
+
safe_array.select { |item| resource_whitelist.include? item[0] }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def safe_array
|
|
42
|
+
(@statements ||= [])
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'picky_guard/validator'
|
|
4
|
+
|
|
5
|
+
module PickyGuard
|
|
6
|
+
class ResourceActions
|
|
7
|
+
def map(resource, actions)
|
|
8
|
+
validate_parameters(actions, resource)
|
|
9
|
+
safe_hash[resource] = actions
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def action_exist?(resource, action)
|
|
13
|
+
raise 'Unknown resource!' if safe_hash[resource].nil?
|
|
14
|
+
safe_hash[resource].include? action
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def safe_hash
|
|
20
|
+
(@map ||= {})
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def validate_parameters(actions, resource)
|
|
24
|
+
Validator.validate_resource_class!(resource)
|
|
25
|
+
Validator.validate_all_actions!(actions)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'picky_guard/validator'
|
|
4
|
+
|
|
5
|
+
module PickyGuard
|
|
6
|
+
class RolePolicies
|
|
7
|
+
def map(role, policies)
|
|
8
|
+
validate_parameters(policies, role)
|
|
9
|
+
safe_map[role] = policies
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def roles
|
|
13
|
+
safe_map.keys
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def policies_for(role)
|
|
17
|
+
safe_map[role]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def safe_map
|
|
23
|
+
(@map ||= {})
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def validate_parameters(policies, role)
|
|
27
|
+
Validator.validate_all_policy_classes!(policies)
|
|
28
|
+
Validator.validate_role!(role)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'picky_guard/validator'
|
|
4
|
+
|
|
5
|
+
module PickyGuard
|
|
6
|
+
class Statement
|
|
7
|
+
attr_reader :effect, :actions, :resource, :conditions
|
|
8
|
+
|
|
9
|
+
EFFECT_ALLOW = :allow
|
|
10
|
+
EFFECT_DENY = :deny
|
|
11
|
+
EFFECTS = [EFFECT_ALLOW, EFFECT_DENY].freeze
|
|
12
|
+
|
|
13
|
+
RESOURCE_TYPE_INSTANCE = :instance
|
|
14
|
+
RESOURCE_TYPE_CLASS = :class
|
|
15
|
+
|
|
16
|
+
def initialize(effect, actions, resource, conditions)
|
|
17
|
+
@effect = effect
|
|
18
|
+
@actions = actions
|
|
19
|
+
@resource = resource
|
|
20
|
+
@conditions = conditions
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def allow?
|
|
24
|
+
@effect == EFFECT_ALLOW
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def deny?
|
|
28
|
+
@effect == EFFECT_DENY
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'picky_guard/statement'
|
|
4
|
+
|
|
5
|
+
module PickyGuard
|
|
6
|
+
class StatementProxy
|
|
7
|
+
def initialize(resource)
|
|
8
|
+
@resource = resource
|
|
9
|
+
allow
|
|
10
|
+
instance_resource
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def allow
|
|
14
|
+
@effect = PickyGuard::Statement::EFFECT_ALLOW
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def deny
|
|
18
|
+
@effect = PickyGuard::Statement::EFFECT_DENY
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def actions(actions)
|
|
22
|
+
@actions = actions
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def conditions(conditions)
|
|
26
|
+
@conditions = conditions
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def instance_resource
|
|
30
|
+
@resource_type = PickyGuard::Statement::RESOURCE_TYPE_INSTANCE
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def class_resource
|
|
34
|
+
@resource_type = PickyGuard::Statement::RESOURCE_TYPE_CLASS
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def validate!
|
|
38
|
+
Validator.validate_effect!(@effect)
|
|
39
|
+
Validator.validate_all_actions!(@actions)
|
|
40
|
+
Validator.validate_resource_class!(@resource)
|
|
41
|
+
Validator.validate_conditions!(@conditions) if instance_resource?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def build
|
|
45
|
+
validate!
|
|
46
|
+
build_statement
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def build_statement
|
|
52
|
+
PickyGuard::Statement.new(@effect, @actions, @resource, conditions_or_nil)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def conditions_or_nil
|
|
56
|
+
@conditions if instance_resource?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def instance_resource?
|
|
60
|
+
@resource_type == PickyGuard::Statement::RESOURCE_TYPE_INSTANCE
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'picky_guard/statement'
|
|
4
|
+
require 'picky_guard/policy'
|
|
5
|
+
|
|
6
|
+
module PickyGuard
|
|
7
|
+
class Validator
|
|
8
|
+
def self.valid_resource_class?(resource)
|
|
9
|
+
child_and_parent?(resource, ActiveRecord::Base)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.valid_role?(role)
|
|
13
|
+
role.is_a?(Symbol) || role.is_a?(String)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.valid_statement?(statement)
|
|
17
|
+
statement.is_a? PickyGuard::Statement
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.valid_action?(action)
|
|
21
|
+
action.is_a?(Symbol) || action.is_a?(String)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.all_valid_actions?(actions)
|
|
25
|
+
return false unless actions.is_a? Array
|
|
26
|
+
actions.all? do |action|
|
|
27
|
+
valid_action?(action)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.valid_policy?(policy)
|
|
32
|
+
child_and_parent?(policy, PickyGuard::Policy)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.all_valid_policy_classes?(policies)
|
|
36
|
+
return false unless policies.is_a? Array
|
|
37
|
+
policies.all? do |policy|
|
|
38
|
+
valid_policy?(policy)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.valid_conditions?(conditions)
|
|
43
|
+
conditions.instance_of?(Hash) || conditions.instance_of?(Proc)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.child_and_parent?(child_class, parent_class)
|
|
47
|
+
child_class < parent_class
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.valid_effect?(effect)
|
|
51
|
+
PickyGuard::Statement::EFFECTS.include? effect
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.validate_statement!(statement)
|
|
55
|
+
raise ArgumentError, 'Invalid Statement' unless Validator.valid_statement?(statement)
|
|
56
|
+
statement
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.validate_resource_class!(resource)
|
|
60
|
+
raise ArgumentError, 'Invalid Resource' unless Validator.valid_resource_class?(resource)
|
|
61
|
+
resource
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.validate_all_actions!(actions)
|
|
65
|
+
raise ArgumentError, 'Invalid actions' unless Validator.all_valid_actions?(actions)
|
|
66
|
+
actions
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.validate_all_policy_classes!(policies)
|
|
70
|
+
raise ArgumentError, 'Invalid policies' unless Validator.all_valid_policy_classes?(policies)
|
|
71
|
+
policies
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.validate_role!(role)
|
|
75
|
+
raise ArgumentError, 'Invalid roles' unless Validator.valid_role?(role)
|
|
76
|
+
role
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.validate_conditions!(conditions)
|
|
80
|
+
raise ArgumentError, 'Invalid conditions' unless Validator.valid_conditions?(conditions)
|
|
81
|
+
conditions
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def self.validate_effect!(effect)
|
|
85
|
+
raise ArgumentError, 'Invalid effect' unless Validator.valid_effect?(effect)
|
|
86
|
+
effect
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
data/lib/picky_guard.rb
ADDED
data/picky_guard.gemspec
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require 'picky_guard/version'
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = 'picky_guard'
|
|
9
|
+
spec.version = PickyGuard::VERSION
|
|
10
|
+
spec.authors = ['Eunjae Lee']
|
|
11
|
+
spec.email = ['karis612@gmail.com']
|
|
12
|
+
|
|
13
|
+
spec.summary = 'PickyGuard is an opinionated authorization library.'
|
|
14
|
+
spec.description = spec.description
|
|
15
|
+
spec.homepage = 'https://github.com/eunjae-lee/picky_guard'
|
|
16
|
+
|
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
|
19
|
+
end
|
|
20
|
+
spec.bindir = 'exe'
|
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
22
|
+
spec.require_paths = ['lib']
|
|
23
|
+
|
|
24
|
+
spec.add_dependency 'activerecord'
|
|
25
|
+
spec.add_dependency 'cancancan'
|
|
26
|
+
|
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
|
28
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3.2'
|
|
30
|
+
spec.add_development_dependency 'sqlite3'
|
|
31
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: picky_guard
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Eunjae Lee
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-04-23 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activerecord
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: cancancan
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: bundler
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '1.16'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '1.16'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '10.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '10.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '3.2'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '3.2'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: sqlite3
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
description: ''
|
|
98
|
+
email:
|
|
99
|
+
- karis612@gmail.com
|
|
100
|
+
executables: []
|
|
101
|
+
extensions: []
|
|
102
|
+
extra_rdoc_files: []
|
|
103
|
+
files:
|
|
104
|
+
- ".gitignore"
|
|
105
|
+
- ".rspec"
|
|
106
|
+
- ".rubocop.yml"
|
|
107
|
+
- ".ruby-version"
|
|
108
|
+
- CHANGELOG.md
|
|
109
|
+
- Gemfile
|
|
110
|
+
- Gemfile.lock
|
|
111
|
+
- README.md
|
|
112
|
+
- Rakefile
|
|
113
|
+
- bin/console
|
|
114
|
+
- bin/setup
|
|
115
|
+
- db/schema.rb
|
|
116
|
+
- lib/generators/picky_guard/install_generator.rb
|
|
117
|
+
- lib/generators/picky_guard/policy_generator.rb
|
|
118
|
+
- lib/generators/picky_guard/templates/ability.rb
|
|
119
|
+
- lib/generators/picky_guard/templates/policy.rb.erb
|
|
120
|
+
- lib/generators/picky_guard/templates/resource_actions.rb
|
|
121
|
+
- lib/generators/picky_guard/templates/role_policies.rb
|
|
122
|
+
- lib/generators/picky_guard/templates/user_role_checker.rb
|
|
123
|
+
- lib/picky_guard.rb
|
|
124
|
+
- lib/picky_guard/loader.rb
|
|
125
|
+
- lib/picky_guard/path_helper.rb
|
|
126
|
+
- lib/picky_guard/policy.rb
|
|
127
|
+
- lib/picky_guard/resource_actions.rb
|
|
128
|
+
- lib/picky_guard/role_policies.rb
|
|
129
|
+
- lib/picky_guard/statement.rb
|
|
130
|
+
- lib/picky_guard/statement_proxy.rb
|
|
131
|
+
- lib/picky_guard/user_role_checker.rb
|
|
132
|
+
- lib/picky_guard/validator.rb
|
|
133
|
+
- lib/picky_guard/version.rb
|
|
134
|
+
- picky_guard.gemspec
|
|
135
|
+
homepage: https://github.com/eunjae-lee/picky_guard
|
|
136
|
+
licenses: []
|
|
137
|
+
metadata: {}
|
|
138
|
+
post_install_message:
|
|
139
|
+
rdoc_options: []
|
|
140
|
+
require_paths:
|
|
141
|
+
- lib
|
|
142
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
143
|
+
requirements:
|
|
144
|
+
- - ">="
|
|
145
|
+
- !ruby/object:Gem::Version
|
|
146
|
+
version: '0'
|
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - ">="
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: '0'
|
|
152
|
+
requirements: []
|
|
153
|
+
rubyforge_project:
|
|
154
|
+
rubygems_version: 2.6.11
|
|
155
|
+
signing_key:
|
|
156
|
+
specification_version: 4
|
|
157
|
+
summary: PickyGuard is an opinionated authorization library.
|
|
158
|
+
test_files: []
|