diff --git a/Gemfile b/Gemfile index 09b10c97f..b681721c0 100644 --- a/Gemfile +++ b/Gemfile @@ -26,6 +26,7 @@ gem 'marcel' gem 'sidekiq-unique-jobs' gem 'redis' gem 'request_store' +gem "zxcvbn-ruby", require: "zxcvbn" gem "diffy" gem "rugged" diff --git a/Gemfile.lock b/Gemfile.lock index d924493e8..17e4506a4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -376,6 +376,7 @@ GEM websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) zeitwerk (2.6.13) + zxcvbn-ruby (1.2.0) PLATFORMS ruby @@ -426,6 +427,7 @@ DEPENDENCIES streamio-ffmpeg webmock webpacker (>= 4.0.x) + zxcvbn-ruby BUNDLED WITH 2.4.10 diff --git a/README.md b/README.md index 04d410097..8af7f592d 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ docker compose up ``` After running the commands once only `docker compose up` is needed to bring up the containers. -1. To confirm the installation worked, open the web browser of your choice and enter `http://localhost:3000` into the address bar and see if the website loads correctly. An admin account has been created automatically, the username and password are `admin` and `qwerty` respectively. +1. To confirm the installation worked, open the web browser of your choice and enter `http://localhost:3000` into the address bar and see if the website loads correctly. An admin account has been created automatically, the username and password are `admin` and `hexerade` respectively. 1. By default, the site will lack any content. For testing purposes, you can generate some using the following command: ``` docker exec -it e621ng-e621-1 /app/bin/populate diff --git a/app/javascript/src/javascripts/password.js b/app/javascript/src/javascripts/password.js new file mode 100644 index 000000000..158ee56a8 --- /dev/null +++ b/app/javascript/src/javascripts/password.js @@ -0,0 +1,70 @@ +import zxcvbn from "zxcvbn"; +import Page from "./utility/page"; + +let Password = {}; + +Password.init_validation = function () { + if (Page.matches("users", "new") || Page.matches("users", "create")) + Password.bootstrap_input($("#user_password"), [$("#user_name"), $("#user_email")]); + + if (Page.matches("maintenance-user-password-resets", "edit")) + Password.bootstrap_input($("#password")); + + if (Page.matches("maintenance-user-passwords", "edit")) + Password.bootstrap_input($("#user_password")); +}; + +Password.bootstrap_input = function ($password, $inputs = []) { + // Set up the UI + $password.parent().addClass("password-input"); + + const hint = $("
") + .addClass("password-feedback") + .insertAfter($password); + const display = $("
") + .addClass("password-strength") + .insertAfter($password); + const progress = $("
") + .addClass("password-progress") + .css("width", 0) + .appendTo(display); + + // Listen to secondary input changes + let extraData = getExtraData(); + for (const one of $inputs) + one.on("input", () => { + extraData = getExtraData(); + }); + + // Listen to main input changes + $password.on("input", () => { + const analysis = zxcvbn($password.val() + "", extraData); + + progress.css("width", ((analysis.score * 25) + 10) + "%"); + hint.html(""); + if (analysis.feedback.warning) + $("") + .text(analysis.feedback.warning) + .addClass("password-warning") + .appendTo(hint); + for (const one of analysis.feedback.suggestions) + $("") + .text(one) + .appendTo(hint); + }); + + function getExtraData () { + const output = []; + for (const one of $inputs) { + const val = one.val() + ""; + if (val) output.push(one.val() + ""); + } + return output; + } +}; + +$(() => { + Password.init_validation(); +}); + +export default Password; diff --git a/app/javascript/src/styles/base.scss b/app/javascript/src/styles/base.scss index e8b01f5e7..0b92714a2 100644 --- a/app/javascript/src/styles/base.scss +++ b/app/javascript/src/styles/base.scss @@ -54,7 +54,6 @@ @import "specific/iqdb_queries.scss"; @import "specific/keyboard_shortcuts.scss"; @import "specific/lockdown.scss"; -@import "specific/maintenance.scss"; @import "specific/meta_searches.scss"; @import "specific/moderator_dashboard.scss"; @import "specific/news_updates.scss"; diff --git a/app/javascript/src/styles/specific/maintenance.scss b/app/javascript/src/styles/specific/maintenance.scss deleted file mode 100644 index 300f58e97..000000000 --- a/app/javascript/src/styles/specific/maintenance.scss +++ /dev/null @@ -1,5 +0,0 @@ -div#c-maintenance-user-login-reminders { - div#a-new { - width: 50em; - } -} diff --git a/app/javascript/src/styles/specific/users.scss b/app/javascript/src/styles/specific/users.scss index 807ac467b..22af7125b 100644 --- a/app/javascript/src/styles/specific/users.scss +++ b/app/javascript/src/styles/specific/users.scss @@ -223,13 +223,83 @@ div#c-users { color: themed("color-link-active"); } } +} - div#a-new { - max-width: 60em; +// User signup and login +#c-users #a-new, +#c-sessions #a-new, +#c-maintenance-user-password-resets #a-new, +#c-maintenance-user-login-reminders #a-new { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(auto, 360px)); + gap: 1em; - p { - font-size: 1.2em; - line-height: 1.4em; + margin-bottom: 1em; +} + +.session_form { + box-sizing: border-box; + max-width: 360px; + margin: 0; + + h1 { + margin-bottom: 0.5em; + text-align: center; + } + + div.input { + input[type="text"], input[type="email"], input[type="password"], select { + width: 100%; + max-width: unset; + box-sizing: border-box; } } } + +.session_info { + display: flex; + flex-flow: column; + justify-content: center; + box-sizing: border-box; + max-width: 360px; + padding: 0.5rem; + border-radius: 3px; + background-color: themed("color-section"); + + h3 { margin-bottom: 1em; } +} + +// Password validation +.password-input { + input[type="password"] { + border-radius: 3px 3px 0 0; + } + + .password-strength { + width: 100%; + height: 0.25rem; + border-radius: 0 0 3px 3px; + + background: white; + overflow: hidden; + margin: 0; + + .password-progress { + background: linear-gradient(to right, palette("text-red") 0%, palette("text-yellow") 25%, palette("text-green") 100%); + background-size: 360px 100%; + + height: 100%; + transition: width 1s ease-in-out; + } + } + + .password-feedback { + display: flex; + flex-flow: column; + padding-left: 1em; + margin-top: 0.5em; + + span { display: list-item; } + .password-warning { font-weight: bold; } + } +} \ No newline at end of file diff --git a/app/models/user.rb b/app/models/user.rb index 5c0956c23..b03d0c410 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "zxcvbn" + class User < ApplicationRecord class Error < Exception ; end class PrivilegeError < Exception @@ -71,7 +73,8 @@ class User < ApplicationRecord validates :per_page, inclusion: { :in => 1..320 } validates :comment_threshold, presence: true validates :comment_threshold, numericality: { only_integer: true, less_than: 50_000, greater_than: -50_000 } - validates :password, length: { :minimum => 6, :if => ->(rec) { rec.new_record? || rec.password.present? || rec.old_password.present? } } + validates :password, length: { minimum: 8, if: ->(rec) { rec.new_record? || rec.password.present? || rec.old_password.present? } } + validate :password_is_secure, if: ->(rec) { rec.new_record? || rec.password.present? || rec.old_password.present? } validates :password, confirmation: true validates :password_confirmation, presence: { if: ->(rec) { rec.new_record? || rec.old_password.present? } } validate :validate_ip_addr_is_not_banned, :on => :create @@ -227,6 +230,16 @@ class User < ApplicationRecord def upgrade_password(pass) self.update_columns(password_hash: '', bcrypt_password_hash: User.bcrypt(pass)) end + + def password_is_secure + analysis = Zxcvbn.test(password, [name, email]) + return unless analysis.score < 2 + if analysis.feedback.warning + errors.add(:password, "is insecure: #{analysis.feedback.warning}") + else + errors.add(:password, "is insecure") + end + end end module AuthenticationMethods diff --git a/app/views/maintenance/user/login_reminders/new.html.erb b/app/views/maintenance/user/login_reminders/new.html.erb index d22249427..a5924a768 100644 --- a/app/views/maintenance/user/login_reminders/new.html.erb +++ b/app/views/maintenance/user/login_reminders/new.html.erb @@ -1,21 +1,21 @@ -
-
+
+ + <%= form_tag(maintenance_user_login_reminder_path, :class => "simple_form session_form") do %>

Login Reminder

+ + + <%= submit_tag "Submit" %> + <% end %> + +

If you supplied an email address when signing up, <%= Danbooru.config.app_name %> can email you your login information. Password details will not be provided and will not be changed.

-

If you didn't supply a valid email address, you are out of luck.

- - <%= form_tag(maintenance_user_login_reminder_path, :class => "simple_form") do %> - - - <%= submit_tag "Submit" %> - <% end %> -
-
+ +
<%= render "sessions/secondary_links" %> diff --git a/app/views/maintenance/user/password_resets/edit.html.erb b/app/views/maintenance/user/password_resets/edit.html.erb index 4b3940f58..7916dc7fa 100644 --- a/app/views/maintenance/user/password_resets/edit.html.erb +++ b/app/views/maintenance/user/password_resets/edit.html.erb @@ -1,26 +1,28 @@ -
-
-

Reset Password

+
- <% if @nonce %> - <%= form_tag(maintenance_user_password_reset_path, :method => :put) do %> - <%= hidden_field_tag :uid, params[:uid] %> - <%= hidden_field_tag :key, params[:key] %> -
- - <%= password_field_tag :password %> -
-
- - <%= password_field_tag :password_confirm %> -
- <%= submit_tag "Reset" %> - <% end %> - <% else %> -

Invalid reset

+ <% if @nonce %> + <%= form_tag(maintenance_user_password_reset_path, :method => :put, :class => "simple_form session_form") do %> +

Reset Password

+ + <%= hidden_field_tag :uid, params[:uid] %> + <%= hidden_field_tag :key, params[:key] %> + +
+ + <%= password_field_tag :password %> +
+ +
+ + <%= password_field_tag :password_confirm %> +
+ + <%= submit_tag "Reset" %> <% end %> -
-
+ <% else %> +

Invalid reset

+ <% end %> +
<%= render "sessions/secondary_links" %> diff --git a/app/views/maintenance/user/password_resets/new.html.erb b/app/views/maintenance/user/password_resets/new.html.erb index 16e754347..821e70c21 100644 --- a/app/views/maintenance/user/password_resets/new.html.erb +++ b/app/views/maintenance/user/password_resets/new.html.erb @@ -1,20 +1,19 @@ -
-
+
+ + <%= form_tag(maintenance_user_password_reset_path, :class => "simple_form session_form") do %>

Reset Password

- + + + <%= submit_tag "Submit" %> + <% end %> +

If you supplied an email address when signing up, <%= Danbooru.config.app_name %> can reset your password. You will receive an email confirming your request for a new password.

-

If you didn't supply a valid email address, there is no way to recover your account.

- - <%= form_tag(maintenance_user_password_reset_path, :class => "simple_form") do %> - - <%= submit_tag "Submit" %> - <% end %> -
-
+ +
<%= render "sessions/secondary_links" %> diff --git a/app/views/maintenance/user/passwords/edit.html.erb b/app/views/maintenance/user/passwords/edit.html.erb index 5352fe654..f212fc900 100644 --- a/app/views/maintenance/user/passwords/edit.html.erb +++ b/app/views/maintenance/user/passwords/edit.html.erb @@ -1,15 +1,12 @@ -
-
+
+ <%= custom_form_for(@user, html: { class: "session_form" }) do |f| %>

Change Password

- - <%= custom_form_for @user do |f| %> - <%= f.input :old_password, :as => :password, :input_html => {:autocomplete => "off"} %> - <%= f.input :password, :label => "New password", :input_html => {:autocomplete => "off"} %> - <%= f.input :password_confirmation %> - <%= f.button :submit, "Submit" %> - <% end %> -
-
+ <%= f.input :old_password, :as => :password, :input_html => {:autocomplete => "off"} %> + <%= f.input :password, :label => "New password", :input_html => {:autocomplete => "off"} %> + <%= f.input :password_confirmation %> + <%= f.button :submit, "Submit" %> + <% end %> +
<% content_for(:page_title) do %> Change Password diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb index fa43c68a3..e7698cb13 100644 --- a/app/views/sessions/new.html.erb +++ b/app/views/sessions/new.html.erb @@ -1,38 +1,40 @@ -
-
-
-

Sign in

- <%= form_tag(session_path, :class => "simple_form") do %> - <%= hidden_field_tag "url", params[:url] %> +
+ <%= form_tag(session_path, :class => "simple_form session_form") do %> +

Sign in

-
- - <%= text_field_tag :name %> -
+ <%= hidden_field_tag "url", params[:url] %> -
- - <%= password_field_tag :password %> -
+
+ + <%= text_field_tag :name %> +
-
- <%= check_box_tag :remember, "1", true %> - -
+
+ + <%= password_field_tag :password %> +
-
- <%= submit_tag "Submit", :data => { :disable_with => "Signing in..." } %> -
- <% end %> -
+
+ <%= check_box_tag :remember, "1", true %> + +
-
-

<%= link_to "Don't have an account? Sign up here.", new_user_path() %>

-

<%= link_to "Reset Password", new_maintenance_user_password_reset_path %>

-

<%= link_to "Login Reminder", new_maintenance_user_login_reminder_path %>

-
-
-
+
+ <%= submit_tag "Submit", :data => { :disable_with => "Signing in..." } %> +
+ <% end %> + +
+

+ Don't have an account?
+ <%= link_to "Sign up here.", new_user_path() %> +

+
+ +

<%= link_to "Reset Password", new_maintenance_user_password_reset_path %>

+

<%= link_to "Login Reminder", new_maintenance_user_login_reminder_path %>

+
+
<% content_for(:page_title) do %> Sign in diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb index 3bf422fef..e2ec32e1e 100644 --- a/app/views/users/new.html.erb +++ b/app/views/users/new.html.erb @@ -1,34 +1,30 @@ -
-
+
+ <%= custom_form_for(@user, html: { id: "signup-form", class: "session_form" }) do |f| %>

Sign Up

-

An account is free and lets you keep favorites, upload artwork, and write comments.

+ <%= f.input :name, label: "Username", as: :string %> + <%= f.input :email, :required => true, as: :email %> + <%= f.input :password %> + <%= f.input :password_confirmation %> -
-

Make sure to read the site rules before continuing.

-

You must confirm your email address, so use something you can receive email with.

-

This site is open to web crawlers so whatever name you choose will be public!

-

This includes favorites, uploads, and comments. Almost everything is public. So don't choose a name you don't want to be associated with.

-

Accounts are prefilled with the same blacklist as guests have. You can access your blacklist in your account settings.

-
+ <%= f.input :time_zone, include_blank: false %> -
- <%= custom_form_for(@user, html: {id: "signup-form"}) do |f| %> - <%= f.input :name, :as => :string %> - <%= f.input :email, :required => true, :as => :email %> - <%= f.input :password %> - <%= f.input :password_confirmation %> - - <%= f.input :time_zone, :include_blank => false %> - - <% if Danbooru.config.enable_recaptcha? %> - <%= recaptcha_tags theme: 'dark', nonce: content_security_policy_nonce %> - <% end %> - <%= f.submit "Sign up", :data => { :disable_with => "Signing up..." } %> - <% end %> -
+ <% if Danbooru.config.enable_recaptcha? %> + <%= recaptcha_tags theme: "dark", nonce: content_security_policy_nonce %> + <% end %> + <%= f.submit "Sign up", :data => { :disable_with => "Signing up..." } %> + <% end %> +
+

+ Already have an account? <%= link_to "Sign In.", new_session_path %> +

+

Please, read the site rules before making an account.

+

You must confirm your email address, so you should only use one that you have access to.

+

This site is open to web crawlers, meaning that almost everything is public.

+

This includes your account name, favorites, uploads, and comments. Do not choose a name you don't want to be associated with.

+

Accounts have the same blacklist as guests by default. You will be able to modify your blacklist in the account settings.

-
+
<%= render "secondary_links" %> diff --git a/db/populate.rb b/db/populate.rb index bbeb5aadb..9af5e2cac 100644 --- a/db/populate.rb +++ b/db/populate.rb @@ -37,7 +37,7 @@ POSTVOTES = presets[:postvotes] POOLS = presets[:pools] DISTRIBUTION = ENV.fetch("DISTRIBUTION", 10).to_i -DEFAULT_PASSWORD = ENV.fetch("PASSWORD", "qwerty") +DEFAULT_PASSWORD = ENV.fetch("PASSWORD", "hexerade") CurrentUser.user = User.system diff --git a/db/seeds.rb b/db/seeds.rb index fa4c38a26..4587c56f9 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -9,8 +9,8 @@ require "tempfile" admin = User.find_or_create_by!(name: "admin") do |user| user.created_at = 2.weeks.ago - user.password = "qwerty" - user.password_confirmation = "qwerty" + user.password = "hexerade" + user.password_confirmation = "hexerade" user.password_hash = "" user.email = "admin@e621.local" user.can_upload_free = true diff --git a/package.json b/package.json index 02d96d044..bf3746417 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "vue": "^3.1.0", "vue-loader": "^17.4.2", "webpack": "^5.51.1", - "zingtouch": "^1.0.6" + "zingtouch": "^1.0.6", + "zxcvbn": "^4.4.2" }, "version": "0.1.0", "devDependencies": { diff --git a/test/factories/user.rb b/test/factories/user.rb index 4190f3bb3..54fb75424 100644 --- a/test/factories/user.rb +++ b/test/factories/user.rb @@ -5,14 +5,14 @@ FactoryBot.define do sequence :name do |n| "user#{n}" end - password { "password" } - password_confirmation { "password" } + password { "6cQE!wbA" } + password_confirmation { "6cQE!wbA" } sequence(:email) { |n| "user_email_#{n}@example.com" } default_image_size { "large" } base_upload_limit { 10 } level { 20 } - created_at {Time.now} - last_logged_in_at {Time.now} + created_at { Time.now } + last_logged_in_at { Time.now } factory(:banned_user) do transient { ban_duration { 3 } } diff --git a/test/functional/application_controller_test.rb b/test/functional/application_controller_test.rb index 6606f3680..1e92e0701 100644 --- a/test/functional/application_controller_test.rb +++ b/test/functional/application_controller_test.rb @@ -36,7 +36,7 @@ class ApplicationControllerTest < ActionDispatch::IntegrationTest context "on api authentication" do setup do - @user = create(:user, password: "password") + @user = create(:user, password: "6cQE!wbA") @api_key = ApiKey.generate!(@user) ActionController::Base.allow_forgery_protection = true @@ -108,7 +108,7 @@ class ApplicationControllerTest < ActionDispatch::IntegrationTest token = css_select("form input[name=authenticity_token]").first["value"] # login - post session_path, params: { authenticity_token: token, name: @user.name, password: "password" } + post session_path, params: { authenticity_token: token, name: @user.name, password: "6cQE!wbA" } assert_redirected_to posts_path # try to submit a form with cookies but without the csrf token @@ -122,9 +122,9 @@ class ApplicationControllerTest < ActionDispatch::IntegrationTest context "on session cookie authentication" do should "succeed" do - user = create(:user, password: "password") + user = create(:user, password: "6cQE!wbA") - post session_path, params: { name: user.name, password: "password" } + post session_path, params: { name: user.name, password: "6cQE!wbA" } get edit_user_path(user) assert_response :success diff --git a/test/functional/maintenance/user/api_keys_controller_test.rb b/test/functional/maintenance/user/api_keys_controller_test.rb index 61d1bdf0d..22b03bb40 100644 --- a/test/functional/maintenance/user/api_keys_controller_test.rb +++ b/test/functional/maintenance/user/api_keys_controller_test.rb @@ -7,7 +7,7 @@ module Maintenance class ApiKeysControllerTest < ActionDispatch::IntegrationTest context "An api keys controller" do setup do - @user = create(:privileged_user, :password => "password") + @user = create(:privileged_user, password: "6cQE!wbA") ApiKey.generate!(@user) end @@ -21,7 +21,7 @@ module Maintenance context "#view" do context "with a correct password" do should "succeed" do - post_auth view_maintenance_user_api_key_path(user_id: @user.id), @user, params: {user: {password: "password"}} + post_auth view_maintenance_user_api_key_path(user_id: @user.id), @user, params: { user: { password: "6cQE!wbA" } } assert_response :success end @@ -37,7 +37,7 @@ module Maintenance # ApiKey.expects(:generate!) # assert_difference("ApiKey.count", 1) do - # post view_maintenance_user_api_key_path(user_id: @user.id), params: {user: {password: "password"}} + # post view_maintenance_user_api_key_path(user_id: @user.id), params: { user: { password: "6cQE!wbA" } } # end # assert_not_nil(@user.reload.api_key) @@ -46,7 +46,7 @@ module Maintenance should "not generate another API key if the user already has one" do assert_difference("ApiKey.count", 0) do - post_auth view_maintenance_user_api_key_path(user_id: @user.id), @user, params: {user: {password: "password"}} + post_auth view_maintenance_user_api_key_path(user_id: @user.id), @user, params: { user: { password: "6cQE!wbA" } } end end end @@ -55,14 +55,14 @@ module Maintenance context "#update" do should "regenerate the API key" do old_key = @user.api_key - put_auth maintenance_user_api_key_path, @user, params: {user_id: @user.id, user: {password: "password"}} + put_auth maintenance_user_api_key_path, @user, params: {user_id: @user.id, user: { password: "6cQE!wbA" } } assert_not_equal(old_key.key, @user.reload.api_key.key) end end context "#destroy" do should "delete the API key" do - delete_auth maintenance_user_api_key_path, @user, params: {user_id: @user.id, user: {password: "password"}} + delete_auth maintenance_user_api_key_path, @user, params: {user_id: @user.id, user: { password: "6cQE!wbA" } } assert_nil(@user.reload.api_key) end end diff --git a/test/functional/maintenance/user/deletions_controller_test.rb b/test/functional/maintenance/user/deletions_controller_test.rb index d9632f46f..cf9731ead 100644 --- a/test/functional/maintenance/user/deletions_controller_test.rb +++ b/test/functional/maintenance/user/deletions_controller_test.rb @@ -19,7 +19,7 @@ module Maintenance context "#destroy" do should "render" do - delete_auth maintenance_user_deletion_path, @user, params: { password: "password" } + delete_auth maintenance_user_deletion_path, @user, params: { password: "6cQE!wbA" } assert_redirected_to(posts_path) end end diff --git a/test/functional/maintenance/user/email_changes_controller_test.rb b/test/functional/maintenance/user/email_changes_controller_test.rb index be5a1ff3b..c0e48573c 100644 --- a/test/functional/maintenance/user/email_changes_controller_test.rb +++ b/test/functional/maintenance/user/email_changes_controller_test.rb @@ -21,7 +21,7 @@ module Maintenance context "#create" do context "with the correct password" do should "work" do - post_auth maintenance_user_email_change_path, @user, params: { email_change: { password: "password", email: "abc@ogres.net" } } + post_auth maintenance_user_email_change_path, @user, params: { email_change: { password: "6cQE!wbA", email: "abc@ogres.net" } } assert_redirected_to(home_users_path) @user.reload assert_equal("abc@ogres.net", @user.email) @@ -37,7 +37,7 @@ module Maintenance end should "not work with an invalid email" do - post_auth maintenance_user_email_change_path, @user, params: { email_change: { password: "password", email: "" } } + post_auth maintenance_user_email_change_path, @user, params: { email_change: { password: "6cQE!wbA", email: "" } } @user.reload assert_not_equal("", @user.email) assert_match(/Email can't be blank/, flash[:notice]) @@ -45,7 +45,7 @@ module Maintenance should "work with a valid email when the users current email is invalid" do @user = create(:user, email: "") - post_auth maintenance_user_email_change_path, @user, params: { email_change: { password: "password", email: "abc@ogres.net" } } + post_auth maintenance_user_email_change_path, @user, params: { email_change: { password: "6cQE!wbA", email: "abc@ogres.net" } } @user.reload assert_equal("abc@ogres.net", @user.email) end diff --git a/test/functional/sessions_controller_test.rb b/test/functional/sessions_controller_test.rb index 249a128c9..66f016a92 100644 --- a/test/functional/sessions_controller_test.rb +++ b/test/functional/sessions_controller_test.rb @@ -15,7 +15,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest should "create a new session" do user = create(:user) - post session_path, params: { name: user.name, password: "password" } + post session_path, params: { name: user.name, password: "6cQE!wbA" } user.reload assert_redirected_to(posts_path) @@ -33,7 +33,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest end should "fail when provided an invalid password" do - user = create(:user, password: "xxxxxx", password_confirmation: "xxxxxx") + user = create(:user, password: "6cQE!wbA", password_confirmation: "6cQE!wbA") post session_path, params: { name: user.name, password: "yyy" } assert_nil(session[:user_id]) @@ -45,7 +45,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest should "clear the session" do user = create(:user) - post session_path, params: { name: user.name, password: "password" } + post session_path, params: { name: user.name, password: "6cQE!wbA" } assert_not_nil(session[:user_id]) delete_auth(session_path, user) diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb index bd9a259d6..0c822f5c8 100644 --- a/test/functional/users_controller_test.rb +++ b/test/functional/users_controller_test.rb @@ -76,7 +76,7 @@ class UsersControllerTest < ActionDispatch::IntegrationTest context "create action" do should "create a user" do assert_difference(-> { User.count }, 1) do - post users_path, params: { user: { name: "xxx", password: "xxxxx1", password_confirmation: "xxxxx1" } } + post users_path, params: { user: { name: "xxx", password: "nePD.3L4", password_confirmation: "nePD.3L4" } } end created_user = User.find(session[:user_id]) assert_equal("xxx", created_user.name) diff --git a/test/unit/user_deletion_test.rb b/test/unit/user_deletion_test.rb index 487bd552c..6ea316dff 100644 --- a/test/unit/user_deletion_test.rb +++ b/test/unit/user_deletion_test.rb @@ -22,7 +22,7 @@ class UserDeletionTest < ActiveSupport::TestCase setup do @user = create(:admin_user) CurrentUser.user = @user - @deletion = UserDeletion.new(@user, "password") + @deletion = UserDeletion.new(@user, "6cQE!wbA") end should "fail" do @@ -43,7 +43,7 @@ class UserDeletionTest < ActiveSupport::TestCase @user.update(email: "ted@danbooru.com") - @deletion = UserDeletion.new(@user, "password") + @deletion = UserDeletion.new(@user, "6cQE!wbA") with_inline_jobs { @deletion.delete! } @user.reload end @@ -58,7 +58,7 @@ class UserDeletionTest < ActiveSupport::TestCase should "reset the password" do assert_raises(BCrypt::Errors::InvalidHash) do - User.authenticate(@user.name, "password") + User.authenticate(@user.name, "6cQE!wbA") end end diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb index 403e4e7de..90d1a511c 100644 --- a/test/unit/user_test.rb +++ b/test/unit/user_test.rb @@ -116,8 +116,8 @@ class UserTest < ActiveSupport::TestCase end should "authenticate" do - assert(User.authenticate(@user.name, "password"), "Authentication should have succeeded") - assert(!User.authenticate(@user.name, "password2"), "Authentication should not have succeeded") + assert(User.authenticate(@user.name, "6cQE!wbA"), "Authentication should have succeeded") + assert_not(User.authenticate(@user.name, "password2"), "Authentication should not have succeeded") end should "normalize its level" do @@ -209,8 +209,8 @@ class UserTest < ActiveSupport::TestCase should "fail if the confirmation does not match" do @user = create(:user) - @user.password = "zugzug6" - @user.password_confirmation = "zugzug5" + @user.password = "6cQE!wbA" + @user.password_confirmation = "7cQE!wbA" @user.save assert_equal(["Password confirmation doesn't match Password"], @user.errors.full_messages) end @@ -220,7 +220,15 @@ class UserTest < ActiveSupport::TestCase @user.password = "x5" @user.password_confirmation = "x5" @user.save - assert_equal(["Password is too short (minimum is 6 characters)"], @user.errors.full_messages) + assert_equal(["Password is too short (minimum is 8 characters)", "Password is insecure"], @user.errors.full_messages) + end + + should "not be insecure" do + @user = create(:user) + @user.password = "qwerty123" + @user.password_confirmation = "qwerty123" + @user.save + assert_equal(["Password is insecure: This is similar to a commonly used password"], @user.errors.full_messages) end # should "not change the password if the password and old password are blank" do diff --git a/yarn.lock b/yarn.lock index 0fa0597a0..809e7f10d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3716,3 +3716,8 @@ zingtouch@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/zingtouch/-/zingtouch-1.0.6.tgz#456cf2b0a69f91a5ffbd8a83b18033c671f1096d" integrity sha512-S7jcR7cSRy28VmQBO0Tq7ZJV4pzfvvrTU9FrrL0K1QPpfBal9wm0oKhoCuifc+PPCq+hQMTJr5E9XKUQDm00VA== + +zxcvbn@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30" + integrity sha512-Bq0B+ixT/DMyG8kgX2xWcI5jUvCwqrMxSFam7m0lAf78nf04hv6lNCsyLYdyYTrCVMqNDY/206K7eExYCeSyUQ==