u-authorization 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/authorization.rb +164 -0
  3. data/u-authorization.rb +3 -0
  4. metadata +45 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1a8a0a045b9fdd6b4f30c7c6b376635b2665b2609450226a329a2f4ad93e8747
4
+ data.tar.gz: 3f52e3df4e835319ca288750b7cedc85cd83189d4ec91054f1559a9f30ceb1cd
5
+ SHA512:
6
+ metadata.gz: a8a2e6243af66478cf723b9df7662eeeaa8c31bc53b487a90e7ba8b501c4546b55b1fcfec416bb917f7bf05a6fb1291216475fec952590c88ec11515a6cc7125
7
+ data.tar.gz: 104a3e02d545745efdfd0ed189725a04caaf2dd1c0c9a2abff44ef0cd8f1c11ad76e02f3886b6fa3f24c970616f24da187da3185794b495c5287d096c45baf03
data/authorization.rb ADDED
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Authorization
4
+ VERSION = '1.0.0'
5
+
6
+ MapValuesAsDowncasedStrings = -> (values) do
7
+ Array(values).map { |value| String(value).downcase }
8
+ end
9
+
10
+ module CheckRolePermission
11
+ extend self
12
+
13
+ def call(context, role_permissions, required_features)
14
+ required_features
15
+ .all? { |feature| has_permission?(context, role_permissions[feature]) }
16
+ end
17
+
18
+ private
19
+
20
+ def has_permission?(context, role_permission)
21
+ return false if role_permission.nil?
22
+
23
+ if !(any = role_permission['any']).nil?
24
+ any
25
+ elsif only = role_permission['only']
26
+ check_feature_permission(only) { |perm| context.include?(perm) }
27
+ elsif except = role_permission['except']
28
+ check_feature_permission(except) { |perm| !context.include?(perm) }
29
+ else
30
+ raise NotImplementedError
31
+ end
32
+ end
33
+
34
+ def check_feature_permission(context_values)
35
+ MapValuesAsDowncasedStrings.(context_values).any? do |context_value|
36
+ Array(context_value.split('.')).all? { |permission| yield(permission) }
37
+ end
38
+ end
39
+ end
40
+
41
+ class Permissions
42
+ attr_reader :role, :context
43
+
44
+ def self.[](instance)
45
+ return instance if instance.is_a?(Permissions)
46
+
47
+ raise ArgumentError, "#{instance.inspect} must be a #{self.name}"
48
+ end
49
+
50
+ def initialize(role_permissions, context: [])
51
+ @role = role_permissions.dup.freeze
52
+ @context = MapValuesAsDowncasedStrings.(context).freeze
53
+
54
+ @cache = {}
55
+ end
56
+
57
+ def to?(features = nil)
58
+ required_features = MapValuesAsDowncasedStrings.(features)
59
+
60
+ cache_key = required_features.inspect
61
+
62
+ return @cache[cache_key] unless @cache[cache_key].nil?
63
+
64
+ @cache[cache_key] = CheckRolePermission.call(
65
+ @context, @role, required_features
66
+ )
67
+ end
68
+
69
+ def to_not?(features = nil)
70
+ !to?(features)
71
+ end
72
+ end
73
+
74
+ class Policy
75
+ def self.type(klass)
76
+ return klass if klass < self
77
+
78
+ raise ArgumentError, "policy must be a #{self.name}"
79
+ end
80
+
81
+ def initialize(user, subject = nil, permissions: nil)
82
+ @user = user
83
+ @subject = subject
84
+ @permissions = permissions
85
+ end
86
+
87
+ def method_missing(method, *args, **keyargs, &block)
88
+ return false if method =~ /\?\z/
89
+ super(method)
90
+ end
91
+
92
+ private
93
+
94
+ def user; @user; end
95
+ def subject; @subject; end
96
+ def permissions; @permissions; end
97
+ end
98
+
99
+ class Model
100
+ attr_reader :user, :permissions
101
+
102
+ def self.build(user, role, context: [], policies: {})
103
+ permissions = Permissions.new(role, context: context)
104
+
105
+ self.new(user, permissions: permissions, policies: policies)
106
+ end
107
+
108
+ def initialize(user, permissions:, policies: {})
109
+ @user = user
110
+ @policies = {}
111
+ @policies_cache = {}
112
+ @permissions = Permissions[permissions]
113
+
114
+ add_policies(policies)
115
+ end
116
+
117
+ def map(context: nil, policies: nil)
118
+ if context.nil? && policies.nil?
119
+ raise ArgumentError, 'context or policies keywords args must be defined'
120
+ end
121
+
122
+ new_permissions =
123
+ Permissions.new(permissions.role, context: context || @context)
124
+
125
+ self.class.new(
126
+ user, permissions: new_permissions, policies: policies || @policies
127
+ )
128
+ end
129
+
130
+ def add_policy(key, policy_klass)
131
+ raise ArgumentError, 'key must be a Symbol' unless key.is_a?(Symbol)
132
+
133
+ @policies[key] ||= Policy.type(policy_klass)
134
+
135
+ self
136
+ end
137
+
138
+ def add_policies(new_policies)
139
+ unless new_policies.is_a?(Hash)
140
+ raise ArgumentError, "policies must be a Hash (key => #{Policy.name})"
141
+ end
142
+
143
+ new_policies.each &method(:add_policy)
144
+
145
+ self
146
+ end
147
+
148
+ def to(policy_key, subject: nil)
149
+ policy_klass = @policies.fetch(policy_key, Policy)
150
+
151
+ return policy_klass.new(user, subject, permissions: permissions) if subject
152
+
153
+ return @policies_cache[policy_key] if @policies_cache[policy_key]
154
+
155
+ policy_klass.new(user, permissions: permissions).tap do |instance|
156
+ @policies_cache[policy_key] = instance if policy_klass != Policy
157
+ end
158
+ end
159
+
160
+ def policy(key = :default, subject: nil)
161
+ to(key, subject: subject)
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'authorization'
metadata ADDED
@@ -0,0 +1,45 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: u-authorization
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Rodrigo Serradura
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-10-18 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Simple authorization library and role managment for Ruby.
14
+ email: rodrigo.serradura@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - authorization.rb
20
+ - u-authorization.rb
21
+ homepage: https://gist.github.com/serradura/7d51b979b90609d8601d0f416a9aa373
22
+ licenses:
23
+ - MIT
24
+ metadata: {}
25
+ post_install_message:
26
+ rdoc_options: []
27
+ require_paths:
28
+ - "."
29
+ required_ruby_version: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.2.2
34
+ required_rubygems_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ requirements: []
40
+ rubyforge_project:
41
+ rubygems_version: 2.7.7
42
+ signing_key:
43
+ specification_version: 4
44
+ summary: Authorization library and role managment
45
+ test_files: []