api_engine_base 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +32 -0
- data/app/controllers/api_engine_base/application_controller.rb +47 -0
- data/app/controllers/api_engine_base/auth/plain_text_controller.rb +132 -0
- data/app/controllers/api_engine_base/username_controller.rb +26 -0
- data/app/controllers/concerns/api_engine_base/schematizable.rb +5 -0
- data/app/helpers/api_engine_base/application_helper.rb +4 -0
- data/app/helpers/api_engine_base/schema_helper.rb +29 -0
- data/app/jobs/api_engine_base/application_job.rb +4 -0
- data/app/mailers/api_engine_base/application_mailer.rb +8 -0
- data/app/mailers/api_engine_base/email_verification_mailer.rb +12 -0
- data/app/models/api_engine_base/application_record.rb +7 -0
- data/app/models/user.rb +50 -0
- data/app/models/user_secret.rb +72 -0
- data/app/services/api_engine_base/argument_validation/class_methods.rb +179 -0
- data/app/services/api_engine_base/argument_validation/instance_methods.rb +136 -0
- data/app/services/api_engine_base/argument_validation.rb +11 -0
- data/app/services/api_engine_base/jwt/authenticate_user.rb +71 -0
- data/app/services/api_engine_base/jwt/decode.rb +21 -0
- data/app/services/api_engine_base/jwt/encode.rb +15 -0
- data/app/services/api_engine_base/jwt/login_create.rb +21 -0
- data/app/services/api_engine_base/jwt/time_delay_token.rb +17 -0
- data/app/services/api_engine_base/login_strategy/plain_text/create.rb +42 -0
- data/app/services/api_engine_base/login_strategy/plain_text/email_verification/generate.rb +29 -0
- data/app/services/api_engine_base/login_strategy/plain_text/email_verification/required.rb +20 -0
- data/app/services/api_engine_base/login_strategy/plain_text/email_verification/send.rb +23 -0
- data/app/services/api_engine_base/login_strategy/plain_text/email_verification/verify.rb +24 -0
- data/app/services/api_engine_base/login_strategy/plain_text/login.rb +50 -0
- data/app/services/api_engine_base/secrets/cleanse.rb +14 -0
- data/app/services/api_engine_base/secrets/generate.rb +62 -0
- data/app/services/api_engine_base/secrets/verify.rb +27 -0
- data/app/services/api_engine_base/secrets.rb +15 -0
- data/app/services/api_engine_base/service_base.rb +90 -0
- data/app/services/api_engine_base/service_logging.rb +41 -0
- data/app/services/api_engine_base/username/available.rb +64 -0
- data/app/views/api_engine_base/email_verification_mailer/verify_email.html.erb +26 -0
- data/config/routes.rb +23 -0
- data/db/migrate/20241117043720_create_api_engine_base_users.rb +33 -0
- data/db/migrate/20241204065708_create_api_engine_base_user_secrets.rb +16 -0
- data/lib/api_engine_base/configuration/application/config.rb +40 -0
- data/lib/api_engine_base/configuration/base.rb +11 -0
- data/lib/api_engine_base/configuration/config.rb +59 -0
- data/lib/api_engine_base/configuration/email/config.rb +87 -0
- data/lib/api_engine_base/configuration/jwt/config.rb +22 -0
- data/lib/api_engine_base/configuration/login/config.rb +18 -0
- data/lib/api_engine_base/configuration/login/strategy/plain_text/config.rb +57 -0
- data/lib/api_engine_base/configuration/login/strategy/plain_text/email_verify.rb +50 -0
- data/lib/api_engine_base/configuration/login/strategy/plain_text/lockable.rb +27 -0
- data/lib/api_engine_base/configuration/otp/config.rb +54 -0
- data/lib/api_engine_base/configuration/username/check.rb +31 -0
- data/lib/api_engine_base/configuration/username/config.rb +41 -0
- data/lib/api_engine_base/engine.rb +21 -0
- data/lib/api_engine_base/schema/error/base.rb +15 -0
- data/lib/api_engine_base/schema/error/invalid_argument.rb +15 -0
- data/lib/api_engine_base/schema/error/invalid_argument_response.rb +17 -0
- data/lib/api_engine_base/schema/plain_text/create_user_request.rb +18 -0
- data/lib/api_engine_base/schema/plain_text/create_user_response.rb +17 -0
- data/lib/api_engine_base/schema/plain_text/email_verify_request.rb +11 -0
- data/lib/api_engine_base/schema/plain_text/email_verify_response.rb +11 -0
- data/lib/api_engine_base/schema/plain_text/email_verify_send_request.rb +9 -0
- data/lib/api_engine_base/schema/plain_text/email_verify_send_response.rb +11 -0
- data/lib/api_engine_base/schema/plain_text/login_request.rb +15 -0
- data/lib/api_engine_base/schema/plain_text/login_response.rb +13 -0
- data/lib/api_engine_base/schema.rb +25 -0
- data/lib/api_engine_base/spec_helper.rb +18 -0
- data/lib/api_engine_base/version.rb +5 -0
- data/lib/api_engine_base.rb +33 -0
- data/lib/generators/api_engine_base/configure/USAGE +8 -0
- data/lib/generators/api_engine_base/configure/configure_generator.rb +12 -0
- data/lib/tasks/auto_annotate_models.rake +60 -0
- metadata +216 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "interactor"
|
4
|
+
|
5
|
+
class ApiEngineBase::ServiceBase
|
6
|
+
class ServiceBaseError < ApiEngineBase::Error; end;
|
7
|
+
class ValidationError < ServiceBaseError; end;
|
8
|
+
class ConfigurationError < ServiceBaseError; end;
|
9
|
+
|
10
|
+
class NameConflictError < ApiEngineBase::Error; end
|
11
|
+
class DefaultValueError < ApiEngineBase::Error; end
|
12
|
+
class OneOfError < ApiEngineBase::Error; end
|
13
|
+
class NestedOneOfError < ApiEngineBase::Error; end
|
14
|
+
class ArgumentValidationError < ApiEngineBase::Error; end
|
15
|
+
|
16
|
+
class KeyValidationError < ApiEngineBase::ServiceBase::ValidationError; end
|
17
|
+
class CompositionValidationError < ApiEngineBase::ServiceBase::ValidationError; end
|
18
|
+
|
19
|
+
include Interactor
|
20
|
+
include ApiEngineBase::ServiceLogging
|
21
|
+
include ApiEngineBase::ArgumentValidation
|
22
|
+
|
23
|
+
ON_ARGUMENT_VALIDATION = [
|
24
|
+
DEFAULT_VALIDATION = :raise,
|
25
|
+
:fail_early,
|
26
|
+
:log,
|
27
|
+
]
|
28
|
+
|
29
|
+
def self.inherited(subclass)
|
30
|
+
# Add the base logging to the subclass.
|
31
|
+
# Since this is done at inheritance time it should always be the first and last hook to run.
|
32
|
+
subclass.around(:service_base_logging)
|
33
|
+
subclass.around(:internal_validate)
|
34
|
+
subclass.after(:sanitize_params)
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate!
|
38
|
+
# overload from child
|
39
|
+
end
|
40
|
+
|
41
|
+
def internal_validate(interactor)
|
42
|
+
# call validate that is overridden from child
|
43
|
+
begin
|
44
|
+
validate!
|
45
|
+
# validate_param!
|
46
|
+
run_validations!
|
47
|
+
rescue StandardError => e
|
48
|
+
log_error("Error during validation. #{e.message}")
|
49
|
+
raise
|
50
|
+
end
|
51
|
+
|
52
|
+
# call interactor
|
53
|
+
interactor.call
|
54
|
+
end
|
55
|
+
|
56
|
+
def service_base_logging(interactor)
|
57
|
+
beginning_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
58
|
+
|
59
|
+
# Pre processing stats
|
60
|
+
log_info("Start")
|
61
|
+
|
62
|
+
# Run the job!
|
63
|
+
interactor.call
|
64
|
+
|
65
|
+
# Set status for use in ensure block
|
66
|
+
status = :complete
|
67
|
+
|
68
|
+
# Capture Interactor::Failure for logging purposes, then reraise
|
69
|
+
rescue ::Interactor::Failure
|
70
|
+
# set status for use in ensure block
|
71
|
+
status = :failure
|
72
|
+
|
73
|
+
# Re-raise to let the core Interactor handle this
|
74
|
+
raise
|
75
|
+
# Capture exception explicitly to try to logging purposes. Then reraise.
|
76
|
+
rescue ::Exception => e # rubocop:disable Lint/RescueException
|
77
|
+
# set status for use in ensure block
|
78
|
+
status = :error
|
79
|
+
|
80
|
+
# Log error
|
81
|
+
log_error("Error #{e.class.name}")
|
82
|
+
|
83
|
+
raise
|
84
|
+
ensure
|
85
|
+
# Always log how long it took along with a status
|
86
|
+
finished_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
87
|
+
elapsed = ((finished_time - beginning_time) * 1000).round(2)
|
88
|
+
log_info("Finished with [#{status}]...elapsed #{elapsed}ms")
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ApiEngineBase::ServiceLogging
|
4
|
+
def log_info(msg)
|
5
|
+
log(level: :info, msg:)
|
6
|
+
end
|
7
|
+
|
8
|
+
def log_warn(msg)
|
9
|
+
log(level: :warn, msg:)
|
10
|
+
end
|
11
|
+
|
12
|
+
def log_error(msg)
|
13
|
+
log(level: :error, msg:)
|
14
|
+
end
|
15
|
+
|
16
|
+
def log(level:, msg:)
|
17
|
+
logger.public_send(level, aletered_message(msg))
|
18
|
+
rescue StandardError
|
19
|
+
Rails.logger.public_send(level, aletered_message(msg))
|
20
|
+
end
|
21
|
+
|
22
|
+
def aletered_message(msg)
|
23
|
+
"#{log_prefix}: #{msg}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def logger
|
27
|
+
defined?(context) ? context.logger : Rails.logger
|
28
|
+
end
|
29
|
+
|
30
|
+
def log_prefix
|
31
|
+
"[#{class_name}-#{service_id}]"
|
32
|
+
end
|
33
|
+
|
34
|
+
def class_name
|
35
|
+
self.class.name
|
36
|
+
end
|
37
|
+
|
38
|
+
def service_id
|
39
|
+
@service_id ||= SecureRandom.alphanumeric(10)
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ApiEngineBase::Username
|
4
|
+
class Available < ApiEngineBase::ServiceBase
|
5
|
+
on_argument_validation :fail_early
|
6
|
+
|
7
|
+
REFRESH_KEY = "username.refresh_after"
|
8
|
+
|
9
|
+
validate :username, is_a: String, required: true
|
10
|
+
validate :force_query, is_a: [TrueClass, FalseClass]
|
11
|
+
|
12
|
+
def initialize(*)
|
13
|
+
super
|
14
|
+
|
15
|
+
@mutex = Mutex.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
populate_local_cache! if refresh?
|
20
|
+
|
21
|
+
context.available = available?
|
22
|
+
context.valid = valid?
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid?
|
26
|
+
return false if username.length < ApiEngineBase.config.username.username_length_min
|
27
|
+
return false if username.length > ApiEngineBase.config.username.username_length_max
|
28
|
+
|
29
|
+
!!username[ApiEngineBase.config.username.username_regex]
|
30
|
+
end
|
31
|
+
|
32
|
+
def available?
|
33
|
+
!realtime.local_cache.exist?(username)
|
34
|
+
end
|
35
|
+
|
36
|
+
# this is a very terrible cache design at scale
|
37
|
+
# If we can use Redis, a bloom filter would be great
|
38
|
+
def populate_local_cache!
|
39
|
+
@mutex.synchronize do
|
40
|
+
values = User.pluck(:username).map { [_1, 1] }.to_h rescue {}
|
41
|
+
realtime.local_cache.write_multi(values)
|
42
|
+
realtime.local_cache.write(REFRESH_KEY, realtime.local_cache_ttl.from_now)
|
43
|
+
|
44
|
+
values
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def refresh?
|
49
|
+
return true if force_query
|
50
|
+
|
51
|
+
refresh_by = realtime.local_cache.read(REFRESH_KEY)
|
52
|
+
return true if refresh_by.nil?
|
53
|
+
|
54
|
+
time = Time.at(refresh_by) rescue nil
|
55
|
+
return true if time.nil?
|
56
|
+
|
57
|
+
time < Time.now
|
58
|
+
end
|
59
|
+
|
60
|
+
def realtime
|
61
|
+
ApiEngineBase.config.username.realtime_username_check
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<p>Hello <%= @user.full_name %>!</p>
|
2
|
+
|
3
|
+
<p>
|
4
|
+
Welcome to <%= ApiEngineBase.config.app.communication_name %>.
|
5
|
+
</p>
|
6
|
+
<p>
|
7
|
+
Please provide the following verification code to confirm your email address.
|
8
|
+
</p>
|
9
|
+
<p></p>
|
10
|
+
<p></p>
|
11
|
+
<p>
|
12
|
+
<h1>
|
13
|
+
<%= @code %>
|
14
|
+
</h1>
|
15
|
+
</p>
|
16
|
+
|
17
|
+
<p></p>
|
18
|
+
<p></p>
|
19
|
+
|
20
|
+
<p>
|
21
|
+
Best wishes, <%= ApiEngineBase.config.app.communication_name %> team
|
22
|
+
</p>
|
23
|
+
<p></p>
|
24
|
+
<p></p>
|
25
|
+
|
26
|
+
Visit us at <a href="<%= ApiEngineBase.config.app.composed_url %>" target="_blank"><%= ApiEngineBase.config.app.composed_url %></a>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Rails.application.routes.draw do
|
2
|
+
append_to_ass = "api_engine_base"
|
3
|
+
|
4
|
+
constraints(->(_req) { ApiEngineBase.config.username.realtime_username_check? }) do
|
5
|
+
scope "username" do
|
6
|
+
get "/available/:username", to: "api_engine_base/username#username_availability", as: :"#{append_to_ass}_username_availability_get"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
scope "auth" do
|
11
|
+
constraints(->(_req) { ApiEngineBase.config.login.plain_text.enable? }) do
|
12
|
+
post "/login", to: "api_engine_base/auth/plain_text#login_post", as: :"#{append_to_ass}_auth_login_post"
|
13
|
+
post "/create", to: "api_engine_base/auth/plain_text#create_post", as: :"#{append_to_ass}_auth_create_post"
|
14
|
+
|
15
|
+
constraints(->(_req) { ApiEngineBase.config.login.plain_text.email_verify? }) do
|
16
|
+
scope "email" do
|
17
|
+
post "/verify", to: "api_engine_base/auth/plain_text#email_verify_post", as: :"#{append_to_ass}_auth_email_verification"
|
18
|
+
post "/send", to: "api_engine_base/auth/plain_text#email_verify_resend_post", as: :"#{append_to_ass}_auth_email_verification_send"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class CreateApiEngineBaseUsers < ActiveRecord::Migration[7.2]
|
2
|
+
def change
|
3
|
+
create_table :users do |t|
|
4
|
+
t.string :first_name, null: false, default: ""
|
5
|
+
t.string :last_name, null: false, default: ""
|
6
|
+
t.string :username
|
7
|
+
|
8
|
+
t.string :last_known_timezone
|
9
|
+
t.timestamp :last_known_timezone_update
|
10
|
+
|
11
|
+
t.integer :successful_login, default: 0
|
12
|
+
t.string :last_login_strategy
|
13
|
+
t.datetime :last_login
|
14
|
+
|
15
|
+
###
|
16
|
+
# Database token to verify JWT
|
17
|
+
# Token will allow JWT values to expire/reset all devices
|
18
|
+
t.string :verifier_token
|
19
|
+
t.datetime :verifier_token_last_reset
|
20
|
+
|
21
|
+
# Login Strategy: PlainText
|
22
|
+
t.string :email, null: false, default: ""
|
23
|
+
t.boolean :email_validated, default: false
|
24
|
+
t.integer :password_consecutive_fail, default: 0
|
25
|
+
t.string :password_digest, null: false, default: ""
|
26
|
+
t.string :recovery_password_digest, null: false, default: ""
|
27
|
+
|
28
|
+
t.timestamps
|
29
|
+
end
|
30
|
+
|
31
|
+
add_index :users, :username, unique: true
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateApiEngineBaseUserSecrets < ActiveRecord::Migration[7.2]
|
2
|
+
def change
|
3
|
+
create_table :user_secrets do |t|
|
4
|
+
t.references :user, null: false, foreign_key: true
|
5
|
+
t.integer :use_count, default: 0
|
6
|
+
t.integer :use_count_max
|
7
|
+
t.string :reason
|
8
|
+
t.string :extra
|
9
|
+
t.string :secret
|
10
|
+
t.datetime :death_time
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
add_index :user_secrets, :secret, unique: true
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "class_composer"
|
4
|
+
|
5
|
+
module ApiEngineBase
|
6
|
+
module Configuration
|
7
|
+
module Application
|
8
|
+
class Config
|
9
|
+
include ClassComposer::Generator
|
10
|
+
|
11
|
+
add_composer :app_name,
|
12
|
+
allowed: String,
|
13
|
+
dynamic_default: ->(_) { ApiEngineBase.default_app_name },
|
14
|
+
desc: "The default name of the application",
|
15
|
+
default_shown: "# Auto Populates to the name of the application"
|
16
|
+
|
17
|
+
add_composer :communication_name,
|
18
|
+
allowed: String,
|
19
|
+
dynamic_default: :app_name,
|
20
|
+
desc: "The name of the application to use in communications like SMS or Email"
|
21
|
+
|
22
|
+
add_composer :url,
|
23
|
+
allowed: String,
|
24
|
+
default: ENV.fetch("API_ENGINE_BASE_URL", "http://localhost"),
|
25
|
+
desc: "When composing SSO's or verification URL's, this is the URL for the application"
|
26
|
+
|
27
|
+
add_composer :port,
|
28
|
+
allowed: [String, NilClass],
|
29
|
+
default: ENV.fetch("API_ENGINE_BASE_PORT", nil),
|
30
|
+
desc: "When composing SSO's or verification URL's, this is the URL for the application"
|
31
|
+
|
32
|
+
add_composer :composed_url,
|
33
|
+
allowed: String,
|
34
|
+
dynamic_default: ->(instance) { "#{instance.url}#{ ":#{instance.port}" if instance.port }" },
|
35
|
+
desc: "The fully composed URL including the port number when needed. This Config variable is not needed as it is composed of the `url` and `port` composed values",
|
36
|
+
default_shown: "Composed String of the URL and PORT. Override this with caution"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "singleton"
|
4
|
+
require "class_composer"
|
5
|
+
require "api_engine_base/configuration/base"
|
6
|
+
require "api_engine_base/configuration/email/config"
|
7
|
+
require "api_engine_base/configuration/jwt/config"
|
8
|
+
require "api_engine_base/configuration/login/config"
|
9
|
+
require "api_engine_base/configuration/otp/config"
|
10
|
+
require "api_engine_base/configuration/username/config"
|
11
|
+
require "api_engine_base/configuration/application/config"
|
12
|
+
|
13
|
+
module ApiEngineBase
|
14
|
+
module Configuration
|
15
|
+
class Config < ::ApiEngineBase::Configuration::Base
|
16
|
+
include ClassComposer::Generator
|
17
|
+
|
18
|
+
add_composer :delete_secret_after_invalid,
|
19
|
+
desc: "Remove Secret after it is found as invalid",
|
20
|
+
allowed: [TrueClass, FalseClass],
|
21
|
+
default: true
|
22
|
+
|
23
|
+
add_composer :jwt,
|
24
|
+
desc: "JWT is the basis for Authorization and Authentication for this Engine. HMAC is the only support algorithm",
|
25
|
+
allowed: Configuration::Jwt::Config,
|
26
|
+
default: Configuration::Jwt::Config.new
|
27
|
+
|
28
|
+
add_composer :login,
|
29
|
+
desc: "Definition of Login Strategies.",
|
30
|
+
allowed: Configuration::Login::Config,
|
31
|
+
default: Configuration::Login::Config.new
|
32
|
+
|
33
|
+
add_composer :email,
|
34
|
+
desc: "Email configuration for the app sending Native Rails emails via ActiveMailer. Config changed here will update the Rails Configuration as well",
|
35
|
+
allowed: Configuration::Email::Config,
|
36
|
+
default: Configuration::Email::Config.new
|
37
|
+
|
38
|
+
add_composer :username,
|
39
|
+
desc: "Username configuration for the app",
|
40
|
+
allowed: Configuration::Username::Config,
|
41
|
+
default: Configuration::Username::Config.new
|
42
|
+
|
43
|
+
add_composer :application,
|
44
|
+
desc: "General configurations for the application. Primarily include application specific names, URL's, etc",
|
45
|
+
allowed: Configuration::Application::Config,
|
46
|
+
default: Configuration::Application::Config.new
|
47
|
+
|
48
|
+
# allow shorthand to be used
|
49
|
+
alias_method :app, :application
|
50
|
+
|
51
|
+
# To be Deleted
|
52
|
+
add_composer :otp,
|
53
|
+
desc: "One Time Password generator is used for all Code validation. This describes defaults not set in other configurations",
|
54
|
+
allowed: Configuration::Otp::Config,
|
55
|
+
default: Configuration::Otp::Config.new
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ApiEngineBase
|
4
|
+
module Configuration
|
5
|
+
module Email
|
6
|
+
class Config < ::ApiEngineBase::Configuration::Base
|
7
|
+
include ClassComposer::Generator
|
8
|
+
|
9
|
+
ASSIGNMENT = Proc.new do |key, value|
|
10
|
+
hash = Rails.configuration.action_mailer.smtp_settings ||= {}
|
11
|
+
Rails.configuration.action_mailer.smtp_settings = hash.merge({key => value})
|
12
|
+
end
|
13
|
+
|
14
|
+
ACTION_MAILER = Proc.new do |key, value|
|
15
|
+
Rails.configuration.action_mailer.public_send(:"#{key}=", value)
|
16
|
+
end
|
17
|
+
|
18
|
+
add_composer :user_name,
|
19
|
+
desc: "User Name for email. Defaults to ENV['GMAIL_USER_NAME']",
|
20
|
+
allowed: String,
|
21
|
+
default: ENV.fetch("GMAIL_USER_NAME", ""),
|
22
|
+
default_shown: "ENV[\"GMAIL_USER_NAME\"]",
|
23
|
+
&ASSIGNMENT
|
24
|
+
|
25
|
+
add_composer :password,
|
26
|
+
desc: "Password for email. Defaults to ENV['GMAIL_PASSWORD']. For more info on how to get this for GMAIL...https://support.google.com/accounts/answer/185833",
|
27
|
+
allowed: String,
|
28
|
+
default: ENV.fetch("GMAIL_PASSWORD", ""),
|
29
|
+
default_shown: "ENV[\"GMAIL_PASSWORD\"]",
|
30
|
+
&ASSIGNMENT
|
31
|
+
|
32
|
+
add_composer :port,
|
33
|
+
allowed: Integer,
|
34
|
+
default: 587,
|
35
|
+
&ASSIGNMENT
|
36
|
+
|
37
|
+
add_composer :address,
|
38
|
+
desc: "SMTP address for email. Defaults to smtp.gmail.com",
|
39
|
+
allowed: String,
|
40
|
+
default: "smtp.gmail.com",
|
41
|
+
&ASSIGNMENT
|
42
|
+
|
43
|
+
add_composer :authentication,
|
44
|
+
desc: "Authentication type for email",
|
45
|
+
allowed: String,
|
46
|
+
default: "plain",
|
47
|
+
&ASSIGNMENT
|
48
|
+
|
49
|
+
add_composer :enable_starttls_auto,
|
50
|
+
allowed: [TrueClass, FalseClass],
|
51
|
+
default: true,
|
52
|
+
&ASSIGNMENT
|
53
|
+
|
54
|
+
add_composer :delivery_method,
|
55
|
+
allowed: Symbol,
|
56
|
+
default: Rails.env.test? ? :test : :smtp,
|
57
|
+
&ACTION_MAILER
|
58
|
+
|
59
|
+
add_composer :perform_deliveries,
|
60
|
+
allowed: [TrueClass, FalseClass],
|
61
|
+
default: true,
|
62
|
+
&ACTION_MAILER
|
63
|
+
|
64
|
+
add_composer :raise_delivery_errors,
|
65
|
+
allowed: [TrueClass, FalseClass],
|
66
|
+
default: true,
|
67
|
+
&ACTION_MAILER
|
68
|
+
|
69
|
+
def gmail!(
|
70
|
+
from: ENV["GMAIL_USER_NAME"],
|
71
|
+
password: ENV["GMAIL_PASSWORD"],
|
72
|
+
port: 587,
|
73
|
+
address: "smtp.gmail.com",
|
74
|
+
authentication: "plain",
|
75
|
+
enable_starttls_auto: true
|
76
|
+
)
|
77
|
+
method(:from=).call(from)
|
78
|
+
method(:password=).call(password)
|
79
|
+
method(:port=).call(port)
|
80
|
+
method(:address=).call(address)
|
81
|
+
method(:authentication=).call(authentication)
|
82
|
+
method(:enable_starttls_auto=).call(enable_starttls_auto)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ApiEngineBase
|
4
|
+
module Configuration
|
5
|
+
module Jwt
|
6
|
+
class Config < ::ApiEngineBase::Configuration::Base
|
7
|
+
include ClassComposer::Generator
|
8
|
+
|
9
|
+
add_composer :ttl,
|
10
|
+
desc: "Default TTL on how long the token is valid for",
|
11
|
+
allowed: ActiveSupport::Duration,
|
12
|
+
default: 7.days
|
13
|
+
|
14
|
+
add_composer :hmac_secret,
|
15
|
+
desc: "HMAC is the only algorithm supported. This is the secret key to encrypt he JWT token",
|
16
|
+
allowed: String,
|
17
|
+
default: ENV.fetch("SECRET_KEY_BASE","Thi$IsASeccretIwi::CH&ang3"),
|
18
|
+
default_shown: "ENV.fetch(\"SECRET_KEY_BASE\",\"Thi$IsASeccretIwi::CH&ang3\")"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "api_engine_base/configuration/login/strategy/plain_text/config"
|
4
|
+
|
5
|
+
module ApiEngineBase
|
6
|
+
module Configuration
|
7
|
+
module Login
|
8
|
+
class Config < ::ApiEngineBase::Configuration::Base
|
9
|
+
include ClassComposer::Generator
|
10
|
+
|
11
|
+
add_composer_blocking :plain_text,
|
12
|
+
desc: "Login strategy for plain text authentication via User/Password combination",
|
13
|
+
composer_class: Strategy::PlainText::Config,
|
14
|
+
enable_attr: :enable
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "api_engine_base/configuration/login/strategy/plain_text/lockable"
|
4
|
+
require "api_engine_base/configuration/login/strategy/plain_text/email_verify"
|
5
|
+
|
6
|
+
module ApiEngineBase
|
7
|
+
module Configuration
|
8
|
+
module Login
|
9
|
+
module Strategy
|
10
|
+
module PlainText
|
11
|
+
class Config < ::ApiEngineBase::Configuration::Base
|
12
|
+
include ClassComposer::Generator
|
13
|
+
|
14
|
+
add_composer :enable,
|
15
|
+
desc: "Login Strategy for User/Password. By default, this is enabled",
|
16
|
+
allowed: [FalseClass, TrueClass],
|
17
|
+
default: true
|
18
|
+
|
19
|
+
add_composer_blocking :lockable,
|
20
|
+
desc: "Enable and change Lockable for User/Password Login strategy.",
|
21
|
+
composer_class: Locakable,
|
22
|
+
enable_attr: :enable
|
23
|
+
|
24
|
+
add_composer_blocking :email_verify,
|
25
|
+
desc: "Enable and change Email Verification for User/Password Login strategy.",
|
26
|
+
composer_class: EmailVerify,
|
27
|
+
enable_attr: :enable
|
28
|
+
|
29
|
+
add_composer :password_length_max,
|
30
|
+
desc: "Max Length for Password",
|
31
|
+
allowed: Integer,
|
32
|
+
default: 64
|
33
|
+
|
34
|
+
add_composer :password_length_min,
|
35
|
+
desc: "Min Length for Password",
|
36
|
+
allowed: Integer,
|
37
|
+
default: 8
|
38
|
+
|
39
|
+
add_composer :email_length_max,
|
40
|
+
desc: "Max Length for Email",
|
41
|
+
allowed: Integer,
|
42
|
+
default: 64
|
43
|
+
|
44
|
+
add_composer :email_length_min,
|
45
|
+
desc: "Min Length for Email",
|
46
|
+
allowed: Integer,
|
47
|
+
default: 8
|
48
|
+
|
49
|
+
def enable?
|
50
|
+
enable
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ApiEngineBase
|
4
|
+
module Configuration
|
5
|
+
module Login
|
6
|
+
module Strategy
|
7
|
+
module PlainText
|
8
|
+
class EmailVerify < ::ApiEngineBase::Configuration::Base
|
9
|
+
include ClassComposer::Generator
|
10
|
+
|
11
|
+
add_composer :enable,
|
12
|
+
desc: "Email Verify will help ensure that users emails are valid by requesting a verify code. By default this is enabled",
|
13
|
+
allowed: [FalseClass, TrueClass],
|
14
|
+
default: true
|
15
|
+
|
16
|
+
add_composer :verify_email_required_within,
|
17
|
+
desc: "Strategy allows user to set time before email verification is required. After time has expired, usage of API is no longer valid until email has been verified. Up until this time, the Login Strategy will allow usage of the API. Default time is set to 0 minutes",
|
18
|
+
allowed: ActiveSupport::Duration,
|
19
|
+
default: 0.minutes,
|
20
|
+
validator: -> (val) { val < 10.days },
|
21
|
+
invalid_message: ->(val) { "Provided #{val}. Value must be less than #{10.days}" }
|
22
|
+
|
23
|
+
|
24
|
+
add_composer :verify_code_link_required_within,
|
25
|
+
desc: "When the email verification is sent, how long will that code be valid for. By default, this is set to 10 minutes",
|
26
|
+
allowed: ActiveSupport::Duration,
|
27
|
+
default: 10.minutes,
|
28
|
+
validator: -> (val) { val < 60.minutes },
|
29
|
+
invalid_message: ->(val) { "Provided #{val}. Value must be less than #{60.minutes}" }
|
30
|
+
|
31
|
+
|
32
|
+
add_composer :verify_code_link_valid_for,
|
33
|
+
desc: "When the email verification is sent, how long will that code be valid for. By default, this is set to 10 minutes",
|
34
|
+
allowed: ActiveSupport::Duration,
|
35
|
+
default: 10.minutes,
|
36
|
+
validator: -> (val) { val < 60.minutes },
|
37
|
+
invalid_message: ->(val) { "Provided #{val}. Value must be less than #{60.minutes}" }
|
38
|
+
|
39
|
+
add_composer :verify_code_length,
|
40
|
+
desc: "The length of the verify code sent via email.",
|
41
|
+
allowed: Integer,
|
42
|
+
default: 6,
|
43
|
+
validator: -> (val) { (val <= 10) && (val >= 4) },
|
44
|
+
invalid_message: ->(val) { "Provided #{val}. Value must be less than or equal to 10 and greater than or equal to 4." }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ApiEngineBase
|
4
|
+
module Configuration
|
5
|
+
module Login
|
6
|
+
module Strategy
|
7
|
+
module PlainText
|
8
|
+
class Locakable < ::ApiEngineBase::Configuration::Base
|
9
|
+
include ClassComposer::Generator
|
10
|
+
|
11
|
+
add_composer :enable,
|
12
|
+
desc: "Disabled by default. When enabled, this adds an additional level of support for brute force attacks on User/Password Logins",
|
13
|
+
allowed: [FalseClass, TrueClass],
|
14
|
+
default: false
|
15
|
+
|
16
|
+
add_composer :password_attempts,
|
17
|
+
desc: "Max failed password attempts before additional verification on account is required.",
|
18
|
+
allowed: Integer,
|
19
|
+
default: 10,
|
20
|
+
validator: -> (val) { val >= 0 && val < 50 },
|
21
|
+
invalid_message: ->(val) { "Max password attempts must be >=0 and less than 50. Received #{val}" }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|