api_engine_base 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +32 -0
  5. data/app/controllers/api_engine_base/application_controller.rb +47 -0
  6. data/app/controllers/api_engine_base/auth/plain_text_controller.rb +132 -0
  7. data/app/controllers/api_engine_base/username_controller.rb +26 -0
  8. data/app/controllers/concerns/api_engine_base/schematizable.rb +5 -0
  9. data/app/helpers/api_engine_base/application_helper.rb +4 -0
  10. data/app/helpers/api_engine_base/schema_helper.rb +29 -0
  11. data/app/jobs/api_engine_base/application_job.rb +4 -0
  12. data/app/mailers/api_engine_base/application_mailer.rb +8 -0
  13. data/app/mailers/api_engine_base/email_verification_mailer.rb +12 -0
  14. data/app/models/api_engine_base/application_record.rb +7 -0
  15. data/app/models/user.rb +50 -0
  16. data/app/models/user_secret.rb +72 -0
  17. data/app/services/api_engine_base/argument_validation/class_methods.rb +179 -0
  18. data/app/services/api_engine_base/argument_validation/instance_methods.rb +136 -0
  19. data/app/services/api_engine_base/argument_validation.rb +11 -0
  20. data/app/services/api_engine_base/jwt/authenticate_user.rb +71 -0
  21. data/app/services/api_engine_base/jwt/decode.rb +21 -0
  22. data/app/services/api_engine_base/jwt/encode.rb +15 -0
  23. data/app/services/api_engine_base/jwt/login_create.rb +21 -0
  24. data/app/services/api_engine_base/jwt/time_delay_token.rb +17 -0
  25. data/app/services/api_engine_base/login_strategy/plain_text/create.rb +42 -0
  26. data/app/services/api_engine_base/login_strategy/plain_text/email_verification/generate.rb +29 -0
  27. data/app/services/api_engine_base/login_strategy/plain_text/email_verification/required.rb +20 -0
  28. data/app/services/api_engine_base/login_strategy/plain_text/email_verification/send.rb +23 -0
  29. data/app/services/api_engine_base/login_strategy/plain_text/email_verification/verify.rb +24 -0
  30. data/app/services/api_engine_base/login_strategy/plain_text/login.rb +50 -0
  31. data/app/services/api_engine_base/secrets/cleanse.rb +14 -0
  32. data/app/services/api_engine_base/secrets/generate.rb +62 -0
  33. data/app/services/api_engine_base/secrets/verify.rb +27 -0
  34. data/app/services/api_engine_base/secrets.rb +15 -0
  35. data/app/services/api_engine_base/service_base.rb +90 -0
  36. data/app/services/api_engine_base/service_logging.rb +41 -0
  37. data/app/services/api_engine_base/username/available.rb +64 -0
  38. data/app/views/api_engine_base/email_verification_mailer/verify_email.html.erb +26 -0
  39. data/config/routes.rb +23 -0
  40. data/db/migrate/20241117043720_create_api_engine_base_users.rb +33 -0
  41. data/db/migrate/20241204065708_create_api_engine_base_user_secrets.rb +16 -0
  42. data/lib/api_engine_base/configuration/application/config.rb +40 -0
  43. data/lib/api_engine_base/configuration/base.rb +11 -0
  44. data/lib/api_engine_base/configuration/config.rb +59 -0
  45. data/lib/api_engine_base/configuration/email/config.rb +87 -0
  46. data/lib/api_engine_base/configuration/jwt/config.rb +22 -0
  47. data/lib/api_engine_base/configuration/login/config.rb +18 -0
  48. data/lib/api_engine_base/configuration/login/strategy/plain_text/config.rb +57 -0
  49. data/lib/api_engine_base/configuration/login/strategy/plain_text/email_verify.rb +50 -0
  50. data/lib/api_engine_base/configuration/login/strategy/plain_text/lockable.rb +27 -0
  51. data/lib/api_engine_base/configuration/otp/config.rb +54 -0
  52. data/lib/api_engine_base/configuration/username/check.rb +31 -0
  53. data/lib/api_engine_base/configuration/username/config.rb +41 -0
  54. data/lib/api_engine_base/engine.rb +21 -0
  55. data/lib/api_engine_base/schema/error/base.rb +15 -0
  56. data/lib/api_engine_base/schema/error/invalid_argument.rb +15 -0
  57. data/lib/api_engine_base/schema/error/invalid_argument_response.rb +17 -0
  58. data/lib/api_engine_base/schema/plain_text/create_user_request.rb +18 -0
  59. data/lib/api_engine_base/schema/plain_text/create_user_response.rb +17 -0
  60. data/lib/api_engine_base/schema/plain_text/email_verify_request.rb +11 -0
  61. data/lib/api_engine_base/schema/plain_text/email_verify_response.rb +11 -0
  62. data/lib/api_engine_base/schema/plain_text/email_verify_send_request.rb +9 -0
  63. data/lib/api_engine_base/schema/plain_text/email_verify_send_response.rb +11 -0
  64. data/lib/api_engine_base/schema/plain_text/login_request.rb +15 -0
  65. data/lib/api_engine_base/schema/plain_text/login_response.rb +13 -0
  66. data/lib/api_engine_base/schema.rb +25 -0
  67. data/lib/api_engine_base/spec_helper.rb +18 -0
  68. data/lib/api_engine_base/version.rb +5 -0
  69. data/lib/api_engine_base.rb +33 -0
  70. data/lib/generators/api_engine_base/configure/USAGE +8 -0
  71. data/lib/generators/api_engine_base/configure/configure_generator.rb +12 -0
  72. data/lib/tasks/auto_annotate_models.rake +60 -0
  73. 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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "class_composer"
4
+ require "pry"
5
+
6
+ module ApiEngineBase
7
+ module Configuration
8
+ class Base
9
+ end
10
+ end
11
+ 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