u-authorization 1.0.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.
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: []