shaf 1.6.0 → 3.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/iana_link_relations.csv.gz +0 -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/app.rb +32 -7
- 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/authenticator.rb +56 -0
- data/lib/shaf/command/base.rb +4 -0
- data/lib/shaf/command/console.rb +1 -1
- data/lib/shaf/command/generate.rb +5 -2
- data/lib/shaf/command/new.rb +24 -7
- data/lib/shaf/command/server.rb +5 -1
- data/lib/shaf/command/templates/Gemfile.erb +1 -0
- data/{templates/config/settings.yml → lib/shaf/command/templates/config/settings.yml.erb} +9 -12
- data/lib/shaf/errors.rb +11 -0
- 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 +95 -137
- data/lib/shaf/extensions/symbolic_routes.rb +9 -19
- data/lib/shaf/extensions.rb +3 -3
- data/lib/shaf/formable/builder.rb +58 -19
- data/lib/shaf/formable/form.rb +14 -11
- data/lib/shaf/formable.rb +10 -24
- data/lib/shaf/generator/base.rb +84 -3
- data/lib/shaf/generator/controller.rb +30 -42
- data/lib/shaf/generator/doc.rb +17 -0
- data/lib/shaf/generator/forms.rb +11 -14
- data/lib/shaf/generator/helper.rb +2 -1
- data/lib/shaf/generator/migration/add_column.rb +0 -4
- data/lib/shaf/generator/migration/add_index.rb +0 -4
- data/lib/shaf/generator/migration/base.rb +15 -3
- data/lib/shaf/generator/migration/create_table.rb +0 -4
- data/lib/shaf/generator/migration/drop_column.rb +0 -4
- data/lib/shaf/generator/migration/rename_column.rb +0 -4
- data/lib/shaf/generator/migration/type.rb +4 -26
- data/lib/shaf/generator/migration/types.rb +45 -16
- data/lib/shaf/generator/model.rb +29 -15
- data/lib/shaf/generator/policy.rb +8 -14
- data/lib/shaf/generator/profile.rb +47 -0
- data/lib/shaf/generator/scaffold.rb +6 -9
- data/lib/shaf/generator/serializer.rb +64 -98
- data/lib/shaf/generator/templates/api/controller.rb.erb +13 -13
- data/lib/shaf/generator/templates/api/forms.rb.erb +2 -2
- data/lib/shaf/generator/templates/api/model.rb.erb +1 -1
- 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 +3 -3
- data/lib/shaf/generator/templates/spec/integration_spec.rb.erb +15 -16
- data/lib/shaf/generator/templates/spec/serializer_spec.rb.erb +5 -5
- data/lib/shaf/generator.rb +2 -0
- data/lib/shaf/helpers/authentication.rb +79 -0
- data/lib/shaf/helpers/paginate.rb +1 -1
- data/lib/shaf/helpers/payload.rb +14 -34
- data/lib/shaf/helpers/vary.rb +8 -0
- data/lib/shaf/helpers.rb +4 -0
- data/lib/shaf/logger.rb +12 -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/parser.rb +65 -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/profile.rb +115 -0
- data/lib/shaf/profiles/shaf_basic.rb +20 -0
- data/lib/shaf/profiles/shaf_error.rb +49 -0
- data/lib/shaf/profiles/shaf_form.rb +110 -0
- data/lib/shaf/profiles.rb +46 -0
- data/lib/shaf/registrable_factory.rb +62 -32
- data/lib/shaf/responder/alps_json.rb +25 -0
- data/lib/shaf/responder/base.rb +20 -17
- data/lib/shaf/responder/hal.rb +66 -5
- data/lib/shaf/responder/html.rb +43 -16
- data/lib/shaf/responder/problem_json.rb +2 -2
- data/lib/shaf/responder.rb +41 -2
- data/lib/shaf/router.rb +65 -12
- data/lib/shaf/serializer.rb +35 -0
- data/lib/shaf/settings.rb +25 -12
- 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 +26 -14
- data/lib/shaf/spec/payload_utils.rb +2 -2
- data/lib/shaf/spec.rb +1 -0
- data/lib/shaf/supported_http_methods.rb +15 -0
- data/lib/shaf/tasks/routes_task.rb +14 -17
- data/lib/shaf/tasks.rb +0 -1
- data/lib/shaf/upgrade/manifest.rb +11 -2
- data/lib/shaf/upgrade/package.rb +73 -45
- 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/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/lib/shaf/yard.rb +34 -0
- data/lib/shaf.rb +6 -0
- data/templates/Rakefile +0 -6
- data/templates/api/controllers/base_controller.rb +0 -12
- 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/root_serializer.rb +0 -11
- 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/authentication.rb +18 -0
- data/templates/config/initializers/db_migrations.rb +2 -2
- data/templates/config/initializers/logging.rb +2 -2
- data/templates/config/initializers/middleware.rb +6 -0
- data/templates/config/initializers.rb +52 -8
- data/templates/config.ru +1 -1
- data/templates/spec/spec_helper.rb +5 -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/1.6.1.tar.gz +0 -0
- data/upgrades/2.0.0.tar.gz +0 -0
- data/upgrades/3.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
- data.tar.gz.sig +2 -2
- metadata +143 -38
- metadata.gz.sig +0 -0
- data/lib/shaf/api_doc/comment.rb +0 -27
- data/lib/shaf/api_doc/document.rb +0 -137
- data/lib/shaf/extensions/current_user.rb +0 -48
- data/lib/shaf/middleware.rb +0 -1
- data/lib/shaf/responder/hal_serializable.rb +0 -64
- data/lib/shaf/tasks/api_doc_task.rb +0 -125
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aa31b5bc9a9cc80cc5b34c5a3f6101eb17ebf8166bd1a6ae46f0008709c3fa0f
|
4
|
+
data.tar.gz: d4fc21aa271c30bb3e52bbc65571917beb0b16029e7ee764fc8705a5d4197ca8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6820ebc741c7392a44a767d1be674394fb307c3e937997c0428dea3663fd57faa3ab9ff6700dedda76e522650373997413b4a5b256198d9646c4741a91ae33d7
|
7
|
+
data.tar.gz: 9315cdd74263a2ae7b39f8a5960267aff7b61c498e0d95ca00559f61a3756f53f84f62b946bb24d8d818d901133cc537ef4402a0b3e201ae34f52b201015f72c
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
Binary file
|
@@ -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
|
data/lib/shaf/app.rb
CHANGED
@@ -1,19 +1,44 @@
|
|
1
|
-
require '
|
1
|
+
require 'sinatra'
|
2
2
|
|
3
3
|
module Shaf
|
4
4
|
class App
|
5
5
|
class << self
|
6
|
+
# Either call `Shaf::App.run!`
|
6
7
|
def run!
|
7
|
-
|
8
|
+
instance.run!
|
8
9
|
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
# Or `run Shaf::App` (in config.ru)
|
12
|
+
def call(*args)
|
13
|
+
instance.call(*args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def instance
|
17
|
+
# This works since Sinatra includes Sinatra::Delegator into
|
18
|
+
# Rack::Builder, which means that Rack::Builder#set will be delegated
|
19
|
+
# to Sinatra::Application
|
20
|
+
@instance ||= Rack::Builder.new(app) do
|
21
|
+
set :port, Settings.port || 3000
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def use(middleware, *args, **kwargs, &block)
|
26
|
+
if args.empty? && kwargs.empty?
|
27
|
+
instance.use middleware, &block
|
28
|
+
elsif kwargs.empty?
|
29
|
+
instance.use middleware, *args, &block
|
30
|
+
elsif args.empty?
|
31
|
+
instance.use middleware, **kwargs, &block
|
32
|
+
else
|
33
|
+
instance.use middleware, *args, **kwargs, &block
|
15
34
|
end
|
16
35
|
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def app
|
40
|
+
Router.new
|
41
|
+
end
|
17
42
|
end
|
18
43
|
end
|
19
44
|
end
|
@@ -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
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Shaf
|
2
|
+
module Authenticator
|
3
|
+
class BasicAuth < Base
|
4
|
+
scheme 'Basic'
|
5
|
+
|
6
|
+
param :realm
|
7
|
+
param :charset, required: false, values: ["UTF-8"]
|
8
|
+
|
9
|
+
def self.credentials(authorization, _request)
|
10
|
+
return unless authorization
|
11
|
+
|
12
|
+
decoded = String(authorization.unpack("m*").first)
|
13
|
+
return {} if decoded.empty?
|
14
|
+
|
15
|
+
user, password = decoded.split(/:/, 2)
|
16
|
+
.map { |str| str unless String(str).empty? }
|
17
|
+
|
18
|
+
{
|
19
|
+
user: user,
|
20
|
+
password: password
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shaf
|
4
|
+
module Authenticator
|
5
|
+
class Challenge
|
6
|
+
attr_reader :scheme, :parameters, :realm
|
7
|
+
|
8
|
+
def initialize(scheme, **parameters, &block)
|
9
|
+
@scheme = scheme
|
10
|
+
@realm = parameters.delete(:realm)&.to_s
|
11
|
+
@parameters = parameters
|
12
|
+
define_singleton_method(:test, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"#{scheme} #{parameter_string}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def realm?(arg)
|
20
|
+
realm&.to_s == arg&.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def parameter_string
|
26
|
+
params = {}
|
27
|
+
params[:realm] = realm if realm
|
28
|
+
params.merge(parameters).map { |k,v| %Q(#{k}="#{v}") }.join(', ')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shaf
|
4
|
+
module Authenticator
|
5
|
+
class Parameter
|
6
|
+
attr_reader :name, :default, :values
|
7
|
+
|
8
|
+
def initialize(name, required: true, default: nil, values: nil)
|
9
|
+
@name = name
|
10
|
+
@required = required
|
11
|
+
@default = default
|
12
|
+
@values = values&.map(&:downcase)
|
13
|
+
end
|
14
|
+
|
15
|
+
def required?
|
16
|
+
@required
|
17
|
+
end
|
18
|
+
|
19
|
+
def optional?
|
20
|
+
!required?
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid?(value)
|
24
|
+
return optional? if value.nil?
|
25
|
+
return true unless values
|
26
|
+
|
27
|
+
values.include?(value.downcase)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rack/auth/abstract/request'
|
2
|
+
|
3
|
+
module Shaf
|
4
|
+
module Authenticator
|
5
|
+
class Request < Rack::Auth::AbstractRequest
|
6
|
+
attr_reader :env
|
7
|
+
|
8
|
+
def valid?
|
9
|
+
!String(authorization).strip.empty?
|
10
|
+
end
|
11
|
+
|
12
|
+
def authorization
|
13
|
+
env[authorization_key]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
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'
|
data/lib/shaf/command/base.rb
CHANGED
data/lib/shaf/command/console.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
require 'file_transactions'
|
1
2
|
require 'shaf/generator'
|
2
3
|
|
3
4
|
module Shaf
|
4
5
|
module Command
|
5
6
|
class Generate < Base
|
6
7
|
|
7
|
-
identifier %r(\Ag(en(erate)
|
8
|
+
identifier %r(\Ag(\b|en(\b|erate))\Z)
|
8
9
|
usage Generator::Factory.usage.flatten.sort
|
9
10
|
|
10
11
|
def self.options(parser, options)
|
@@ -19,7 +20,9 @@ module Shaf
|
|
19
20
|
|
20
21
|
def call
|
21
22
|
in_project_root do
|
22
|
-
|
23
|
+
FileTransactions.transaction do
|
24
|
+
Generator::Factory.create(*args, **options).call
|
25
|
+
end
|
23
26
|
end
|
24
27
|
rescue StandardError => e
|
25
28
|
raise Command::ArgumentError, e.message
|
data/lib/shaf/command/new.rb
CHANGED
@@ -10,21 +10,26 @@ module Shaf
|
|
10
10
|
usage 'new PROJECT_NAME'
|
11
11
|
|
12
12
|
def call
|
13
|
-
|
14
|
-
if
|
13
|
+
self.project_name = args.first
|
14
|
+
if project_name.nil? || project_name.empty?
|
15
15
|
raise ArgumentError,
|
16
16
|
"Please provide a project name when using command 'new'!"
|
17
17
|
end
|
18
18
|
|
19
|
-
create_dir
|
20
|
-
Dir.chdir(
|
19
|
+
create_dir project_name
|
20
|
+
Dir.chdir(project_name) do
|
21
21
|
copy_templates
|
22
22
|
create_gemfile
|
23
|
+
create_settings_file
|
23
24
|
write_shaf_version
|
24
25
|
create_ruby_version_file
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_accessor :project_name
|
32
|
+
|
28
33
|
def create_dir(name)
|
29
34
|
return if Dir.exist? name
|
30
35
|
FileUtils.mkdir_p(name)
|
@@ -38,9 +43,21 @@ module Shaf
|
|
38
43
|
File.write "Gemfile", erb(content)
|
39
44
|
end
|
40
45
|
|
41
|
-
def
|
42
|
-
|
43
|
-
|
46
|
+
def create_settings_file
|
47
|
+
settings_file = 'config/settings.yml'
|
48
|
+
template_file = File.expand_path("../templates/#{settings_file}.erb", __FILE__)
|
49
|
+
content = File.read(template_file)
|
50
|
+
locals = {
|
51
|
+
project_name: project_name.capitalize,
|
52
|
+
default_port: "<%= ENV.fetch('PORT', 3000) %>"
|
53
|
+
}
|
54
|
+
File.write settings_file,
|
55
|
+
erb(content, locals)
|
56
|
+
end
|
57
|
+
|
58
|
+
def erb(content, locals = {})
|
59
|
+
return ERB.new(content, 0, '%-<>').result_with_hash(locals) if RUBY_VERSION < "2.6.0"
|
60
|
+
ERB.new(content, trim_mode: '-<>').result_with_hash(locals)
|
44
61
|
end
|
45
62
|
|
46
63
|
def copy_templates
|
data/lib/shaf/command/server.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'rack'
|
2
3
|
|
3
4
|
module Shaf
|
4
5
|
module Command
|
@@ -16,7 +17,10 @@ module Shaf
|
|
16
17
|
def call
|
17
18
|
Settings.port = options[:port] if options[:port]
|
18
19
|
bootstrap
|
19
|
-
|
20
|
+
Rack::Server.start(
|
21
|
+
app: App,
|
22
|
+
Port: Settings.port
|
23
|
+
)
|
20
24
|
end
|
21
25
|
end
|
22
26
|
end
|