shaf 1.6.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/iana_link_relations.csv.gz +0 -0
- data/lib/shaf.rb +6 -0
- data/lib/shaf/alps/attribute_serializer.rb +41 -0
- data/lib/shaf/alps/json_serializer.rb +50 -0
- data/lib/shaf/alps/relation_serializer.rb +70 -0
- data/lib/shaf/api_doc/link_relations.rb +77 -0
- data/lib/shaf/authenticator.rb +56 -0
- data/lib/shaf/authenticator/base.rb +161 -0
- data/lib/shaf/authenticator/basic_auth.rb +25 -0
- data/lib/shaf/authenticator/challenge.rb +32 -0
- data/lib/shaf/authenticator/parameter.rb +31 -0
- data/lib/shaf/authenticator/request.rb +17 -0
- data/lib/shaf/command/console.rb +1 -1
- data/lib/shaf/command/generate.rb +5 -2
- data/lib/shaf/command/new.rb +20 -7
- data/lib/shaf/command/templates/Gemfile.erb +1 -0
- data/{templates/config/settings.yml → lib/shaf/command/templates/config/settings.yml.erb} +1 -5
- data/lib/shaf/errors.rb +11 -0
- data/lib/shaf/extensions.rb +3 -3
- data/lib/shaf/extensions/api_routes.rb +60 -0
- data/lib/shaf/extensions/authorize.rb +11 -9
- data/lib/shaf/extensions/log.rb +1 -1
- data/lib/shaf/extensions/resource_uris.rb +139 -63
- data/lib/shaf/extensions/symbolic_routes.rb +22 -19
- data/lib/shaf/formable.rb +1 -2
- data/lib/shaf/formable/form.rb +1 -1
- data/lib/shaf/generator.rb +2 -0
- data/lib/shaf/generator/base.rb +2 -3
- data/lib/shaf/generator/controller.rb +11 -7
- data/lib/shaf/generator/doc.rb +17 -0
- data/lib/shaf/generator/forms.rb +1 -0
- data/lib/shaf/generator/helper.rb +2 -1
- data/lib/shaf/generator/migration/base.rb +7 -3
- data/lib/shaf/generator/migration/type.rb +4 -26
- data/lib/shaf/generator/migration/types.rb +45 -16
- data/lib/shaf/generator/model.rb +1 -2
- data/lib/shaf/generator/profile.rb +52 -0
- data/lib/shaf/generator/serializer.rb +38 -73
- data/lib/shaf/generator/templates/api/policy.rb.erb +2 -2
- data/lib/shaf/generator/templates/api/profile.rb.erb +16 -0
- data/lib/shaf/generator/templates/api/serializer.rb.erb +2 -2
- data/lib/shaf/generator/templates/spec/integration_spec.rb.erb +1 -2
- data/lib/shaf/generator/templates/spec/serializer_spec.rb.erb +5 -5
- data/lib/shaf/helpers.rb +4 -0
- data/lib/shaf/helpers/authentication.rb +79 -0
- data/lib/shaf/helpers/payload.rb +14 -34
- data/lib/shaf/helpers/vary.rb +8 -0
- data/lib/shaf/logger.rb +12 -0
- data/lib/shaf/parser.rb +65 -0
- data/lib/shaf/parser/base.rb +44 -0
- data/lib/shaf/parser/form_data.rb +15 -0
- data/lib/shaf/parser/json.rb +26 -0
- data/lib/shaf/profile.rb +110 -0
- data/lib/shaf/profile/attribute.rb +29 -0
- data/lib/shaf/profile/evaluator.rb +46 -0
- data/lib/shaf/profile/relation.rb +29 -0
- data/lib/shaf/profile/unique_id.rb +58 -0
- data/lib/shaf/profiles.rb +42 -0
- data/lib/shaf/profiles/shaf_basic.rb +20 -0
- data/lib/shaf/profiles/shaf_error.rb +48 -0
- data/lib/shaf/profiles/shaf_form.rb +109 -0
- data/lib/shaf/responder.rb +41 -2
- data/lib/shaf/responder/alps_json.rb +25 -0
- data/lib/shaf/responder/base.rb +20 -17
- data/lib/shaf/responder/hal.rb +65 -4
- data/lib/shaf/responder/html.rb +43 -16
- data/lib/shaf/responder/problem_json.rb +1 -1
- data/lib/shaf/serializer.rb +31 -0
- data/lib/shaf/settings.rb +25 -12
- data/lib/shaf/spec.rb +1 -0
- data/lib/shaf/spec/authenticator.rb +13 -0
- data/lib/shaf/spec/base.rb +1 -1
- data/lib/shaf/spec/http_method_utils.rb +1 -1
- data/lib/shaf/spec/integration_spec.rb +25 -13
- data/lib/shaf/spec/payload_utils.rb +2 -2
- data/lib/shaf/supported_http_methods.rb +15 -0
- data/lib/shaf/tasks/api_doc_task.rb +24 -3
- data/lib/shaf/tasks/routes_task.rb +14 -17
- data/lib/shaf/upgrade/manifest.rb +11 -2
- data/lib/shaf/upgrade/package.rb +73 -43
- data/lib/shaf/upgrade/version.rb +11 -10
- data/lib/shaf/utils.rb +19 -5
- data/lib/shaf/version.rb +3 -1
- data/lib/shaf/yard.rb +34 -0
- data/lib/shaf/yard/attribute_method_handler.rb +19 -0
- data/lib/shaf/yard/attribute_object.rb +30 -0
- data/lib/shaf/yard/base_method_handler.rb +30 -0
- data/lib/shaf/yard/link_method_handler.rb +39 -0
- data/lib/shaf/yard/link_object.rb +60 -0
- data/lib/shaf/yard/nested_attributes.rb +37 -0
- data/lib/shaf/yard/parser.rb +64 -0
- data/lib/shaf/yard/profile_method_handler.rb +51 -0
- data/lib/shaf/yard/profile_object.rb +21 -0
- data/lib/shaf/yard/resource_object.rb +55 -0
- data/lib/shaf/yard/serializer_handler.rb +27 -0
- data/templates/api/controllers/base_controller.rb +0 -10
- data/templates/api/controllers/docs_controller.rb +5 -3
- data/templates/api/controllers/root_controller.rb +7 -1
- data/templates/api/policies/base_policy.rb +2 -0
- data/templates/api/serializers/base_serializer.rb +1 -3
- data/templates/api/serializers/error_serializer.rb +1 -5
- data/templates/api/serializers/form_serializer.rb +1 -5
- data/templates/api/serializers/validation_error_serializer.rb +1 -5
- data/templates/config/bootstrap.rb +1 -2
- data/templates/config/directories.rb +52 -44
- data/templates/config/helpers.rb +1 -1
- data/templates/config/initializers.rb +52 -8
- data/templates/config/initializers/authentication.rb +18 -0
- data/templates/config/initializers/db_migrations.rb +2 -2
- data/templates/config/initializers/logging.rb +2 -2
- data/templates/spec/spec_helper.rb +2 -0
- data/upgrades/0.5.0.tar.gz +0 -0
- data/upgrades/1.0.4.tar.gz +0 -0
- data/upgrades/1.1.0.tar.gz +0 -0
- data/upgrades/2.0.0.tar.gz +0 -0
- data/yard_templates/api_doc/doc_index/html/body.erb +3 -0
- data/yard_templates/api_doc/doc_index/setup.rb +8 -0
- data/yard_templates/api_doc/html/css/api-doc.css +222 -0
- data/yard_templates/api_doc/html/favicon.ico +0 -0
- data/yard_templates/api_doc/html/js/switch_tab.js +17 -0
- data/yard_templates/api_doc/html/setup.rb +59 -0
- data/yard_templates/api_doc/layout/html/footer.erb +3 -0
- data/yard_templates/api_doc/layout/html/header.erb +7 -0
- data/yard_templates/api_doc/layout/html/layout.erb +13 -0
- data/yard_templates/api_doc/layout/setup.rb +24 -0
- data/yard_templates/api_doc/profile/html/attributes.erb +10 -0
- data/yard_templates/api_doc/profile/html/profile.erb +6 -0
- data/yard_templates/api_doc/profile/html/relations.erb +10 -0
- data/yard_templates/api_doc/profile/setup.rb +38 -0
- data/yard_templates/api_doc/profile_attribute/html/attribute.erb +23 -0
- data/yard_templates/api_doc/profile_attribute/setup.rb +21 -0
- data/yard_templates/api_doc/profile_relation/html/relation.erb +37 -0
- data/yard_templates/api_doc/profile_relation/setup.rb +41 -0
- data/yard_templates/api_doc/resource/html/attributes.erb +10 -0
- data/yard_templates/api_doc/resource/html/profile.erb +14 -0
- data/yard_templates/api_doc/resource/html/relations.erb +10 -0
- data/yard_templates/api_doc/resource/html/resource.erb +5 -0
- data/yard_templates/api_doc/resource/setup.rb +56 -0
- data/yard_templates/api_doc/resource_attribute/html/attribute.erb +23 -0
- data/yard_templates/api_doc/resource_attribute/setup.rb +20 -0
- data/yard_templates/api_doc/resource_relation/html/relation.erb +47 -0
- data/yard_templates/api_doc/resource_relation/setup.rb +80 -0
- data/yard_templates/api_doc/setup.rb +31 -0
- data/yard_templates/api_doc/sidebar/html/profile_list.erb +8 -0
- data/yard_templates/api_doc/sidebar/html/search.erb +7 -0
- data/yard_templates/api_doc/sidebar/html/serializer_list.erb +8 -0
- data/yard_templates/api_doc/sidebar/html/sidebar.erb +13 -0
- data/yard_templates/api_doc/sidebar/setup.rb +56 -0
- metadata +137 -30
- metadata.gz.sig +1 -3
- data/lib/shaf/extensions/current_user.rb +0 -48
- data/lib/shaf/responder/hal_serializable.rb +0 -64
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87353c742213c291ee2def2d0cc55c6743ef429d52655824935eb131f43c4868
|
4
|
+
data.tar.gz: 7cec9186f28226651cbc07d0310d945bb901573e5d992cc980ff9863f36c6d09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7fb1521b3f4eba94982da604cd42996f9c744bda53cb04ba22fd92af02c687be0a3c3a1c22bd02a9f1478e633ef9d05e2334f212bcdb4a4a5aacb2bd9b203e61
|
7
|
+
data.tar.gz: 05eba3ff47be63a14be932312465410186407974b523c108e3bf759ec72bc94eaed3452571f397009261a12025243c19071bc7dea8b09078e0053284a7cd5dfd
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
Binary file
|
data/lib/shaf.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'shaf/version'
|
4
|
+
require 'shaf/logger'
|
2
5
|
require 'shaf/settings'
|
3
6
|
require 'shaf/command'
|
4
7
|
require 'shaf/app'
|
8
|
+
require 'shaf/supported_http_methods'
|
5
9
|
require 'shaf/router'
|
6
10
|
require 'shaf/errors'
|
7
11
|
require 'shaf/formable'
|
@@ -9,3 +13,5 @@ require 'shaf/extensions'
|
|
9
13
|
require 'shaf/helpers'
|
10
14
|
require 'shaf/resource_doc'
|
11
15
|
require 'shaf/upgrade'
|
16
|
+
require 'shaf/profiles'
|
17
|
+
require 'shaf/serializer'
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shaf
|
4
|
+
module ALPS
|
5
|
+
class AttributeSerializer
|
6
|
+
attr_reader :attribute
|
7
|
+
|
8
|
+
def self.call(arg)
|
9
|
+
new(arg).to_hash
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(attribute)
|
13
|
+
@attribute = attribute
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_hash
|
17
|
+
{
|
18
|
+
id: attribute.id,
|
19
|
+
type: 'semantic',
|
20
|
+
}.merge(optional_properties)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def optional_properties
|
26
|
+
descriptors = serialized_descriptors
|
27
|
+
hash = {}
|
28
|
+
hash[:href] = attribute.href if attribute.href
|
29
|
+
hash[:doc] = { value: attribute.doc } if attribute.doc
|
30
|
+
hash[:name] = attribute.name.to_s if attribute.name
|
31
|
+
hash[:descriptor] = descriptors unless descriptors.empty?
|
32
|
+
hash
|
33
|
+
end
|
34
|
+
|
35
|
+
def serialized_descriptors
|
36
|
+
descriptors = attribute.attributes.map { |attr| self.class.call(attr) }
|
37
|
+
descriptors += attribute.relations.map { |rel| RelationSerializer.call(rel) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'shaf/alps/attribute_serializer'
|
4
|
+
require 'shaf/alps/relation_serializer'
|
5
|
+
|
6
|
+
module Shaf
|
7
|
+
module ALPS
|
8
|
+
class JsonSerializer
|
9
|
+
ALPS_VERSION = '1.0'
|
10
|
+
|
11
|
+
def self.call(profile)
|
12
|
+
new(profile).to_hash
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :profile
|
16
|
+
|
17
|
+
def initialize(profile)
|
18
|
+
@profile = profile
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_hash
|
22
|
+
{
|
23
|
+
alps: {
|
24
|
+
version: ALPS_VERSION,
|
25
|
+
doc: profile.doc,
|
26
|
+
descriptor: descriptors,
|
27
|
+
}.compact
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def descriptors
|
34
|
+
attribute_descriptors + relation_descriptors
|
35
|
+
end
|
36
|
+
|
37
|
+
def attribute_descriptors
|
38
|
+
profile.attributes.map do |desc|
|
39
|
+
AttributeSerializer.call(desc)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def relation_descriptors
|
44
|
+
profile.relations.map do |desc|
|
45
|
+
RelationSerializer.call(desc)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shaf
|
4
|
+
module ALPS
|
5
|
+
class RelationSerializer
|
6
|
+
SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS']
|
7
|
+
IDEMPOTENT_METHODS = ['PUT', 'PATCH', 'DELETE']
|
8
|
+
UNSAFE_METHODS = ['POST']
|
9
|
+
|
10
|
+
attr_reader :rel
|
11
|
+
|
12
|
+
def self.call(arg)
|
13
|
+
new(arg).to_hash
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(rel)
|
17
|
+
@rel = rel
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_hash
|
21
|
+
{
|
22
|
+
id: rel.id,
|
23
|
+
type: type,
|
24
|
+
}.merge(optional_properties)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def optional_properties
|
30
|
+
descriptors = serialized_descriptors
|
31
|
+
hash = {}
|
32
|
+
hash[:href] = rel.href if rel.href
|
33
|
+
hash[:doc] = { value: rel.doc } if rel.doc
|
34
|
+
hash[:name] = rel.name.to_s if rel.name
|
35
|
+
hash[:rt] = rel.content_type if rel.content_type
|
36
|
+
hash[:descriptor] = descriptors unless descriptors.empty?
|
37
|
+
hash[:ext] = extension if extension
|
38
|
+
hash
|
39
|
+
end
|
40
|
+
|
41
|
+
def type
|
42
|
+
methods = rel.http_methods
|
43
|
+
if methods.all? { |m| SAFE_METHODS.include? m }
|
44
|
+
'safe'
|
45
|
+
elsif methods.all? { |m| (SAFE_METHODS + IDEMPOTENT_METHODS).include? m }
|
46
|
+
'idempotent'
|
47
|
+
else
|
48
|
+
'unsafe'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def extension
|
53
|
+
methods = rel.http_methods
|
54
|
+
return unless methods
|
55
|
+
|
56
|
+
[
|
57
|
+
{
|
58
|
+
id: :http_method,
|
59
|
+
href: 'https://gist.github.com/sammyhenningsson/2103d839eb79a7baf8854bfb96bda7ae',
|
60
|
+
value: methods,
|
61
|
+
}
|
62
|
+
]
|
63
|
+
end
|
64
|
+
|
65
|
+
def serialized_descriptors
|
66
|
+
rel.attributes.map { |attr| AttributeSerializer.call(attr) }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'uri'
|
6
|
+
require 'csv'
|
7
|
+
|
8
|
+
module Shaf
|
9
|
+
module ApiDoc
|
10
|
+
class LinkRelations
|
11
|
+
class LinkRelation
|
12
|
+
attr_reader :name, :description, :reference, :notes
|
13
|
+
|
14
|
+
def initialize(name, description, reference, notes)
|
15
|
+
@name = name.to_sym
|
16
|
+
@description = description.freeze
|
17
|
+
@reference = reference.freeze
|
18
|
+
@notes = notes.freeze
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
IANA_URL = URI('https://www.iana.org/assignments/link-relations/link-relations-1.csv')
|
24
|
+
|
25
|
+
def all
|
26
|
+
relations.values
|
27
|
+
end
|
28
|
+
|
29
|
+
def [](key)
|
30
|
+
relations[key]
|
31
|
+
end
|
32
|
+
|
33
|
+
def []=(key, value)
|
34
|
+
relations[key] = value
|
35
|
+
end
|
36
|
+
|
37
|
+
def add(link_relation)
|
38
|
+
relations[link_relation.name] = link_relation
|
39
|
+
end
|
40
|
+
|
41
|
+
def load_iana
|
42
|
+
csv.each do |name, desc, ref, notes|
|
43
|
+
next if name == 'Relation Name'
|
44
|
+
add LinkRelation.new(name, desc, ref, notes)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def relations
|
51
|
+
@relations ||= {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def tmp_file_name
|
55
|
+
File.join(Dir.tmpdir, 'shaf_iana_link_relations')
|
56
|
+
end
|
57
|
+
|
58
|
+
def csv
|
59
|
+
if File.readable? tmp_file_name
|
60
|
+
content = File.read(tmp_file_name)
|
61
|
+
return CSV.new(content)
|
62
|
+
end
|
63
|
+
|
64
|
+
response = Net::HTTP.get_response(IANA_URL)
|
65
|
+
|
66
|
+
if response.code.to_i == 200
|
67
|
+
content = response.body
|
68
|
+
File.open(tmp_file_name, 'w') { |file| file.write(content) }
|
69
|
+
CSV.new(content)
|
70
|
+
else
|
71
|
+
Utils.iana_link_relations_csv
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'digest'
|
3
|
+
require 'shaf/authenticator/request'
|
4
|
+
|
5
|
+
module Shaf
|
6
|
+
module Authenticator
|
7
|
+
class << self
|
8
|
+
def register(authenticator)
|
9
|
+
authenticators << authenticator
|
10
|
+
end
|
11
|
+
|
12
|
+
def unregister(authenticator)
|
13
|
+
authenticators.delete_if { |auth| auth == authenticator }
|
14
|
+
end
|
15
|
+
|
16
|
+
def challenges_for(realm: nil)
|
17
|
+
authenticators.each_with_object([]) do |authenticator, challenges|
|
18
|
+
challenges.concat Array(authenticator.challenges_for(realm))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def user(env, realm: nil)
|
23
|
+
request = Request.new(env)
|
24
|
+
return unless request.provided?
|
25
|
+
|
26
|
+
authenticator = authenticator_for(request)
|
27
|
+
authenticator&.user(request, realm: realm)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def authenticators
|
33
|
+
@authenticators ||= Set.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def authenticator_for(request)
|
37
|
+
scheme = request.scheme
|
38
|
+
authenticator = authenticators.find { |auth| auth.scheme? scheme }
|
39
|
+
|
40
|
+
logger.warn(
|
41
|
+
"Client tried to authenticate with an unsupported " \
|
42
|
+
"authentication scheme: #{scheme}"
|
43
|
+
) unless authenticator
|
44
|
+
|
45
|
+
authenticator
|
46
|
+
end
|
47
|
+
|
48
|
+
def logger
|
49
|
+
Shaf.logger
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
require 'shaf/authenticator/base'
|
56
|
+
require 'shaf/authenticator/basic_auth'
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'shaf/authenticator/challenge'
|
2
|
+
require 'shaf/authenticator/parameter'
|
3
|
+
require 'shaf/errors'
|
4
|
+
|
5
|
+
module Shaf
|
6
|
+
module Authenticator
|
7
|
+
class Base
|
8
|
+
class MissingParametersError < Error
|
9
|
+
def initialize(authenticator, *args)
|
10
|
+
super("Missing required parameters: [#{args.join(', ')}] for #{authenticator}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class InvalidParameterError < Error
|
15
|
+
def initialize(authenticator, *args)
|
16
|
+
str = args.map { |key, value| "#{key}: #{value}" }.join(', ')
|
17
|
+
super("Invalid parameters for #{authenticator}: #{str}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class WrongCredentialsReturnTypeError < Error
|
22
|
+
def initialize(authenticator, clazz)
|
23
|
+
super <<~ERR
|
24
|
+
#{authenticator}.credentials return an instance of #{clazz}
|
25
|
+
It must return an instance of Hash.
|
26
|
+
Location: #{authenticator.method(:credentials).source_location})
|
27
|
+
ERR
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class << self
|
32
|
+
def inherited(child)
|
33
|
+
Authenticator.register(child)
|
34
|
+
end
|
35
|
+
|
36
|
+
def scheme(scheme = nil)
|
37
|
+
if scheme
|
38
|
+
@scheme = scheme.to_s
|
39
|
+
elsif @scheme
|
40
|
+
@scheme
|
41
|
+
else
|
42
|
+
raise Error, "#{self} must specify a scheme!"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def scheme?(str)
|
47
|
+
return false unless scheme
|
48
|
+
|
49
|
+
str.to_s.downcase == scheme.downcase
|
50
|
+
end
|
51
|
+
|
52
|
+
def param(name, required: true, default: nil, values: nil)
|
53
|
+
params[name.to_sym] = Parameter.new(
|
54
|
+
name,
|
55
|
+
required: required,
|
56
|
+
default: default,
|
57
|
+
values: values
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
def restricted(**parameters, &block)
|
62
|
+
validate! parameters
|
63
|
+
add_defaults! parameters
|
64
|
+
challenges << Challenge.new(scheme, **parameters, &block)
|
65
|
+
end
|
66
|
+
|
67
|
+
def challenges_for(realm)
|
68
|
+
challenges.select do |challenge|
|
69
|
+
challenge.realm? realm
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def user(request, realm: nil)
|
74
|
+
auth = authorization(request)
|
75
|
+
cred = credentials(auth, request) || {}
|
76
|
+
raise WrongCredentialsReturnTypeError.new(self, cred.class) unless cred.kind_of? Hash
|
77
|
+
|
78
|
+
return if cred.compact.empty?
|
79
|
+
|
80
|
+
challenges_for(realm).each do |challenge|
|
81
|
+
user = challenge.test(**cred)
|
82
|
+
return user if user
|
83
|
+
end
|
84
|
+
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
|
88
|
+
def params
|
89
|
+
@params ||= superclass.respond_to?(:params) ? superclass.params.dup : {}
|
90
|
+
end
|
91
|
+
|
92
|
+
protected
|
93
|
+
|
94
|
+
# Subclasses should implement this method. The return value should be and array
|
95
|
+
# that will get passed as block arguments to the block passed to #restricted
|
96
|
+
def credentials(authorization, request); end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def challenges
|
101
|
+
@challenges ||= []
|
102
|
+
end
|
103
|
+
|
104
|
+
def validate!(parameters)
|
105
|
+
validate_required(parameters)
|
106
|
+
validate_params(parameters)
|
107
|
+
end
|
108
|
+
|
109
|
+
def validate_required(parameters)
|
110
|
+
errors = []
|
111
|
+
|
112
|
+
required_params.each do |param|
|
113
|
+
next if parameters.key? param.name
|
114
|
+
next if param.default
|
115
|
+
errors << param.name
|
116
|
+
end
|
117
|
+
|
118
|
+
raise MissingParametersError.new(self, *errors) unless errors.empty?
|
119
|
+
end
|
120
|
+
|
121
|
+
def validate_params(parameters)
|
122
|
+
errors = []
|
123
|
+
|
124
|
+
parameters.each do |key, value|
|
125
|
+
if params.key? key
|
126
|
+
errors << [key, value] unless params[key].valid? value
|
127
|
+
else
|
128
|
+
logger.warn "Unsupported authenticator parameter " \
|
129
|
+
"for #{self}: #{key} = \"#{value}\""
|
130
|
+
parameters.delete(key)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
raise InvalidParameterError.new(self, *errors) unless errors.empty?
|
135
|
+
end
|
136
|
+
|
137
|
+
def logger
|
138
|
+
Shaf.logger
|
139
|
+
end
|
140
|
+
|
141
|
+
def add_defaults!(parameters)
|
142
|
+
params.each do |key, param|
|
143
|
+
next unless param.default
|
144
|
+
parameters[key] ||= param.default
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def required_params
|
149
|
+
params.values.select(&:required?)
|
150
|
+
end
|
151
|
+
|
152
|
+
def authorization(request)
|
153
|
+
return unless request.authorization
|
154
|
+
|
155
|
+
request.authorization.sub(/^#{scheme} /i, '')
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|