auto_auth 0.0.1

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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +57 -0
  7. data/Rakefile +20 -0
  8. data/auto_auth.gemspec +24 -0
  9. data/lib/auto_auth.rb +2 -0
  10. data/lib/generators/auto_auth/install_generator.rb +142 -0
  11. data/lib/generators/auto_auth/version.rb +3 -0
  12. data/lib/generators/templates/config/locales/auto_auth.en.yml +24 -0
  13. data/lib/generators/templates/controllers/concerns/authentication.rb +52 -0
  14. data/lib/generators/templates/controllers/passwords_controller.rb +47 -0
  15. data/lib/generators/templates/controllers/registrations_controller.rb +46 -0
  16. data/lib/generators/templates/controllers/sessions_controller.rb +32 -0
  17. data/lib/generators/templates/mailers/identity_mailer.rb +15 -0
  18. data/lib/generators/templates/migration.rb +21 -0
  19. data/lib/generators/templates/models/concerns/token_verification.rb +35 -0
  20. data/lib/generators/templates/models/domain_model.rb +7 -0
  21. data/lib/generators/templates/models/identity_model.rb +62 -0
  22. data/lib/generators/templates/models/registration.rb +30 -0
  23. data/lib/generators/templates/views/mailers/confirm_email.html.erb +4 -0
  24. data/lib/generators/templates/views/mailers/reset_password.html.erb +4 -0
  25. data/lib/generators/templates/views/passwords/edit.html.erb +14 -0
  26. data/lib/generators/templates/views/passwords/new.html.erb +9 -0
  27. data/lib/generators/templates/views/registrations/new.html.erb +30 -0
  28. data/lib/generators/templates/views/sessions/new.html.erb +13 -0
  29. data/test/auto_auth_test.rb +12 -0
  30. data/test/dummy/README.rdoc +28 -0
  31. data/test/dummy/Rakefile +6 -0
  32. data/test/dummy/app/assets/images/.keep +0 -0
  33. data/test/dummy/app/assets/javascripts/application.js +13 -0
  34. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  35. data/test/dummy/app/controllers/application_controller.rb +5 -0
  36. data/test/dummy/app/controllers/concerns/.keep +0 -0
  37. data/test/dummy/app/helpers/application_helper.rb +2 -0
  38. data/test/dummy/app/mailers/.keep +0 -0
  39. data/test/dummy/app/models/.keep +0 -0
  40. data/test/dummy/app/models/concerns/.keep +0 -0
  41. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  42. data/test/dummy/bin/bundle +3 -0
  43. data/test/dummy/bin/rails +4 -0
  44. data/test/dummy/bin/rake +4 -0
  45. data/test/dummy/config/application.rb +23 -0
  46. data/test/dummy/config/boot.rb +5 -0
  47. data/test/dummy/config/database.yml +25 -0
  48. data/test/dummy/config/environment.rb +5 -0
  49. data/test/dummy/config/environments/development.rb +29 -0
  50. data/test/dummy/config/environments/production.rb +80 -0
  51. data/test/dummy/config/environments/test.rb +36 -0
  52. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  53. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  54. data/test/dummy/config/initializers/inflections.rb +16 -0
  55. data/test/dummy/config/initializers/mime_types.rb +5 -0
  56. data/test/dummy/config/initializers/secret_token.rb +12 -0
  57. data/test/dummy/config/initializers/session_store.rb +3 -0
  58. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  59. data/test/dummy/config/locales/en.yml +23 -0
  60. data/test/dummy/config/routes.rb +56 -0
  61. data/test/dummy/config.ru +4 -0
  62. data/test/dummy/db/test.sqlite3 +0 -0
  63. data/test/dummy/lib/assets/.keep +0 -0
  64. data/test/dummy/log/.keep +0 -0
  65. data/test/dummy/log/test.log +5 -0
  66. data/test/dummy/public/404.html +58 -0
  67. data/test/dummy/public/422.html +58 -0
  68. data/test/dummy/public/500.html +57 -0
  69. data/test/dummy/public/favicon.ico +0 -0
  70. data/test/test_helper.rb +17 -0
  71. metadata +211 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e35be5a61d4d181863f37c1fcdd055428c113a47
4
+ data.tar.gz: ab2b7f4fc1f8aa1a0d7516485cb4c85448c54dfa
5
+ SHA512:
6
+ metadata.gz: ebec47215fe6608392c8e7aea468986afff70116da6159162a0b6a0f0513bc9144caf4c99b6a072893abeec6580d599d2a1816c9e4426f91981c8a2061e2b18e
7
+ data.tar.gz: e75c6b28666af1dd82a2539299aac2274ecb123b3f9a903b031ffff51bc243dd106977e1a8d0f72387ca963071671e20b7f847d08a4a206c12d73526b918e6b7
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.log
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.5
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Tony Schneider
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # AutoAuth
2
+
3
+ A simple and sane rails authentication generator.
4
+
5
+ Generates roughly the following:
6
+ ```
7
+ app/
8
+ models/
9
+ concerns/
10
+ token_verification.rb
11
+ user.rb
12
+ identity.rb
13
+ registration.rb
14
+ controllers/
15
+ concerns/
16
+ authentication.rb
17
+ sessions_controller.rb
18
+ registrations_controller.rb
19
+ passwords_controller.rb
20
+ mailers
21
+ identity_mailer.rb
22
+ views
23
+ passwords/
24
+ edit.html.erb
25
+ new.html.erb
26
+ sessions/
27
+ new.html.erb
28
+ registrations/
29
+ new.html.erb
30
+ identity_mailer/
31
+ confirm_email.html.erb
32
+ reset_password.html.erb
33
+ config/
34
+ locales/
35
+ auto_auth.en.yml
36
+
37
+ ```
38
+
39
+ ## Installation
40
+
41
+ I wouldn't add this to my gemfile, since it's really only meant to be run once. Instead, do `gem install auto_auth`
42
+
43
+ And then execute:
44
+
45
+ $ rails generate auto_auth:install
46
+
47
+ ## Usage
48
+
49
+ By default I have my domain model named `User` and my identity model named `Identity`. You can customize this by passing `--domain_model` and `--identity_model` to the rails generator.
50
+
51
+ ## Contributing
52
+
53
+ 1. Fork it ( http://github.com/<my-github-username>/auto_auth/fork )
54
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
55
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
56
+ 4. Push to the branch (`git push origin my-new-feature`)
57
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ Bundler::GemHelper.install_tasks
8
+
9
+ require 'rake/testtask'
10
+
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs << 'lib'
13
+ t.libs << 'test'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = false
16
+ end
17
+
18
+
19
+ task default: :test
20
+
data/auto_auth.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'generators/auto_auth/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "auto_auth"
8
+ spec.version = AutoAuth::VERSION
9
+ spec.authors = ["Tony Schneider"]
10
+ spec.email = ["tonywok@gmail.com"]
11
+ spec.summary = %q{Simple authentication templates ready to be customized.}
12
+ spec.homepage = "https://github.com/tonywok/auto_auth"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "rails", "~> 4.1.8"
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "sqlite3"
24
+ end
data/lib/auto_auth.rb ADDED
@@ -0,0 +1,2 @@
1
+ module AutoAuth
2
+ end
@@ -0,0 +1,142 @@
1
+ require 'rails/generators/base'
2
+
3
+ module AutoAuth
4
+ class InstallGenerator < Rails::Generators::Base
5
+
6
+ include Rails::Generators::Migration
7
+
8
+ def self.source_root
9
+ File.expand_path(File.join(File.dirname(__FILE__), "..", "templates"))
10
+ end
11
+
12
+ def self.next_migration_number(dirname) #:nodoc:
13
+ if ActiveRecord::Base.timestamped_migrations
14
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
15
+ else
16
+ "%.3d" % (current_migration_number(dirname) + 1)
17
+ end
18
+ end
19
+
20
+ desc "Install base authentication strategy into app ready to be customized"
21
+
22
+ class_option :domain_model, :type => :string, :default => 'user', :desc => 'Domain resource for users of the system (e.g user, admin, student etc)'
23
+ class_option :identity_model, :type => :string, :default => 'identity', :desc => 'Login credentials owned by a user (e.g login, credential, identity etc)'
24
+
25
+ def add_gems
26
+ gem "bcrypt"
27
+ end
28
+
29
+ def create_model_files
30
+ template "models/domain_model.rb", "app/models/#{domain_model}.rb"
31
+ template "models/identity_model.rb", "app/models/#{identity_model}.rb"
32
+ template "models/registration.rb", "app/models/registration.rb"
33
+ template "models/concerns/token_verification.rb", "app/models/concerns/token_verification.rb"
34
+ end
35
+
36
+ def create_controller_files
37
+ template "controllers/sessions_controller.rb", "app/controllers/sessions_controller.rb"
38
+ template "controllers/passwords_controller.rb", "app/controllers/passwords_controller.rb"
39
+ template "controllers/registrations_controller.rb", "app/controllers/registrations_controller.rb"
40
+ end
41
+
42
+ def create_mailer_files
43
+ template "mailers/identity_mailer.rb", "app/mailers/#{identity_model}_mailer.rb"
44
+
45
+ template "views/mailers/reset_password.html.erb", "app/views/#{identity_model}_mailer/reset_password.html.erb"
46
+ template "views/mailers/confirm_email.html.erb", "app/views/#{identity_model}_mailer/confirm_email.html.erb"
47
+ end
48
+
49
+ def create_routes
50
+ route <<-REGISTRATION_ROUTE
51
+ resources :registrations, only: [:create] do
52
+ get :confirm, on: :collection
53
+ end
54
+ REGISTRATION_ROUTE
55
+ route "resources :passwords, only: [:edit, :create, :update]"
56
+ route "resources :sessions, only: [:create]"
57
+ route "get :forgot_password, to: 'passwords#new'"
58
+ route "get :sign_up, to: 'registrations#new'"
59
+ route "get :sign_out, to: 'sessions#destroy'"
60
+ route "get :sign_in, to: 'sessions#new'"
61
+ end
62
+
63
+ def create_migration_files
64
+ migration_template "migration.rb", "db/migrate/create_#{domain_model}_and_#{identity_model}.rb"
65
+ end
66
+
67
+ def include_authentication
68
+ template "controllers/concerns/authentication.rb", "app/controllers/concerns/authentication.rb"
69
+ inject_into_file "app/controllers/application_controller.rb", after: /protect_from_forgery.*$/ do
70
+ <<-AUTOAUTH
71
+
72
+
73
+ include Authentication
74
+
75
+ before_action :authenticate!
76
+
77
+ rescue_from ActiveSupport::MessageVerifier::InvalidSignature, with: :handle_invalid_signature
78
+
79
+
80
+ private
81
+
82
+ def handle_invalid_signature
83
+ redirect_to(root_path, alert: t(:'auto_auth.application.invalid_signature'))
84
+ end
85
+ AUTOAUTH
86
+ end
87
+ end
88
+
89
+ def create_view_files
90
+ template "views/sessions/new.html.erb", "app/views/sessions/new.html.erb"
91
+
92
+ template "views/passwords/new.html.erb", "app/views/passwords/new.html.erb"
93
+ template "views/passwords/edit.html.erb", "app/views/passwords/edit.html.erb"
94
+
95
+ template "views/registrations/new.html.erb", "app/views/registrations/new.html.erb"
96
+
97
+ template "config/locales/auto_auth.en.yml", "config/locales/auto_auth.en.yml"
98
+ end
99
+
100
+
101
+ private
102
+
103
+ # Domain model helpers
104
+ #
105
+ def domain_model
106
+ @domain_model ||= options.domain_model.underscore
107
+ end
108
+
109
+ def domain_model_class
110
+ @domain_model_class ||= domain_model.classify
111
+ end
112
+
113
+ def domain_model_id
114
+ @domain_model_id ||= "#{domain_model}_id"
115
+ end
116
+
117
+ def plural_domain_model
118
+ @plural_domain_model ||= domain_model.pluralize
119
+ end
120
+
121
+
122
+ # Identity Model Helpers
123
+ #
124
+ def identity_model
125
+ @identity_model ||= options.identity_model.underscore
126
+ end
127
+
128
+ def identity_model_class
129
+ @identity_model_class ||= identity_model.classify
130
+ end
131
+
132
+ def plural_identity_model
133
+ @plural_identity_model ||= identity_model.pluralize
134
+ end
135
+
136
+ def identity_mailer_class
137
+ @identity_mailer_class ||= "#{identity_model_class}Mailer".classify
138
+ end
139
+
140
+
141
+ end
142
+ end
@@ -0,0 +1,3 @@
1
+ module AutoAuth
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,24 @@
1
+ auto_auth:
2
+
3
+ mailers:
4
+ reset_password:
5
+ subject: "Reset your password"
6
+ confirm_email:
7
+ subject: "Confirm your email"
8
+
9
+ application:
10
+ invalid_signature: "The token you've provided is invalid."
11
+ sessions:
12
+ existing: "You're already signed in"
13
+ required: "You must be signed in to perform that action"
14
+ signed_in: "Successfully signed in"
15
+ signed_out: "You've been signed out"
16
+ bad_combination: "Bad combination of email and password"
17
+ passwords:
18
+ reset_instructions: "Check your email for password reset instructions"
19
+ reset: "Password successfully updated"
20
+ expired: "Your password reset link has expired"
21
+ registrations:
22
+ account_created: "You've succuessfully registered"
23
+ confirmed: "You've successfully confirmed your account"
24
+ expired: "Your email confirmation link has expired"
@@ -0,0 +1,52 @@
1
+ require 'active_support/concern'
2
+
3
+ module Authentication
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ helper_method :current_<%= domain_model %>, :signed_in?, :redirect_to_or_default
9
+ end
10
+
11
+ def sign_in!(<%= domain_model %>)
12
+ session[:user_id] = user.id
13
+ end
14
+
15
+ def current_<%= domain_model %>
16
+ if session[:<%= domain_model %>_id].present?
17
+ @current_<%= domain_model %> ||= <%= domain_model_class %>.find(session[:<%= domain_model %>_id])
18
+ end
19
+ rescue ActiveRecord::RecordNotFound => e
20
+ reset_session
21
+ end
22
+
23
+ def signed_in?
24
+ !!current_<%= domain_model %>
25
+ end
26
+
27
+ def authenticate!
28
+ unless signed_in?
29
+ store_target_location
30
+ redirect_to(sign_in_url, alert: t(:"auto_auth.sessions.required"))
31
+ end
32
+ end
33
+
34
+ def redirect_to_target_or_default(default, *args)
35
+ redirect_to(session[:return_to] || default, *args)
36
+ session[:return_to] = nil
37
+ end
38
+
39
+ def redirect_authenticated
40
+ if signed_in?
41
+ redirect_to_target_or_default(root_path, notice: t(:"auto_auth.sessions.existing"))
42
+ end
43
+ end
44
+
45
+
46
+ private
47
+
48
+ def store_target_location
49
+ session[:return_to] = request.url
50
+ end
51
+
52
+ end
@@ -0,0 +1,47 @@
1
+ class PasswordsController < ApplicationController
2
+
3
+ respond_to :html
4
+
5
+ skip_before_action :authenticate!
6
+ before_action :setup_<%= identity_model %>, only: [:edit, :update]
7
+
8
+ def new
9
+ end
10
+
11
+ def edit
12
+ end
13
+
14
+ def create
15
+ email = params[:<%= identity_model %>][:email]
16
+ if <%= identity_model %> = <%= identity_model_class %>.find_by(email: email)
17
+ <%= identity_mailer_class %>.reset_password(<%= identity_model %>).deliver
18
+ end
19
+ redirect_to(root_path, notice: t(:'auth_auth.passwords.reset_instructions'))
20
+ end
21
+
22
+ def update
23
+ if @<%= identity_model %>.update!(password_params)
24
+ redirect_to(root_path, notice: t(:'auto_auth.passwords.reset'))
25
+ else
26
+ render :edit
27
+ end
28
+ end
29
+
30
+
31
+ private
32
+
33
+ def setup_<%= identity_model %>
34
+ <%= identity_model_class %>.verify_signature!(TokenVerification::PASSWORD_RESET, params.require(:password_reset_token)) do |record, expires_at|
35
+ if expires_at < Time.current
36
+ redirect_to(root_path, alert: t(:"auto_auth.passwords.expired")) and return
37
+ else
38
+ @<%= identity_model %> = record
39
+ end
40
+ end
41
+ end
42
+
43
+ def password_params
44
+ params.require(:<%= identity_model %>).permit(:password, :password_confirmation)
45
+ end
46
+
47
+ end
@@ -0,0 +1,46 @@
1
+ class RegistrationsController < ApplicationController
2
+
3
+ respond_to :html
4
+
5
+ skip_before_action :authenticate!, only: [:new, :confirm, :create]
6
+ before_action :redirect_authenticated, only: [:new, :create]
7
+ before_action :setup_identity, only: [:confirm]
8
+
9
+ def new
10
+ @registration = Registration.new
11
+ end
12
+
13
+ def confirm
14
+ @<%= identity_model %>.confirm!
15
+ redirect_to(root_path, notice: t(:'auto_auth.registration.confirmed'))
16
+ end
17
+
18
+ def create
19
+ @registration = Registration.new(registration_params)
20
+ if <%= domain_model %> = @registration.save!
21
+ sign_in!(<%= domain_model %>)
22
+ <%= domain_model %>.<%= identity_model %>.send_confirmation_email
23
+ redirect_to(root_path, notice: t(:'auto_auth.registrations.account_created'))
24
+ else
25
+ render :new
26
+ end
27
+ end
28
+
29
+
30
+ private
31
+
32
+ def setup_<%= identity_model %>
33
+ <%= identity_model_class %>.verify_signature!(TokenVerification::EMAIL_CONFIRMATION, params.require(:email_confirmation_token)) do |record, expires_at|
34
+ if expires_at < Time.current
35
+ redirect_to(root_path, alert: t(:'auto_auth.registrations.expired'))
36
+ else
37
+ @<%= identity_model %> = record
38
+ end
39
+ end
40
+ end
41
+
42
+ def registration_params
43
+ params.require(:registration).permit(:name, :email, :password, :password_confirmation)
44
+ end
45
+
46
+ end
@@ -0,0 +1,32 @@
1
+ class SessionsController < ApplicationController
2
+
3
+ skip_before_action :authenticate!, only: :new
4
+ before_action :redirect_authenticated, only: [:new]
5
+
6
+ def new
7
+ end
8
+
9
+ def create
10
+ identity = <%= identity_model.classify %>.find_by(email: params[:session][:email])
11
+ if identity && identity.authenticate(params[:session].delete(:password))
12
+ session[:<%= "#{domain_model.underscore}_id" %>] = identity.<%= "#{domain_model.underscore}_id" %>
13
+ redirect_to(after_sign_in_path, notice: t(:'auto_auth.sessions.signed_in'))
14
+ else
15
+ flash.now[:alert] = t(:'auto_auth.sessions.bad_combination')
16
+ render :new
17
+ end
18
+ end
19
+
20
+ def destroy
21
+ reset_session
22
+ redirect_to(sign_in_path, alert: t(:'auto_auth.sessions.signed_out'))
23
+ end
24
+
25
+
26
+ private
27
+
28
+ def after_sign_in_path
29
+ root_path
30
+ end
31
+
32
+ end
@@ -0,0 +1,15 @@
1
+ class <%= identity_mailer_class %> < ActionMailer::Base
2
+
3
+ def reset_password(<%= identity_model %>)
4
+ @<%= identity_model %> = <%= identity_model %>
5
+ @reset_password_url = edit_password_url(<%= identity_model %>, password_reset_token: @<%= identity_model %>.password_reset_token)
6
+ mail(to: <%= identity_model %>.email, subject: t(:'auto_auth.mailers.reset_password.subject'))
7
+ end
8
+
9
+ def confirm_email(<%= identity_model %>)
10
+ @<%= identity_model %> = <%= identity_model %>
11
+ @confirm_email_url = confirm_registrations_url(email_confirmation_token: @<%= identity_model %>.email_confirmation_token)
12
+ mail(to: <%= identity_model %>.email, subject: t(:'auto_auth.mailers.confirm_email.subject'))
13
+ end
14
+
15
+ end
@@ -0,0 +1,21 @@
1
+ class Create<%= domain_model_class %>And<%= identity_model_class %> < ActiveRecord::Migration
2
+ def change
3
+ create_table(:<%= plural_domain_model %>) do |t|
4
+ t.string :name
5
+
6
+ t.timestamps
7
+ end
8
+
9
+ create_table(:<%= plural_identity_model %>) do |t|
10
+ t.string :email
11
+ t.string :password_digest, null: false, default: ""
12
+ t.datetime :confirmed_at
13
+ t.references :<%= domain_model %>
14
+
15
+ t.timestamps
16
+ end
17
+
18
+ add_index :<%= plural_identity_model %>, :email, unique: true
19
+ add_index :<%= plural_identity_model %>, :<%= domain_model_id %>
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ require 'active_support/concern'
2
+
3
+ module TokenVerification
4
+
5
+ class ExpiredToken < StandardError; end
6
+
7
+ extend ActiveSupport::Concern
8
+
9
+ def expiration_token(expires_at, purpose)
10
+ verifier = self.class.verifier_for(purpose)
11
+ verifier.generate({ id: id, expires_at: expires_at })
12
+ end
13
+
14
+
15
+ module ClassMethods
16
+
17
+ def verifier_for(purpose)
18
+ @verifiers ||= {}
19
+ @verifiers.fetch(purpose) do |p|
20
+ @verifiers[p] = Rails.application.message_verifier("#{self.name}-#{p.to_s}")
21
+ end
22
+ end
23
+
24
+ def verify_signature!(purpose, token)
25
+ data = self.verifier_for(purpose).verify(token)
26
+ record = self.find(data[:id])
27
+ record.tap do
28
+ if block_given?
29
+ yield(record, data[:expires_at])
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,7 @@
1
+ class <%= domain_model_class %> < ActiveRecord::Base
2
+
3
+ has_one :<%= identity_model %>, inverse_of: :<%= domain_model %>, dependent: :destroy
4
+
5
+ delegate :email, :confirmed?, to: :<%= identity_model %>
6
+
7
+ end
@@ -0,0 +1,62 @@
1
+ class <%= identity_model_class %> < ActiveRecord::Base
2
+
3
+ include TokenVerification
4
+
5
+ PASSWORD_RESET_KEY = :password_reset
6
+ EMAIL_CONFIRMATION_KEY = :email_confirmation
7
+
8
+ has_secure_password
9
+
10
+ belongs_to :<%= domain_model %>, inverse_of: :<%= identity_model %>
11
+
12
+ validates_presence_of :email
13
+ validates_uniqueness_of :email
14
+ validates_format_of :email, with: /\A[^@\s]+@([^@\s]+\.)+[^@\s]+\z/, allow_blank: true
15
+
16
+ validates_presence_of :password, if: :password_required?
17
+ validates_confirmation_of :password, if: :password_required?
18
+ validates_length_of :password, within: 8..128, allow_blank: true
19
+
20
+ before_save :downcase_email, -> { |ident| ident.email.downcase! }
21
+
22
+ def update_email(new_email)
23
+ self.email = new_email
24
+ if email_changed?
25
+ self.confirmed_at = nil
26
+ save && send_confirmation_email
27
+ else
28
+ save
29
+ end
30
+ end
31
+
32
+ def confirm!
33
+ self.confirmed_at = Time.current
34
+ save(validate: false)
35
+ end
36
+
37
+ def confirmed?
38
+ confirmed_at.present?
39
+ end
40
+
41
+ def send_confirmation_email
42
+ <%= identity_mailer_class %>.confirm_email(self).deliver
43
+ end
44
+
45
+ def email_confirmation_token
46
+ expires_at = 7.days.from_now
47
+ expiration_token(expires_at, EMAIL_CONFIRMATION_KEY)
48
+ end
49
+
50
+ def password_reset_token
51
+ expires_at = 1.day.from_now
52
+ expiration_token(expires_at, PASSWORD_RESET_KEY)
53
+ end
54
+
55
+
56
+ private
57
+
58
+ def password_required?
59
+ !persisted? || !password.nil? || !password_confirmation.nil?
60
+ end
61
+
62
+ end