ingress 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 06ab79a2f02f52156f88da841da4a010386ad71f
4
+ data.tar.gz: 47c37172ee0415e0046c3998aae2f4cbdcc4828f
5
+ SHA512:
6
+ metadata.gz: 8286f4993ee6bfd2aeb60434c781bb271c7b56988ef4010171de7ae1b2b007240ece07b6ccaf068ffaf788099a03743bc4735b12dd30128e8096b3424b1d7d67
7
+ data.tar.gz: b7875313e10cbe3a91a7593c0d942a0bb16dd44158c827659eabf4120d147eefcb3534f44b212c759431fb481028ae252976abd543d1b14d454b09da7bc2da91
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.3.1
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ingress.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Alan Skorkin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,298 @@
1
+ # Ingress
2
+
3
+ A simple role based authorization framework inspired by CanCan (similar syntax) with a nicer interface for defining the permissions for the roles in your system.
4
+
5
+ The biggest problem I had with CanCan was the fact that it mostly forced you define the permissions for all the roles in one class (really one method). And when the set of permissions in your system grew very large, you had to bend over backwards to allow you to break things down.
6
+
7
+ In the OO world we're used to being able to break down functionality into multiple smaller classes which we can them compose into a greater whole. This is the main idea behind this gem, keep the nice syntax that CanCan had, but allow composing the main permission object in your system from many smaller classes.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'ingress'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install ingress
24
+
25
+ ## Usage
26
+
27
+ Let's say you have a user object in your system and the user can have multiple roles. Our set of roles will be `guest`, `member`, `admin`.
28
+
29
+ First we create the main permission object in our system, let's call it `UserPermissions`:
30
+
31
+ ```ruby
32
+ class UserPermissions < Ingress::Permissions
33
+ def user_role_identifiers
34
+ user.roles.map do |role|
35
+ role.name.to_sym
36
+ end
37
+ end
38
+ end
39
+ ```
40
+
41
+ A couple of things of note is that it inherits from `Ingress::Permissions`, from which it inherits the initializer:
42
+
43
+ ```ruby
44
+ attr_reader :user
45
+
46
+ def initialize(user)
47
+ @user = user
48
+ end
49
+ ```
50
+
51
+ So this object is always instantiated with a user. The second thing to note is that we have to provide a method called
52
+ `user_role_identifiers` which needs to return a list of role identifier that this particular user has. Above we make the
53
+ assumptions that a user has many roles and that a role has a name. So we iterate over the roles collect the symbolized names
54
+ and return them. This is essentially what ties everything together. We haven't defined any permissions just yet, but we
55
+ can already do the following:
56
+
57
+ ```ruby
58
+ user_permissions = UserPermissions.new(user)
59
+ user_permissions.can?(:do, :stuff) # returns false
60
+ ```
61
+
62
+ So now we have an object that we can instantiate anywhere, and ask it if our user has a particular permission.
63
+ Let us now define some permissions for a role. We'll start with the guest role. First, let's update our `UserPermissions`
64
+ object:
65
+
66
+ ```ruby
67
+ class UserPermissions < Ingress::Permissions
68
+ define_role_permissions :guest, GuestPermissions
69
+
70
+ def user_role_identifiers
71
+ user.roles.map do |role|
72
+ role.name.to_sym
73
+ end
74
+ end
75
+ end
76
+ ```
77
+
78
+ We've now said that the permission for the role with the `guest` identifier live in the `GuestPermissions` class. Let's create
79
+ it:
80
+
81
+ ```ruby
82
+ class GuestPermissions < Ingress::Permissions
83
+ define_role_permissions do
84
+ can :view, :non_sensitive_info
85
+ can [:create], :session
86
+ end
87
+ end
88
+ ```
89
+
90
+ It's pretty self explanatory, the class again inherits from `Ingress::Permissions` as that's where the simple DSL for defining
91
+ permissions lives. The thing to note is that we called the class `GuestPermissions`, but it could be called anything, the permissions
92
+ we define here are not attached to any role. They only get attached to the role via the `define_role_permissions :guest, GuestPermissions`
93
+ line in the `UserPermissions` class. The syntax for defining permissions is:
94
+
95
+ ```
96
+ can 'action', 'subject'
97
+ or
98
+ cannot 'action', 'subject'
99
+ ```
100
+
101
+ Similar to CanCan, the `action` can be any string, symbol or array of strings or symbols. The `subject` can also be a string or symbol, or
102
+ it can be a class constant. Let's define permissions for the next role in our system, `member` which is more complex. Firstly, update our `UserPermissions`.
103
+
104
+ ```ruby
105
+ class UserPermissions < Ingress::Permissions
106
+ define_role_permissions :guest, GuestPermissions
107
+ define_role_permissions :member, MemberPermissions
108
+
109
+ def user_role_identifiers
110
+ user.roles.map do |role|
111
+ role.name.to_sym
112
+ end
113
+ end
114
+ end
115
+ ```
116
+
117
+ Simple, next `MemberPermissions` class:
118
+
119
+ ```ruby
120
+ class MemberPermissions < Ingress::Permissions
121
+ define_role_permissions do
122
+ can [:show, :update, :destroy], :session
123
+ can :accept, :terms
124
+ can [:view, :create], Post
125
+ can [:update, :destroy], Post, if: ->(user, post) do
126
+ user.id == post.user_id
127
+ end
128
+ end
129
+ end
130
+ ```
131
+
132
+ It's a little bit more complex, but still fairly self explanatory. As you can see, we have a `Post` object in our system. So we allow
133
+ user with a `member` role to view and create posts, and they can update and destroy posts that they own. So we could do:
134
+
135
+ ```ruby
136
+ user_permissions = UserPermissions.new(user)
137
+ user_permissions.can?(:create, Post) # returns true
138
+ post = user.posts.first # assume we can get the list of posts form the user object
139
+ user_permissions.can?(:update, post) # returns true
140
+ ```
141
+
142
+ The condition lambda always takes two parameters, the `user` and an `object`, the object is whatever we supply to the `can?` method,
143
+ when we check permissions.
144
+
145
+ Let's add our admin role:
146
+
147
+ ```ruby
148
+ class UserPermissions < Ingress::Permissions
149
+ define_role_permissions :guest, GuestPermissions
150
+ define_role_permissions :member, MemberPermissions
151
+ define_role_permissions :admin, AdminPermissions
152
+
153
+ def user_role_identifiers
154
+ user.roles.map do |role|
155
+ role.name.to_sym
156
+ end
157
+ end
158
+ end
159
+ ```
160
+
161
+ And the class:
162
+
163
+ ```ruby
164
+ class AdminPermissions < Ingress::Permissions
165
+ define_role_permissions do
166
+ can "*", "*" # you can also use can_do_anything
167
+ end
168
+ end
169
+ ```
170
+
171
+ As you can see both `action` and `subject` can be wildcards, so in this case an admin would be able to do anything in the system, i.e.
172
+ any call to `can?` will always return `true`.
173
+
174
+ So what else can we do? Well let's say we wanted another role called `limited_admin` which would be similar to admin, but can't destroy
175
+ comments:
176
+
177
+ ```ruby
178
+ class UserPermissions < Ingress::Permissions
179
+ define_role_permissions :guest, GuestPermissions
180
+ define_role_permissions :member, MemberPermissions
181
+ define_role_permissions :admin, AdminPermissions
182
+ define_role_permissions :limited_admin, LimitedAdminPermissions
183
+
184
+ def user_role_identifiers
185
+ user.roles.map do |role|
186
+ role.name.to_sym
187
+ end
188
+ end
189
+ end
190
+ ```
191
+
192
+ And the class:
193
+
194
+ ```ruby
195
+ class LimitedAdminPermissions < Ingress::Permissions
196
+ inherits AdminPermissions
197
+
198
+ define_role_permissions do
199
+ cannot :destroy, Comment
200
+ end
201
+ end
202
+ ```
203
+
204
+ So basically, we can inherit permissions that are defined in other classes, and either switch off some or add others. Let's create
205
+ some sort of `super_member` role, which can do everything a member can do, but can also update anything in the system:
206
+
207
+ ```ruby
208
+ class UserPermissions < Ingress::Permissions
209
+ define_role_permissions :guest, GuestPermissions
210
+ define_role_permissions :member, MemberPermissions
211
+ define_role_permissions :admin, AdminPermissions
212
+ define_role_permissions :limited_admin, LimitedAdminPermissions
213
+ define_role_permissions :super_member, SuperMemberPermissions
214
+
215
+ def user_role_identifiers
216
+ user.roles.map do |role|
217
+ role.name.to_sym
218
+ end
219
+ end
220
+ end
221
+ ```
222
+
223
+ And the class:
224
+
225
+ ```ruby
226
+ class SuperMemberPermissions < Ingress::Permissions
227
+ inherits MemberPermissions
228
+
229
+ define_role_permissions do
230
+ can :update, "*"
231
+ end
232
+ end
233
+ ```
234
+
235
+ We can inherit permissions, and we use a wildcard subject, to allow a user with the `super_member` role to be able to update anything. We
236
+ can even define a common set of permissions which we want multiple roles to share and have the permission class for each of those roles
237
+ inherit from the common set. Let's say we want a `financial_officer` role and a `reporting_officer` role both of which should have the ability to do anything with a `Transaction` object in our system (for whatever reason):
238
+
239
+ ```ruby
240
+ class UserPermissions < Ingress::Permissions
241
+ define_role_permissions :guest, GuestPermissions
242
+ define_role_permissions :member, MemberPermissions
243
+ define_role_permissions :admin, AdminPermissions
244
+ define_role_permissions :limited_admin, LimitedAdminPermissions
245
+ define_role_permissions :super_member, SuperMemberPermissions
246
+ define_role_permissions :financial_officer, FinancialOfficerPermissions
247
+ define_role_permissions :reporting_officer, ReportingOfficerPermissions
248
+
249
+ def user_role_identifiers
250
+ user.roles.map do |role|
251
+ role.name.to_sym
252
+ end
253
+ end
254
+ end
255
+ ```
256
+
257
+ And the classes:
258
+
259
+ ```ruby
260
+ class CommonPermissions < Ingress::Permissions
261
+ define_role_permissions do
262
+ can "*", Transaction
263
+ end
264
+ end
265
+
266
+ class FinancialOfficerPermissions < Ingress::Permissions
267
+ inherits CommonPermissions
268
+ end
269
+
270
+ class ReportingOfficerPermissions < Ingress::Permissions
271
+ inherits CommonPermissions
272
+ end
273
+ ```
274
+
275
+ Now we wildcard the action, so we can do anything to `Transaction` objects. And we have to other sets of permission inherit from
276
+ the `CommonPermissions` class.
277
+
278
+ I hope it's relatively clear that it's pretty flexible, you can almost endlessly decompose the permission definitions into smaller classes
279
+ then combine via `inherits` and assign the final permission set to a role identifier via `define_role_permissions` on the main
280
+ `UserPermissions` class.
281
+
282
+ So now the authorization in your system can be defined in a much more OO way, without nasty and complex tricks. And you can still enjoy a nice syntax very similar to CanCan.
283
+
284
+ This framework has no hooks into Rails (these would be trivial to write if necessary, e.g. you can instantiate the `user_permissions` object on your `ApplicationController` and then do the `can?` checks anywhere you want) and can therefore be used with any web framework, or even outside of the context of a web framework (if such a use case makes sense).
285
+
286
+ ## Development
287
+
288
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
289
+
290
+ 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).
291
+
292
+ ## Contributing
293
+
294
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/ingress.
295
+
296
+ ## License
297
+
298
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ingress"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/ingress.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ingress/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ingress"
8
+ spec.version = Ingress::VERSION
9
+ spec.authors = ["Alan Skorkin"]
10
+ spec.email = ["alan@skorks.com"]
11
+
12
+ spec.summary = %q{Simple role based authorization for Ruby applications}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "bin"
18
+ spec.executables = spec.files.grep(%r{^script/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.10"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec"
24
+ end
@@ -0,0 +1,15 @@
1
+ require "ingress/permissions_dsl"
2
+
3
+ module Ingress
4
+ module Services
5
+ class BuildPermissionsRepositoryForRole
6
+ class << self
7
+ def perform(role_identifier, &block)
8
+ permissions_dsl = PermissionsDsl.new(role_identifier)
9
+ permissions_dsl.instance_eval(&block)
10
+ permissions_dsl.permission_repository
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require "ingress/permissions_repository"
2
+
3
+ module Ingress
4
+ module Services
5
+ class CopyPermissionsRepositoryIntoRole
6
+ class << self
7
+ def perform(role_identifier, template_permission_repository)
8
+ permission_repository = PermissionsRepository.new
9
+ permission_repository.copy_to_role(role_identifier, template_permission_repository)
10
+ permission_repository
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,57 @@
1
+ module Ingress
2
+ class PermissionRule
3
+ attr_reader :action, :subject, :conditions
4
+
5
+ def initialize(allows:, action:, subject:, conditions: nil)
6
+ @allows = allows
7
+ @action = action
8
+ @subject = subject
9
+ @conditions = [conditions].compact.flatten
10
+ end
11
+
12
+ def allows?
13
+ @allows
14
+ end
15
+
16
+ def match?(given_action, given_subject, user)
17
+ return false unless action_matches?(given_action)
18
+ return false unless subject_matches?(given_subject)
19
+
20
+ conditions_match?(user, given_subject)
21
+ end
22
+
23
+ private
24
+
25
+ def action_matches?(given_action)
26
+ given_action == action ||
27
+ given_action == "*" ||
28
+ "*" == action
29
+ end
30
+
31
+ def subject_matches?(given_subject)
32
+ given_subject == subject ||
33
+ given_subject.class == subject ||
34
+ given_subject == "*" ||
35
+ "*" == subject
36
+ end
37
+
38
+ def conditions_match?(user, given_subject)
39
+ conditions.all? do |condition|
40
+ condition.call(user, given_subject)
41
+ end
42
+ rescue => e
43
+ log_error(e)
44
+ false
45
+ end
46
+
47
+ def log_error(error)
48
+ if defined?(Rails)
49
+ Rails.logger.error error.message
50
+ Rails.logger.error error.backtrace.join("\n")
51
+ else
52
+ $stderr.puts error.message
53
+ $stderr.puts error.backtrace.join("\n")
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,64 @@
1
+ require "ingress/permissions_repository"
2
+ require "ingress/copy_permissions_repository_into_role"
3
+ require "ingress/build_permissions_repository_for_role"
4
+
5
+ module Ingress
6
+ class Permissions
7
+ class << self
8
+ def permissions_repository
9
+ @permissions_repository ||= PermissionsRepository.new
10
+ end
11
+
12
+ def inherits(permissions_class)
13
+ role_identifier = :dummy
14
+
15
+ if permissions_class
16
+ @permissions_repository = permissions_repository.merge(
17
+ Services::CopyPermissionsRepositoryIntoRole.perform(role_identifier, permissions_class.permissions_repository),
18
+ )
19
+ end
20
+ end
21
+
22
+ def define_role_permissions(role_identifier = nil, permissions_class = nil, &block)
23
+ if role_identifier.nil?
24
+ role_identifier = :dummy
25
+ end
26
+
27
+ if permissions_class
28
+ @permissions_repository = permissions_repository.merge(
29
+ Services::CopyPermissionsRepositoryIntoRole.perform(role_identifier, permissions_class.permissions_repository),
30
+ )
31
+ end
32
+
33
+ if block_given?
34
+ @permissions_repository = permissions_repository.merge(Services::BuildPermissionsRepositoryForRole.perform(role_identifier, &block))
35
+ end
36
+ end
37
+ end
38
+
39
+ attr_reader :user
40
+
41
+ def initialize(user)
42
+ @user = user
43
+ end
44
+
45
+ def can?(action, subject)
46
+ find_matching_rules(action, subject).any? do |rule|
47
+ rule.match?(action, subject, user)
48
+ end
49
+ end
50
+
51
+ def user_role_identifiers
52
+ []
53
+ end
54
+
55
+ private
56
+
57
+ def find_matching_rules(action, subject)
58
+ user_role_identifiers.reduce([]) do |rules, role_identifier|
59
+ rules += self.class.permissions_repository.rules_for(role_identifier, action, subject)
60
+ rules
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,49 @@
1
+ require "ingress/permissions_repository"
2
+
3
+ module Ingress
4
+ class PermissionsDsl
5
+ attr_reader :role_identifier, :permission_repository
6
+
7
+ def initialize(role_identifier)
8
+ @role_identifier = role_identifier
9
+ @permission_repository = PermissionsRepository.new
10
+ end
11
+
12
+ def can_do_anything
13
+ permission_repository.add_permission(role_identifier, true, "*", "*")
14
+ end
15
+
16
+ def can(actions, subjects, options = {}, &block)
17
+ for_each_action_and_subject(actions, subjects) do |action, subject|
18
+ condition = condition_from_options(options, block)
19
+ permission_repository.add_permission(role_identifier, true, action, subject, condition)
20
+ end
21
+ end
22
+
23
+ def cannot(actions, subjects, options = {}, &block)
24
+ for_each_action_and_subject(actions, subjects) do |action, subject|
25
+ condition = condition_from_options(options, block)
26
+ permission_repository.add_permission(role_identifier, false, action, subject, condition)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def for_each_action_and_subject(actions, subjects)
33
+ return unless block_given?
34
+ actions = [actions].flatten
35
+ subjects = [subjects].flatten
36
+
37
+ actions.each do |action|
38
+ subjects.each do |subject|
39
+ yield(action, subject)
40
+ end
41
+ end
42
+ end
43
+
44
+ def condition_from_options(options, block)
45
+ if_condition = options[:if] || block
46
+ if_condition.respond_to?(:call) ? if_condition : nil
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,97 @@
1
+ require "ingress/permission_rule"
2
+
3
+ module Ingress
4
+ class PermissionsRepository
5
+ attr_reader :role_rules
6
+
7
+ def initialize
8
+ @role_rules = Hashes.role_rules
9
+ @role_subject_action_rule = Hashes.role_subject_action_rule
10
+ end
11
+
12
+ def add_permission(role_identifier, allow, action, subject, conditions = nil)
13
+ rule = PermissionRule.new(allows: allow, action: action, subject: subject, conditions: conditions)
14
+ add_rule(role_identifier, rule)
15
+ end
16
+
17
+ def rules_for(role_identifier, action, subject)
18
+ rules = []
19
+
20
+ rules += find_rules(role_identifier, action, subject)
21
+ rules += find_rules(role_identifier, "*", "*")
22
+ rules += find_rules(role_identifier, action, "*")
23
+ rules += find_rules(role_identifier, "*", subject)
24
+ rules = apply_negative_rules(rules)
25
+
26
+ rules
27
+ end
28
+
29
+ def merge(permission_repository)
30
+ permission_repository.role_rules.each_pair do |role_identifier, rules|
31
+ rules.each do |rule|
32
+ add_rule(role_identifier, rule)
33
+ end
34
+ end
35
+ self
36
+ end
37
+
38
+ def copy_to_role(role_identifier, permission_repository)
39
+ permission_repository.role_rules.each_pair do |_, rules|
40
+ rules.each do |rule|
41
+ add_rule(role_identifier, rule)
42
+ end
43
+ end
44
+ self
45
+ end
46
+
47
+ private
48
+
49
+ def apply_negative_rules(rules)
50
+ # remove rules that cancel each other out since we're within the
51
+ # context of 1 role, i.e. if any rule is a negation it will
52
+ # cancel all other ones
53
+ if rules.any? { |rule| !rule.allows? }
54
+ []
55
+ else
56
+ rules
57
+ end
58
+ end
59
+
60
+ def find_rules(role_identifier, action, subject)
61
+ rules = []
62
+ rules += @role_subject_action_rule[role_identifier][subject][action]
63
+ unless subject == "*"
64
+ rules += @role_subject_action_rule[role_identifier][subject.class][action]
65
+ end
66
+
67
+ rules
68
+ end
69
+
70
+ def add_rule(role_identifier, rule)
71
+ @role_rules[role_identifier] << rule
72
+ @role_subject_action_rule[role_identifier][rule.subject][rule.action] << rule
73
+ end
74
+
75
+ # creates hashes with the default values required in
76
+ # PermissionsRepository's constructor
77
+ module Hashes
78
+ module_function
79
+
80
+ # Three level deep hash returning an array
81
+ def role_subject_action_rule
82
+ Hash.new do |hash1, key1|
83
+ hash1[key1] = Hash.new do |hash2, key2|
84
+ hash2[key2] = Hash.new do |hash3, key3|
85
+ hash3[key3] = []
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ # One level deep hash returning an array
92
+ def role_rules
93
+ Hash.new { |hash, key| hash[key] = [] }
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,3 @@
1
+ module Ingress
2
+ VERSION = "0.1.0"
3
+ end
data/lib/ingress.rb ADDED
@@ -0,0 +1,2 @@
1
+ require "ingress/version"
2
+ require "ingress/permissions"
data/script/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ingress"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/script/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ingress
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alan Skorkin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-04-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description:
56
+ email:
57
+ - alan@skorks.com
58
+ executables:
59
+ - console
60
+ - setup
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ".gitignore"
65
+ - ".rspec"
66
+ - ".ruby-version"
67
+ - ".travis.yml"
68
+ - Gemfile
69
+ - LICENSE.txt
70
+ - README.md
71
+ - Rakefile
72
+ - bin/console
73
+ - bin/setup
74
+ - ingress.gemspec
75
+ - lib/ingress.rb
76
+ - lib/ingress/build_permissions_repository_for_role.rb
77
+ - lib/ingress/copy_permissions_repository_into_role.rb
78
+ - lib/ingress/permission_rule.rb
79
+ - lib/ingress/permissions.rb
80
+ - lib/ingress/permissions_dsl.rb
81
+ - lib/ingress/permissions_repository.rb
82
+ - lib/ingress/version.rb
83
+ - script/console
84
+ - script/setup
85
+ homepage: ''
86
+ licenses:
87
+ - MIT
88
+ metadata: {}
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 2.5.1
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: Simple role based authorization for Ruby applications
109
+ test_files: []