chronos_authz 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +104 -0
- data/README.md +107 -0
- data/Rakefile +6 -0
- data/chronos_authz.gemspec +28 -0
- data/lib/chronos_authz.rb +16 -0
- data/lib/chronos_authz/acl.rb +81 -0
- data/lib/chronos_authz/authorizer.rb +39 -0
- data/lib/chronos_authz/configuration.rb +7 -0
- data/lib/chronos_authz/rule.rb +20 -0
- data/lib/chronos_authz/user.rb +13 -0
- data/lib/chronos_authz/validations/options_validator.rb +60 -0
- data/lib/chronos_authz/validations/validation_error.rb +11 -0
- data/lib/chronos_authz/version.rb +3 -0
- data/spec/acl_spec.rb +186 -0
- data/spec/authorizer_spec.rb +96 -0
- data/spec/config/authorizer_acl_test.yml +18 -0
- data/spec/config/error_page.html +1 -0
- data/spec/helpers/custom_rule.rb +9 -0
- data/spec/options_validator_spec.rb +85 -0
- data/spec/spec_helper.rb +115 -0
- metadata +179 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dc17a6fb18f79cbc7f42761ead6902e96c09d3b3
|
4
|
+
data.tar.gz: 57e17def29d8341147bb81ef427055384914e1f2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 53887e05e23adf136dff4856aa8bccec32815e36fde7add0012456db29b1a663a6e56c283f61d64298d634d7be42d4ab05a51530dcd795fda839a842ff91df1b
|
7
|
+
data.tar.gz: 92ccdfc2374509a8fd33132c7dbff4f188925b891f7143c8322e3d179f4660eba2d2336e06f6627b81fd718f198c3a6c66a3d3ce92d50089f4e07c47fdb1d4f9
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
.idea/
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
chronos-authz (0.0.1)
|
5
|
+
activesupport
|
6
|
+
railties (>= 4.2)
|
7
|
+
request_store
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
actionpack (5.2.0)
|
13
|
+
actionview (= 5.2.0)
|
14
|
+
activesupport (= 5.2.0)
|
15
|
+
rack (~> 2.0)
|
16
|
+
rack-test (>= 0.6.3)
|
17
|
+
rails-dom-testing (~> 2.0)
|
18
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
19
|
+
actionview (5.2.0)
|
20
|
+
activesupport (= 5.2.0)
|
21
|
+
builder (~> 3.1)
|
22
|
+
erubi (~> 1.4)
|
23
|
+
rails-dom-testing (~> 2.0)
|
24
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
25
|
+
activesupport (5.2.0)
|
26
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
27
|
+
i18n (>= 0.7, < 2)
|
28
|
+
minitest (~> 5.1)
|
29
|
+
tzinfo (~> 1.1)
|
30
|
+
ansi (1.5.0)
|
31
|
+
builder (3.2.3)
|
32
|
+
concurrent-ruby (1.0.5)
|
33
|
+
crass (1.0.4)
|
34
|
+
diff-lcs (1.3)
|
35
|
+
docile (1.3.1)
|
36
|
+
erubi (1.7.1)
|
37
|
+
hirb (0.7.3)
|
38
|
+
i18n (1.0.1)
|
39
|
+
concurrent-ruby (~> 1.0)
|
40
|
+
json (2.1.0)
|
41
|
+
loofah (2.2.2)
|
42
|
+
crass (~> 1.0.2)
|
43
|
+
nokogiri (>= 1.5.9)
|
44
|
+
method_source (0.9.0)
|
45
|
+
mini_portile2 (2.3.0)
|
46
|
+
minitest (5.11.3)
|
47
|
+
nokogiri (1.8.4)
|
48
|
+
mini_portile2 (~> 2.3.0)
|
49
|
+
rack (2.0.5)
|
50
|
+
rack-test (1.1.0)
|
51
|
+
rack (>= 1.0, < 3)
|
52
|
+
rails-dom-testing (2.0.3)
|
53
|
+
activesupport (>= 4.2.0)
|
54
|
+
nokogiri (>= 1.6)
|
55
|
+
rails-html-sanitizer (1.0.4)
|
56
|
+
loofah (~> 2.2, >= 2.2.2)
|
57
|
+
railties (5.2.0)
|
58
|
+
actionpack (= 5.2.0)
|
59
|
+
activesupport (= 5.2.0)
|
60
|
+
method_source
|
61
|
+
rake (>= 0.8.7)
|
62
|
+
thor (>= 0.18.1, < 2.0)
|
63
|
+
rake (12.0.0)
|
64
|
+
request_store (1.4.1)
|
65
|
+
rack (>= 1.4)
|
66
|
+
rspec (3.7.0)
|
67
|
+
rspec-core (~> 3.7.0)
|
68
|
+
rspec-expectations (~> 3.7.0)
|
69
|
+
rspec-mocks (~> 3.7.0)
|
70
|
+
rspec-core (3.7.1)
|
71
|
+
rspec-support (~> 3.7.0)
|
72
|
+
rspec-expectations (3.7.0)
|
73
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
74
|
+
rspec-support (~> 3.7.0)
|
75
|
+
rspec-mocks (3.7.0)
|
76
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
77
|
+
rspec-support (~> 3.7.0)
|
78
|
+
rspec-support (3.7.1)
|
79
|
+
simplecov (0.16.1)
|
80
|
+
docile (~> 1.1)
|
81
|
+
json (>= 1.8, < 3)
|
82
|
+
simplecov-html (~> 0.10.0)
|
83
|
+
simplecov-console (0.4.2)
|
84
|
+
ansi
|
85
|
+
hirb
|
86
|
+
simplecov
|
87
|
+
simplecov-html (0.10.2)
|
88
|
+
thor (0.20.0)
|
89
|
+
thread_safe (0.3.6)
|
90
|
+
tzinfo (1.2.5)
|
91
|
+
thread_safe (~> 0.1)
|
92
|
+
|
93
|
+
PLATFORMS
|
94
|
+
ruby
|
95
|
+
|
96
|
+
DEPENDENCIES
|
97
|
+
chronos-authz!
|
98
|
+
rake (>= 11.3.0)
|
99
|
+
rspec
|
100
|
+
simplecov
|
101
|
+
simplecov-console
|
102
|
+
|
103
|
+
BUNDLED WITH
|
104
|
+
1.16.2
|
data/README.md
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# chronos_authz
|
2
|
+
A declarative authorization Rack middleware that supports custom authorization logic on a per-resource basis
|
3
|
+
|
4
|
+
## Usage sample for Rails
|
5
|
+
### 1. Install the gem
|
6
|
+
```ruby
|
7
|
+
gem 'chronos_authz'
|
8
|
+
```
|
9
|
+
|
10
|
+
### 2. Configure the ACL config/my_acl.yml
|
11
|
+
An incoming request's http method and path will be checked against the ACL file's records for a match. At minimum, you MUST configure the path of the resource in an ACL record and SHOULD configure the http_method. If the http_method isn't configured, http method checking will not be done. You can define any other configuration here per resource as needed (ex. :permissions is a custom configuration and is defined here as this will be used in the custom authorization rule.
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
manage_accounts:
|
15
|
+
path: "/accounts/.*" # regex pattern
|
16
|
+
http_method: # if array is used, this would work as an OR operation: checking if the incoming http request's method matches ANY of the configured http_method
|
17
|
+
- GET
|
18
|
+
- put
|
19
|
+
permissions:
|
20
|
+
- VIEW_ACCOUNTS
|
21
|
+
- UPDATE_ACCOUNTS
|
22
|
+
|
23
|
+
create_users:
|
24
|
+
path: "/users"
|
25
|
+
http_method: POST
|
26
|
+
permissions:
|
27
|
+
- CREATE_USERS
|
28
|
+
|
29
|
+
update_users:
|
30
|
+
path: "/users/.*"
|
31
|
+
http_method: PUT
|
32
|
+
permissions:
|
33
|
+
- UPDATE_USERS
|
34
|
+
# rule: AnotherCustomRule # Use a different rule for this ACL record
|
35
|
+
```
|
36
|
+
|
37
|
+
### 2. Create an authorization rule initializers/MyCustomRule.rb
|
38
|
+
An authorization rule MUST implement the __request_authorized?__ method. The rule has an access to the ff. instance variables:
|
39
|
+
|
40
|
+
1. @request - [__Rack::Request__](https://www.rubydoc.info/gems/rack/Rack/Request) for the incoming HTTP request
|
41
|
+
2. @acl_record - __ChronosAuthz::ACL::Record__ from the ACL yml that matches the incoming request's http method and path. Custom configuration in the ACL yaml will be accessible from this object. ex. @acl_record.permissions
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
class MyCustomRule < ChronosAuthz::Rule
|
45
|
+
|
46
|
+
# Must return a boolean to check
|
47
|
+
def request_authorized?
|
48
|
+
(@acl_record.permissions - user_claims).blank?
|
49
|
+
end
|
50
|
+
|
51
|
+
# Optional. Implement how claims are retrieved for a given user. Normally claims could be retrieved using cookies,
|
52
|
+
# JWT/id_token decoded from the request header, API calls, or a database query. Any value returned here will be available to the ChronosAuthz::User.claims helper module as well.
|
53
|
+
def user_claims
|
54
|
+
# SAMPLE ONLY! In this sample configuration, only the user with the access token '1d1234913em23' would only be able to successfully send a POST request to /users. Access token 'm123493429304' bearer could both create and update a User.
|
55
|
+
access_tokens = {
|
56
|
+
"1d1234913em23" => ["CREATE_USERS"],
|
57
|
+
"m123493429304" => ["CREATE_USERS", "UPDATE_USERS", "SomeOtherClaimInOtherFormat", "any-format-should-work-claim"]
|
58
|
+
}
|
59
|
+
|
60
|
+
access_token_from_request = @request.get_header("HTTP_AUTHORIZATION").gsub("Bearer ")
|
61
|
+
return (access_tokens[access_token_from_request] || [])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
### 4. Use the middleware
|
67
|
+
```ruby
|
68
|
+
Rails.application.config.middleware.use ChronosAuthz::Authorizer do |config|
|
69
|
+
# Required. Default authorization rule to use
|
70
|
+
config.default_rule = MyCustomRule
|
71
|
+
|
72
|
+
# Optional. Default is false. If set to true, the ACL is treated as a whitelist of resource paths: authorization would return a 403 error if no ACL Record has been configured for a given resource path. If set to false, authorization check will only be done to the resources configured in the ACL.
|
73
|
+
config.strict_mode = true
|
74
|
+
|
75
|
+
# Optional. Configure the error page to render when authorization fails
|
76
|
+
config.error_page = "public/403.html"
|
77
|
+
|
78
|
+
# Optional. Default behavior will look for 'config/authorizer_acl.yml'. Configure which ACL yml to use
|
79
|
+
config.acl_yaml = "config/my_acl.yml"
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
## Helpers
|
84
|
+
Include the helper module __ChronosAuthz::User__ as needed to have access to the current user's claims via the __.claims__ method.
|
85
|
+
example:
|
86
|
+
```ruby
|
87
|
+
class User < ActiveRecord
|
88
|
+
include ChronosAuthz::User
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
With this helper included in your User model and assuming you are using Devise or any other AuthN solution, you may do the ff.:
|
93
|
+
```ruby
|
94
|
+
current_user.claims
|
95
|
+
=> ["CREATE_USERS"]
|
96
|
+
```
|
97
|
+
|
98
|
+
If the return value of user_claims method in your implementation is a hash:
|
99
|
+
```ruby
|
100
|
+
current_user.claims
|
101
|
+
=> {permissions: ["CREATE_USERS"], email: "someemail@yourdomain.com"}
|
102
|
+
|
103
|
+
current_user.claim[:email]
|
104
|
+
=> someemail@yourdomain.com
|
105
|
+
```
|
106
|
+
|
107
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
$LOAD_PATH.push File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
require "chronos_authz/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "chronos_authz"
|
7
|
+
s.version = ChronosAuthz::VERSION
|
8
|
+
s.authors = ["Marianne Angelie del Mundo", "Rodette Pedro", "JR Respino", "Jayson Uy"]
|
9
|
+
s.email = %w(marianne@chronoscloud.com rodette@chronoscloud.com jr@chronoscloud.com jayson@chronoscloud.com)
|
10
|
+
s.homepage = "https://github.com/chronoscloud/chronoscloud-authz"
|
11
|
+
s.summary = "A minimal and declarative authorization layer"
|
12
|
+
s.description = "A declarative authorization Rack middleware that supports custom authorization logic on a per-resource basis"
|
13
|
+
s.license = 'N/A'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_dependency "railties", ">= 4.2"
|
20
|
+
s.add_dependency "request_store"
|
21
|
+
s.add_dependency "activesupport"
|
22
|
+
s.required_ruby_version = ">= 2.4"
|
23
|
+
|
24
|
+
s.add_development_dependency "rake", ">= 11.3.0"
|
25
|
+
s.add_development_dependency "rspec-rails"
|
26
|
+
s.add_development_dependency "simplecov"
|
27
|
+
s.add_development_dependency "simplecov-console"
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'active_support/core_ext/string'
|
2
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'request_store'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
require 'chronos_authz/validations/validation_error'
|
8
|
+
require 'chronos_authz/validations/options_validator'
|
9
|
+
require 'chronos_authz/configuration'
|
10
|
+
require 'chronos_authz/acl'
|
11
|
+
require 'chronos_authz/rule'
|
12
|
+
require 'chronos_authz/user'
|
13
|
+
require 'chronos_authz/authorizer'
|
14
|
+
|
15
|
+
module ChronosAuthz
|
16
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module ChronosAuthz
|
2
|
+
class ACL
|
3
|
+
attr_accessor :acl_hash, :records
|
4
|
+
|
5
|
+
def self.build_from_yaml(acl_yaml = nil)
|
6
|
+
acl_yaml ||= 'config/authorizer_acl.yml'
|
7
|
+
acl_hash = YAML.load_file(acl_yaml)
|
8
|
+
records = ACL.records_from_acl_hash(acl_hash)
|
9
|
+
return ACL.new(records, acl_hash)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Populate @records with instances of ACL::Record and validate each instances
|
13
|
+
def self.records_from_acl_hash(acl_hash)
|
14
|
+
return acl_hash.map do |record_name, record_options = {}|
|
15
|
+
ChronosAuthz::ACL::Record.new(record_options.merge!(name: record_name))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(records = [], acl_hash = nil)
|
20
|
+
@records = records
|
21
|
+
@acl_hash = acl_hash
|
22
|
+
end
|
23
|
+
|
24
|
+
# Find matching ACL Record
|
25
|
+
def find_match(http_method, request_path)
|
26
|
+
record = @records.select{ |record| record.matches?(http_method, request_path) }.first
|
27
|
+
# puts "[ChronosAuthz] Found ACL match: #{record.to_s}" if record
|
28
|
+
|
29
|
+
return record
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
class Record < OpenStruct
|
34
|
+
include ChronosAuthz::Validations::OptionsValidator
|
35
|
+
|
36
|
+
VALID_HTTP_METHODS = ["GET",
|
37
|
+
"POST",
|
38
|
+
"PUT",
|
39
|
+
"PATCH",
|
40
|
+
"DELETE",
|
41
|
+
"HEAD"].freeze
|
42
|
+
|
43
|
+
required :name, :path
|
44
|
+
check_constraint :http_method, VALID_HTTP_METHODS, allow_nil: true
|
45
|
+
|
46
|
+
def initialize(value = {})
|
47
|
+
value = value.with_indifferent_access
|
48
|
+
value[:http_method] = normalize_http_methods(value[:http_method])
|
49
|
+
value[:path] = normalize_path(value[:path])
|
50
|
+
|
51
|
+
super(value)
|
52
|
+
validate!
|
53
|
+
end
|
54
|
+
|
55
|
+
def matches?(http_method, request_path)
|
56
|
+
return false if request_path.nil?
|
57
|
+
|
58
|
+
request_path = normalize_path(request_path)
|
59
|
+
path_pattern = /\A#{self.path}\z/
|
60
|
+
|
61
|
+
method_matched = self.http_method.empty? || self.http_method.include?(http_method.to_s.upcase)
|
62
|
+
|
63
|
+
return false if !method_matched
|
64
|
+
return !request_path.match(path_pattern).nil?
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def normalize_path(resource_path)
|
70
|
+
return resource_path.squish if !resource_path.nil?
|
71
|
+
end
|
72
|
+
|
73
|
+
def normalize_http_methods(http_methods)
|
74
|
+
http_methods = [http_methods] if !http_methods.is_a? Array
|
75
|
+
http_methods.map!{ |http_method| http_method.to_s.upcase }
|
76
|
+
|
77
|
+
return http_methods.reject { |http_method| http_method.blank? }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ChronosAuthz
|
2
|
+
|
3
|
+
class Authorizer
|
4
|
+
|
5
|
+
attr_accessor :configuration, :acl
|
6
|
+
|
7
|
+
def initialize(app, options = {})
|
8
|
+
@app, @configuration = app, ::ChronosAuthz::Configuration.new(options)
|
9
|
+
|
10
|
+
yield @configuration if block_given?
|
11
|
+
@configuration.validate!
|
12
|
+
@acl = ChronosAuthz::ACL.build_from_yaml(@configuration.acl_yaml)
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
matched_acl_record = @acl.find_match(env["REQUEST_METHOD"], env["PATH_INFO"])
|
18
|
+
|
19
|
+
return render_unauthorized if @configuration.strict_mode && matched_acl_record.nil?
|
20
|
+
|
21
|
+
request = Rack::Request.new(env)
|
22
|
+
rule_class = matched_acl_record.try(:rule).try(:constantize) || @configuration.default_rule
|
23
|
+
rule_instance = rule_class.new(request, matched_acl_record)
|
24
|
+
|
25
|
+
return render_unauthorized if !rule_instance.request_authorized?
|
26
|
+
|
27
|
+
RequestStore.store[:chronos_authz_claims] = rule_instance.user_claims
|
28
|
+
status, headers, response = @app.call(env)
|
29
|
+
end
|
30
|
+
|
31
|
+
def render_unauthorized
|
32
|
+
if @configuration.error_page
|
33
|
+
# html = ActionView::Base.new.render(file: @configuration.error_page)
|
34
|
+
return [403, {'Content-Type' => 'text/html'}, [File.read(@configuration.error_page)]]
|
35
|
+
end
|
36
|
+
return [403, {'Content-Type' => 'text/plain'}, ["Unauthorized"]]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ChronosAuthz
|
2
|
+
class Rule
|
3
|
+
|
4
|
+
attr_accessor :request, :acl_record
|
5
|
+
|
6
|
+
def initialize(request, acl_record)
|
7
|
+
@request = request
|
8
|
+
@acl_record = acl_record
|
9
|
+
end
|
10
|
+
|
11
|
+
def user_claims
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def request_authorized?
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module ChronosAuthz
|
2
|
+
module Validations
|
3
|
+
module OptionsValidator
|
4
|
+
|
5
|
+
def self.included base
|
6
|
+
base.extend OptionsValidatorClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module OptionsValidatorClassMethods
|
10
|
+
attr_accessor :required_options, :predefined_value_map
|
11
|
+
|
12
|
+
def required(*options)
|
13
|
+
self.required_options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def check_constraint(option, predefined_values = [], constraint_options = {})
|
17
|
+
self.predefined_value_map ||= {}
|
18
|
+
self.predefined_value_map[option] = { check_values: predefined_values,
|
19
|
+
constraint_options: constraint_options }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate!
|
24
|
+
|
25
|
+
# Required options
|
26
|
+
if !self.class.required_options.nil?
|
27
|
+
self.class.required_options.each do |required_option|
|
28
|
+
option_value = send(required_option) if respond_to?(required_option)
|
29
|
+
raise ChronosAuthz::Validations::ValidationError.new("Missing option #{required_option} in #{self.class}") if option_value.blank?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Check constraint
|
34
|
+
if !self.class.predefined_value_map.nil?
|
35
|
+
self.class.predefined_value_map.each do |key, value|
|
36
|
+
option_values = send(key)
|
37
|
+
check_values = value[:check_values]
|
38
|
+
|
39
|
+
if !option_values.is_a? Array
|
40
|
+
option_values = [option_values]
|
41
|
+
end
|
42
|
+
|
43
|
+
if value[:constraint_options][:case_sensitive].nil? || !value[:constraint_options][:case_sensitive]
|
44
|
+
option_values = option_values.map{|option_value| option_value.to_s.upcase }
|
45
|
+
check_values = check_values.map{|check_value| check_value.to_s.upcase }
|
46
|
+
end
|
47
|
+
|
48
|
+
option_values.each do |option_value|
|
49
|
+
next if value[:constraint_options][:allow_nil] && (option_value.nil? || option_value.empty?)
|
50
|
+
raise ChronosAuthz::Validations::ValidationError.new("Invalid option value #{option_value} for #{key} in #{self.class}. Valid values are #{check_values}.") if !check_values.include?(option_value)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
self
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
data/spec/acl_spec.rb
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ChronosAuthz::ACL do
|
4
|
+
let(:acl_yaml) { 'spec/config/authorizer_acl_test.yml' }
|
5
|
+
|
6
|
+
describe 'attribute accessors' do
|
7
|
+
it 'assigns acl_hash' do
|
8
|
+
acl_object = ChronosAuthz::ACL.build_from_yaml(acl_yaml)
|
9
|
+
|
10
|
+
expect(acl_object.acl_hash).to_not be_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'assigns records' do
|
14
|
+
acl_object = ChronosAuthz::ACL.build_from_yaml(acl_yaml)
|
15
|
+
|
16
|
+
expect(acl_object.records).to_not be_empty
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '.build_from_yaml' do
|
21
|
+
context 'when acl_yaml is specified' do
|
22
|
+
it 'loads a YAML file from the acl_yaml' do
|
23
|
+
allow(YAML).to receive(:load_file).and_return(YAML.load_file(acl_yaml))
|
24
|
+
expect(YAML).to receive(:load_file).with(acl_yaml)
|
25
|
+
|
26
|
+
ChronosAuthz::ACL.build_from_yaml(acl_yaml)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when no acl_yaml is specified' do
|
31
|
+
it 'loads a YAML file using a default path' do
|
32
|
+
allow(YAML).to receive(:load_file).and_return(YAML.load_file(acl_yaml))
|
33
|
+
expect(YAML).to receive(:load_file).with('config/authorizer_acl.yml')
|
34
|
+
|
35
|
+
ChronosAuthz::ACL.build_from_yaml
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'initializes a new ACL' do
|
40
|
+
expect(ChronosAuthz::ACL).to receive(:new)
|
41
|
+
|
42
|
+
ChronosAuthz::ACL.build_from_yaml(acl_yaml)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'returns an ACL object' do
|
46
|
+
expect(ChronosAuthz::ACL.build_from_yaml(acl_yaml)).to be_an_instance_of(ChronosAuthz::ACL)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '.records_from_acl_hash' do
|
52
|
+
it 'returns an array of ACL::Record from a YAML hash' do
|
53
|
+
records = ChronosAuthz::ACL.records_from_acl_hash(YAML.load_file(acl_yaml))
|
54
|
+
expect(records).to_not be_empty
|
55
|
+
expect(records).to all(be_an(ChronosAuthz::ACL::Record))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#find_match' do
|
60
|
+
let(:acl) { ChronosAuthz::ACL.build_from_yaml(acl_yaml) }
|
61
|
+
|
62
|
+
context 'when using valid parameters' do
|
63
|
+
context 'when http_method and request_path is in config' do
|
64
|
+
|
65
|
+
it 'returns an ACLRecord' do
|
66
|
+
expect(acl.find_match('POST', '/users')).to be_an_instance_of(ChronosAuthz::ACL::Record)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'finds the record that matches the path pattern' do
|
70
|
+
expect(acl.find_match('GET', '/accounts/2')).to_not be_nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when http_method is not in config' do
|
75
|
+
it 'returns nil' do
|
76
|
+
expect(acl.find_match('DELETE', '/users')).to be_nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'when request_path is not in config' do
|
81
|
+
it 'returns nil' do
|
82
|
+
expect(acl.find_match('GET', '/test')).to be_nil
|
83
|
+
expect(acl.find_match('POST', '/users////')).to be_nil
|
84
|
+
expect(acl.find_match('POST', '/user/1/1/1/')).to be_nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'when using null parameters' do
|
90
|
+
context 'when http_method is null' do
|
91
|
+
it 'returns nil' do
|
92
|
+
expect(acl.find_match(nil, '/test')).to be_nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'when resource_path is null' do
|
97
|
+
it 'returns nil' do
|
98
|
+
expect(acl.find_match('GET', 'nil')).to be_nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
describe ChronosAuthz::ACL::Record do
|
107
|
+
let(:acl_record_string_method) { ChronosAuthz::ACL::Record.new(name: 'create_users',
|
108
|
+
http_method: 'post',
|
109
|
+
path: ' /users ') }
|
110
|
+
let(:acl_record_array_method) { ChronosAuthz::ACL::Record.new(name: 'create_users',
|
111
|
+
http_method: ['POST', 'put'],
|
112
|
+
path: '/users') }
|
113
|
+
let(:acl_record_implicit_all_method) { ChronosAuthz::ACL::Record.new(name: 'create_users',
|
114
|
+
path: '/users') }
|
115
|
+
|
116
|
+
describe 'initialization' do
|
117
|
+
context 'when http_method is specified' do
|
118
|
+
context 'with a valid HTTP method' do
|
119
|
+
it 'assigns http_method as a normalized array' do
|
120
|
+
expect(acl_record_string_method.http_method).to_not be_empty
|
121
|
+
expect(acl_record_string_method.http_method).eql?(['POST'])
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'with an invalid HTTP method' do
|
126
|
+
it 'raises a validation error' do
|
127
|
+
expect{ ChronosAuthz::ACL::Record.new(name: 'create_users',
|
128
|
+
http_method: 'GETS',
|
129
|
+
path: '/users') }.to raise_error(ChronosAuthz::Validations::ValidationError)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context 'when path is specified' do
|
135
|
+
it 'assigns a squished http_method' do
|
136
|
+
expect(acl_record_string_method.path).eql?("/users")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'raises a validation error if name is not specified' do
|
141
|
+
expect{ ChronosAuthz::ACL::Record.new(path: '/users') }.to raise_error(ChronosAuthz::Validations::ValidationError)
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'raises a validation error if path is not specified' do
|
145
|
+
expect{ ChronosAuthz::ACL::Record.new(name: 'create_users') }.to raise_error(ChronosAuthz::Validations::ValidationError)
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
describe '#matches?' do
|
151
|
+
context 'when request_path matches the request_path regex pattern config' do
|
152
|
+
context 'with an array http_method config' do
|
153
|
+
it 'returns true if http_method has a match from the http_method array config' do
|
154
|
+
expect(acl_record_array_method.matches?('POST', '/users')).to be true
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'returns false if http_method doesn\'t have a match from the http_method array config' do
|
158
|
+
expect(acl_record_array_method.matches?('GET', '/accounts')).to be false
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'with a string http_method config' do
|
163
|
+
it 'returns true if http_method matches the http_method string config' do
|
164
|
+
expect(acl_record_string_method.matches?('POST', '/users')).to be true
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'returns false if http_method doesn\'t match the http_method string config' do
|
168
|
+
expect(acl_record_string_method.matches?('get', '/accounts')).to be false
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'with a nil http_method config' do
|
173
|
+
it 'returns true' do
|
174
|
+
expect(acl_record_implicit_all_method.matches?('post', '/users')).to be true
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'when request_path doesn\'t match the request_path regex pattern config' do
|
180
|
+
it 'returns false' do
|
181
|
+
expect(acl_record_implicit_all_method.matches?('post', '/account/')).to be false
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rack'
|
3
|
+
describe ChronosAuthz::Authorizer do
|
4
|
+
|
5
|
+
class AuthorizeAllRequestRule < ChronosAuthz::Rule
|
6
|
+
def request_authorized?
|
7
|
+
true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class BlockAllRequestsRule < ChronosAuthz::Rule
|
12
|
+
def request_authorized?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "Middleware configuration" do
|
18
|
+
|
19
|
+
it "should raise an error if default_rule isn't configured" do
|
20
|
+
env = mock_env("/")
|
21
|
+
app = lambda{}
|
22
|
+
expect{ rack_app(app).call(env) }.to raise_error(ChronosAuthz::Validations::ValidationError)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should use default_rule for authorization check when no :rule option was specified in the matched ACL record" do
|
26
|
+
env = mock_env("/accounts/")
|
27
|
+
app = lambda{ |env| [200, {'Content-Type' => 'text/plain'}, ["OK"]] }
|
28
|
+
default_rule_class = AuthorizeAllRequestRule
|
29
|
+
expect_any_instance_of(default_rule_class).to receive(:request_authorized?)
|
30
|
+
rack_app(app, :default_rule => default_rule_class, :strict_mode => false).call(env)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should use the matched ACL Record's :rule option if it is specified" do
|
34
|
+
env = mock_env("/users", method: "POST")
|
35
|
+
app = lambda{ |env| [200, {'Content-Type' => 'text/plain'}, ["OK"]] }
|
36
|
+
expect_any_instance_of(ChronosAuthz::Spec::Helpers::CustomRule).to receive(:request_authorized?)
|
37
|
+
expect_any_instance_of(AuthorizeAllRequestRule).to_not receive(:request_authorized?)
|
38
|
+
rack_app(app, :default_rule => AuthorizeAllRequestRule, :strict_mode => false).call(env)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should use the configured error_page if the request is unauthorized" do
|
42
|
+
env = mock_env("/accounts/123", method: "PUT")
|
43
|
+
app = lambda{ |env| [200, {'Content-Type' => 'text/plain'}, ["OK"]] }
|
44
|
+
error_page_path = 'spec/config/error_page.html'
|
45
|
+
error_page_contents = File.read(error_page_path)
|
46
|
+
result = rack_app(app, :default_rule => BlockAllRequestsRule, :error_page => error_page_path).call(env)
|
47
|
+
expect(result.last.first).to eq(error_page_contents)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "Authorization" do
|
52
|
+
|
53
|
+
it "should fail with a 403 response if strict_mode configuration is set to true and no ACL Record was configured for the incoming request" do
|
54
|
+
env = mock_env("/some_unknown_path")
|
55
|
+
app = lambda{}
|
56
|
+
result = rack_app(app, :default_rule => AuthorizeAllRequestRule, :strict_mode => true).call(env)
|
57
|
+
expect(result.first).to eq(403)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should not fail with a 403 response if strict_mode configuration is not set to true and no ACL Record was configured for the incoming request" do
|
61
|
+
env = mock_env("/")
|
62
|
+
app = lambda{ |env| [200, {'Content-Type' => 'text/plain'}, ["OK"]] }
|
63
|
+
result = rack_app(app, :default_rule => AuthorizeAllRequestRule, :strict_mode => false).call(env)
|
64
|
+
expect(result.first).to eq(200)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should fail with a 403 response if authorization check failed" do
|
68
|
+
env = mock_env("/")
|
69
|
+
app = lambda{}
|
70
|
+
result = rack_app(app, :default_rule => BlockAllRequestsRule).call(env)
|
71
|
+
expect(result.first).to eq(403)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should not fail with a 403 response if authorization check succeeded" do
|
75
|
+
env = mock_env("/")
|
76
|
+
app = lambda{ |env| [200, {'Content-Type' => 'text/plain'}, ["OK"]] }
|
77
|
+
result = rack_app(app, :default_rule => AuthorizeAllRequestRule).call(env)
|
78
|
+
expect(result.first).to eq(200)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def rack_app(app, options= {})
|
83
|
+
options[:acl_yaml] ||= 'spec/config/authorizer_acl_test.yml'
|
84
|
+
Rack::Builder.new do
|
85
|
+
use ChronosAuthz::Authorizer, options
|
86
|
+
run app
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def mock_env(path = "/", params = {})
|
91
|
+
method = params.delete(:method) || "GET"
|
92
|
+
env = { 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => "#{method}" }
|
93
|
+
Rack::MockRequest.env_for("#{path}?#{Rack::Utils.build_query(params)}", env)
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
manage_accounts:
|
2
|
+
path: "/accounts/.*"
|
3
|
+
http_method:
|
4
|
+
- GET
|
5
|
+
- put
|
6
|
+
permissions:
|
7
|
+
- AUTH::VIEW_ACCOUNT
|
8
|
+
- AUTH::MANAGE_ACCOUNTS
|
9
|
+
|
10
|
+
create_users:
|
11
|
+
path: "/users"
|
12
|
+
http_method: POST
|
13
|
+
permissions:
|
14
|
+
- AUTH::CREATE_USERS
|
15
|
+
rule: ChronosAuthz::Spec::Helpers::CustomRule
|
16
|
+
|
17
|
+
shipment:
|
18
|
+
path: "/*"
|
@@ -0,0 +1 @@
|
|
1
|
+
Custom error page
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ChronosAuthz::Validations::OptionsValidator do
|
4
|
+
PRIMARY_COLORS = ["red", "yellow", "blue"].freeze
|
5
|
+
|
6
|
+
class WithRequiredAndCheckConstraint < OpenStruct
|
7
|
+
include ChronosAuthz::Validations::OptionsValidator
|
8
|
+
required :some_required_attribute
|
9
|
+
check_constraint :primary_color, PRIMARY_COLORS
|
10
|
+
end
|
11
|
+
|
12
|
+
class WithRequiredCheckConstraintOption < OpenStruct
|
13
|
+
include ChronosAuthz::Validations::OptionsValidator
|
14
|
+
check_constraint :primary_color, PRIMARY_COLORS, allow_nil: false
|
15
|
+
end
|
16
|
+
|
17
|
+
class WithCheckConstraint < OpenStruct
|
18
|
+
include ChronosAuthz::Validations::OptionsValidator
|
19
|
+
check_constraint :primary_color, ["red", "yellow", "blue"], allow_nil: true
|
20
|
+
end
|
21
|
+
|
22
|
+
class WithCaseSensitiveCheckConstraintOption < OpenStruct
|
23
|
+
include ChronosAuthz::Validations::OptionsValidator
|
24
|
+
check_constraint :primary_color, ["red", "yellow", "blue"], case_sensitive: true
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'required options validation' do
|
28
|
+
|
29
|
+
it 'raises a validation error if a required option is nil' do
|
30
|
+
expect{ WithRequiredAndCheckConstraint.new.validate! }.to raise_error(ChronosAuthz::Validations::ValidationError)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'check constraint validation' do
|
36
|
+
|
37
|
+
context 'when option value is a string' do
|
38
|
+
it 'raises a validation error if the option value isn\'t found from the valid option values' do
|
39
|
+
expect{ WithRequiredAndCheckConstraint.new(some_required_attribute: 'some_value', primary_color: "brown").validate! }.to raise_error(ChronosAuthz::Validations::ValidationError)
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'with allow_nil config set to false' do
|
43
|
+
it 'raises a validation error if option value is nil' do
|
44
|
+
expect{ WithRequiredCheckConstraintOption.new.validate! }.to raise_error(ChronosAuthz::Validations::ValidationError)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'with allow_nil config set to true' do
|
49
|
+
it 'doesn\'t raise any validation errors if option value is nil' do
|
50
|
+
expect{ WithCheckConstraint.new.validate! }.to_not raise_error(ChronosAuthz::Validations::ValidationError)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with case_sensitive config set to true' do
|
55
|
+
it 'raises a validation error if option value is meaningfully equal to any of the valid option values but in different case' do
|
56
|
+
expect{ WithCaseSensitiveCheckConstraintOption.new(primary_color: "RED").validate! }.to raise_error(ChronosAuthz::Validations::ValidationError)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when option value is an array' do
|
62
|
+
it 'raises a validation error if an element from the option value isn\'t found from the valid option values' do
|
63
|
+
expect{ WithRequiredAndCheckConstraint.new(some_required_attribute: 'some_value', primary_color: ["RED", "brown"]).validate! }.to raise_error(ChronosAuthz::Validations::ValidationError)
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'with allow_nil config set to false' do
|
67
|
+
it 'raises a validation error if an element from the option value is nil' do
|
68
|
+
expect{ WithRequiredCheckConstraintOption.new(primary_color: ["blue", nil]).validate! }.to raise_error(ChronosAuthz::Validations::ValidationError)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with allow_nil config set to true' do
|
73
|
+
it 'doesn\'t raise any validation errors if an element from the option value is nil' do
|
74
|
+
expect{ WithCheckConstraint.new(primary_color: ["blue", nil]).validate! }.to_not raise_error(ChronosAuthz::Validations::ValidationError)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'with case_sensitive config set to true' do
|
79
|
+
it 'raises a validation error if an element from the option value is meaningfully equal to any of the valid option values but in different case' do
|
80
|
+
expect{ WithCaseSensitiveCheckConstraintOption.new(primary_color: ["blue", "ReD"]).validate! }.to raise_error(ChronosAuthz::Validations::ValidationError)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
require 'chronos_authz'
|
5
|
+
|
6
|
+
require 'simplecov'
|
7
|
+
require 'simplecov-console'
|
8
|
+
|
9
|
+
Dir[File.join(File.dirname(__FILE__), "helpers", "**/*.rb")].each do |f|
|
10
|
+
require f
|
11
|
+
end
|
12
|
+
|
13
|
+
SimpleCov.formatter = SimpleCov.formatter = SimpleCov::Formatter::Console
|
14
|
+
SimpleCov.start
|
15
|
+
|
16
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
17
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
18
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
19
|
+
# this file to always be loaded, without a need to explicitly require it in any
|
20
|
+
# files.
|
21
|
+
#
|
22
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
23
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
24
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
25
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
26
|
+
# a separate helper file that requires the additional dependencies and performs
|
27
|
+
# the additional setup, and require it from the spec files that actually need
|
28
|
+
# it.
|
29
|
+
#
|
30
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
31
|
+
# users commonly want.
|
32
|
+
#
|
33
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
34
|
+
RSpec::Expectations.configuration.on_potential_false_positives = :nothing
|
35
|
+
RSpec.configure do |config|
|
36
|
+
# rspec-expectations config goes here. You can use an alternate
|
37
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
38
|
+
# assertions if you prefer.
|
39
|
+
# config.include(ChronosAuthz::Spec::Helpers)
|
40
|
+
|
41
|
+
config.expect_with :rspec do |expectations|
|
42
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
43
|
+
# and `failure_message` of custom matchers include text for helper methods
|
44
|
+
# defined using `chain`, e.g.:
|
45
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
46
|
+
# # => "be bigger than 2 and smaller than 4"
|
47
|
+
# ...rather than:
|
48
|
+
# # => "be bigger than 2"
|
49
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
54
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
55
|
+
config.mock_with :rspec do |mocks|
|
56
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
57
|
+
# a real object. This is generally recommended, and will default to
|
58
|
+
# `true` in RSpec 4.
|
59
|
+
mocks.verify_partial_doubles = true
|
60
|
+
end
|
61
|
+
|
62
|
+
# The settings below are suggested to provide a good initial experience
|
63
|
+
# with RSpec, but feel free to customize to your heart's content.
|
64
|
+
=begin
|
65
|
+
# These two settings work together to allow you to limit a spec run
|
66
|
+
# to individual examples or groups you care about by tagging them with
|
67
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
68
|
+
# get run.
|
69
|
+
config.filter_run :focus
|
70
|
+
config.run_all_when_everything_filtered = true
|
71
|
+
|
72
|
+
# Allows RSpec to persist some state between runs in order to support
|
73
|
+
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
74
|
+
# you configure your source control system to ignore this file.
|
75
|
+
config.example_status_persistence_file_path = "spec/examples.txt"
|
76
|
+
|
77
|
+
# Limits the available syntax to the non-monkey patched syntax that is
|
78
|
+
# recommended. For more details, see:
|
79
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
80
|
+
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
81
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
82
|
+
config.disable_monkey_patching!
|
83
|
+
|
84
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
85
|
+
# be too noisy due to issues in dependencies.
|
86
|
+
config.warnings = true
|
87
|
+
|
88
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
89
|
+
# file, and it's useful to allow more verbose output when running an
|
90
|
+
# individual spec file.
|
91
|
+
if config.files_to_run.one?
|
92
|
+
# Use the documentation formatter for detailed output,
|
93
|
+
# unless a formatter has already been configured
|
94
|
+
# (e.g. via a command-line flag).
|
95
|
+
config.default_formatter = 'doc'
|
96
|
+
end
|
97
|
+
|
98
|
+
# Print the 10 slowest examples and example groups at the
|
99
|
+
# end of the spec run, to help surface which specs are running
|
100
|
+
# particularly slow.
|
101
|
+
config.profile_examples = 10
|
102
|
+
|
103
|
+
# Run specs in random order to surface order dependencies. If you find an
|
104
|
+
# order dependency and want to debug it, you can fix the order by providing
|
105
|
+
# the seed, which is printed after each run.
|
106
|
+
# --seed 1234
|
107
|
+
config.order = :random
|
108
|
+
|
109
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
110
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
111
|
+
# test failures related to randomization by passing the same `--seed` value
|
112
|
+
# as the one that triggered the failure.
|
113
|
+
Kernel.srand config.seed
|
114
|
+
=end
|
115
|
+
end
|
metadata
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chronos_authz
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marianne Angelie del Mundo
|
8
|
+
- Rodette Pedro
|
9
|
+
- JR Respino
|
10
|
+
- Jayson Uy
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
date: 2018-08-10 00:00:00.000000000 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: railties
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '4.2'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '4.2'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: request_store
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
- !ruby/object:Gem::Dependency
|
45
|
+
name: activesupport
|
46
|
+
requirement: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
type: :runtime
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: rake
|
60
|
+
requirement: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 11.3.0
|
65
|
+
type: :development
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: 11.3.0
|
72
|
+
- !ruby/object:Gem::Dependency
|
73
|
+
name: rspec-rails
|
74
|
+
requirement: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
type: :development
|
80
|
+
prerelease: false
|
81
|
+
version_requirements: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
- !ruby/object:Gem::Dependency
|
87
|
+
name: simplecov
|
88
|
+
requirement: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
type: :development
|
94
|
+
prerelease: false
|
95
|
+
version_requirements: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
- !ruby/object:Gem::Dependency
|
101
|
+
name: simplecov-console
|
102
|
+
requirement: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
type: :development
|
108
|
+
prerelease: false
|
109
|
+
version_requirements: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
description: A declarative authorization Rack middleware that supports custom authorization
|
115
|
+
logic on a per-resource basis
|
116
|
+
email:
|
117
|
+
- marianne@chronoscloud.com
|
118
|
+
- rodette@chronoscloud.com
|
119
|
+
- jr@chronoscloud.com
|
120
|
+
- jayson@chronoscloud.com
|
121
|
+
executables: []
|
122
|
+
extensions: []
|
123
|
+
extra_rdoc_files: []
|
124
|
+
files:
|
125
|
+
- ".gitignore"
|
126
|
+
- ".rspec"
|
127
|
+
- Gemfile
|
128
|
+
- Gemfile.lock
|
129
|
+
- README.md
|
130
|
+
- Rakefile
|
131
|
+
- chronos_authz.gemspec
|
132
|
+
- lib/chronos_authz.rb
|
133
|
+
- lib/chronos_authz/acl.rb
|
134
|
+
- lib/chronos_authz/authorizer.rb
|
135
|
+
- lib/chronos_authz/configuration.rb
|
136
|
+
- lib/chronos_authz/rule.rb
|
137
|
+
- lib/chronos_authz/user.rb
|
138
|
+
- lib/chronos_authz/validations/options_validator.rb
|
139
|
+
- lib/chronos_authz/validations/validation_error.rb
|
140
|
+
- lib/chronos_authz/version.rb
|
141
|
+
- spec/acl_spec.rb
|
142
|
+
- spec/authorizer_spec.rb
|
143
|
+
- spec/config/authorizer_acl_test.yml
|
144
|
+
- spec/config/error_page.html
|
145
|
+
- spec/helpers/custom_rule.rb
|
146
|
+
- spec/options_validator_spec.rb
|
147
|
+
- spec/spec_helper.rb
|
148
|
+
homepage: https://github.com/chronoscloud/chronoscloud-authz
|
149
|
+
licenses:
|
150
|
+
- N/A
|
151
|
+
metadata: {}
|
152
|
+
post_install_message:
|
153
|
+
rdoc_options: []
|
154
|
+
require_paths:
|
155
|
+
- lib
|
156
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '2.4'
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
requirements: []
|
167
|
+
rubyforge_project:
|
168
|
+
rubygems_version: 2.6.8
|
169
|
+
signing_key:
|
170
|
+
specification_version: 4
|
171
|
+
summary: A minimal and declarative authorization layer
|
172
|
+
test_files:
|
173
|
+
- spec/acl_spec.rb
|
174
|
+
- spec/authorizer_spec.rb
|
175
|
+
- spec/config/authorizer_acl_test.yml
|
176
|
+
- spec/config/error_page.html
|
177
|
+
- spec/helpers/custom_rule.rb
|
178
|
+
- spec/options_validator_spec.rb
|
179
|
+
- spec/spec_helper.rb
|