authenticatable 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +19 -0
  3. data/README.md +144 -0
  4. data/app/controllers/authenticatable/passwords_controller.rb +51 -0
  5. data/app/controllers/authenticatable/registrations_controller.rb +28 -0
  6. data/app/controllers/authenticatable/sessions_controller.rb +49 -0
  7. data/app/controllers/authenticatable_controller.rb +101 -0
  8. data/app/mailers/authenticatable/mailer.rb +17 -0
  9. data/app/views/authenticatable/mailer/reset_password.html.erb +8 -0
  10. data/app/views/authenticatable/passwords/_edit_form.html.erb +6 -0
  11. data/app/views/authenticatable/passwords/_new_form.html.erb +6 -0
  12. data/app/views/authenticatable/passwords/edit.html.erb +7 -0
  13. data/app/views/authenticatable/passwords/new.html.erb +7 -0
  14. data/app/views/authenticatable/registrations/_form.html.erb +12 -0
  15. data/app/views/authenticatable/registrations/new.html.erb +5 -0
  16. data/app/views/authenticatable/sessions/_form.html.erb +5 -0
  17. data/app/views/authenticatable/sessions/new.html.erb +7 -0
  18. data/app/views/authenticatable/shared/_errors.html.erb +7 -0
  19. data/app/views/authenticatable/shared/_flash_messages.html.erb +3 -0
  20. data/app/views/authenticatable/shared/_links.html.erb +12 -0
  21. data/config/locales/en.yml +14 -0
  22. data/lib/authenticatable/controllers/helpers.rb +72 -0
  23. data/lib/authenticatable/controllers/url_helpers.rb +67 -0
  24. data/lib/authenticatable/controllers.rb +9 -0
  25. data/lib/authenticatable/engine.rb +17 -0
  26. data/lib/authenticatable/errors/unauthenticated_error.rb +6 -0
  27. data/lib/authenticatable/errors.rb +6 -0
  28. data/lib/authenticatable/manager.rb +16 -0
  29. data/lib/authenticatable/models/email_validator.rb +17 -0
  30. data/lib/authenticatable/models/identifier.rb +43 -0
  31. data/lib/authenticatable/models/password.rb +73 -0
  32. data/lib/authenticatable/models.rb +67 -0
  33. data/lib/authenticatable/proxy.rb +103 -0
  34. data/lib/authenticatable/rails/routes.rb +61 -0
  35. data/lib/authenticatable/rspec.rb +8 -0
  36. data/lib/authenticatable/scope.rb +110 -0
  37. data/lib/authenticatable/serializers/base.rb +39 -0
  38. data/lib/authenticatable/serializers/session.rb +36 -0
  39. data/lib/authenticatable/serializers.rb +9 -0
  40. data/lib/authenticatable/testing/controller_helpers.rb +31 -0
  41. data/lib/authenticatable/token.rb +13 -0
  42. data/lib/authenticatable/version.rb +5 -0
  43. data/lib/authenticatable.rb +100 -0
  44. data/lib/generators/active_record/authenticatable_generator.rb +63 -0
  45. data/lib/generators/active_record/templates/migration.tt +15 -0
  46. data/lib/generators/active_record/templates/migration_existing.tt +23 -0
  47. data/lib/generators/authenticatable/authenticatable_generator.rb +18 -0
  48. data/lib/generators/authenticatable/orm_helpers.rb +30 -0
  49. data/lib/generators/authenticatable/views_generator.rb +19 -0
  50. metadata +136 -0
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "authenticatable/serializers/base"
4
+ require "authenticatable/serializers/session"
5
+
6
+ module Authenticatable
7
+ module Serializers
8
+ end
9
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Authenticatable
4
+ module Testing
5
+ module ControllerHelpers
6
+ def setup_controller_request_and_response
7
+ super
8
+ @request.env["authenticatable"] = Authenticatable::Proxy.new(@request.env)
9
+
10
+ # Set default testing scope to :users
11
+ @request.env["authenticatable.scope"] = Authenticatable.add_scope(:users, {})
12
+ end
13
+
14
+ def sign_in(resource, store = nil)
15
+ @request.env["authenticatable"].sign_in(resource, store)
16
+ end
17
+
18
+ # def sign_out(resource, store = nil)
19
+ # @request.env["authenticatable"].sign_out(resource, store)
20
+ # end
21
+
22
+ def authenticatable_scope(scope, options = {})
23
+ @request.env["authenticatable.scope"] = Authenticatable.add_scope(scope, options)
24
+ end
25
+
26
+ def authenticatable
27
+ @request.env["authenticatable"]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Authenticatable
4
+ # Random token used for password reset and remember tokens.
5
+ class Token
6
+ # Generate a new random hex token.
7
+ class << self
8
+ def new(bytes = 20)
9
+ SecureRandom.hex(bytes)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Authenticatable
4
+ VERSION = "1.0.0"
5
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "authenticatable/controllers"
4
+ require "authenticatable/errors"
5
+ require "authenticatable/manager"
6
+ require "authenticatable/models"
7
+ require "authenticatable/rails/routes"
8
+ require "authenticatable/scope"
9
+ require "authenticatable/proxy"
10
+ require "authenticatable/serializers"
11
+ require "authenticatable/token"
12
+ require "authenticatable/version"
13
+ require "authenticatable/engine"
14
+
15
+ require "dry-configurable"
16
+
17
+ # Authenticatable is an authentication solution for Rails. All related modules,
18
+ # classes and methods will be namespaced under the namespace 'Authenticatable':
19
+ module Authenticatable
20
+ extend Dry::Configurable
21
+
22
+ # Store authenticatable resources as scopes.
23
+ @scopes = {}
24
+
25
+ # Default extensions to load into your authenticatable models. This can for example
26
+ # be a custom Warden strategy, concerns with validations, or basically anything that
27
+ # modifies the default behavior of your authenticatable model. Feel free to develop
28
+ # add publish your own extensions online. We'd be happy to see what you can accomplish.
29
+ #
30
+ # email_validator: Validates emails with the valid_email2 gem. You can remove this
31
+ # if you want to implement your own email validations or if you
32
+ # don't want to validate emails at all.
33
+ #
34
+ # password: Enables password authentication with has_secure_password. This extension
35
+ # includes a Warden Strategy for authentication with password. It does also
36
+ # add password validations to your authenticatable model.
37
+ #
38
+ setting :default_extensions, %i[password]
39
+
40
+ # Default column to use when looking up an authenticatable record in the database.
41
+ # Can be for example email or a username. Default is :email. This can also be changed
42
+ # for an individual resource/scope by passing :identifier to the authentication
43
+ # method in your model:
44
+ #
45
+ # authenticatable identifier: :username
46
+ #
47
+ setting :default_identifier, :email
48
+
49
+ # The controller class that all Authenticatable controllers will inherit from.
50
+ # Defaults to `ApplicationController`.
51
+ setting :parent_controller, "ApplicationController"
52
+
53
+ # The mailer class that all Authenticatable mailers will inherit from.
54
+ # Defaults to `ApplicationMailer`.
55
+ setting :parent_mailer, "ApplicationMailer"
56
+
57
+ # Range validation for password length
58
+ setting :password_length, 6..128
59
+
60
+ # Requests method(s) allowed for sign out. You can for example add :get
61
+ # to enable sign out by visiting /users/sign_out.
62
+ setting :allowed_sign_out_methods, %i[delete]
63
+
64
+ # Which formats should be treated as navigational. Navigational formats
65
+ # like :html & :turbo_stream should redirect and show flash messages,
66
+ # but other formats like :xml or :json, should return 401. The "*/*"
67
+ # format below is required to match Internet Explorer.
68
+ setting :navigational_formats, ["*/*", :html, :turbo_stream]
69
+
70
+ # Email regex used to validate email formats. It simply asserts that
71
+ # one (and only one) @ exists in the given string. This is mainly
72
+ # to give user feedback and not to assert the e-mail validity.
73
+ setting :email_regexp, /\A[^@\s]+@[^@\s]+\z/
74
+
75
+ # Specify for how long a reset_password_token will be valid.
76
+ # When the time expires, their token will be invalid and they
77
+ # have to request a "reset password link" to recover their account.
78
+ setting :reset_password_expiration_time, 1.hour
79
+
80
+ class << self
81
+ attr_reader :scopes
82
+
83
+ def reset_default_values!
84
+ remove_instance_variable :@config
85
+ end
86
+
87
+ # Add a authenticatable model to list of scopes. It could be for example
88
+ # :users, :admins, :subscribers or any other model that should be authenticatable.
89
+ def add_scope(resource, options = {})
90
+ scope = Authenticatable::Scope.new(resource, options)
91
+ @scopes[scope.singular_name.to_sym] = scope
92
+
93
+ # Define helpers like current_{scope}, authenticate_{scope}!
94
+ # {scope}_signed_in? etc for the given scope that can be accessed from all controllers.
95
+ Authenticatable::Controllers::Helpers.define_helpers(scope.singular_name)
96
+
97
+ scope
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/active_record"
4
+ require "generators/authenticatable/orm_helpers"
5
+
6
+ module ActiveRecord
7
+ module Generators
8
+ class AuthenticatableGenerator < ActiveRecord::Generators::Base
9
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
10
+
11
+ include Authenticatable::Generators::OrmHelpers
12
+ source_root File.expand_path("templates", __dir__)
13
+
14
+ def copy_authenticatable_migration
15
+ if (behavior == :invoke && model_exists?) || (behavior == :revoke && migration_exists?(table_name))
16
+ migration_template "migration_existing.tt", "#{db_migrate_path}/add_authenticatable_to_#{table_name}.rb",
17
+ migration_version: migration_version
18
+ else
19
+ migration_template "migration.tt", "#{db_migrate_path}/authenticatable_create_#{table_name}.rb",
20
+ migration_version: migration_version
21
+ end
22
+ end
23
+
24
+ def generate_model
25
+ invoke "active_record:model", [name], migration: false unless model_exists? && behavior == :invoke
26
+ end
27
+
28
+ # rubocop:disable Metrics/AbcSize
29
+ def inject_authenticatable
30
+ content = model_contents
31
+ class_path = (namespaced? ? class_name.to_s.split("::") : [class_name])
32
+ indent_depth = class_path.size - 1
33
+ content = content.split("\n").map { |line| (" " * indent_depth) + line }.join("\n") << "\n"
34
+
35
+ inject_into_class(model_path, class_path.last, content) if model_exists?
36
+ end
37
+ # rubocop:enable Metrics/AbcSize
38
+
39
+ def add_route
40
+ route_content = "authenticatable :#{plural_name}"
41
+ route route_content
42
+ end
43
+
44
+ private
45
+
46
+ def model_contents
47
+ " authenticatable\n"
48
+ end
49
+
50
+ # rubocop:disable Layout/IndentationWidth
51
+ def migration_data
52
+ <<RUBY
53
+ ## Authenticatable
54
+ t.string :email, null: false, default: ""
55
+ t.string :password_digest, null: false, default: ""
56
+ t.string :reset_password_token, null: false, default: ""
57
+ t.datetime :reset_password_sent_at
58
+ RUBY
59
+ end
60
+ # rubocop:enable Layout/IndentationWidth
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AuthenticatableCreate<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
4
+ def change
5
+ create_table :<%= table_name %><%= primary_key_type %> do |t|
6
+ <%= migration_data -%>
7
+ <% attributes.each do |attribute| -%>
8
+ t.<%= attribute.type %> :<%= attribute.name %>
9
+ <% end -%>
10
+ t.timestamps null: false
11
+ end
12
+ add_index :<%= table_name %>, :email, unique: true
13
+ add_index :<%= table_name %>, :reset_password_token
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddAuthenticatableTo<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
4
+ def self.up
5
+ change_table :<%= table_name %> do |t|
6
+ <%= migration_data -%>
7
+
8
+ <% attributes.each do |attribute| -%>
9
+ t.<%= attribute.type %> :<%= attribute.name %>
10
+ <% end -%>
11
+ # Uncomment below if timestamps were not included in your original model.
12
+ # t.timestamps null: false
13
+ end
14
+ add_index :<%= table_name %>, :email, unique: true
15
+ add_index :<%= table_name %>, :reset_password_token
16
+ end
17
+
18
+ def self.down
19
+ # By default, we don't want to make any assumption about how to roll back a migration when your
20
+ # model already existed. Please edit below which fields you would like to remove in this migration.
21
+ raise ActiveRecord::IrreversibleMigration
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/named_base"
4
+
5
+ module Authenticatable
6
+ module Generators
7
+ class AuthenticatableGenerator < Rails::Generators::NamedBase
8
+ include Rails::Generators::ResourceHelpers
9
+
10
+ namespace "authenticatable"
11
+
12
+ desc "Generates a model with the given NAME (if one does not exist) with authenticatable " \
13
+ "configuration plus a migration file and authentication routes."
14
+
15
+ hook_for :orm, required: true
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Authenticatable
4
+ module Generators
5
+ module OrmHelpers
6
+ private
7
+
8
+ def model_exists?
9
+ File.exist?(File.join(destination_root, model_path))
10
+ end
11
+
12
+ def migration_exists?(table_name)
13
+ Dir.glob("#{File.join(destination_root, db_migrate_path)}/[0-9]*_*.rb")
14
+ .grep(/\d+_add_authenticatable_to_#{table_name}.rb$/).first
15
+ end
16
+
17
+ def model_path
18
+ @model_path ||= File.join("app", "models", "#{file_path}.rb")
19
+ end
20
+
21
+ def migration_version
22
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" if rails5_and_up?
23
+ end
24
+
25
+ def rails5_and_up?
26
+ Rails::VERSION::MAJOR >= 5
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+ require "active_support/core_ext/string/inflections"
5
+
6
+ module Authenticatable
7
+ module Generators
8
+ class ViewsGenerator < Rails::Generators::Base
9
+ desc "Copy view files to app/views"
10
+ source_root Authenticatable::Engine.root
11
+ argument :scope, default: "authenticatable", type: :string
12
+
13
+ def copy_views
14
+ scope_directory = (scope == "authenticatable" ? "authenticatable" : ActiveSupport::Inflector.pluralize(scope))
15
+ directory "app/views/authenticatable", "app/views/#{scope_directory}"
16
+ end
17
+ end
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: authenticatable
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Rasmus Kjellberg
8
+ - KIQR
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2021-10-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bcrypt
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 3.1.16
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 3.1.16
28
+ - !ruby/object:Gem::Dependency
29
+ name: dry-configurable
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 0.11.0
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 0.11.0
42
+ - !ruby/object:Gem::Dependency
43
+ name: valid_email2
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: 4.0.0
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: 4.0.0
56
+ description:
57
+ email: hello@kiqr.dev
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - LICENSE.md
63
+ - README.md
64
+ - app/controllers/authenticatable/passwords_controller.rb
65
+ - app/controllers/authenticatable/registrations_controller.rb
66
+ - app/controllers/authenticatable/sessions_controller.rb
67
+ - app/controllers/authenticatable_controller.rb
68
+ - app/mailers/authenticatable/mailer.rb
69
+ - app/views/authenticatable/mailer/reset_password.html.erb
70
+ - app/views/authenticatable/passwords/_edit_form.html.erb
71
+ - app/views/authenticatable/passwords/_new_form.html.erb
72
+ - app/views/authenticatable/passwords/edit.html.erb
73
+ - app/views/authenticatable/passwords/new.html.erb
74
+ - app/views/authenticatable/registrations/_form.html.erb
75
+ - app/views/authenticatable/registrations/new.html.erb
76
+ - app/views/authenticatable/sessions/_form.html.erb
77
+ - app/views/authenticatable/sessions/new.html.erb
78
+ - app/views/authenticatable/shared/_errors.html.erb
79
+ - app/views/authenticatable/shared/_flash_messages.html.erb
80
+ - app/views/authenticatable/shared/_links.html.erb
81
+ - config/locales/en.yml
82
+ - lib/authenticatable.rb
83
+ - lib/authenticatable/controllers.rb
84
+ - lib/authenticatable/controllers/helpers.rb
85
+ - lib/authenticatable/controllers/url_helpers.rb
86
+ - lib/authenticatable/engine.rb
87
+ - lib/authenticatable/errors.rb
88
+ - lib/authenticatable/errors/unauthenticated_error.rb
89
+ - lib/authenticatable/manager.rb
90
+ - lib/authenticatable/models.rb
91
+ - lib/authenticatable/models/email_validator.rb
92
+ - lib/authenticatable/models/identifier.rb
93
+ - lib/authenticatable/models/password.rb
94
+ - lib/authenticatable/proxy.rb
95
+ - lib/authenticatable/rails/routes.rb
96
+ - lib/authenticatable/rspec.rb
97
+ - lib/authenticatable/scope.rb
98
+ - lib/authenticatable/serializers.rb
99
+ - lib/authenticatable/serializers/base.rb
100
+ - lib/authenticatable/serializers/session.rb
101
+ - lib/authenticatable/testing/controller_helpers.rb
102
+ - lib/authenticatable/token.rb
103
+ - lib/authenticatable/version.rb
104
+ - lib/generators/active_record/authenticatable_generator.rb
105
+ - lib/generators/active_record/templates/migration.tt
106
+ - lib/generators/active_record/templates/migration_existing.tt
107
+ - lib/generators/authenticatable/authenticatable_generator.rb
108
+ - lib/generators/authenticatable/orm_helpers.rb
109
+ - lib/generators/authenticatable/views_generator.rb
110
+ homepage: https://github.com/kiqr/authenticatable
111
+ licenses:
112
+ - MIT
113
+ metadata:
114
+ bug_tracker_uri: https://github.com/kiqr/authenticatable/issues
115
+ documentation_uri: https://github.com/kiqr/authenticatable/issues
116
+ source_code_uri: https://github.com/kiqr/authenticatable
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '2.6'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubygems_version: 3.2.26
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: Authentication solution for Ruby on Rails
136
+ test_files: []