iron_hide 0.2.1
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/lib/iron_hide/condition.rb +148 -0
- data/lib/iron_hide/errors.rb +18 -0
- data/lib/iron_hide/rule.rb +69 -0
- data/lib/iron_hide/storage/file_adapter.rb +71 -0
- data/lib/iron_hide/storage.rb +37 -0
- data/lib/iron_hide/version.rb +6 -0
- data/lib/iron_hide.rb +91 -0
- metadata +150 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 43f6efb8400709edf09c958d29466af574d7bbeb
|
4
|
+
data.tar.gz: 122df1373619876ef2cd2c5e1d7c109839b59373
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 50020dee2524090274d7191e43bfb24a4271e8da4dfa6ddb4a73df560badfc4fc42f1090536171d9221ff352d0d72694cf3fda2a29b5b60249b0c68badc23574
|
7
|
+
data.tar.gz: 03112c2cee64e74ed11962aa6fcc2308b13686654726c617978df4700478c7bd15dc2cd67a907e2504afb11531be4dbae036582608880dac31fea47829c8ff26
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module IronHide
|
4
|
+
class Condition
|
5
|
+
VALID_TYPES = {
|
6
|
+
'equal'=> :EqualCondition,
|
7
|
+
'not_equal'=> :NotEqualCondition
|
8
|
+
}.freeze
|
9
|
+
|
10
|
+
# @param params [Hash] It has a single key, which is the conditional operator
|
11
|
+
# type. The value is the set of conditionals that must be met.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# { :equal => {
|
15
|
+
# 'resource::manager_id' => ['user::manager_id'],
|
16
|
+
# 'user::user_role_ids' => ['8']
|
17
|
+
# }
|
18
|
+
# }
|
19
|
+
#
|
20
|
+
# @return [EqualCondition, NotEqualCondition]
|
21
|
+
# @raise [IronHide::InvalidConditional] for too many keys
|
22
|
+
#
|
23
|
+
def self.new(params)
|
24
|
+
if params.length > 1
|
25
|
+
raise InvalidConditional, "Expected #{params} to have one key"
|
26
|
+
end
|
27
|
+
type, conditionals = params.first
|
28
|
+
#=> :equal, { key: val, key: val }
|
29
|
+
#
|
30
|
+
# See: http://ruby-doc.org/core-1.9.3/Class.html#method-i-allocate
|
31
|
+
klass = VALID_TYPES.fetch(type){ raise InvalidConditional, "#{type} is not valid"}
|
32
|
+
cond = IronHide.const_get(klass).allocate
|
33
|
+
cond.send(:initialize, (conditionals))
|
34
|
+
cond
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param conditionals [Hash]
|
38
|
+
# @example
|
39
|
+
# {
|
40
|
+
# 'resource::manager_id' => ['user::manager_id'],
|
41
|
+
# 'user::user_role_ids' => ['8']
|
42
|
+
# }
|
43
|
+
#
|
44
|
+
def initialize(conditionals)
|
45
|
+
@conditionals = conditionals
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :conditionals
|
49
|
+
|
50
|
+
# @param user [Object]
|
51
|
+
# @param resource [Object]
|
52
|
+
# return [Boolean] if is met
|
53
|
+
def met?(user, resource)
|
54
|
+
raise NotImplementedError
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
EVALUATE_REGEX = /
|
60
|
+
(
|
61
|
+
\Auser\z| # 'user' or 'resource'
|
62
|
+
\Aresource\z
|
63
|
+
)
|
64
|
+
| # OR
|
65
|
+
\A\w+:{2}\w+ # "word::word"
|
66
|
+
(:{2}\w+)* # Followed by any number of "::word"
|
67
|
+
\z # End of string
|
68
|
+
/x
|
69
|
+
|
70
|
+
# *Safely* evaluate a conditional expression
|
71
|
+
#
|
72
|
+
# @note
|
73
|
+
# This does not guarantee that conditions are correctly specified.
|
74
|
+
# For example, 'user:::manager' will not resolve to anything, and
|
75
|
+
# and an exception will *not* be raised. The same goes for 'user:::' and
|
76
|
+
# 'user:id'.
|
77
|
+
#
|
78
|
+
# @param expressions [Array<String, Object>, String, Object] an array or
|
79
|
+
# a single expression. This represents either an immediate value (e.g.,
|
80
|
+
# '1', 99) or a valid expression that can be interpreted (see example)
|
81
|
+
#
|
82
|
+
# @example
|
83
|
+
# ['user::manager_id'] #=> [1]
|
84
|
+
# ['user::role_ids'] #=> [1,2,3,4]
|
85
|
+
# ['resource::manager_id'] #=> [1]
|
86
|
+
# [1,2,3,4] #=> [1,2,3,4]
|
87
|
+
# 'user::id' #=> [1]
|
88
|
+
# 'resource::id' #=> [2]
|
89
|
+
#
|
90
|
+
# @return [Array<Object>] a collection of 0 or more objects
|
91
|
+
# representing attributes on the user or resource
|
92
|
+
#
|
93
|
+
def evaluate(expression, user, resource)
|
94
|
+
Array(expression).flat_map do |el|
|
95
|
+
if expression?(el)
|
96
|
+
type, *ary = el.split('::')
|
97
|
+
if type == 'user'
|
98
|
+
Array(ary.inject(user) do |rval, attr|
|
99
|
+
rval.freeze.public_send(attr)
|
100
|
+
end)
|
101
|
+
elsif type == 'resource'
|
102
|
+
Array(ary.inject(resource) do |rval, attr|
|
103
|
+
rval.freeze.public_send(attr)
|
104
|
+
end)
|
105
|
+
else
|
106
|
+
raise "Expected #{type} to be 'resource' or 'user'"
|
107
|
+
end
|
108
|
+
else
|
109
|
+
el
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def expression?(expression)
|
115
|
+
!!(expression =~ EVALUATE_REGEX)
|
116
|
+
end
|
117
|
+
|
118
|
+
def with_error_handling
|
119
|
+
yield
|
120
|
+
rescue => e
|
121
|
+
new_exception = InvalidConditional.new(e.to_s)
|
122
|
+
new_exception.set_backtrace(e.backtrace)
|
123
|
+
raise new_exception
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# @api private
|
128
|
+
class EqualCondition < Condition
|
129
|
+
def met?(user, resource)
|
130
|
+
with_error_handling do
|
131
|
+
conditionals.all? do |left, right|
|
132
|
+
(evaluate(left, user, resource) & evaluate(right, user, resource)).size > 0
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# @api private
|
139
|
+
class NotEqualCondition < Condition
|
140
|
+
def met?(user, resource)
|
141
|
+
with_error_handling do
|
142
|
+
conditionals.all? do |left, right|
|
143
|
+
!((evaluate(left, user, resource) & evaluate(right, user, resource)).size > 0)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module IronHide
|
2
|
+
# All exceptions inherit from IronHideError to allow rescuing from all
|
3
|
+
# exceptions that occur in this gem.
|
4
|
+
#
|
5
|
+
class IronHideError < StandardError ; end
|
6
|
+
|
7
|
+
# Exception raised when an authorization failure occurs.
|
8
|
+
# Typically when IronHide::authorize! is invoked
|
9
|
+
#
|
10
|
+
class AuthorizationError < IronHideError ; end
|
11
|
+
|
12
|
+
# Exception raised when a conditional is incorrectly defined
|
13
|
+
# in the rules.
|
14
|
+
# @see IronHide::Condition
|
15
|
+
#
|
16
|
+
class InvalidConditional < IronHideError ; end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module IronHide
|
2
|
+
class Rule
|
3
|
+
ALLOW = 'allow'.freeze
|
4
|
+
DENY = 'deny'.freeze
|
5
|
+
|
6
|
+
attr_reader :description, :effect, :conditions, :user, :resource
|
7
|
+
|
8
|
+
def initialize(user, resource, params = {})
|
9
|
+
@user = user
|
10
|
+
@resource = resource
|
11
|
+
@description = params['description']
|
12
|
+
@effect = params.fetch('effect', DENY) # Default DENY
|
13
|
+
@conditions = Array(params['conditions']).map { |c| Condition.new(c) }
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns all applicable rules matching on resource and action
|
17
|
+
#
|
18
|
+
# @param user [Object]
|
19
|
+
# @param action [String]
|
20
|
+
# @param resource [Object]
|
21
|
+
# @return [Array<IronHide::Rule>]
|
22
|
+
def self.find(user, action, resource)
|
23
|
+
ns_resource = "#{IronHide.namespace}::#{resource.class.name}"
|
24
|
+
storage.where(resource: ns_resource, action: action).map do |json|
|
25
|
+
new(user, resource, json)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# NOTE: If any Rule is an explicit DENY, then an allow cannot override the Rule
|
30
|
+
# If any Rule is explicit ALLOW, and there is no explicit DENY, then ALLOW
|
31
|
+
# If no Rules match, then DENY
|
32
|
+
#
|
33
|
+
# @return [Boolean]
|
34
|
+
# @param user [Object]
|
35
|
+
# @param action [String]
|
36
|
+
# @param resource [String]
|
37
|
+
#
|
38
|
+
def self.allow?(user, action, resource)
|
39
|
+
find(user, action, resource).inject(false) do |rval, rule|
|
40
|
+
# For an explicit DENY, stop evaluating, and return false
|
41
|
+
rval = false and break if rule.explicit_deny?
|
42
|
+
|
43
|
+
# For an explicit ALLOW, true
|
44
|
+
rval = true if rule.allow?
|
45
|
+
|
46
|
+
rval
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# An abstraction over the storage of the rules
|
51
|
+
# @see IronHide::Storage
|
52
|
+
# @return [IronHide::Storage]
|
53
|
+
def self.storage
|
54
|
+
IronHide.storage
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Boolean]
|
58
|
+
def allow?
|
59
|
+
effect == ALLOW && conditions.all? { |c| c.met?(user,resource) }
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [Boolean]
|
63
|
+
def explicit_deny?
|
64
|
+
effect == DENY && conditions.all? { |c| c.met?(user,resource) }
|
65
|
+
end
|
66
|
+
|
67
|
+
alias_method :deny?, :explicit_deny?
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module IronHide
|
2
|
+
class Storage
|
3
|
+
# @api private
|
4
|
+
class FileAdapter < AbstractAdapter
|
5
|
+
attr_reader :rules
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
json = IronHide.json.each_with_object([]) do |files, ary|
|
9
|
+
Array(files).map do |file|
|
10
|
+
ary.concat(MultiJson.load(File.open(file).read, minify: true))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
@rules = unfold(json)
|
14
|
+
rescue MultiJson::ParseError => e
|
15
|
+
raise IronHideError, "#{e.cause}: #{e.data}"
|
16
|
+
rescue => e
|
17
|
+
raise IronHideError, "Invalid or missing JSON file: #{e.to_s}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def where(opts = {})
|
21
|
+
self[opts[:resource]][opts[:action]]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Unfold the JSON definitions of the rules into a Hash with this structure:
|
25
|
+
# {
|
26
|
+
# "com::test::TestResource" => {
|
27
|
+
# "action" => [
|
28
|
+
# { ... }, { ... }, { ... }
|
29
|
+
# ]
|
30
|
+
# }
|
31
|
+
# }
|
32
|
+
#
|
33
|
+
# @param json [Array<Hash>]
|
34
|
+
# @return [Hash]
|
35
|
+
def unfold(json)
|
36
|
+
json.inject(hash_of_hashes) do |rules, json_rule|
|
37
|
+
resource, actions = json_rule["resource"], json_rule["action"]
|
38
|
+
actions.each { |act| rules[resource][act] << json_rule }
|
39
|
+
rules
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Return a Hash with default value that is a Hash with default value of Array
|
46
|
+
# @return [Hash<Hash, Array>]
|
47
|
+
def hash_of_hashes
|
48
|
+
Hash.new { |h1,k1|
|
49
|
+
h1[k1] = Hash.new { |h,k| h[k] = [] }
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
# Implements an interface that makes selecting rules look like a Hash:
|
54
|
+
# @example
|
55
|
+
# {
|
56
|
+
# 'com::test::TestResource' => {
|
57
|
+
# 'read' => [],
|
58
|
+
# ...
|
59
|
+
# }
|
60
|
+
# }
|
61
|
+
# adapter['com::test::TestResource']['read']
|
62
|
+
# #=> [Array<Hash>]
|
63
|
+
#
|
64
|
+
# @param [Symbol] val
|
65
|
+
# @return [Array<Hash>] array of canonical JSON representation of rules
|
66
|
+
def [](val)
|
67
|
+
rules[val]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# IronHide::Storage provides a common interface regardless of storage type
|
2
|
+
# by implementing the Adapter pattern to decouple _how_ JSON
|
3
|
+
#
|
4
|
+
require 'multi_json'
|
5
|
+
|
6
|
+
module IronHide
|
7
|
+
# @api private
|
8
|
+
class Storage
|
9
|
+
|
10
|
+
ADAPTERS = {
|
11
|
+
file: :FileAdapter
|
12
|
+
}
|
13
|
+
|
14
|
+
attr_reader :adapter
|
15
|
+
|
16
|
+
def initialize(adapter_type)
|
17
|
+
@adapter = self.class.const_get(ADAPTERS[adapter_type]).new
|
18
|
+
end
|
19
|
+
|
20
|
+
# @see AbstractAdapter#where
|
21
|
+
def where(opts = {})
|
22
|
+
adapter.where(opts)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @abstract Subclass and override {#where} to implement an Adapter class
|
27
|
+
class AbstractAdapter
|
28
|
+
|
29
|
+
# @option opts [String] :resource *required*
|
30
|
+
# @option opts [String] :action *required*
|
31
|
+
def where(opts = {})
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
require 'iron_hide/storage/file_adapter'
|
data/lib/iron_hide.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
module IronHide
|
2
|
+
# @raise [IronHide::AuthorizationError] if authorization fails
|
3
|
+
# @return [true] if authorization succeeds
|
4
|
+
#
|
5
|
+
def self.authorize!(user, action, resource)
|
6
|
+
unless can?(user, action, resource)
|
7
|
+
raise AuthorizationError
|
8
|
+
end
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [Boolean]
|
13
|
+
# @param user [Object]
|
14
|
+
# @param action [Symbol, String]
|
15
|
+
# @param resource [Object]
|
16
|
+
# @see IronHide::Rule::allow?
|
17
|
+
#
|
18
|
+
def self.can?(user, action, resource)
|
19
|
+
Rule.allow?(user, action.to_s, resource)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Specify where to load rules from. This is specified in a config file
|
23
|
+
# @param type [:file] Specify the adapter type. Only json is supported
|
24
|
+
# for now
|
25
|
+
def self.adapter=(type)
|
26
|
+
@adapter_type = type
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.adapter
|
30
|
+
@adapter_type
|
31
|
+
end
|
32
|
+
|
33
|
+
# Set the top-level namespace for the application's Rules
|
34
|
+
#
|
35
|
+
# @param val [String]
|
36
|
+
# @example
|
37
|
+
# 'com::myCompany::myProject'
|
38
|
+
def self.namespace=(val)
|
39
|
+
@namespace = val
|
40
|
+
end
|
41
|
+
|
42
|
+
# Default namespace is com::IronHide
|
43
|
+
#
|
44
|
+
# @return [String]
|
45
|
+
def self.namespace
|
46
|
+
@namespace || 'com::IronHide'
|
47
|
+
end
|
48
|
+
|
49
|
+
# Specify the file path for the JSON flat-file for rules
|
50
|
+
# Only applicable if using the JSON adapter
|
51
|
+
# @param files [String, Array<String>]
|
52
|
+
#
|
53
|
+
def self.json=(*files)
|
54
|
+
@json_files = files
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Array<String>]
|
58
|
+
def self.json
|
59
|
+
@json_files
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [IronHide::Storage]
|
63
|
+
def self.storage
|
64
|
+
@storage ||= begin
|
65
|
+
if @adapter_type.nil?
|
66
|
+
raise IronHideError, "Storage adapter not defined"
|
67
|
+
end
|
68
|
+
IronHide::Storage.new(@adapter_type)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Allow the module to be configurable from a config file
|
73
|
+
# See: {file:README.md}
|
74
|
+
# @yield [IronHide]
|
75
|
+
def self.config
|
76
|
+
yield self
|
77
|
+
end
|
78
|
+
|
79
|
+
# Resets internal state
|
80
|
+
#
|
81
|
+
# @return [void]
|
82
|
+
def self.reset
|
83
|
+
instance_variables.each { |i| instance_variable_set(i,nil) }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
require "iron_hide/version"
|
88
|
+
require 'iron_hide/errors'
|
89
|
+
require 'iron_hide/rule'
|
90
|
+
require 'iron_hide/condition'
|
91
|
+
require 'iron_hide/storage'
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: iron_hide
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alan Cohen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: multi_json
|
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: json_minify
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.2'
|
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'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1'
|
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'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: yard
|
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
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: A Ruby authorization library
|
112
|
+
email:
|
113
|
+
- acohen@climate.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- lib/iron_hide.rb
|
119
|
+
- lib/iron_hide/condition.rb
|
120
|
+
- lib/iron_hide/errors.rb
|
121
|
+
- lib/iron_hide/rule.rb
|
122
|
+
- lib/iron_hide/storage.rb
|
123
|
+
- lib/iron_hide/storage/file_adapter.rb
|
124
|
+
- lib/iron_hide/version.rb
|
125
|
+
homepage: http://github.com/TheClimateCorporation/iron_hide
|
126
|
+
licenses:
|
127
|
+
- MIT
|
128
|
+
metadata: {}
|
129
|
+
post_install_message:
|
130
|
+
rdoc_options: []
|
131
|
+
require_paths:
|
132
|
+
- lib
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
requirements: []
|
144
|
+
rubyforge_project:
|
145
|
+
rubygems_version: 2.2.2
|
146
|
+
signing_key:
|
147
|
+
specification_version: 4
|
148
|
+
summary: Describe your authorization rules with JSON
|
149
|
+
test_files: []
|
150
|
+
has_rdoc:
|