├── .documentation ├── .gitkeep └── youtube_thumbnail.png ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .standard.yml ├── CHANGELOG.md ├── Gemfile ├── Gemfile.lock ├── MIT-LICENSE ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── rails_mvp_authentication_manifest.js │ ├── images │ │ └── rails_mvp_authentication │ │ │ └── .keep │ └── stylesheets │ │ └── rails_mvp_authentication │ │ └── application.css ├── controllers │ ├── concerns │ │ └── .keep │ └── rails_mvp_authentication │ │ └── application_controller.rb ├── helpers │ └── rails_mvp_authentication │ │ └── application_helper.rb ├── jobs │ └── rails_mvp_authentication │ │ └── application_job.rb ├── mailers │ └── rails_mvp_authentication │ │ └── application_mailer.rb ├── models │ ├── concerns │ │ └── .keep │ └── rails_mvp_authentication │ │ └── application_record.rb └── views │ └── layouts │ └── rails_mvp_authentication │ └── application.html.erb ├── bin └── rails ├── config └── routes.rb ├── lib ├── generators │ └── rails_mvp_authentication │ │ ├── USAGE │ │ ├── install_generator.rb │ │ └── templates │ │ ├── README │ │ ├── active_session.rb.tt │ │ ├── active_sessions_controller.rb.tt │ │ ├── authentication.rb.tt │ │ ├── confirmations_controller.rb.tt │ │ ├── current.rb.tt │ │ ├── passwords_controller.rb.tt │ │ ├── sessions_controller.rb.tt │ │ ├── test │ │ ├── controllers │ │ │ ├── active_sessions_controller_test.rb.tt │ │ │ ├── confirmations_controller_test.rb.tt │ │ │ ├── passwords_controller_test.rb.tt │ │ │ ├── sessions_controller_test.rb.tt │ │ │ └── users_controller_test.rb.tt │ │ ├── integration │ │ │ ├── friendly_redirects_test.rb.tt │ │ │ └── user_interface_test.rb.tt │ │ ├── mailers │ │ │ ├── previews │ │ │ │ └── user_mailer_preview.rb.tt │ │ │ └── user_mailer_test.rb.tt │ │ ├── models │ │ │ ├── active_session_test.rb.tt │ │ │ └── user_test.rb.tt │ │ └── system │ │ │ └── logins_test.rb.tt │ │ ├── user.rb.tt │ │ ├── user_mailer.rb.tt │ │ ├── users_controller.rb.tt │ │ └── views │ │ ├── confirmations │ │ └── new.html.erb.tt │ │ ├── passwords │ │ ├── edit.html.erb.tt │ │ └── new.html.erb.tt │ │ ├── sessions │ │ └── new.html.erb.tt │ │ ├── user_mailer │ │ ├── confirmation.html.erb.tt │ │ ├── confirmation.text.erb.tt │ │ ├── password_reset.html.erb.tt │ │ └── password_reset.text.erb.tt │ │ └── users │ │ ├── edit.html.erb.tt │ │ └── new.html.erb.tt ├── rails_mvp_authentication.rb ├── rails_mvp_authentication │ ├── engine.rb │ └── version.rb └── tasks │ └── rails_mvp_authentication_tasks.rake ├── rails_mvp_authentication.gemspec └── test ├── controllers └── .keep ├── dummy ├── Rakefile ├── app │ ├── assets │ │ ├── config │ │ │ └── manifest.js │ │ ├── images │ │ │ └── .keep │ │ └── stylesheets │ │ │ └── application.css │ ├── channels │ │ └── application_cable │ │ │ ├── channel.rb │ │ │ └── connection.rb │ ├── controllers │ │ ├── application_controller.rb │ │ └── concerns │ │ │ └── .keep │ ├── helpers │ │ └── application_helper.rb │ ├── jobs │ │ └── application_job.rb │ ├── mailers │ │ └── application_mailer.rb │ ├── models │ │ ├── application_record.rb │ │ └── concerns │ │ │ └── .keep │ └── views │ │ └── layouts │ │ ├── application.html.erb │ │ ├── mailer.html.erb │ │ └── mailer.text.erb ├── bin │ ├── rails │ ├── rake │ └── setup ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── cable.yml │ ├── database.yml │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── assets.rb │ │ ├── content_security_policy.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ └── permissions_policy.rb │ ├── locales │ │ └── en.yml │ ├── puma.rb │ ├── routes.rb │ └── storage.yml ├── db │ └── schema.rb ├── lib │ └── assets │ │ └── .keep ├── log │ └── .keep └── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ ├── apple-touch-icon-precomposed.png │ ├── apple-touch-icon.png │ └── favicon.ico ├── fixtures └── files │ └── .keep ├── helpers └── .keep ├── integration ├── .keep └── navigation_test.rb ├── lib └── generators │ └── rails_mvp_authentication │ └── install_generator_test.rb ├── mailers └── .keep ├── models └── .keep ├── rails_mvp_authentication_test.rb └── test_helper.rb /.documentation/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.documentation/youtube_thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/.documentation/youtube_thumbnail.png -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Ruby 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | test: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up Ruby 17 | uses: ruby/setup-ruby@v1 18 | with: 19 | ruby-version: '2.7.0' 20 | - name: Install dependencies 21 | run: bundle install 22 | - name: Run Standard 23 | run: bundle exec standardrb 24 | - name: Run tests 25 | run: bundle exec rails test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /doc/ 3 | /log/*.log 4 | /pkg/ 5 | /tmp/ 6 | /test/dummy/db/*.sqlite3 7 | /test/dummy/db/*.sqlite3-* 8 | /test/dummy/log/*.log 9 | /test/dummy/storage/ 10 | /test/dummy/tmp/ 11 | -------------------------------------------------------------------------------- /.standard.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - test/dummy/config/environments/production.rb 3 | - test/dummy/config/puma 4 | - test/dummy/config/puma.rb 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 (February 18, 2022) ## 2 | 3 | * Initial release 🚀 -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | # Specify your gem's dependencies in rails_mvp_authentication.gemspec. 5 | gemspec 6 | 7 | group :development do 8 | gem "sprockets-rails", "~> 3.4", ">= 3.4.2" 9 | gem "sqlite3" 10 | end 11 | 12 | group :development, :test do 13 | gem "standard", "~> 1.7" 14 | gem "bcrypt", "~> 3.1.7" 15 | end 16 | 17 | # Start debugger with binding.b -- Read more: https://github.com/ruby/debug 18 | # gem "debug", ">= 1.0.0", group: %i[ development test ] 19 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | rails_mvp_authentication (1.0.0) 5 | rails (>= 7.0.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | actioncable (7.0.1) 11 | actionpack (= 7.0.1) 12 | activesupport (= 7.0.1) 13 | nio4r (~> 2.0) 14 | websocket-driver (>= 0.6.1) 15 | actionmailbox (7.0.1) 16 | actionpack (= 7.0.1) 17 | activejob (= 7.0.1) 18 | activerecord (= 7.0.1) 19 | activestorage (= 7.0.1) 20 | activesupport (= 7.0.1) 21 | mail (>= 2.7.1) 22 | net-imap 23 | net-pop 24 | net-smtp 25 | actionmailer (7.0.1) 26 | actionpack (= 7.0.1) 27 | actionview (= 7.0.1) 28 | activejob (= 7.0.1) 29 | activesupport (= 7.0.1) 30 | mail (~> 2.5, >= 2.5.4) 31 | net-imap 32 | net-pop 33 | net-smtp 34 | rails-dom-testing (~> 2.0) 35 | actionpack (7.0.1) 36 | actionview (= 7.0.1) 37 | activesupport (= 7.0.1) 38 | rack (~> 2.0, >= 2.2.0) 39 | rack-test (>= 0.6.3) 40 | rails-dom-testing (~> 2.0) 41 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 42 | actiontext (7.0.1) 43 | actionpack (= 7.0.1) 44 | activerecord (= 7.0.1) 45 | activestorage (= 7.0.1) 46 | activesupport (= 7.0.1) 47 | globalid (>= 0.6.0) 48 | nokogiri (>= 1.8.5) 49 | actionview (7.0.1) 50 | activesupport (= 7.0.1) 51 | builder (~> 3.1) 52 | erubi (~> 1.4) 53 | rails-dom-testing (~> 2.0) 54 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 55 | activejob (7.0.1) 56 | activesupport (= 7.0.1) 57 | globalid (>= 0.3.6) 58 | activemodel (7.0.1) 59 | activesupport (= 7.0.1) 60 | activerecord (7.0.1) 61 | activemodel (= 7.0.1) 62 | activesupport (= 7.0.1) 63 | activestorage (7.0.1) 64 | actionpack (= 7.0.1) 65 | activejob (= 7.0.1) 66 | activerecord (= 7.0.1) 67 | activesupport (= 7.0.1) 68 | marcel (~> 1.0) 69 | mini_mime (>= 1.1.0) 70 | activesupport (7.0.1) 71 | concurrent-ruby (~> 1.0, >= 1.0.2) 72 | i18n (>= 1.6, < 2) 73 | minitest (>= 5.1) 74 | tzinfo (~> 2.0) 75 | ast (2.4.2) 76 | bcrypt (3.1.16) 77 | builder (3.2.4) 78 | concurrent-ruby (1.1.9) 79 | crass (1.0.6) 80 | digest (3.1.0) 81 | erubi (1.10.0) 82 | globalid (1.0.0) 83 | activesupport (>= 5.0) 84 | i18n (1.9.1) 85 | concurrent-ruby (~> 1.0) 86 | io-wait (0.2.1) 87 | loofah (2.13.0) 88 | crass (~> 1.0.2) 89 | nokogiri (>= 1.5.9) 90 | mail (2.7.1) 91 | mini_mime (>= 0.1.1) 92 | marcel (1.0.2) 93 | method_source (1.0.0) 94 | mini_mime (1.1.2) 95 | minitest (5.15.0) 96 | net-imap (0.2.3) 97 | digest 98 | net-protocol 99 | strscan 100 | net-pop (0.1.1) 101 | digest 102 | net-protocol 103 | timeout 104 | net-protocol (0.1.2) 105 | io-wait 106 | timeout 107 | net-smtp (0.3.1) 108 | digest 109 | net-protocol 110 | timeout 111 | nio4r (2.5.8) 112 | nokogiri (1.13.1-arm64-darwin) 113 | racc (~> 1.4) 114 | parallel (1.21.0) 115 | parser (3.1.0.0) 116 | ast (~> 2.4.1) 117 | racc (1.6.0) 118 | rack (2.2.3) 119 | rack-test (1.1.0) 120 | rack (>= 1.0, < 3) 121 | rails (7.0.1) 122 | actioncable (= 7.0.1) 123 | actionmailbox (= 7.0.1) 124 | actionmailer (= 7.0.1) 125 | actionpack (= 7.0.1) 126 | actiontext (= 7.0.1) 127 | actionview (= 7.0.1) 128 | activejob (= 7.0.1) 129 | activemodel (= 7.0.1) 130 | activerecord (= 7.0.1) 131 | activestorage (= 7.0.1) 132 | activesupport (= 7.0.1) 133 | bundler (>= 1.15.0) 134 | railties (= 7.0.1) 135 | rails-dom-testing (2.0.3) 136 | activesupport (>= 4.2.0) 137 | nokogiri (>= 1.6) 138 | rails-html-sanitizer (1.4.2) 139 | loofah (~> 2.3) 140 | railties (7.0.1) 141 | actionpack (= 7.0.1) 142 | activesupport (= 7.0.1) 143 | method_source 144 | rake (>= 12.2) 145 | thor (~> 1.0) 146 | zeitwerk (~> 2.5) 147 | rainbow (3.1.1) 148 | rake (13.0.6) 149 | regexp_parser (2.2.0) 150 | rexml (3.2.5) 151 | rubocop (1.25.0) 152 | parallel (~> 1.10) 153 | parser (>= 3.1.0.0) 154 | rainbow (>= 2.2.2, < 4.0) 155 | regexp_parser (>= 1.8, < 3.0) 156 | rexml 157 | rubocop-ast (>= 1.15.1, < 2.0) 158 | ruby-progressbar (~> 1.7) 159 | unicode-display_width (>= 1.4.0, < 3.0) 160 | rubocop-ast (1.15.1) 161 | parser (>= 3.0.1.1) 162 | rubocop-performance (1.13.2) 163 | rubocop (>= 1.7.0, < 2.0) 164 | rubocop-ast (>= 0.4.0) 165 | ruby-progressbar (1.11.0) 166 | sprockets (4.0.2) 167 | concurrent-ruby (~> 1.0) 168 | rack (> 1, < 3) 169 | sprockets-rails (3.4.2) 170 | actionpack (>= 5.2) 171 | activesupport (>= 5.2) 172 | sprockets (>= 3.0.0) 173 | sqlite3 (1.4.2) 174 | standard (1.7.0) 175 | rubocop (= 1.25.0) 176 | rubocop-performance (= 1.13.2) 177 | strscan (3.0.1) 178 | thor (1.2.1) 179 | timeout (0.2.0) 180 | tzinfo (2.0.4) 181 | concurrent-ruby (~> 1.0) 182 | unicode-display_width (2.1.0) 183 | websocket-driver (0.7.5) 184 | websocket-extensions (>= 0.1.0) 185 | websocket-extensions (0.1.5) 186 | zeitwerk (2.5.4) 187 | 188 | PLATFORMS 189 | arm64-darwin-21 190 | 191 | DEPENDENCIES 192 | bcrypt (~> 3.1.7) 193 | rails_mvp_authentication! 194 | sprockets-rails (~> 3.4, >= 3.4.2) 195 | sqlite3 196 | standard (~> 1.7) 197 | 198 | BUNDLED WITH 199 | 2.2.32 200 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Steve Polito 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🔐 Rails MVP Authentication 2 | 3 | An authentication generator for Rails 7. Based on the [step-by-step guide on how to build your own authentication system in Rails from scratch](https://github.com/stevepolitodesign/rails-authentication-from-scratch). 4 | 5 | ## 🎬 Demo 6 | 7 | 8 | Demo 9 | 10 | 11 | ## 🚀 Installation 12 | 13 | Add this line to your application's Gemfile: 14 | 15 | ```ruby 16 | gem "rails_mvp_authentication" 17 | ``` 18 | 19 | And then execute: 20 | ```bash 21 | bundle 22 | ``` 23 | 24 | Or install it yourself as: 25 | ```bash 26 | gem install rails_mvp_authentication 27 | ``` 28 | 29 | Then run the installation command: 30 | ```bash 31 | rails g rails_mvp_authentication:install 32 | ``` 33 | 34 | Once installed make follow these steps: 35 | 36 | 1. Run `bundle install` to install [bcrypt](https://rubygems.org/gems/bcrypt/) 37 | 2. Run `rails db:migrate` to add the `users` and `active_sessions` tables 38 | 3. Add a root path in `config/routes.rb` 39 | 4. Ensure you have flash messages in `app/views/layouts/application.html.erb` 40 | 41 | ```html+erb 42 |

<%= notice %>

43 |

<%= alert %>

44 | ``` 45 | 46 | After completing these steps you can uninstall the gem: 47 | 48 | ```bash 49 | bundle remove "rails_mvp_authentication" --install 50 | ``` 51 | 52 | ## 📝 Features 53 | 54 | - Requires a user to confirm their email address before they can log in. 55 | - Allows a user to remain logged into the application even if they exit their browser. 56 | - Allows a user to have multiple sessions. This gives users the ability to log out of all sessions at once. This also makes it easy to detect suspicious login activity. 57 | - Allows a user to change their email address. 58 | - Allows a user to recover their account if they forget their password. 59 | - Requires users to submit their password anytime they're chaning their account information. 60 | 61 | ## 🔨 Usage 62 | 63 | The following methods are automatically included in the corresponding generated files. 64 | 65 | ### Controller Methods 66 | 67 | #### authenticate_user! 68 | 69 | Redirects the visitor to the `login_path` if they're not logged in. Useful for preventing an anonymous user from accessing a page intended for an authenticated user. 70 | 71 | #### current_user 72 | 73 | Returns an instance of `User` if there's one in the session. Othwerwise returns `nil`. 74 | 75 | #### forget_active_session 76 | 77 | Deletes the `:remember_token` cookie. For added security, the associated `active_session` should be deleted too. 78 | 79 | #### login(user) 80 | 81 | [Resets](https://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-reset_session) the session and then creates a new `active_session` with on the `user` that was passed in. Stores the `id` of the `active_session` in the `session`. Returns the new `active_session`. 82 | 83 | #### logout 84 | 85 | [Resets](https://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-reset_session) the session and deletes the associated `active_session` record. 86 | 87 | #### user_signed_in? 88 | 89 | Returns `true` if `current_user` does not return `nil`. Othwerwise returns `false`. 90 | 91 | #### redirect_if_authenticated 92 | 93 | Redirects the user to the `root_path` if the user is logged in. Useful for keeping a user from accessing a page intended for an anonymous user. 94 | 95 | #### remember(active_session) 96 | 97 | Creates a cookie to store the value of the `remember_token` from the `active_session` that was passed in. 98 | 99 | ### View Helpers 100 | 101 | #### current_user 102 | 103 | Returns an instance of `User` if there's one in the session. Othwerwise returns `nil`. 104 | 105 | #### user_signed_in? 106 | 107 | Returns `true` if `current_user` does not return `nil`. Othwerwise returns `false`. 108 | 109 | ### User Model 110 | 111 | #### self.authenticate_by(attributes) 112 | 113 | A copy of the [authenticate_by](https://edgeapi.rubyonrails.org/classes/ActiveRecord/SecurePassword/ClassMethods.html#method-i-authenticate_by) class method that is set to ship in rails 7.1 114 | 115 | #### confirm! 116 | 117 | Sets the `confirmed_at` column to `Time.current`. Updates the `email` column if reconfirming a new email address. Returns `true` or `false`. 118 | 119 | #### confirmed? 120 | 121 | Returns `true` or `false` based on if the `confirmed_at` column is present. 122 | 123 | #### confirmable_email 124 | 125 | Returns the value of the `email` column if the `unconfirmed_email` column is empty. Otherwise, the value of `unconfirmed_email` is returned. 126 | 127 | #### generate_confirmation_token 128 | 129 | Generates a [signed_id](https://api.rubyonrails.org/classes/ActiveRecord/SignedId.html#method-i-signed_id) used in the confirmation mailer. 130 | 131 | #### generate_password_reset_token 132 | 133 | Generates a [signed_id](https://api.rubyonrails.org/classes/ActiveRecord/SignedId.html#method-i-signed_id) used in the password reset mailer. 134 | 135 | #### send_confirmation_email! 136 | 137 | Send a confirmation email to the user. 138 | 139 | #### send_password_reset_email! 140 | 141 | Send a password reset email to the user. 142 | 143 | #### reconfirming? 144 | 145 | Returns `true` if there's a value for `unconfirmed_email`. Otherwise `false` is returned. 146 | 147 | #### unconfirmed? 148 | 149 | Returns `true` if there's no value for `confirmed_at`. Otherwise `false` is returned. 150 | 151 | #### unconfirmed_or_reconfirming? 152 | 153 | Returns `true` if the user is unconfirmed or reconfirming a new email address. Otherwise `false` is returned. 154 | 155 | ### Test Helpers 156 | 157 | #### current_user 158 | 159 | Returns an instance of `User` if there's one in the test session. Othwerwise returns `nil`. 160 | 161 | #### login(user, remember_user: nil) 162 | 163 | Creates a `post` request to the `login_path`. Simulates a real login. 164 | 165 | #### logout 166 | 167 | Deletes the `current_active_session_id` test session. Simulates a login. 168 | 169 | ## ⚖️ Benefits 170 | 171 | What makes this gem _different_ (not better) from [devise](https://github.com/heartcombo/devise), [clearance](https://github.com/thoughtbot/clearance/), etc? 172 | 173 | 1. This gem is less of an [engine](https://guides.rubyonrails.org/engines.html) and more of a [generator](https://guides.rubyonrails.org/generators.html). It generates all necessary models, views, controllers, mailers, and migrations. This means you have complete control over your authentication system and don't have to worry about learning a new DSL or API. 174 | 2. It also generates tests. That way you can ship with confidence if and when you decide to change how your authentication system works. 175 | 3. It utilizes modern core features of Rails, such as [ActiveSupport::CurrentAttributes](https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html) and [Active Record Signed Id](https://api.rubyonrails.org/classes/ActiveRecord/SignedId.html#method-i-signed_id), [has_secure_password](https://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password) and [has_secure_token](https://api.rubyonrails.org/classes/ActiveRecord/SecureToken/ClassMethods.html#method-i-has_secure_token). 176 | 4. It stores the session in the database. This gives users the ability to log out of all sessions at once. This also makes it easy to detect suspicious login activity. 177 | 178 | ## 🙏 Contributing 179 | 180 | If you'd like to open a PR please make sure the following things pass: 181 | 182 | ```ruby 183 | bin/rails test 184 | bundle exec standardrb 185 | ``` 186 | ## 📜 License 187 | 188 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 189 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/setup" 2 | 3 | APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__) 4 | load "rails/tasks/engine.rake" 5 | 6 | load "rails/tasks/statistics.rake" 7 | 8 | require "bundler/gem_tasks" 9 | -------------------------------------------------------------------------------- /app/assets/config/rails_mvp_authentication_manifest.js: -------------------------------------------------------------------------------- 1 | //= link_directory ../stylesheets/rails_mvp_authentication .css 2 | -------------------------------------------------------------------------------- /app/assets/images/rails_mvp_authentication/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/app/assets/images/rails_mvp_authentication/.keep -------------------------------------------------------------------------------- /app/assets/stylesheets/rails_mvp_authentication/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/rails_mvp_authentication/application_controller.rb: -------------------------------------------------------------------------------- 1 | module RailsMvpAuthentication 2 | class ApplicationController < ActionController::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/helpers/rails_mvp_authentication/application_helper.rb: -------------------------------------------------------------------------------- 1 | module RailsMvpAuthentication 2 | module ApplicationHelper 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/jobs/rails_mvp_authentication/application_job.rb: -------------------------------------------------------------------------------- 1 | module RailsMvpAuthentication 2 | class ApplicationJob < ActiveJob::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/mailers/rails_mvp_authentication/application_mailer.rb: -------------------------------------------------------------------------------- 1 | module RailsMvpAuthentication 2 | class ApplicationMailer < ActionMailer::Base 3 | default from: "from@example.com" 4 | layout "mailer" 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/rails_mvp_authentication/application_record.rb: -------------------------------------------------------------------------------- 1 | module RailsMvpAuthentication 2 | class ApplicationRecord < ActiveRecord::Base 3 | self.abstract_class = true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/views/layouts/rails_mvp_authentication/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Rails mvp authentication 5 | <%= csrf_meta_tags %> 6 | <%= csp_meta_tag %> 7 | 8 | <%= stylesheet_link_tag "rails_mvp_authentication/application", media: "all" %> 9 | 10 | 11 | 12 | <%= yield %> 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails gems 3 | # installed from the root of your application. 4 | 5 | ENGINE_ROOT = File.expand_path("..", __dir__) 6 | ENGINE_PATH = File.expand_path("../lib/rails_mvp_authentication/engine", __dir__) 7 | APP_PATH = File.expand_path("../test/dummy/config/application", __dir__) 8 | 9 | # Set up gems listed in the Gemfile. 10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 11 | require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) 12 | 13 | require "rails/all" 14 | require "rails/engine/commands" 15 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | RailsMvpAuthentication::Engine.routes.draw do 2 | end 3 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Rails authentication via a generator. 3 | 4 | Example: 5 | bin/rails generate rails_mvp_authentication:install -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/install_generator.rb: -------------------------------------------------------------------------------- 1 | module RailsMvpAuthentication 2 | module Generators 3 | class InstallGenerator < Rails::Generators::Base 4 | source_root File.expand_path("templates", __dir__) 5 | 6 | desc "Rails authentication via a generator." 7 | 8 | def perform 9 | create_users_table 10 | modify_users_table 11 | create_user_model 12 | create_active_sessions_table 13 | modify_active_sessions_table 14 | create_active_session_model 15 | add_bcrypt 16 | add_routes 17 | create_current_model 18 | create_users_controller 19 | create_user_views 20 | create_active_sessions_controller 21 | create_confirmations_controller 22 | create_confirmation_views 23 | ceate_user_mailer 24 | ceate_user_mailer_views 25 | configure_hosts 26 | create_authentication_concern 27 | modify_application_controller 28 | create_sessions_controller 29 | create_session_views 30 | create_passwords_controller 31 | create_password_views 32 | if using_default_test_suite 33 | create_tests 34 | modify_test_helper 35 | end 36 | add_links 37 | print_instructions 38 | end 39 | 40 | private 41 | 42 | def add_bcrypt 43 | return unless gemfile_exists 44 | 45 | if bcrypt_is_commented_out 46 | uncomment_lines(gemfile, /gem "bcrypt", "~> 3.1.7"/) 47 | elsif !bcrypt_is_not_commented 48 | gem "bcrypt", "~> 3.1.7" 49 | end 50 | end 51 | 52 | def add_links 53 | inject_into_file "app/views/layouts/application.html.erb", after: "\n" do 54 | <<-ERB 55 | 66 | ERB 67 | end 68 | end 69 | 70 | def add_routes 71 | route %( 72 | post "sign_up", to: "users#create" 73 | get "sign_up", to: "users#new" 74 | put "account", to: "users#update" 75 | get "account", to: "users#edit" 76 | delete "account", to: "users#destroy" 77 | resources :confirmations, only: [:create, :edit, :new], param: :confirmation_token 78 | post "login", to: "sessions#create" 79 | delete "logout", to: "sessions#destroy" 80 | get "login", to: "sessions#new" 81 | resources :passwords, only: [:create, :edit, :new, :update], param: :password_reset_token 82 | resources :active_sessions, only: [:destroy] do 83 | collection do 84 | delete "destroy_all" 85 | end 86 | end 87 | ) 88 | end 89 | 90 | def bcrypt_is_commented_out 91 | gemfile = path_to("Gemfile") 92 | 93 | File.open(gemfile).each_line do |line| 94 | return true if line.strip.start_with?("#") && line.include?("bcrypt") 95 | end 96 | 97 | false 98 | end 99 | 100 | def bcrypt_is_not_commented 101 | gemfile = path_to("Gemfile") 102 | 103 | File.open(gemfile).each_line do |line| 104 | return true if /gem "bcrypt", "~> 3.1.7"/.match?(line) 105 | end 106 | 107 | false 108 | end 109 | 110 | def configure_hosts 111 | application(nil, env: "test") do 112 | 'config.action_mailer.default_url_options = {host: "example.com"}' 113 | end 114 | application(nil, env: "development") do 115 | 'config.action_mailer.default_url_options = {host: "localhost", port: 3000}' 116 | end 117 | end 118 | 119 | def create_active_sessions_controller 120 | template "active_sessions_controller.rb", "app/controllers/active_sessions_controller.rb" 121 | end 122 | 123 | def create_active_session_model 124 | template "active_session.rb", "app/models/active_session.rb" 125 | end 126 | 127 | def create_active_sessions_table 128 | generate "migration", "create_active_sessions user:references user_agent:string ip_address:string remember_token:string:index" 129 | end 130 | 131 | def create_authentication_concern 132 | template "authentication.rb", "app/controllers/concerns/authentication.rb" 133 | end 134 | 135 | def create_confirmations_controller 136 | template "confirmations_controller.rb", "app/controllers/confirmations_controller.rb" 137 | end 138 | 139 | def create_confirmation_views 140 | template "views/confirmations/new.html.erb", "app/views/confirmations/new.html.erb" 141 | end 142 | 143 | def create_current_model 144 | template "current.rb", "app/models/current.rb" 145 | end 146 | 147 | def create_passwords_controller 148 | template "passwords_controller.rb", "app/controllers/passwords_controller.rb" 149 | end 150 | 151 | def create_password_views 152 | template "views/passwords/new.html.erb", "app/views/passwords/new.html.erb" 153 | template "views/passwords/edit.html.erb", "app/views/passwords/edit.html.erb" 154 | end 155 | 156 | def create_sessions_controller 157 | template "sessions_controller.rb", "app/controllers/sessions_controller.rb" 158 | end 159 | 160 | def create_session_views 161 | template "views/sessions/new.html.erb", "app/views/sessions/new.html.erb" 162 | end 163 | 164 | def create_users_controller 165 | template "users_controller.rb", "app/controllers/users_controller.rb" 166 | end 167 | 168 | def create_tests 169 | template "test/controllers/active_sessions_controller_test.rb", "test/controllers/active_sessions_controller_test.rb" 170 | template "test/controllers/confirmations_controller_test.rb", "test/controllers/confirmations_controller_test.rb" 171 | template "test/controllers/active_sessions_controller_test.rb", "test/controllers/active_sessions_controller_test.rb" 172 | template "test/controllers/passwords_controller_test.rb", "test/controllers/passwords_controller_test.rb" 173 | template "test/controllers/sessions_controller_test.rb", "test/controllers/sessions_controller_test.rb" 174 | template "test/controllers/users_controller_test.rb", "test/controllers/users_controller_test.rb" 175 | 176 | template "test/integration/friendly_redirects_test.rb", "test/integration/friendly_redirects_test.rb" 177 | template "test/integration/user_interface_test.rb", "test/integration/user_interface_test.rb" 178 | 179 | template "test/mailers/previews/user_mailer_preview.rb", "test/mailers/previews/user_mailer_preview.rb" 180 | template "test/mailers/user_mailer_test.rb", "test/mailers/user_mailer_test.rb" 181 | 182 | template "test/models/user_test.rb", "test/models/user_test.rb" 183 | template "test/models/active_session_test.rb", "test/models/active_session_test.rb" 184 | 185 | template "test/system/logins_test.rb", "test/system/logins_test.rb" 186 | end 187 | 188 | def ceate_user_mailer 189 | template "user_mailer.rb", "app/mailers/user_mailer.rb" 190 | end 191 | 192 | def ceate_user_mailer_views 193 | template "views/user_mailer/confirmation.html.erb", "app/views/user_mailer/confirmation.html.erb" 194 | template "views/user_mailer/confirmation.text.erb", "app/views/user_mailer/confirmation.text.erb" 195 | template "views/user_mailer/password_reset.html.erb", "app/views/user_mailer/password_reset.html.erb" 196 | template "views/user_mailer/password_reset.text.erb", "app/views/user_mailer/password_reset.text.erb" 197 | end 198 | 199 | def create_user_model 200 | template "user.rb", "app/models/user.rb" 201 | end 202 | 203 | def create_users_table 204 | generate "migration", "create_users email:string:index confirmed_at:datetime password_digest:string unconfirmed_email:string" 205 | end 206 | 207 | def create_user_views 208 | template "views/users/edit.html.erb", "app/views/users/edit.html.erb" 209 | template "views/users/new.html.erb", "app/views/users/new.html.erb" 210 | end 211 | 212 | def directory_exists(directory) 213 | File.directory?(directory) 214 | end 215 | 216 | def gemfile 217 | path_to("Gemfile") 218 | end 219 | 220 | def gemfile_exists 221 | File.exist?(gemfile) 222 | end 223 | 224 | def modify_active_sessions_table 225 | migration = Dir.glob(Rails.root.join("db/migrate/*")).max_by { |f| File.mtime(f) } 226 | gsub_file migration, /t.string :remember_token/, "t.string :remember_token, null: false" 227 | gsub_file migration, /add_index :active_sessions, :remember_token/, "add_index :active_sessions, :remember_token, unique: true" 228 | end 229 | 230 | def modify_application_controller 231 | inject_into_file "app/controllers/application_controller.rb", "\tinclude Authentication\n", after: "class ApplicationController < ActionController::Base\n" 232 | end 233 | 234 | def modify_test_helper 235 | inject_into_class "test/test_helper.rb", "ActiveSupport::TestCase" do 236 | <<-RUBY 237 | def current_user 238 | if session[:current_active_session_id].present? 239 | ActiveSession.find_by(id: session[:current_active_session_id])&.user 240 | else 241 | cookies[:remember_token].present? 242 | ActiveSession.find_by(remember_token: cookies[:remember_token])&.user 243 | end 244 | end 245 | 246 | def login(user, remember_user: nil) 247 | post login_path, params: { 248 | user: { 249 | email: user.email, 250 | password: user.password, 251 | remember_me: remember_user == true ? 1 : 0 252 | } 253 | } 254 | end 255 | 256 | def logout 257 | session.delete(:current_active_session_id) 258 | end 259 | RUBY 260 | end 261 | end 262 | 263 | def modify_users_table 264 | migration = Dir.glob(Rails.root.join("db/migrate/*")).max_by { |f| File.mtime(f) } 265 | gsub_file migration, /t.string :email/, "t.string :email, null: false" 266 | gsub_file migration, /t.string :password_digest/, "t.string :password_digest, null: false" 267 | gsub_file migration, /add_index :users, :email/, "add_index :users, :email, unique: true" 268 | end 269 | 270 | def path_to(path) 271 | Rails.root.join(path) 272 | end 273 | 274 | def print_instructions 275 | readme "README" 276 | end 277 | 278 | def using_default_test_suite 279 | directory_exists(path_to("test")) 280 | end 281 | end 282 | end 283 | end 284 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/README: -------------------------------------------------------------------------------- 1 | ========== Setup complete! 🎉 ========== 2 | 3 | Next steps 👇 4 | 5 | 1️⃣ Run bundle install 6 | 2️⃣ Run rails db:migrate 7 | 3️⃣ Add a root path in config/routes.rb 8 | 4️⃣ Ensure you have flash messages in app/views/layouts/application.html.erb. 9 | 10 |

<%= notice %>

11 |

<%= alert %>

12 | 13 | ========================================= -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/active_session.rb.tt: -------------------------------------------------------------------------------- 1 | class ActiveSession < ApplicationRecord 2 | belongs_to :user 3 | 4 | has_secure_token :remember_token 5 | end 6 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/active_sessions_controller.rb.tt: -------------------------------------------------------------------------------- 1 | class ActiveSessionsController < ApplicationController 2 | before_action :authenticate_user! 3 | 4 | def destroy 5 | @active_session = current_user.active_sessions.find(params[:id]) 6 | 7 | @active_session.destroy 8 | 9 | if current_user 10 | redirect_to account_path, notice: "Session deleted." 11 | else 12 | forget_active_session 13 | reset_session 14 | redirect_to root_path, notice: "Signed out." 15 | end 16 | end 17 | 18 | def destroy_all 19 | forget_active_session 20 | current_user.active_sessions.destroy_all 21 | reset_session 22 | 23 | redirect_to root_path, notice: "Signed out." 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/authentication.rb.tt: -------------------------------------------------------------------------------- 1 | module Authentication 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | before_action :current_user 6 | helper_method :current_user 7 | helper_method :user_signed_in? 8 | end 9 | 10 | def authenticate_user! 11 | store_location 12 | redirect_to login_path, alert: "You need to login to access that page." unless user_signed_in? 13 | end 14 | 15 | def login(user) 16 | reset_session 17 | active_session = user.active_sessions.create!(user_agent: request.user_agent, ip_address: request.ip) 18 | session[:current_active_session_id] = active_session.id 19 | 20 | active_session 21 | end 22 | 23 | def forget_active_session 24 | cookies.delete :remember_token 25 | end 26 | 27 | def logout 28 | active_session = ActiveSession.find_by(id: session[:current_active_session_id]) 29 | reset_session 30 | active_session.destroy! if active_session.present? 31 | end 32 | 33 | def redirect_if_authenticated 34 | redirect_to root_path, alert: "You are already logged in." if user_signed_in? 35 | end 36 | 37 | def remember(active_session) 38 | cookies.permanent.encrypted[:remember_token] = active_session.remember_token 39 | end 40 | 41 | private 42 | 43 | def current_user 44 | Current.user = if session[:current_active_session_id].present? 45 | ActiveSession.find_by(id: session[:current_active_session_id])&.user 46 | elsif cookies.permanent.encrypted[:remember_token].present? 47 | ActiveSession.find_by(remember_token: cookies.permanent.encrypted[:remember_token])&.user 48 | end 49 | end 50 | 51 | def user_signed_in? 52 | Current.user.present? 53 | end 54 | 55 | def store_location 56 | session[:user_return_to] = request.original_url if request.get? && request.local? 57 | end 58 | end -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/confirmations_controller.rb.tt: -------------------------------------------------------------------------------- 1 | class ConfirmationsController < ApplicationController 2 | before_action :redirect_if_authenticated, only: [:create, :new] 3 | 4 | def create 5 | @user = User.find_by(email: params[:user][:email].downcase) 6 | 7 | if @user.present? && @user.unconfirmed? 8 | @user.send_confirmation_email! 9 | redirect_to root_path, notice: "Check your email for confirmation instructions." 10 | else 11 | redirect_to new_confirmation_path, alert: "We could not find a user with that email or that email has already been confirmed." 12 | end 13 | end 14 | 15 | def edit 16 | @user = User.find_signed(params[:confirmation_token], purpose: :confirm_email) 17 | if @user.present? && @user.unconfirmed_or_reconfirming? 18 | if @user.confirm! 19 | login @user 20 | redirect_to root_path, notice: "Your account has been confirmed." 21 | else 22 | redirect_to new_confirmation_path, alert: "Something went wrong." 23 | end 24 | else 25 | redirect_to new_confirmation_path, alert: "Invalid token." 26 | end 27 | end 28 | 29 | def new 30 | @user = User.new 31 | end 32 | end -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/current.rb.tt: -------------------------------------------------------------------------------- 1 | class Current < ActiveSupport::CurrentAttributes 2 | attribute :user 3 | end -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/passwords_controller.rb.tt: -------------------------------------------------------------------------------- 1 | class PasswordsController < ApplicationController 2 | before_action :redirect_if_authenticated 3 | 4 | def create 5 | @user = User.find_by(email: params[:user][:email].downcase) 6 | if @user.present? 7 | if @user.confirmed? 8 | @user.send_password_reset_email! 9 | redirect_to root_path, notice: "If that user exists we've sent instructions to their email." 10 | else 11 | redirect_to new_confirmation_path, alert: "Please confirm your email first." 12 | end 13 | else 14 | redirect_to root_path, notice: "If that user exists we've sent instructions to their email." 15 | end 16 | end 17 | 18 | def edit 19 | @user = User.find_signed(params[:password_reset_token], purpose: :reset_password) 20 | if @user.present? && @user.unconfirmed? 21 | redirect_to new_confirmation_path, alert: "You must confirm your email before you can sign in." 22 | elsif @user.nil? 23 | redirect_to new_password_path, alert: "Invalid or expired token." 24 | end 25 | end 26 | 27 | def new 28 | end 29 | 30 | def update 31 | @user = User.find_signed(params[:password_reset_token], purpose: :reset_password) 32 | if @user 33 | if @user.unconfirmed? 34 | redirect_to new_confirmation_path, alert: "You must confirm your email before you can sign in." 35 | elsif @user.update(password_params) 36 | redirect_to login_path, notice: "Sign in." 37 | else 38 | flash.now[:alert] = @user.errors.full_messages.to_sentence 39 | render :edit, status: :unprocessable_entity 40 | end 41 | else 42 | flash.now[:alert] = "Invalid or expired token." 43 | render :new, status: :unprocessable_entity 44 | end 45 | end 46 | 47 | private 48 | 49 | def password_params 50 | params.require(:user).permit(:password, :password_confirmation) 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/sessions_controller.rb.tt: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | before_action :redirect_if_authenticated, only: [:create, :new] 3 | before_action :authenticate_user!, only: [:destroy] 4 | 5 | def create 6 | @user = User.authenticate_by(email: params[:user][:email].downcase, password: params[:user][:password]) 7 | if @user 8 | if @user.unconfirmed? 9 | redirect_to new_confirmation_path, alert: "Incorrect email or password." 10 | else 11 | after_login_path = session[:user_return_to] || root_path 12 | active_session = login @user 13 | remember(active_session) if params[:user][:remember_me] == "1" 14 | redirect_to after_login_path, notice: "Signed in." 15 | end 16 | else 17 | flash.now[:alert] = "Incorrect email or password." 18 | render :new, status: :unprocessable_entity 19 | end 20 | end 21 | 22 | def destroy 23 | forget_active_session 24 | logout 25 | redirect_to root_path, notice: "Signed out." 26 | end 27 | 28 | def new 29 | end 30 | end -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/test/controllers/active_sessions_controller_test.rb.tt: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ActiveSessionsControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @confirmed_user = User.create!(email: "confirmed_user@example.com", password: "password", password_confirmation: "password", confirmed_at: Time.current) 6 | end 7 | 8 | test "should destroy all active sessions" do 9 | login @confirmed_user 10 | @confirmed_user.active_sessions.create! 11 | 12 | assert_difference("ActiveSession.count", -2) do 13 | delete destroy_all_active_sessions_path 14 | end 15 | 16 | assert_redirected_to root_path 17 | assert_nil current_user 18 | assert_not_nil flash[:notice] 19 | end 20 | 21 | test "should destroy all active sessions and forget active sessions" do 22 | login @confirmed_user, remember_user: true 23 | @confirmed_user.active_sessions.create! 24 | 25 | assert_difference("ActiveSession.count", -2) do 26 | delete destroy_all_active_sessions_path 27 | end 28 | 29 | assert_nil current_user 30 | assert cookies[:remember_token].blank? 31 | end 32 | 33 | test "should destroy another session" do 34 | login @confirmed_user 35 | @confirmed_user.active_sessions.create! 36 | 37 | assert_difference("ActiveSession.count", -1) do 38 | delete active_session_path(@confirmed_user.active_sessions.last) 39 | end 40 | 41 | assert_redirected_to account_path 42 | assert_not_nil current_user 43 | assert_not_nil flash[:notice] 44 | end 45 | 46 | test "should destroy current session" do 47 | login @confirmed_user 48 | 49 | assert_difference("ActiveSession.count", -1) do 50 | delete active_session_path(@confirmed_user.active_sessions.last) 51 | end 52 | 53 | assert_redirected_to root_path 54 | assert_nil current_user 55 | assert_not_nil flash[:notice] 56 | end 57 | 58 | test "should destroy current session and forget current active session" do 59 | login @confirmed_user, remember_user: true 60 | 61 | assert_difference("ActiveSession.count", -1) do 62 | delete active_session_path(@confirmed_user.active_sessions.last) 63 | end 64 | 65 | assert_nil current_user 66 | assert cookies[:remember_token].blank? 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/test/controllers/confirmations_controller_test.rb.tt: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ConfirmationsControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @reconfirmed_user = User.create!(email: "reconfirmed_user@example.com", password: "password", password_confirmation: "password", confirmed_at: 1.week.ago, unconfirmed_email: "unconfirmed_email@example.com") 6 | @confirmed_user = User.create!(email: "confirmed_user@example.com", password: "password", password_confirmation: "password", confirmed_at: 1.week.ago) 7 | @unconfirmed_user = User.create!(email: "unconfirmed_user@example.com", password: "password", password_confirmation: "password") 8 | end 9 | 10 | test "should confirm unconfirmed user" do 11 | freeze_time do 12 | confirmation_token = @unconfirmed_user.generate_confirmation_token 13 | 14 | get edit_confirmation_path(confirmation_token) 15 | 16 | assert @unconfirmed_user.reload.confirmed? 17 | assert_equal Time.now, @unconfirmed_user.confirmed_at 18 | assert_redirected_to root_path 19 | assert_not_nil flash[:notice] 20 | end 21 | end 22 | 23 | test "should reconfirm confirmed user" do 24 | unconfirmed_email = @reconfirmed_user.unconfirmed_email 25 | 26 | freeze_time do 27 | confirmation_token = @reconfirmed_user.generate_confirmation_token 28 | 29 | get edit_confirmation_path(confirmation_token) 30 | 31 | assert @reconfirmed_user.reload.confirmed? 32 | assert_equal Time.current, @reconfirmed_user.reload.confirmed_at 33 | assert_equal unconfirmed_email, @reconfirmed_user.reload.email 34 | assert_nil @reconfirmed_user.reload.unconfirmed_email 35 | assert_redirected_to root_path 36 | assert_not_nil flash[:notice] 37 | end 38 | end 39 | 40 | test "should not update email address if already taken" do 41 | original_email = @reconfirmed_user.email 42 | @reconfirmed_user.update(unconfirmed_email: @confirmed_user.email) 43 | 44 | freeze_time do 45 | confirmation_token = @reconfirmed_user.generate_confirmation_token 46 | 47 | get edit_confirmation_path(confirmation_token) 48 | 49 | assert_equal original_email, @reconfirmed_user.reload.email 50 | assert_redirected_to new_confirmation_path 51 | assert_not_nil flash[:alert] 52 | end 53 | end 54 | 55 | test "should redirect if confirmation link expired" do 56 | confirmation_token = @unconfirmed_user.generate_confirmation_token 57 | 58 | travel_to 601.seconds.from_now do 59 | get edit_confirmation_path(confirmation_token) 60 | 61 | assert_nil @unconfirmed_user.reload.confirmed_at 62 | assert_not @unconfirmed_user.reload.confirmed? 63 | assert_redirected_to new_confirmation_path 64 | assert_not_nil flash[:alert] 65 | end 66 | end 67 | 68 | test "should redirect if confirmation link is incorrect" do 69 | get edit_confirmation_path("not_a_real_token") 70 | assert_redirected_to new_confirmation_path 71 | assert_not_nil flash[:alert] 72 | end 73 | 74 | test "should resend confirmation email if user is unconfirmed" do 75 | assert_emails 1 do 76 | post confirmations_path, params: {user: {email: @unconfirmed_user.email}} 77 | end 78 | 79 | assert_redirected_to root_path 80 | assert_not_nil flash[:notice] 81 | end 82 | 83 | test "should prevent user from confirming if they are already confirmed" do 84 | assert_no_emails do 85 | post confirmations_path, params: {user: {email: @confirmed_user.email}} 86 | end 87 | assert_redirected_to new_confirmation_path 88 | assert_not_nil flash[:alert] 89 | end 90 | 91 | test "should get new if not authenticated" do 92 | get new_confirmation_path 93 | assert_response :ok 94 | end 95 | 96 | test "should prevent authenticated user from confirming" do 97 | freeze_time do 98 | confirmation_token = @confirmed_user.generate_confirmation_token 99 | 100 | login @confirmed_user 101 | 102 | get edit_confirmation_path(confirmation_token) 103 | 104 | assert_not_equal Time.current, @confirmed_user.reload.confirmed_at 105 | assert_redirected_to new_confirmation_path 106 | assert_not_nil flash[:alert] 107 | end 108 | end 109 | 110 | test "should not prevent authenticated user confirming their unconfirmed_email" do 111 | unconfirmed_email = @reconfirmed_user.unconfirmed_email 112 | 113 | freeze_time do 114 | login @reconfirmed_user 115 | 116 | confirmation_token = @reconfirmed_user.generate_confirmation_token 117 | 118 | get edit_confirmation_path(confirmation_token) 119 | 120 | assert_equal Time.current, @reconfirmed_user.reload.confirmed_at 121 | assert @reconfirmed_user.reload.confirmed? 122 | assert_equal unconfirmed_email, @reconfirmed_user.reload.email 123 | assert_nil @reconfirmed_user.reload.unconfirmed_email 124 | assert_redirected_to root_path 125 | assert_not_nil flash[:notice] 126 | end 127 | end 128 | 129 | test "should prevent authenticated user from submitting the confirmation form" do 130 | login @confirmed_user 131 | 132 | get new_confirmation_path 133 | assert_redirected_to root_path 134 | assert_not_nil flash[:alert] 135 | 136 | assert_no_emails do 137 | post confirmations_path, params: {user: {email: @confirmed_user.email}} 138 | end 139 | 140 | assert_redirected_to root_path 141 | assert_not_nil flash[:alert] 142 | end 143 | end 144 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/test/controllers/passwords_controller_test.rb.tt: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class PasswordsControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @confirmed_user = User.create!(email: "confirmed_user@example.com", password: "password", password_confirmation: "password", confirmed_at: 1.week.ago) 6 | end 7 | 8 | test "should get edit" do 9 | password_reset_token = @confirmed_user.generate_password_reset_token 10 | 11 | get edit_password_path(password_reset_token) 12 | assert_response :ok 13 | end 14 | 15 | test "should redirect from edit if password link expired" do 16 | password_reset_token = @confirmed_user.generate_password_reset_token 17 | 18 | travel_to 601.seconds.from_now 19 | get edit_password_path(password_reset_token) 20 | 21 | assert_redirected_to new_password_path 22 | assert_not_nil flash[:alert] 23 | end 24 | 25 | test "should redirect from edit if password link is incorrect" do 26 | get edit_password_path("not_a_real_token") 27 | 28 | assert_redirected_to new_password_path 29 | assert_not_nil flash[:alert] 30 | end 31 | 32 | test "should redirect from edit if user is not confirmed" do 33 | @confirmed_user.update!(confirmed_at: nil) 34 | password_reset_token = @confirmed_user.generate_password_reset_token 35 | 36 | get edit_password_path(password_reset_token) 37 | 38 | assert_redirected_to new_confirmation_path 39 | assert_not_nil flash[:alert] 40 | end 41 | 42 | test "should redirect from edit if user is authenticated" do 43 | password_reset_token = @confirmed_user.generate_password_reset_token 44 | 45 | login @confirmed_user 46 | 47 | get edit_password_path(password_reset_token) 48 | assert_redirected_to root_path 49 | end 50 | 51 | test "should get new" do 52 | get new_password_path 53 | assert_response :ok 54 | end 55 | 56 | test "should redirect from new if user is authenticated" do 57 | login @confirmed_user 58 | 59 | get new_password_path 60 | assert_redirected_to root_path 61 | end 62 | 63 | test "should send password reset mailer" do 64 | assert_emails 1 do 65 | post passwords_path, params: { 66 | user: { 67 | email: @confirmed_user.email.upcase 68 | } 69 | } 70 | end 71 | 72 | assert_redirected_to root_path 73 | assert_not_nil flash[:notice] 74 | end 75 | 76 | test "should update password" do 77 | password_reset_token = @confirmed_user.generate_password_reset_token 78 | 79 | put password_path(password_reset_token), params: { 80 | user: { 81 | password: "password", 82 | password_confirmation: "password" 83 | } 84 | } 85 | 86 | assert_redirected_to login_path 87 | assert_not_nil flash[:notice] 88 | end 89 | 90 | test "should handle errors" do 91 | password_reset_token = @confirmed_user.generate_password_reset_token 92 | 93 | put password_path(password_reset_token), params: { 94 | user: { 95 | password: "password", 96 | password_confirmation: "password_that_does_not_match" 97 | } 98 | } 99 | 100 | assert_not_nil flash[:alert] 101 | end 102 | 103 | test "should not update password if authenticated" do 104 | password_reset_token = @confirmed_user.generate_password_reset_token 105 | 106 | login @confirmed_user 107 | 108 | put password_path(password_reset_token), params: { 109 | user: { 110 | password: "password", 111 | password_confirmation: "password" 112 | 113 | } 114 | } 115 | 116 | get new_password_path 117 | assert_redirected_to root_path 118 | end 119 | end 120 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/test/controllers/sessions_controller_test.rb.tt: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class SessionsControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @unconfirmed_user = User.create!(email: "unconfirmed_user@example.com", password: "password", password_confirmation: "password") 6 | @confirmed_user = User.create!(email: "confirmed_user@example.com", password: "password", password_confirmation: "password", confirmed_at: Time.current) 7 | end 8 | 9 | test "should get login if anonymous" do 10 | get login_path 11 | assert_response :ok 12 | end 13 | 14 | test "should redirect from login if authenticated" do 15 | login @confirmed_user 16 | 17 | get login_path 18 | assert_redirected_to root_path 19 | end 20 | 21 | test "should login and create active session if confirmed" do 22 | assert_difference("@confirmed_user.active_sessions.count") do 23 | post login_path, params: { 24 | user: { 25 | email: @confirmed_user.email, 26 | password: @confirmed_user.password 27 | } 28 | } 29 | end 30 | assert_redirected_to root_path 31 | assert_equal @confirmed_user, current_user 32 | end 33 | 34 | test "should remember user when logging in" do 35 | assert_nil cookies[:remember_token] 36 | 37 | post login_path, params: { 38 | user: { 39 | email: @confirmed_user.email, 40 | password: @confirmed_user.password, 41 | remember_me: 1 42 | } 43 | } 44 | 45 | assert_not_nil current_user 46 | assert_not_nil cookies[:remember_token] 47 | end 48 | 49 | test "should forget user when logging out" do 50 | login @confirmed_user, remember_user: true 51 | 52 | delete logout_path 53 | 54 | # FIXME: Expected "" to be nil. 55 | # When I run byebug in SessionsController#destroy cookies[:remember_token] does == nil. 56 | # I think this might be a bug in Rails? 57 | # assert_nil cookies[:remember_token] 58 | assert cookies[:remember_token].blank? 59 | assert_nil current_user 60 | assert_redirected_to root_path 61 | assert_not_nil flash[:notice] 62 | end 63 | 64 | test "should not login if unconfirmed" do 65 | post login_path, params: { 66 | user: { 67 | email: @unconfirmed_user.email, 68 | password: @unconfirmed_user.password 69 | } 70 | } 71 | assert_equal "Incorrect email or password.", flash[:alert] 72 | assert_nil current_user 73 | assert_redirected_to new_confirmation_path 74 | end 75 | 76 | test "should handle invalid login" do 77 | post login_path, params: { 78 | user: { 79 | email: @confirmed_user.email, 80 | password: "foo" 81 | } 82 | } 83 | assert_not_nil flash[:alert] 84 | assert_nil current_user 85 | end 86 | 87 | test "should logout and delete current active session if authenticated" do 88 | login @confirmed_user 89 | 90 | assert_difference("@confirmed_user.active_sessions.count", -1) do 91 | delete logout_path 92 | end 93 | 94 | assert_nil current_user 95 | assert_redirected_to root_path 96 | assert_not_nil flash[:notice] 97 | end 98 | 99 | test "should not logout if anonymous" do 100 | login @confirmed_user 101 | 102 | delete logout_path 103 | assert_redirected_to root_path 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/test/controllers/users_controller_test.rb.tt: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @confirmed_user = User.create!(email: "confirmed_user@example.com", password: "password", password_confirmation: "password", confirmed_at: Time.current) 6 | end 7 | 8 | test "should load sign up page for anonymous users" do 9 | get sign_up_path 10 | assert_response :ok 11 | end 12 | 13 | test "should redirect authenticated users from signing up" do 14 | login @confirmed_user 15 | 16 | get sign_up_path 17 | assert_redirected_to root_path 18 | 19 | assert_no_difference("User.count") do 20 | post sign_up_path, params: { 21 | user: { 22 | email: "some_unique_email@example.com", 23 | password: "password", 24 | password_confirmation: "password" 25 | } 26 | } 27 | end 28 | end 29 | 30 | test "should create user and send confirmation instructions" do 31 | assert_difference("User.count", 1) do 32 | assert_emails 1 do 33 | post sign_up_path, params: { 34 | user: { 35 | email: "some_unique_email@example.com", 36 | password: "password", 37 | password_confirmation: "password" 38 | } 39 | } 40 | end 41 | end 42 | 43 | assert_redirected_to root_path 44 | assert_not_nil flash[:notice] 45 | end 46 | 47 | test "should handle errors when signing up" do 48 | assert_no_difference("User.count") do 49 | assert_no_emails do 50 | post sign_up_path, params: { 51 | user: { 52 | email: "some_unique_email@example.com", 53 | password: "password", 54 | password_confirmation: "wrong_password" 55 | } 56 | } 57 | end 58 | end 59 | end 60 | 61 | test "should get edit if authorized" do 62 | login(@confirmed_user) 63 | 64 | get account_path 65 | assert_response :ok 66 | end 67 | 68 | test "should redirect unauthorized user from editing account" do 69 | get account_path 70 | assert_redirected_to login_path 71 | assert_not_nil flash[:alert] 72 | end 73 | 74 | test "should edit email" do 75 | unconfirmed_email = "unconfirmed_user@example.com" 76 | current_email = @confirmed_user.email 77 | 78 | login(@confirmed_user) 79 | 80 | assert_emails 1 do 81 | put account_path, params: { 82 | user: { 83 | unconfirmed_email: unconfirmed_email, 84 | current_password: "password" 85 | } 86 | } 87 | end 88 | 89 | assert_not_nil flash[:notice] 90 | assert_equal current_email, @confirmed_user.reload.email 91 | end 92 | 93 | test "should not edit email if current_password is incorrect" do 94 | unconfirmed_email = "unconfirmed_user@example.com" 95 | current_email = @confirmed_user.email 96 | 97 | login(@confirmed_user) 98 | 99 | assert_no_emails do 100 | put account_path, params: { 101 | user: { 102 | unconfirmed_email: unconfirmed_email, 103 | current_password: "wrong_password" 104 | } 105 | } 106 | end 107 | 108 | assert_not_nil flash[:notice] 109 | assert_equal current_email, @confirmed_user.reload.email 110 | end 111 | 112 | test "should update password" do 113 | login(@confirmed_user) 114 | 115 | put account_path, params: { 116 | user: { 117 | current_password: "password", 118 | password: "new_password", 119 | password_confirmation: "new_password" 120 | } 121 | } 122 | 123 | assert_redirected_to root_path 124 | assert_not_nil flash[:notice] 125 | end 126 | 127 | test "should not update password if current_password is incorrect" do 128 | login(@confirmed_user) 129 | 130 | put account_path, params: { 131 | user: { 132 | current_password: "wrong_password", 133 | password: "new_password", 134 | password_confirmation: "new_password" 135 | } 136 | } 137 | 138 | assert_response :unprocessable_entity 139 | end 140 | 141 | test "should delete user" do 142 | login(@confirmed_user) 143 | 144 | delete account_path(@confirmed_user) 145 | 146 | assert_nil current_user 147 | assert_redirected_to root_path 148 | assert_not_nil flash[:notice] 149 | end 150 | end 151 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/test/integration/friendly_redirects_test.rb.tt: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class FriendlyRedirectsTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @confirmed_user = User.create!(email: "confirmed_user@example.com", password: "password", password_confirmation: "password", confirmed_at: Time.current) 6 | end 7 | 8 | test "redirect to requested url after sign in" do 9 | get account_path 10 | 11 | assert_redirected_to login_path 12 | login(@confirmed_user) 13 | 14 | assert_redirected_to account_path 15 | end 16 | 17 | test "redirects to root path after sign in" do 18 | get login_path 19 | login(@confirmed_user) 20 | 21 | assert_redirected_to root_path 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/test/integration/user_interface_test.rb.tt: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserInterfaceTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @confirmed_user = User.create!(email: "confirmed_user@example.com", password: "password", password_confirmation: "password", confirmed_at: Time.current) 6 | end 7 | 8 | test "should render active sessions on account page" do 9 | login @confirmed_user 10 | @confirmed_user.active_sessions.last.update!(user_agent: "Mozilla", ip_address: "123.457.789") 11 | 12 | get account_path 13 | 14 | assert_match "Mozilla", @response.body 15 | assert_match "123.457.789", @response.body 16 | end 17 | 18 | test "should render buttons to delete specific active sessions" do 19 | login @confirmed_user 20 | 21 | get account_path 22 | 23 | assert_select "button", "Log out of all other sessions" 24 | assert_match destroy_all_active_sessions_path, @response.body 25 | 26 | assert_select "table" do 27 | assert_select "button", "Sign Out" 28 | end 29 | assert_match active_session_path(@confirmed_user.active_sessions.last), @response.body 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/test/mailers/previews/user_mailer_preview.rb.tt: -------------------------------------------------------------------------------- 1 | # Preview all emails at http://localhost:3000/rails/mailers/user_mailer 2 | class UserMailerPreview < ActionMailer::Preview 3 | # Preview this email at http://localhost:3000/rails/mailers/user_mailer/confirmation 4 | def confirmation 5 | @unconfirmed_user = User.find_by(email: "unconfirmed_user@example.com") || User.create!(email: "unconfirmed_user@example.com", password: "password", password_confirmation: "password") 6 | @unconfirmed_user.update!(confirmed_at: nil) 7 | confirmation_token = @unconfirmed_user.generate_confirmation_token 8 | UserMailer.confirmation(@unconfirmed_user, confirmation_token) 9 | end 10 | 11 | # Preview this email at http://localhost:3000/rails/mailers/user_mailer/password_reset 12 | def password_reset 13 | @password_reset_user = User.find_by(email: "password_reset_user@example.com") || User.create!(email: "password_reset_user@example.com", password: "password", password_confirmation: "password", confirmed_at: Time.current) 14 | password_reset_token = @password_reset_user.generate_password_reset_token 15 | UserMailer.password_reset(@password_reset_user, password_reset_token) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/test/mailers/user_mailer_test.rb.tt: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserMailerTest < ActionMailer::TestCase 4 | setup do 5 | @user = User.create!(email: "some_unique_email@example.com", password: "password", password_confirmation: "password") 6 | end 7 | 8 | test "confirmation" do 9 | confirmation_token = @user.generate_confirmation_token 10 | mail = UserMailer.confirmation(@user, confirmation_token) 11 | assert_equal "Confirmation Instructions", mail.subject 12 | assert_equal [@user.email], mail.to 13 | assert_equal [User::MAILER_FROM_EMAIL], mail.from 14 | assert_match confirmation_token, mail.body.encoded 15 | end 16 | 17 | test "password_reset" do 18 | password_reset_token = @user.generate_password_reset_token 19 | mail = UserMailer.password_reset(@user, password_reset_token) 20 | assert_equal "Password Reset Instructions", mail.subject 21 | assert_equal [@user.email], mail.to 22 | assert_equal [User::MAILER_FROM_EMAIL], mail.from 23 | assert_match password_reset_token, mail.body.encoded 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/test/models/active_session_test.rb.tt: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ActiveSessionTest < ActiveSupport::TestCase 4 | setup do 5 | @user = User.new(email: "unique_email@example.com", password: "password", password_confirmation: "password") 6 | @active_session = @user.active_sessions.build 7 | end 8 | 9 | test "should be valid" do 10 | assert @active_session.valid? 11 | end 12 | 13 | test "should have a user" do 14 | @active_session.user = nil 15 | 16 | assert_not @active_session.valid? 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/test/models/user_test.rb.tt: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | include ActionMailer::TestHelper 5 | 6 | setup do 7 | @user = User.new(email: "unique_email@example.com", password: "password", password_confirmation: "password") 8 | end 9 | 10 | test "should be valid" do 11 | assert @user.valid? 12 | end 13 | 14 | test "should have email" do 15 | @user.email = nil 16 | assert_not @user.valid? 17 | end 18 | 19 | test "email should be unique" do 20 | @user.save! 21 | @invalid_user = User.new(email: @user.email) 22 | 23 | assert_not @invalid_user.valid? 24 | end 25 | 26 | test "email should be saved as lowercase" do 27 | email = "unique_email@example.com" 28 | 29 | @user = User.new(email: email.upcase, password: "password", password_confirmation: "password") 30 | @user.save! 31 | 32 | assert_equal email.downcase, @user.email 33 | end 34 | 35 | test "email should be valid" do 36 | invalid_emails = %w[foo foo@ foo@bar.] 37 | 38 | invalid_emails.each do |invalid_email| 39 | @user.email = invalid_email 40 | assert_not @user.valid? 41 | end 42 | end 43 | 44 | test "should respond to confirmed?" do 45 | assert_not @user.confirmed? 46 | 47 | @user.confirmed_at = Time.now 48 | 49 | assert @user.confirmed? 50 | end 51 | 52 | test "should respond to unconfirmed?" do 53 | assert @user.unconfirmed? 54 | 55 | @user.confirmed_at = Time.now 56 | 57 | assert_not @user.unconfirmed? 58 | end 59 | 60 | test "should respond to reconfirming?" do 61 | assert_not @user.reconfirming? 62 | 63 | @user.unconfirmed_email = "unconfirmed_email@example.com" 64 | 65 | assert @user.reconfirming? 66 | end 67 | 68 | test "should respond to unconfirmed_or_reconfirming?" do 69 | assert @user.unconfirmed_or_reconfirming? 70 | 71 | @user.unconfirmed_email = "unconfirmed_email@example.com" 72 | @user.confirmed_at = Time.now 73 | 74 | assert @user.unconfirmed_or_reconfirming? 75 | end 76 | 77 | test "should send confirmation email" do 78 | @user.save! 79 | 80 | assert_emails 1 do 81 | @user.send_confirmation_email! 82 | end 83 | 84 | assert_equal @user.email, ActionMailer::Base.deliveries.last.to[0] 85 | end 86 | 87 | test "should send confirmation email to unconfirmed_email" do 88 | @user.save! 89 | @user.update!(unconfirmed_email: "unconfirmed_email@example.com") 90 | 91 | assert_emails 1 do 92 | @user.send_confirmation_email! 93 | end 94 | 95 | assert_equal @user.unconfirmed_email, ActionMailer::Base.deliveries.last.to[0] 96 | end 97 | 98 | test "should respond to send_password_reset_email!" do 99 | @user.save! 100 | 101 | assert_emails 1 do 102 | @user.send_password_reset_email! 103 | end 104 | end 105 | 106 | test "should downcase unconfirmed_email" do 107 | email = "UNCONFIRMED_EMAIL@EXAMPLE.COM" 108 | @user.unconfirmed_email = email 109 | @user.save! 110 | 111 | assert_equal email.downcase, @user.unconfirmed_email 112 | end 113 | 114 | test "unconfirmed_email should be valid" do 115 | invalid_emails = %w[foo foo@ foo@bar.] 116 | 117 | invalid_emails.each do |invalid_email| 118 | @user.unconfirmed_email = invalid_email 119 | assert_not @user.valid? 120 | end 121 | end 122 | 123 | test "unconfirmed_email does not need to be available" do 124 | @user.save! 125 | @user.unconfirmed_email = @user.email 126 | assert @user.valid? 127 | end 128 | 129 | test ".confirm! should return false if already confirmed" do 130 | @confirmed_user = User.new(email: "unique_email@example.com", password: "password", password_confirmation: "password", confirmed_at: Time.current) 131 | 132 | assert_not @confirmed_user.confirm! 133 | end 134 | 135 | test ".confirm! should update email if reconfirming" do 136 | @reconfirmed_user = User.new(email: "unique_email@example.com", password: "password", password_confirmation: "password", confirmed_at: 1.week.ago, unconfirmed_email: "unconfirmed_email@example.com") 137 | new_email = @reconfirmed_user.unconfirmed_email 138 | 139 | freeze_time do 140 | @reconfirmed_user.confirm! 141 | 142 | assert_equal new_email, @reconfirmed_user.reload.email 143 | assert_nil @reconfirmed_user.reload.unconfirmed_email 144 | assert_equal Time.current, @reconfirmed_user.reload.confirmed_at 145 | end 146 | end 147 | 148 | test ".confirm! should not update email if already taken" do 149 | @confirmed_user = User.create!(email: "user1@example.com", password: "password", password_confirmation: "password") 150 | @reconfirmed_user = User.create!(email: "user2@example.com", password: "password", password_confirmation: "password", confirmed_at: 1.week.ago, unconfirmed_email: @confirmed_user.email) 151 | 152 | freeze_time do 153 | assert_not @reconfirmed_user.confirm! 154 | end 155 | end 156 | 157 | test ".confirm! should set confirmed_at" do 158 | @unconfirmed_user = User.create!(email: "unique_email@example.com", password: "password", password_confirmation: "password") 159 | 160 | freeze_time do 161 | @unconfirmed_user.confirm! 162 | 163 | assert_equal Time.current, @unconfirmed_user.reload.confirmed_at 164 | end 165 | end 166 | 167 | test "should create active session" do 168 | @user.save! 169 | 170 | assert_difference("@user.active_sessions.count", 1) do 171 | @user.active_sessions.create! 172 | end 173 | end 174 | 175 | test "should destroy associated active session when destryoed" do 176 | @user.save! 177 | @user.active_sessions.create! 178 | 179 | assert_difference("@user.active_sessions.count", -1) do 180 | @user.destroy! 181 | end 182 | end 183 | end 184 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/test/system/logins_test.rb.tt: -------------------------------------------------------------------------------- 1 | require "application_system_test_case" 2 | 3 | class LoginsTest < ApplicationSystemTestCase 4 | setup do 5 | @confirmed_user = User.create!(email: "confirmed_user@example.com", password: "password", password_confirmation: "password", confirmed_at: Time.current) 6 | end 7 | 8 | test "should login and create active session if confirmed" do 9 | visit login_path 10 | 11 | fill_in "Email", with: @confirmed_user.email 12 | fill_in "Password", with: @confirmed_user.password 13 | click_on "Sign In" 14 | 15 | sleep 0.1 16 | 17 | assert_not_nil @confirmed_user.active_sessions.last.user_agent 18 | assert_not_nil @confirmed_user.active_sessions.last.ip_address 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/user.rb.tt: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | CONFIRMATION_TOKEN_EXPIRATION = 10.minutes 3 | MAILER_FROM_EMAIL = "no-reply@example.com" 4 | PASSWORD_RESET_TOKEN_EXPIRATION = 10.minutes 5 | 6 | attr_accessor :current_password 7 | 8 | has_secure_password 9 | 10 | has_many :active_sessions, dependent: :destroy 11 | 12 | before_save :downcase_email 13 | before_save :downcase_unconfirmed_email 14 | 15 | validates :email, format: {with: URI::MailTo::EMAIL_REGEXP}, presence: true, uniqueness: true 16 | validates :unconfirmed_email, format: {with: URI::MailTo::EMAIL_REGEXP, allow_blank: true} 17 | 18 | def self.authenticate_by(attributes) 19 | passwords, identifiers = attributes.to_h.partition do |name, value| 20 | !has_attribute?(name) && has_attribute?("#{name}_digest") 21 | end.map(&:to_h) 22 | 23 | raise ArgumentError, "One or more password arguments are required" if passwords.empty? 24 | raise ArgumentError, "One or more finder arguments are required" if identifiers.empty? 25 | if (record = find_by(identifiers)) 26 | record if passwords.count { |name, value| record.public_send(:"authenticate_#{name}", value) } == passwords.size 27 | else 28 | new(passwords) 29 | nil 30 | end 31 | end 32 | 33 | def confirm! 34 | if unconfirmed_or_reconfirming? 35 | if unconfirmed_email.present? 36 | return false unless update(email: unconfirmed_email, unconfirmed_email: nil) 37 | end 38 | update_columns(confirmed_at: Time.current) 39 | else 40 | false 41 | end 42 | end 43 | 44 | def confirmed? 45 | confirmed_at.present? 46 | end 47 | 48 | def confirmable_email 49 | if unconfirmed_email.present? 50 | unconfirmed_email 51 | else 52 | email 53 | end 54 | end 55 | 56 | def generate_confirmation_token 57 | signed_id expires_in: CONFIRMATION_TOKEN_EXPIRATION, purpose: :confirm_email 58 | end 59 | 60 | def generate_password_reset_token 61 | signed_id expires_in: PASSWORD_RESET_TOKEN_EXPIRATION, purpose: :reset_password 62 | end 63 | 64 | def send_confirmation_email! 65 | confirmation_token = generate_confirmation_token 66 | UserMailer.confirmation(self, confirmation_token).deliver_now 67 | end 68 | 69 | def send_password_reset_email! 70 | password_reset_token = generate_password_reset_token 71 | UserMailer.password_reset(self, password_reset_token).deliver_now 72 | end 73 | 74 | def reconfirming? 75 | unconfirmed_email.present? 76 | end 77 | 78 | def unconfirmed? 79 | !confirmed? 80 | end 81 | 82 | def unconfirmed_or_reconfirming? 83 | unconfirmed? || reconfirming? 84 | end 85 | 86 | private 87 | 88 | def downcase_email 89 | self.email = email.downcase 90 | end 91 | 92 | def downcase_unconfirmed_email 93 | return if unconfirmed_email.nil? 94 | self.unconfirmed_email = unconfirmed_email.downcase 95 | end 96 | end -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/user_mailer.rb.tt: -------------------------------------------------------------------------------- 1 | class UserMailer < ApplicationMailer 2 | default from: User::MAILER_FROM_EMAIL 3 | 4 | # Subject can be set in your I18n file at config/locales/en.yml 5 | # with the following lookup: 6 | # 7 | # en.user_mailer.confirmation.subject 8 | # 9 | def confirmation(user, confirmation_token) 10 | @user = user 11 | @confirmation_token = confirmation_token 12 | 13 | mail to: @user.confirmable_email, subject: "Confirmation Instructions" 14 | end 15 | 16 | def password_reset(user, password_reset_token) 17 | @user = user 18 | @password_reset_token = password_reset_token 19 | 20 | mail to: @user.email, subject: "Password Reset Instructions" 21 | end 22 | end -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/users_controller.rb.tt: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :authenticate_user!, only: [:edit, :destroy, :update] 3 | before_action :redirect_if_authenticated, only: [:create, :new] 4 | 5 | def create 6 | @user = User.new(create_user_params) 7 | if @user.save 8 | @user.send_confirmation_email! 9 | redirect_to root_path, notice: "Please check your email for confirmation instructions." 10 | else 11 | render :new, status: :unprocessable_entity 12 | end 13 | end 14 | 15 | def destroy 16 | current_user.destroy 17 | reset_session 18 | redirect_to root_path, notice: "Your account has been deleted." 19 | end 20 | 21 | def edit 22 | @user = current_user 23 | @active_sessions = @user.active_sessions.order(created_at: :desc) 24 | end 25 | 26 | def new 27 | @user = User.new 28 | end 29 | 30 | def update 31 | @user = current_user 32 | @active_sessions = @user.active_sessions.order(created_at: :desc) 33 | if @user.authenticate(params[:user][:current_password]) 34 | if @user.update(update_user_params) 35 | if params[:user][:unconfirmed_email].present? 36 | @user.send_confirmation_email! 37 | redirect_to root_path, notice: "Check your email for confirmation instructions." 38 | else 39 | redirect_to root_path, notice: "Account updated." 40 | end 41 | else 42 | render :edit, status: :unprocessable_entity 43 | end 44 | else 45 | flash.now[:error] = "Incorrect password" 46 | render :edit, status: :unprocessable_entity 47 | end 48 | end 49 | 50 | private 51 | 52 | def create_user_params 53 | params.require(:user).permit(:email, :password, :password_confirmation) 54 | end 55 | 56 | def update_user_params 57 | params.require(:user).permit(:current_password, :password, :password_confirmation, :unconfirmed_email) 58 | end 59 | end -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/views/confirmations/new.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%%= form_with model: @user, url: confirmations_path do |form| %> 2 | <%%= form.email_field :email, required: true %> 3 | <%%= form.submit "Confirm Email" %> 4 | <%% end %> -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/views/passwords/edit.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%%= form_with url: password_path(params[:password_reset_token]), scope: :user, method: :put do |form| %> 2 |
3 | <%%= form.label :password %> 4 | <%%= form.password_field :password, required: true %> 5 |
6 |
7 | <%%= form.label :password_confirmation %> 8 | <%%= form.password_field :password_confirmation, required: true %> 9 |
10 | <%%= form.submit "Update Password" %> 11 | <%% end %> -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/views/passwords/new.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%%= form_with url: passwords_path, scope: :user do |form| %> 2 | <%%= form.email_field :email, required: true %> 3 | <%%= form.submit "Reset Password" %> 4 | <%% end %> -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/views/sessions/new.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%%= form_with url: login_path, scope: :user do |form| %> 2 |
3 | <%%= form.label :email %> 4 | <%%= form.email_field :email, required: true %> 5 |
6 |
7 | <%%= form.label :password %> 8 | <%%= form.password_field :password, required: true %> 9 |
10 |
11 | <%%= form.label :remember_me %> 12 | <%%= form.check_box :remember_me %> 13 |
14 | <%%= form.submit "Sign In" %> 15 | <%% end %> -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/views/user_mailer/confirmation.html.erb.tt: -------------------------------------------------------------------------------- 1 |

Confirmation Instructions

2 | 3 | <%%= link_to "Click here to confirm your email.", edit_confirmation_url(@confirmation_token) %> -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/views/user_mailer/confirmation.text.erb.tt: -------------------------------------------------------------------------------- 1 | Confirmation Instructions 2 | 3 | <%%= edit_confirmation_url(@confirmation_token) %> -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/views/user_mailer/password_reset.html.erb.tt: -------------------------------------------------------------------------------- 1 |

Password Reset Instructions

2 | 3 | <%%= link_to "Click here to reset your password.", edit_password_url(@password_reset_token) %> -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/views/user_mailer/password_reset.text.erb.tt: -------------------------------------------------------------------------------- 1 | Password Reset Instructions 2 | 3 | <%%= edit_password_url(@password_reset_token) %> -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/views/users/edit.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%%= form_with model: @user, url: account_path, method: :put do |form| %> 2 | <%% if form.object.errors.any? %> 3 | 8 | <%% end %> 9 |
10 | <%%= form.label :email, "Current Email" %> 11 | <%%= form.email_field :email, disabled: true %> 12 |
13 |
14 | <%%= form.label :unconfirmed_email, "New Email" %> 15 | <%%= form.text_field :unconfirmed_email %> 16 |
17 |
18 | <%%= form.label :password, "Password (leave blank if you don't want to change it)" %> 19 | <%%= form.password_field :password %> 20 |
21 |
22 | <%%= form.label :password_confirmation %> 23 | <%%= form.password_field :password_confirmation %> 24 |
25 |
26 |
27 | <%%= form.label :current_password, "Current password (we need your current password to confirm your changes)" %> 28 | <%%= form.password_field :current_password, required: true %> 29 |
30 | <%%= form.submit "Update Account" %> 31 | <%% end %> 32 |

Current Logins

33 | <%% if @active_sessions.any? %> 34 | <%%= button_to "Log out of all other sessions", destroy_all_active_sessions_path, method: :delete %> 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | <%% @active_sessions.each do |active_session| %> 46 | 47 | 48 | 49 | 50 | 51 | 52 | <%% end %> 53 | 54 |
User AgentIP AddressSigned In AtSign Out
<%%= active_session.user_agent %><%%= active_session.ip_address %><%%= active_session.created_at %><%%= button_to "Sign Out", active_session_path(active_session), method: :delete %>
55 | <%% end %> -------------------------------------------------------------------------------- /lib/generators/rails_mvp_authentication/templates/views/users/new.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%%= form_with model: @user, url: sign_up_path do |form| %> 2 | <%% if form.object.errors.any? %> 3 | 8 | <%% end %> 9 |
10 | <%%= form.label :email %> 11 | <%%= form.email_field :email, required: true %> 12 |
13 |
14 | <%%= form.label :password %> 15 | <%%= form.password_field :password, required: true %> 16 |
17 |
18 | <%%= form.label :password_confirmation %> 19 | <%%= form.password_field :password_confirmation, required: true %> 20 |
21 | <%%= form.submit "Sign Up" %> 22 | <%% end %> -------------------------------------------------------------------------------- /lib/rails_mvp_authentication.rb: -------------------------------------------------------------------------------- 1 | require "rails_mvp_authentication/version" 2 | require "rails_mvp_authentication/engine" 3 | 4 | module RailsMvpAuthentication 5 | # Your code goes here... 6 | end 7 | -------------------------------------------------------------------------------- /lib/rails_mvp_authentication/engine.rb: -------------------------------------------------------------------------------- 1 | module RailsMvpAuthentication 2 | class Engine < ::Rails::Engine 3 | isolate_namespace RailsMvpAuthentication 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/rails_mvp_authentication/version.rb: -------------------------------------------------------------------------------- 1 | module RailsMvpAuthentication 2 | VERSION = "1.0.0" 3 | end 4 | -------------------------------------------------------------------------------- /lib/tasks/rails_mvp_authentication_tasks.rake: -------------------------------------------------------------------------------- 1 | # desc "Explaining what the task does" 2 | # task :rails_mvp_authentication do 3 | # # Task goes here 4 | # end 5 | -------------------------------------------------------------------------------- /rails_mvp_authentication.gemspec: -------------------------------------------------------------------------------- 1 | require_relative "lib/rails_mvp_authentication/version" 2 | 3 | Gem::Specification.new do |spec| 4 | spec.name = "rails_mvp_authentication" 5 | spec.version = RailsMvpAuthentication::VERSION 6 | spec.authors = ["Steve Polito"] 7 | spec.email = ["stevepolito@hey.com"] 8 | spec.homepage = "https://github.com/stevepolitodesign/rails_mvp_authentication" 9 | spec.summary = "Rails authentication via a generator." 10 | spec.description = "Rails authentication via a generator." 11 | spec.license = "MIT" 12 | 13 | spec.metadata["homepage_uri"] = spec.homepage 14 | spec.metadata["source_code_uri"] = "https://github.com/stevepolitodesign/rails_mvp_authentication" 15 | spec.metadata["changelog_uri"] = "https://github.com/stevepolitodesign/rails_mvp_authentication/blob/main/CHANGELOG.md" 16 | 17 | spec.files = Dir.chdir(File.expand_path(__dir__)) do 18 | Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] 19 | end 20 | 21 | spec.add_dependency "rails", ">= 7.0.0" 22 | end 23 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/test/controllers/.keep -------------------------------------------------------------------------------- /test/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative "config/application" 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /test/dummy/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | //= link rails_mvp_authentication_manifest.js 4 | -------------------------------------------------------------------------------- /test/dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/test/dummy/app/assets/images/.keep -------------------------------------------------------------------------------- /test/dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /test/dummy/app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | end 3 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/test/dummy/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /test/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /test/dummy/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "from@example.com" 3 | layout "mailer" 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | primary_abstract_class 3 | end 4 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/test/dummy/app/models/concerns/.keep -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | 6 | <%= csrf_meta_tags %> 7 | <%= csp_meta_tag %> 8 | 9 | <%= stylesheet_link_tag "application" %> 10 | 11 | 12 | 13 | <%= yield %> 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /test/dummy/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /test/dummy/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /test/dummy/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path("..", __dir__) 6 | 7 | def system!(*args) 8 | system(*args) || abort("\n== Command #{args} failed ==") 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to set up or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome. 14 | # Add necessary setup steps to this file. 15 | 16 | puts "== Installing dependencies ==" 17 | system! "gem install bundler --conservative" 18 | system("bundle check") || system!("bundle install") 19 | 20 | # puts "\n== Copying sample files ==" 21 | # unless File.exist?("config/database.yml") 22 | # FileUtils.cp "config/database.yml.sample", "config/database.yml" 23 | # end 24 | 25 | puts "\n== Preparing database ==" 26 | system! "bin/rails db:prepare" 27 | 28 | puts "\n== Removing old logs and tempfiles ==" 29 | system! "bin/rails log:clear tmp:clear" 30 | 31 | puts "\n== Restarting application server ==" 32 | system! "bin/rails restart" 33 | end 34 | -------------------------------------------------------------------------------- /test/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative "config/environment" 4 | 5 | run Rails.application 6 | Rails.application.load_server 7 | -------------------------------------------------------------------------------- /test/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | 3 | require "sprockets/railtie" 4 | require "rails/all" 5 | 6 | # Require the gems listed in Gemfile, including any gems 7 | # you've limited to :test, :development, or :production. 8 | Bundler.require(*Rails.groups) 9 | require "rails_mvp_authentication" 10 | 11 | module Dummy 12 | class Application < Rails::Application 13 | config.load_defaults Rails::VERSION::STRING.to_f 14 | 15 | # For compatibility with applications that use this config 16 | config.action_controller.include_all_helpers = false 17 | 18 | # Configuration for the application, engines, and railties goes here. 19 | # 20 | # These settings can be overridden in specific environments using the files 21 | # in config/environments, which are processed later. 22 | # 23 | # config.time_zone = "Central Time (US & Canada)" 24 | # config.eager_load_paths << Rails.root.join("extras") 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__) 3 | 4 | require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) 5 | $LOAD_PATH.unshift File.expand_path("../../../lib", __dir__) 6 | -------------------------------------------------------------------------------- /test/dummy/config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: test 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: dummy_production 11 | -------------------------------------------------------------------------------- /test/dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite. Versions 3.8.0 and up are supported. 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem "sqlite3" 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /test/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative "application" 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /test/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # In the development environment your application's code is reloaded any time 7 | # it changes. This slows down response time but is perfect for development 8 | # since you don't have to restart the web server when you make code changes. 9 | config.cache_classes = false 10 | 11 | # Do not eager load code on boot. 12 | config.eager_load = false 13 | 14 | # Show full error reports. 15 | config.consider_all_requests_local = true 16 | 17 | # Enable server timing 18 | config.server_timing = true 19 | 20 | # Enable/disable caching. By default caching is disabled. 21 | # Run rails dev:cache to toggle caching. 22 | if Rails.root.join("tmp/caching-dev.txt").exist? 23 | config.action_controller.perform_caching = true 24 | config.action_controller.enable_fragment_cache_logging = true 25 | 26 | config.cache_store = :memory_store 27 | config.public_file_server.headers = { 28 | "Cache-Control" => "public, max-age=#{2.days.to_i}" 29 | } 30 | else 31 | config.action_controller.perform_caching = false 32 | 33 | config.cache_store = :null_store 34 | end 35 | 36 | # Store uploaded files on the local file system (see config/storage.yml for options). 37 | config.active_storage.service = :local 38 | 39 | # Don't care if the mailer can't send. 40 | config.action_mailer.raise_delivery_errors = false 41 | 42 | config.action_mailer.perform_caching = false 43 | 44 | # Print deprecation notices to the Rails logger. 45 | config.active_support.deprecation = :log 46 | 47 | # Raise exceptions for disallowed deprecations. 48 | config.active_support.disallowed_deprecation = :raise 49 | 50 | # Tell Active Support which deprecation messages to disallow. 51 | config.active_support.disallowed_deprecation_warnings = [] 52 | 53 | # Raise an error on page load if there are pending migrations. 54 | config.active_record.migration_error = :page_load 55 | 56 | # Highlight code that triggered database queries in logs. 57 | config.active_record.verbose_query_logs = true 58 | 59 | # Suppress logger output for asset requests. 60 | config.assets.quiet = true 61 | 62 | # Raises error for missing translations. 63 | # config.i18n.raise_on_missing_translations = true 64 | 65 | # Annotate rendered view with file names. 66 | # config.action_view.annotate_rendered_view_with_filenames = true 67 | 68 | # Uncomment if you wish to allow Action Cable access from any origin. 69 | # config.action_cable.disable_request_forgery_protection = true 70 | end 71 | -------------------------------------------------------------------------------- /test/dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # Code is not reloaded between requests. 7 | config.cache_classes = true 8 | 9 | # Eager load code on boot. This eager loads most of Rails and 10 | # your application in memory, allowing both threaded web servers 11 | # and those relying on copy on write to perform better. 12 | # Rake tasks automatically ignore this option for performance. 13 | config.eager_load = true 14 | 15 | # Full error reports are disabled and caching is turned on. 16 | config.consider_all_requests_local = false 17 | config.action_controller.perform_caching = true 18 | 19 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] 20 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). 21 | # config.require_master_key = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? 26 | 27 | # Compress CSS using a preprocessor. 28 | # config.assets.css_compressor = :sass 29 | 30 | # Do not fallback to assets pipeline if a precompiled asset is missed. 31 | config.assets.compile = false 32 | 33 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 34 | # config.asset_host = "http://assets.example.com" 35 | 36 | # Specifies the header that your server uses for sending files. 37 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache 38 | # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX 39 | 40 | # Store uploaded files on the local file system (see config/storage.yml for options). 41 | config.active_storage.service = :local 42 | 43 | # Mount Action Cable outside main process or domain. 44 | # config.action_cable.mount_path = nil 45 | # config.action_cable.url = "wss://example.com/cable" 46 | # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ] 47 | 48 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 49 | # config.force_ssl = true 50 | 51 | # Include generic and useful information about system operation, but avoid logging too much 52 | # information to avoid inadvertent exposure of personally identifiable information (PII). 53 | config.log_level = :info 54 | 55 | # Prepend all log lines with the following tags. 56 | config.log_tags = [:request_id] 57 | 58 | # Use a different cache store in production. 59 | # config.cache_store = :mem_cache_store 60 | 61 | # Use a real queuing backend for Active Job (and separate queues per environment). 62 | # config.active_job.queue_adapter = :resque 63 | # config.active_job.queue_name_prefix = "dummy_production" 64 | 65 | config.action_mailer.perform_caching = false 66 | 67 | # Ignore bad email addresses and do not raise email delivery errors. 68 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 69 | # config.action_mailer.raise_delivery_errors = false 70 | 71 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 72 | # the I18n.default_locale when a translation cannot be found). 73 | config.i18n.fallbacks = true 74 | 75 | # Don't log any deprecations. 76 | config.active_support.report_deprecations = false 77 | 78 | # Use default logging formatter so that PID and timestamp are not suppressed. 79 | config.log_formatter = ::Logger::Formatter.new 80 | 81 | # Use a different logger for distributed setups. 82 | # require "syslog/logger" 83 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name") 84 | 85 | if ENV["RAILS_LOG_TO_STDOUT"].present? 86 | logger = ActiveSupport::Logger.new(STDOUT) 87 | logger.formatter = config.log_formatter 88 | config.logger = ActiveSupport::TaggedLogging.new(logger) 89 | end 90 | 91 | # Do not dump schema after migrations. 92 | config.active_record.dump_schema_after_migration = false 93 | end 94 | -------------------------------------------------------------------------------- /test/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | # The test environment is used exclusively to run your application's 4 | # test suite. You never need to work with it otherwise. Remember that 5 | # your test database is "scratch space" for the test suite and is wiped 6 | # and recreated between test runs. Don't rely on the data there! 7 | 8 | Rails.application.configure do 9 | # Settings specified here will take precedence over those in config/application.rb. 10 | 11 | # Turn false under Spring and add config.action_view.cache_template_loading = true 12 | config.cache_classes = true 13 | 14 | # Eager loading loads your whole application. When running a single test locally, 15 | # this probably isn't necessary. It's a good idea to do in a continuous integration 16 | # system, or in some way before deploying your code. 17 | config.eager_load = ENV["CI"].present? 18 | 19 | # Configure public file server for tests with Cache-Control for performance. 20 | config.public_file_server.enabled = true 21 | config.public_file_server.headers = { 22 | "Cache-Control" => "public, max-age=#{1.hour.to_i}" 23 | } 24 | 25 | # Show full error reports and disable caching. 26 | config.consider_all_requests_local = true 27 | config.action_controller.perform_caching = false 28 | config.cache_store = :null_store 29 | 30 | # Raise exceptions instead of rendering exception templates. 31 | config.action_dispatch.show_exceptions = false 32 | 33 | # Disable request forgery protection in test environment. 34 | config.action_controller.allow_forgery_protection = false 35 | 36 | # Store uploaded files on the local file system in a temporary directory. 37 | config.active_storage.service = :test 38 | 39 | config.action_mailer.perform_caching = false 40 | 41 | # Tell Action Mailer not to deliver emails to the real world. 42 | # The :test delivery method accumulates sent emails in the 43 | # ActionMailer::Base.deliveries array. 44 | config.action_mailer.delivery_method = :test 45 | 46 | # Print deprecation notices to the stderr. 47 | config.active_support.deprecation = :stderr 48 | 49 | # Raise exceptions for disallowed deprecations. 50 | config.active_support.disallowed_deprecation = :raise 51 | 52 | # Tell Active Support which deprecation messages to disallow. 53 | config.active_support.disallowed_deprecation_warnings = [] 54 | 55 | # Raises error for missing translations. 56 | # config.i18n.raise_on_missing_translations = true 57 | 58 | # Annotate rendered view with file names. 59 | # config.action_view.annotate_rendered_view_with_filenames = true 60 | end 61 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = "1.0" 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in the app/assets 11 | # folder are already added. 12 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 13 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy 4 | # For further information see the following documentation 5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 6 | 7 | # Rails.application.configure do 8 | # config.content_security_policy do |policy| 9 | # policy.default_src :self, :https 10 | # policy.font_src :self, :https, :data 11 | # policy.img_src :self, :https, :data 12 | # policy.object_src :none 13 | # policy.script_src :self, :https 14 | # policy.style_src :self, :https 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | # 19 | # # Generate session nonces for permitted importmap and inline scripts 20 | # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } 21 | # config.content_security_policy_nonce_directives = %w(script-src) 22 | # 23 | # # Report CSP violations to a specified URI. See: 24 | # # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 25 | # # config.content_security_policy_report_only = true 26 | # end 27 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [ 5 | :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn 6 | ] 7 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, "\\1en" 8 | # inflect.singular /^(ox)en/i, "\\1" 9 | # inflect.irregular "person", "people" 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym "RESTful" 16 | # end 17 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/permissions_policy.rb: -------------------------------------------------------------------------------- 1 | # Define an application-wide HTTP permissions policy. For further 2 | # information see https://developers.google.com/web/updates/2018/06/feature-policy 3 | # 4 | # Rails.application.config.permissions_policy do |f| 5 | # f.camera :none 6 | # f.gyroscope :none 7 | # f.microphone :none 8 | # f.usb :none 9 | # f.fullscreen :self 10 | # f.payment :self, "https://secure.example.com" 11 | # end 12 | -------------------------------------------------------------------------------- /test/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t "hello" 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t("hello") %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # "true": "foo" 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at https://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /test/dummy/config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } 9 | threads min_threads_count, max_threads_count 10 | 11 | # Specifies the `worker_timeout` threshold that Puma will use to wait before 12 | # terminating a worker in development environments. 13 | # 14 | worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" 15 | 16 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 17 | # 18 | port ENV.fetch("PORT") { 3000 } 19 | 20 | # Specifies the `environment` that Puma will run in. 21 | # 22 | environment ENV.fetch("RAILS_ENV") { "development" } 23 | 24 | # Specifies the `pidfile` that Puma will use. 25 | pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } 26 | 27 | # Specifies the number of `workers` to boot in clustered mode. 28 | # Workers are forked web server processes. If using threads and workers together 29 | # the concurrency of the application would be max `threads` * `workers`. 30 | # Workers do not work on JRuby or Windows (both of which do not support 31 | # processes). 32 | # 33 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 34 | 35 | # Use the `preload_app!` method when specifying a `workers` number. 36 | # This directive tells Puma to first boot the application and load code 37 | # before forking the application. This takes advantage of Copy On Write 38 | # process behavior so workers use less memory. 39 | # 40 | # preload_app! 41 | 42 | # Allow puma to be restarted by `bin/rails restart` command. 43 | plugin :tmp_restart 44 | -------------------------------------------------------------------------------- /test/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | mount RailsMvpAuthentication::Engine => "/rails_mvp_authentication" 3 | end 4 | -------------------------------------------------------------------------------- /test/dummy/config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket-<%= Rails.env %> 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket-<%= Rails.env %> 23 | 24 | # Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name-<%= Rails.env %> 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /test/dummy/db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # This file is the source Rails uses to define your schema when running `bin/rails 6 | # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to 7 | # be faster and is potentially less error prone than running all of your 8 | # migrations from scratch. Old migrations may fail to apply correctly if those 9 | # migrations use external dependencies or application code. 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 0) do 14 | end 15 | -------------------------------------------------------------------------------- /test/dummy/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/test/dummy/lib/assets/.keep -------------------------------------------------------------------------------- /test/dummy/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/test/dummy/log/.keep -------------------------------------------------------------------------------- /test/dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /test/dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /test/dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /test/dummy/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/test/dummy/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /test/dummy/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/test/dummy/public/apple-touch-icon.png -------------------------------------------------------------------------------- /test/dummy/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/test/dummy/public/favicon.ico -------------------------------------------------------------------------------- /test/fixtures/files/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/test/fixtures/files/.keep -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/test/helpers/.keep -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/test/integration/.keep -------------------------------------------------------------------------------- /test/integration/navigation_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class NavigationTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/lib/generators/rails_mvp_authentication/install_generator_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | require "generators/rails_mvp_authentication/install_generator" 3 | 4 | class RailsMvpAuthentication::InstallGeneratorTest < Rails::Generators::TestCase 5 | tests ::RailsMvpAuthentication::Generators::InstallGenerator 6 | destination Rails.root 7 | 8 | setup :prepare_destination 9 | teardown :restore_destination 10 | 11 | test "creates migration for users table" do 12 | run_generator 13 | 14 | assert_migration "db/migrate/create_users.rb" do |migration| 15 | assert_match(/create_table :users do |t|/, migration) 16 | assert_match(/add_index :users, :email, unique: true/, migration) 17 | assert_match(/t.string :email, null: false/, migration) 18 | assert_match(/t.string :password_digest, null: false/, migration) 19 | end 20 | end 21 | 22 | test "creates migration for active sessions table" do 23 | run_generator 24 | 25 | assert_migration "db/migrate/create_active_sessions.rb" do |migration| 26 | assert_match(/create_table :active_sessions do |t|/, migration) 27 | assert_match(/t.references :user, null: false, foreign_key: true/, migration) 28 | assert_match(/t.string :user_agent/, migration) 29 | assert_match(/t.string :ip_address/, migration) 30 | assert_match(/t.string :remember_token, null: false/, migration) 31 | assert_match(/add_index :active_sessions, :remember_token, unique: true/, migration) 32 | end 33 | end 34 | 35 | test "create user model" do 36 | run_generator 37 | 38 | assert_file "app/models/user.rb" 39 | end 40 | 41 | test "create active session model" do 42 | run_generator 43 | 44 | assert_file "app/models/active_session.rb" 45 | end 46 | 47 | test "does not error if there is no Gemfile" do 48 | assert_nothing_raised do 49 | run_generator 50 | end 51 | end 52 | 53 | test "adds bcrypt to Gemfile" do 54 | FileUtils.touch Rails.root.join("Gemfile") 55 | 56 | run_generator 57 | 58 | assert_file "Gemfile", /gem "bcrypt", "~> 3.1.7"/ 59 | end 60 | 61 | test "uncomments bcrypt from Gemfile" do 62 | File.atomic_write(Rails.root.join("Gemfile")) do |file| 63 | file.write('# gem "bcrypt", "~> 3.1.7"') 64 | end 65 | 66 | run_generator 67 | 68 | assert_file "Gemfile", /gem "bcrypt", "~> 3.1.7"/ 69 | end 70 | 71 | test "should add routes" do 72 | run_generator 73 | 74 | assert_file "config/routes.rb" do |file| 75 | assert_match(/post "sign_up", to: "users#create"/, file) 76 | assert_match(/get "sign_up", to: "users#new"/, file) 77 | assert_match(/put "account", to: "users#update"/, file) 78 | assert_match(/get "account", to: "users#edit"/, file) 79 | assert_match(/delete "account", to: "users#destroy"/, file) 80 | assert_match(/resources :confirmations, only: \[:create, :edit, :new\], param: :confirmation_token/, file) 81 | assert_match(/post "login", to: "sessions#create"/, file) 82 | assert_match(/delete "logout", to: "sessions#destroy"/, file) 83 | assert_match(/get "login", to: "sessions#new"/, file) 84 | assert_match(/resources :passwords, only: \[:create, :edit, :new, :update\], param: :password_reset_token/, file) 85 | assert_match(/resources :active_sessions, only: \[:destroy\] do/, file) 86 | assert_match(/delete "destroy_all"/, file) 87 | end 88 | end 89 | 90 | test "should add current model" do 91 | run_generator 92 | 93 | assert_file "app/models/current.rb" 94 | end 95 | 96 | test "should create users controller" do 97 | run_generator 98 | 99 | assert_file "app/controllers/users_controller.rb" 100 | end 101 | 102 | test "should create user views" do 103 | run_generator 104 | 105 | assert_file "app/views/users/edit.html.erb" 106 | assert_file "app/views/users/new.html.erb" 107 | end 108 | 109 | test "should create confirmations controller" do 110 | run_generator 111 | 112 | assert_file "app/controllers/confirmations_controller.rb" 113 | end 114 | 115 | test "should create confirmation views" do 116 | run_generator 117 | 118 | assert_file "app/views/confirmations/new.html.erb" 119 | end 120 | 121 | test "should create user mailer" do 122 | run_generator 123 | 124 | assert_file "app/mailers/user_mailer.rb" 125 | end 126 | 127 | test "should create user mailer views" do 128 | run_generator 129 | 130 | assert_file "app/views/user_mailer/confirmation.html.erb" 131 | assert_file "app/views/user_mailer/confirmation.text.erb" 132 | assert_file "app/views/user_mailer/password_reset.html.erb" 133 | assert_file "app/views/user_mailer/confirmation.text.erb" 134 | end 135 | 136 | test "should configure hosts" do 137 | run_generator 138 | 139 | assert_file "config/environments/test.rb" do |file| 140 | assert_match(/config.action_mailer.default_url_options = {host: "example.com"}/, file) 141 | end 142 | assert_file "config/environments/development.rb" do |file| 143 | assert_match(/config.action_mailer.default_url_options = {host: "localhost", port: 3000}/, file) 144 | end 145 | end 146 | 147 | test "should create authentication concern" do 148 | run_generator 149 | 150 | assert_file "app/controllers/concerns/authentication.rb" 151 | assert_file "app/controllers/application_controller.rb" do |file| 152 | assert_match(/include Authentication/, file) 153 | end 154 | end 155 | 156 | test "should create sessions controller" do 157 | run_generator 158 | 159 | assert_file "app/controllers/sessions_controller.rb" 160 | end 161 | 162 | test "should create session views" do 163 | run_generator 164 | 165 | assert_file "app/views/sessions/new.html.erb" 166 | end 167 | 168 | test "should create passwords controller" do 169 | run_generator 170 | 171 | assert_file "app/controllers/passwords_controller.rb" 172 | end 173 | 174 | test "should create password views" do 175 | run_generator 176 | 177 | assert_file "app/views/passwords/new.html.erb" 178 | assert_file "app/views/passwords/edit.html.erb" 179 | end 180 | 181 | test "should create tests if using default test suite" do 182 | FileUtils.mkdir_p(Rails.root.join("test")) 183 | 184 | run_generator 185 | 186 | assert_file "test/controllers/active_sessions_controller_test.rb" 187 | assert_file "test/controllers/confirmations_controller_test.rb" 188 | assert_file "test/controllers/passwords_controller_test.rb" 189 | assert_file "test/controllers/sessions_controller_test.rb" 190 | assert_file "test/controllers/users_controller_test.rb" 191 | assert_file "test/integration/friendly_redirects_test.rb" 192 | assert_file "test/integration/user_interface_test.rb" 193 | assert_file "test/mailers/previews/user_mailer_preview.rb" 194 | assert_file "test/mailers/user_mailer_test.rb" 195 | assert_file "test/models/active_session_test.rb" 196 | assert_file "test/models/user_test.rb" 197 | assert_file "test/system/logins_test.rb" 198 | end 199 | 200 | test "should modify test helper if using default test suite" do 201 | FileUtils.mkdir_p(Rails.root.join("test")) 202 | FileUtils.touch Rails.root.join("test/test_helper.rb") 203 | File.atomic_write(Rails.root.join("test/test_helper.rb")) do |file| 204 | file.write("class ActiveSupport::TestCase\nend") 205 | end 206 | 207 | run_generator 208 | 209 | assert_file "test/test_helper.rb" do |file| 210 | assert_match(/current_user/, file) 211 | assert_match(/login/, file) 212 | assert_match(/logout/, file) 213 | end 214 | end 215 | 216 | test "should not create tests if not using default test suite" do 217 | remove_if_exists("test") 218 | 219 | run_generator 220 | 221 | assert_no_file "test/controllers/active_sessions_controller_test.rb" 222 | assert_no_file "test/controllers/confirmations_controller_test.rb" 223 | assert_no_file "test/controllers/passwords_controller_test.rb" 224 | assert_no_file "test/controllers/sessions_controller_test.rb" 225 | assert_no_file "test/controllers/users_controller_test.rb" 226 | assert_no_file "test/integration/friendly_redirects_test.rb" 227 | assert_no_file "test/integration/user_interface_test.rb" 228 | assert_no_file "test/mailers/previews/user_mailer_preview.rb" 229 | assert_no_file "test/mailers/user_mailer_test.rb" 230 | assert_no_file "test/models/active_session_test.rb" 231 | assert_no_file "test/models/user_test.rb" 232 | assert_no_file "test/system/logins_test.rb" 233 | end 234 | 235 | test "should not modify test helper if not using default test suite" do 236 | remove_if_exists("test") 237 | 238 | run_generator 239 | 240 | assert_no_file "test/test_helper.rb" 241 | end 242 | 243 | test "should add links" do 244 | run_generator 245 | 246 | assert_file "app/views/layouts/application.html.erb" do |file| 247 | assert_match(/account_path/, file) 248 | assert_match(/logout_path/, file) 249 | assert_match(/login_path/, file) 250 | assert_match(/sign_up_path/, file) 251 | assert_match(/new_password_path/, file) 252 | assert_match(/new_confirmation_path/, file) 253 | end 254 | end 255 | 256 | test "should create active sessions controller" do 257 | run_generator 258 | 259 | assert_file "app/controllers/active_sessions_controller.rb" 260 | end 261 | 262 | private 263 | 264 | def backup_file(path) 265 | copy_file Rails.root.join(path), Rails.root.join("#{path}.bak") 266 | end 267 | 268 | def prepare_destination 269 | backup_file("config/routes.rb") 270 | backup_file("config/environments/test.rb") 271 | backup_file("config/environments/development.rb") 272 | backup_file("app/controllers/application_controller.rb") 273 | backup_file("app/views/layouts/application.html.erb") 274 | end 275 | 276 | def remove_if_exists(path) 277 | full_path = Rails.root.join(path) 278 | FileUtils.rm_rf(full_path) 279 | end 280 | 281 | def restore_destination 282 | remove_if_exists("db/migrate") 283 | remove_if_exists("app/models/current.rb") 284 | remove_if_exists("app/models/active_session.rb") 285 | remove_if_exists("app/models/user.rb") 286 | remove_if_exists("app/controllers/confirmations_controller.rb") 287 | remove_if_exists("app/controllers/users_controller.rb") 288 | remove_if_exists("app/views/confirmations") 289 | remove_if_exists("app/views/users") 290 | remove_if_exists("app/mailers/user_mailer.rb") 291 | remove_if_exists("app/views/user_mailer") 292 | remove_if_exists("Gemfile") 293 | remove_if_exists("app/controllers/concerns/authentication.rb") 294 | remove_if_exists("app/controllers/sessions_controller.rb") 295 | remove_if_exists("app/views/sessions/new.html.erb") 296 | remove_if_exists("app/controllers/passwords_controller.rb") 297 | remove_if_exists("app/views/passwords/new.html.erb") 298 | remove_if_exists("app/views/passwords/edit.html.erb") 299 | remove_if_exists("app/controllers/active_sessions_controller.rb") 300 | remove_if_exists("test") 301 | restore_file("config/routes.rb") 302 | restore_file("config/environments/test.rb") 303 | restore_file("config/environments/development.rb") 304 | restore_file("app/controllers/application_controller.rb") 305 | restore_file("app/views/layouts/application.html.erb") 306 | end 307 | 308 | def restore_file(path) 309 | File.delete(Rails.root.join(path)) 310 | copy_file Rails.root.join("#{path}.bak"), Rails.root.join(path) 311 | File.delete(Rails.root.join("#{path}.bak")) 312 | end 313 | end 314 | -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/test/mailers/.keep -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevepolitodesign/rails_mvp_authentication/a2a8cfe220b7b6eeeba918b23bbc12909dc2b494/test/models/.keep -------------------------------------------------------------------------------- /test/rails_mvp_authentication_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class RailsMvpAuthenticationTest < ActiveSupport::TestCase 4 | test "it has a version number" do 5 | assert RailsMvpAuthentication::VERSION 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # Configure Rails Environment 2 | ENV["RAILS_ENV"] = "test" 3 | 4 | require_relative "../test/dummy/config/environment" 5 | ActiveRecord::Migrator.migrations_paths = [File.expand_path("../test/dummy/db/migrate", __dir__)] 6 | ActiveRecord::Migrator.migrations_paths << File.expand_path("../db/migrate", __dir__) 7 | require "rails/test_help" 8 | 9 | # Load fixtures from the engine 10 | if ActiveSupport::TestCase.respond_to?(:fixture_path=) 11 | ActiveSupport::TestCase.fixture_path = File.expand_path("fixtures", __dir__) 12 | ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path 13 | ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files" 14 | ActiveSupport::TestCase.fixtures :all 15 | end 16 | --------------------------------------------------------------------------------