forked from e621ng/e621ng
[Users] Rework login pages and increase password requirements (#825)
This commit is contained in:
parent
1c84f551dc
commit
a0b51e40bc
1
Gemfile
1
Gemfile
@ -26,6 +26,7 @@ gem 'marcel'
|
|||||||
gem 'sidekiq-unique-jobs'
|
gem 'sidekiq-unique-jobs'
|
||||||
gem 'redis'
|
gem 'redis'
|
||||||
gem 'request_store'
|
gem 'request_store'
|
||||||
|
gem "zxcvbn-ruby", require: "zxcvbn"
|
||||||
|
|
||||||
gem "diffy"
|
gem "diffy"
|
||||||
gem "rugged"
|
gem "rugged"
|
||||||
|
@ -376,6 +376,7 @@ GEM
|
|||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.5)
|
websocket-extensions (0.1.5)
|
||||||
zeitwerk (2.6.13)
|
zeitwerk (2.6.13)
|
||||||
|
zxcvbn-ruby (1.2.0)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
@ -426,6 +427,7 @@ DEPENDENCIES
|
|||||||
streamio-ffmpeg
|
streamio-ffmpeg
|
||||||
webmock
|
webmock
|
||||||
webpacker (>= 4.0.x)
|
webpacker (>= 4.0.x)
|
||||||
|
zxcvbn-ruby
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.4.10
|
2.4.10
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
docker compose up
|
docker compose up
|
||||||
```
|
```
|
||||||
After running the commands once only `docker compose up` is needed to bring up the containers.
|
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:
|
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
|
docker exec -it e621ng-e621-1 /app/bin/populate
|
||||||
|
70
app/javascript/src/javascripts/password.js
Normal file
70
app/javascript/src/javascripts/password.js
Normal file
@ -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 = $("<div>")
|
||||||
|
.addClass("password-feedback")
|
||||||
|
.insertAfter($password);
|
||||||
|
const display = $("<div>")
|
||||||
|
.addClass("password-strength")
|
||||||
|
.insertAfter($password);
|
||||||
|
const progress = $("<div>")
|
||||||
|
.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)
|
||||||
|
$("<span>")
|
||||||
|
.text(analysis.feedback.warning)
|
||||||
|
.addClass("password-warning")
|
||||||
|
.appendTo(hint);
|
||||||
|
for (const one of analysis.feedback.suggestions)
|
||||||
|
$("<span>")
|
||||||
|
.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;
|
@ -54,7 +54,6 @@
|
|||||||
@import "specific/iqdb_queries.scss";
|
@import "specific/iqdb_queries.scss";
|
||||||
@import "specific/keyboard_shortcuts.scss";
|
@import "specific/keyboard_shortcuts.scss";
|
||||||
@import "specific/lockdown.scss";
|
@import "specific/lockdown.scss";
|
||||||
@import "specific/maintenance.scss";
|
|
||||||
@import "specific/meta_searches.scss";
|
@import "specific/meta_searches.scss";
|
||||||
@import "specific/moderator_dashboard.scss";
|
@import "specific/moderator_dashboard.scss";
|
||||||
@import "specific/news_updates.scss";
|
@import "specific/news_updates.scss";
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
div#c-maintenance-user-login-reminders {
|
|
||||||
div#a-new {
|
|
||||||
width: 50em;
|
|
||||||
}
|
|
||||||
}
|
|
@ -223,13 +223,83 @@ div#c-users {
|
|||||||
color: themed("color-link-active");
|
color: themed("color-link-active");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
div#a-new {
|
// User signup and login
|
||||||
max-width: 60em;
|
#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 {
|
margin-bottom: 1em;
|
||||||
font-size: 1.2em;
|
}
|
||||||
line-height: 1.4em;
|
|
||||||
|
.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; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "zxcvbn"
|
||||||
|
|
||||||
class User < ApplicationRecord
|
class User < ApplicationRecord
|
||||||
class Error < Exception ; end
|
class Error < Exception ; end
|
||||||
class PrivilegeError < Exception
|
class PrivilegeError < Exception
|
||||||
@ -71,7 +73,8 @@ class User < ApplicationRecord
|
|||||||
validates :per_page, inclusion: { :in => 1..320 }
|
validates :per_page, inclusion: { :in => 1..320 }
|
||||||
validates :comment_threshold, presence: true
|
validates :comment_threshold, presence: true
|
||||||
validates :comment_threshold, numericality: { only_integer: true, less_than: 50_000, greater_than: -50_000 }
|
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: true
|
||||||
validates :password_confirmation, presence: { if: ->(rec) { rec.new_record? || rec.old_password.present? } }
|
validates :password_confirmation, presence: { if: ->(rec) { rec.new_record? || rec.old_password.present? } }
|
||||||
validate :validate_ip_addr_is_not_banned, :on => :create
|
validate :validate_ip_addr_is_not_banned, :on => :create
|
||||||
@ -227,6 +230,16 @@ class User < ApplicationRecord
|
|||||||
def upgrade_password(pass)
|
def upgrade_password(pass)
|
||||||
self.update_columns(password_hash: '', bcrypt_password_hash: User.bcrypt(pass))
|
self.update_columns(password_hash: '', bcrypt_password_hash: User.bcrypt(pass))
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
module AuthenticationMethods
|
module AuthenticationMethods
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
<div id="c-maintenance-user-login-reminders">
|
<div id="c-maintenance-user-login-reminders"><div id="a-new">
|
||||||
<div id="a-new">
|
|
||||||
|
<%= form_tag(maintenance_user_login_reminder_path, :class => "simple_form session_form") do %>
|
||||||
<h1>Login Reminder</h1>
|
<h1>Login Reminder</h1>
|
||||||
|
|
||||||
<p>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.</p>
|
|
||||||
|
|
||||||
<p>If you didn't supply a valid email address, you are out of luck.</p>
|
|
||||||
|
|
||||||
<%= form_tag(maintenance_user_login_reminder_path, :class => "simple_form") do %>
|
|
||||||
<div class="input email required">
|
<div class="input email required">
|
||||||
<label for="user_email" class="required">Email</label>
|
<label for="user_email" class="required">Email</label>
|
||||||
<%= email_field(:user, :email) %>
|
<%= email_field(:user, :email) %>
|
||||||
@ -14,8 +10,12 @@
|
|||||||
|
|
||||||
<%= submit_tag "Submit" %>
|
<%= submit_tag "Submit" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
|
||||||
</div>
|
<section class="session_info">
|
||||||
|
<p>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.</p>
|
||||||
|
<p>If you didn't supply a valid email address, you are out of luck.</p>
|
||||||
|
</section>
|
||||||
|
</div></div>
|
||||||
|
|
||||||
<%= render "sessions/secondary_links" %>
|
<%= render "sessions/secondary_links" %>
|
||||||
|
|
||||||
|
@ -1,26 +1,28 @@
|
|||||||
<div id="c-maintenance-user-password-resets">
|
<div id="c-maintenance-user-password-resets"><div id="a-edit">
|
||||||
<div id="a-edit">
|
|
||||||
<h1>Reset Password</h1>
|
|
||||||
|
|
||||||
<% if @nonce %>
|
<% if @nonce %>
|
||||||
<%= form_tag(maintenance_user_password_reset_path, :method => :put) do %>
|
<%= form_tag(maintenance_user_password_reset_path, :method => :put, :class => "simple_form session_form") do %>
|
||||||
|
<h1>Reset Password</h1>
|
||||||
|
|
||||||
<%= hidden_field_tag :uid, params[:uid] %>
|
<%= hidden_field_tag :uid, params[:uid] %>
|
||||||
<%= hidden_field_tag :key, params[:key] %>
|
<%= hidden_field_tag :key, params[:key] %>
|
||||||
|
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<label for="password">Password</label>
|
<label for="password">Password</label>
|
||||||
<%= password_field_tag :password %>
|
<%= password_field_tag :password %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<label for="password_confirm">Confirm Password</label>
|
<label for="password_confirm">Confirm Password</label>
|
||||||
<%= password_field_tag :password_confirm %>
|
<%= password_field_tag :password_confirm %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= submit_tag "Reset" %>
|
<%= submit_tag "Reset" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<p>Invalid reset</p>
|
<p>Invalid reset</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div></div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<%= render "sessions/secondary_links" %>
|
<%= render "sessions/secondary_links" %>
|
||||||
|
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
<div id="c-maintenance-user-password-resets">
|
<div id="c-maintenance-user-password-resets"><div id="a-new">
|
||||||
<div id="a-new">
|
|
||||||
|
<%= form_tag(maintenance_user_password_reset_path, :class => "simple_form session_form") do %>
|
||||||
<h1>Reset Password</h1>
|
<h1>Reset Password</h1>
|
||||||
|
|
||||||
<p>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.</p>
|
|
||||||
|
|
||||||
<p>If you didn't supply a valid email address, there is no way to recover your account.</p>
|
|
||||||
|
|
||||||
<%= form_tag(maintenance_user_password_reset_path, :class => "simple_form") do %>
|
|
||||||
<div class="input email required">
|
<div class="input email required">
|
||||||
<label for="nonce_email" class="required">Email</label>
|
<label for="nonce_email" class="required">Email</label>
|
||||||
<%= text_field_tag :email %>
|
<%= text_field_tag :email %>
|
||||||
</div>
|
</div>
|
||||||
<%= submit_tag "Submit" %>
|
<%= submit_tag "Submit" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
<section class="session_info">
|
||||||
</div>
|
<p>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.</p>
|
||||||
|
<p>If you didn't supply a valid email address, there is no way to recover your account.</p>
|
||||||
|
</section>
|
||||||
|
</div></div>
|
||||||
|
|
||||||
<%= render "sessions/secondary_links" %>
|
<%= render "sessions/secondary_links" %>
|
||||||
|
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
<div id="c-maintenance-user-passwords">
|
<div id="c-maintenance-user-passwords"><div id="a-edit">
|
||||||
<div id="a-edit">
|
<%= custom_form_for(@user, html: { class: "session_form" }) do |f| %>
|
||||||
<h1>Change Password</h1>
|
<h1>Change Password</h1>
|
||||||
|
|
||||||
<%= custom_form_for @user do |f| %>
|
|
||||||
<%= f.input :old_password, :as => :password, :input_html => {:autocomplete => "off"} %>
|
<%= f.input :old_password, :as => :password, :input_html => {:autocomplete => "off"} %>
|
||||||
<%= f.input :password, :label => "New password", :input_html => {:autocomplete => "off"} %>
|
<%= f.input :password, :label => "New password", :input_html => {:autocomplete => "off"} %>
|
||||||
<%= f.input :password_confirmation %>
|
<%= f.input :password_confirmation %>
|
||||||
<%= f.button :submit, "Submit" %>
|
<%= f.button :submit, "Submit" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div></div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<% content_for(:page_title) do %>
|
<% content_for(:page_title) do %>
|
||||||
Change Password
|
Change Password
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
<div id="c-sessions">
|
<div id="c-sessions"><div id="a-new">
|
||||||
<div id="a-new">
|
<%= form_tag(session_path, :class => "simple_form session_form") do %>
|
||||||
<section>
|
|
||||||
<h1>Sign in</h1>
|
<h1>Sign in</h1>
|
||||||
<%= form_tag(session_path, :class => "simple_form") do %>
|
|
||||||
<%= hidden_field_tag "url", params[:url] %>
|
<%= hidden_field_tag "url", params[:url] %>
|
||||||
|
|
||||||
<div class="input">
|
<div class="input">
|
||||||
@ -24,15 +23,18 @@
|
|||||||
<%= submit_tag "Submit", :data => { :disable_with => "Signing in..." } %>
|
<%= submit_tag "Submit", :data => { :disable_with => "Signing in..." } %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="box-section">
|
<section class="session_info">
|
||||||
<h2><%= link_to "Don't have an account? Sign up here.", new_user_path() %></h2>
|
<h3>
|
||||||
<h2><%= link_to "Reset Password", new_maintenance_user_password_reset_path %></h2>
|
Don't have an account?<br />
|
||||||
<h2><%= link_to "Login Reminder", new_maintenance_user_login_reminder_path %></h2>
|
<%= link_to "Sign up here.", new_user_path() %>
|
||||||
|
</h3>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<h3><%= link_to "Reset Password", new_maintenance_user_password_reset_path %></h2>
|
||||||
|
<h3><%= link_to "Login Reminder", new_maintenance_user_login_reminder_path %></h2>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div></div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<% content_for(:page_title) do %>
|
<% content_for(:page_title) do %>
|
||||||
Sign in
|
Sign in
|
||||||
|
@ -1,34 +1,30 @@
|
|||||||
<div id="c-users">
|
<div id="c-users"><div id="a-new">
|
||||||
<div id="a-new">
|
<%= custom_form_for(@user, html: { id: "signup-form", class: "session_form" }) do |f| %>
|
||||||
<h1>Sign Up</h1>
|
<h1>Sign Up</h1>
|
||||||
|
|
||||||
<p>An account is <strong>free</strong> and lets you keep favorites, upload artwork, and write comments.</p>
|
<%= f.input :name, label: "Username", as: :string %>
|
||||||
|
<%= f.input :email, :required => true, as: :email %>
|
||||||
<div class="box-section background-red">
|
|
||||||
<p>Make sure to read the <a href="/wiki_pages/e621:rules">site rules</a> before continuing.</p>
|
|
||||||
<p>You must confirm your email address, so use something you can receive email with.</p>
|
|
||||||
<p>This site is open to web crawlers so whatever name you choose will be public!</p>
|
|
||||||
<p>This includes favorites, uploads, and comments. Almost everything is public. So don't choose a name you don't want to be associated with.</p>
|
|
||||||
<p>Accounts are prefilled with the same blacklist as guests have. You can access your blacklist in your account settings.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="p3">
|
|
||||||
<%= 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 %>
|
||||||
<%= f.input :password_confirmation %>
|
<%= f.input :password_confirmation %>
|
||||||
|
|
||||||
<%= f.input :time_zone, :include_blank => false %>
|
<%= f.input :time_zone, include_blank: false %>
|
||||||
|
|
||||||
<% if Danbooru.config.enable_recaptcha? %>
|
<% if Danbooru.config.enable_recaptcha? %>
|
||||||
<%= recaptcha_tags theme: 'dark', nonce: content_security_policy_nonce %>
|
<%= recaptcha_tags theme: "dark", nonce: content_security_policy_nonce %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= f.submit "Sign up", :data => { :disable_with => "Signing up..." } %>
|
<%= f.submit "Sign up", :data => { :disable_with => "Signing up..." } %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<section class="session_info">
|
||||||
|
<h3>
|
||||||
|
Already have an account? <%= link_to "Sign In.", new_session_path %>
|
||||||
|
</h3>
|
||||||
|
<p>Please, read the <a href="/wiki_pages/e621:rules">site rules</a> before making an account.</p>
|
||||||
|
<p>You must confirm your email address, so you should only use one that you have access to.</p>
|
||||||
|
<p>This site is open to web crawlers, meaning that almost everything is public.</p>
|
||||||
|
<p>This includes your account name, favorites, uploads, and comments. Do not choose a name you don't want to be associated with.</p>
|
||||||
|
<p>Accounts have the same blacklist as guests by default. You will be able to modify your blacklist in the account settings.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div></div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<%= render "secondary_links" %>
|
<%= render "secondary_links" %>
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ POSTVOTES = presets[:postvotes]
|
|||||||
POOLS = presets[:pools]
|
POOLS = presets[:pools]
|
||||||
|
|
||||||
DISTRIBUTION = ENV.fetch("DISTRIBUTION", 10).to_i
|
DISTRIBUTION = ENV.fetch("DISTRIBUTION", 10).to_i
|
||||||
DEFAULT_PASSWORD = ENV.fetch("PASSWORD", "qwerty")
|
DEFAULT_PASSWORD = ENV.fetch("PASSWORD", "hexerade")
|
||||||
|
|
||||||
CurrentUser.user = User.system
|
CurrentUser.user = User.system
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ require "tempfile"
|
|||||||
|
|
||||||
admin = User.find_or_create_by!(name: "admin") do |user|
|
admin = User.find_or_create_by!(name: "admin") do |user|
|
||||||
user.created_at = 2.weeks.ago
|
user.created_at = 2.weeks.ago
|
||||||
user.password = "qwerty"
|
user.password = "hexerade"
|
||||||
user.password_confirmation = "qwerty"
|
user.password_confirmation = "hexerade"
|
||||||
user.password_hash = ""
|
user.password_hash = ""
|
||||||
user.email = "admin@e621.local"
|
user.email = "admin@e621.local"
|
||||||
user.can_upload_free = true
|
user.can_upload_free = true
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
"vue": "^3.1.0",
|
"vue": "^3.1.0",
|
||||||
"vue-loader": "^17.4.2",
|
"vue-loader": "^17.4.2",
|
||||||
"webpack": "^5.51.1",
|
"webpack": "^5.51.1",
|
||||||
"zingtouch": "^1.0.6"
|
"zingtouch": "^1.0.6",
|
||||||
|
"zxcvbn": "^4.4.2"
|
||||||
},
|
},
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -5,8 +5,8 @@ FactoryBot.define do
|
|||||||
sequence :name do |n|
|
sequence :name do |n|
|
||||||
"user#{n}"
|
"user#{n}"
|
||||||
end
|
end
|
||||||
password { "password" }
|
password { "6cQE!wbA" }
|
||||||
password_confirmation { "password" }
|
password_confirmation { "6cQE!wbA" }
|
||||||
sequence(:email) { |n| "user_email_#{n}@example.com" }
|
sequence(:email) { |n| "user_email_#{n}@example.com" }
|
||||||
default_image_size { "large" }
|
default_image_size { "large" }
|
||||||
base_upload_limit { 10 }
|
base_upload_limit { 10 }
|
||||||
|
@ -36,7 +36,7 @@ class ApplicationControllerTest < ActionDispatch::IntegrationTest
|
|||||||
|
|
||||||
context "on api authentication" do
|
context "on api authentication" do
|
||||||
setup do
|
setup do
|
||||||
@user = create(:user, password: "password")
|
@user = create(:user, password: "6cQE!wbA")
|
||||||
@api_key = ApiKey.generate!(@user)
|
@api_key = ApiKey.generate!(@user)
|
||||||
|
|
||||||
ActionController::Base.allow_forgery_protection = true
|
ActionController::Base.allow_forgery_protection = true
|
||||||
@ -108,7 +108,7 @@ class ApplicationControllerTest < ActionDispatch::IntegrationTest
|
|||||||
token = css_select("form input[name=authenticity_token]").first["value"]
|
token = css_select("form input[name=authenticity_token]").first["value"]
|
||||||
|
|
||||||
# login
|
# 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
|
assert_redirected_to posts_path
|
||||||
|
|
||||||
# try to submit a form with cookies but without the csrf token
|
# 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
|
context "on session cookie authentication" do
|
||||||
should "succeed" 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)
|
get edit_user_path(user)
|
||||||
|
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
@ -7,7 +7,7 @@ module Maintenance
|
|||||||
class ApiKeysControllerTest < ActionDispatch::IntegrationTest
|
class ApiKeysControllerTest < ActionDispatch::IntegrationTest
|
||||||
context "An api keys controller" do
|
context "An api keys controller" do
|
||||||
setup do
|
setup do
|
||||||
@user = create(:privileged_user, :password => "password")
|
@user = create(:privileged_user, password: "6cQE!wbA")
|
||||||
ApiKey.generate!(@user)
|
ApiKey.generate!(@user)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ module Maintenance
|
|||||||
context "#view" do
|
context "#view" do
|
||||||
context "with a correct password" do
|
context "with a correct password" do
|
||||||
should "succeed" 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
|
assert_response :success
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ module Maintenance
|
|||||||
# ApiKey.expects(:generate!)
|
# ApiKey.expects(:generate!)
|
||||||
|
|
||||||
# assert_difference("ApiKey.count", 1) do
|
# 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
|
# end
|
||||||
|
|
||||||
# assert_not_nil(@user.reload.api_key)
|
# 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
|
should "not generate another API key if the user already has one" do
|
||||||
assert_difference("ApiKey.count", 0) 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
|
end
|
||||||
end
|
end
|
||||||
@ -55,14 +55,14 @@ module Maintenance
|
|||||||
context "#update" do
|
context "#update" do
|
||||||
should "regenerate the API key" do
|
should "regenerate the API key" do
|
||||||
old_key = @user.api_key
|
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)
|
assert_not_equal(old_key.key, @user.reload.api_key.key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "#destroy" do
|
context "#destroy" do
|
||||||
should "delete the API key" 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)
|
assert_nil(@user.reload.api_key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -19,7 +19,7 @@ module Maintenance
|
|||||||
|
|
||||||
context "#destroy" do
|
context "#destroy" do
|
||||||
should "render" 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)
|
assert_redirected_to(posts_path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -21,7 +21,7 @@ module Maintenance
|
|||||||
context "#create" do
|
context "#create" do
|
||||||
context "with the correct password" do
|
context "with the correct password" do
|
||||||
should "work" 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)
|
assert_redirected_to(home_users_path)
|
||||||
@user.reload
|
@user.reload
|
||||||
assert_equal("abc@ogres.net", @user.email)
|
assert_equal("abc@ogres.net", @user.email)
|
||||||
@ -37,7 +37,7 @@ module Maintenance
|
|||||||
end
|
end
|
||||||
|
|
||||||
should "not work with an invalid email" do
|
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
|
@user.reload
|
||||||
assert_not_equal("", @user.email)
|
assert_not_equal("", @user.email)
|
||||||
assert_match(/Email can't be blank/, flash[:notice])
|
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
|
should "work with a valid email when the users current email is invalid" do
|
||||||
@user = create(:user, email: "")
|
@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
|
@user.reload
|
||||||
assert_equal("abc@ogres.net", @user.email)
|
assert_equal("abc@ogres.net", @user.email)
|
||||||
end
|
end
|
||||||
|
@ -15,7 +15,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
should "create a new session" do
|
should "create a new session" do
|
||||||
user = create(:user)
|
user = create(:user)
|
||||||
|
|
||||||
post session_path, params: { name: user.name, password: "password" }
|
post session_path, params: { name: user.name, password: "6cQE!wbA" }
|
||||||
user.reload
|
user.reload
|
||||||
|
|
||||||
assert_redirected_to(posts_path)
|
assert_redirected_to(posts_path)
|
||||||
@ -33,7 +33,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
end
|
end
|
||||||
|
|
||||||
should "fail when provided an invalid password" do
|
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" }
|
post session_path, params: { name: user.name, password: "yyy" }
|
||||||
|
|
||||||
assert_nil(session[:user_id])
|
assert_nil(session[:user_id])
|
||||||
@ -45,7 +45,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
should "clear the session" do
|
should "clear the session" do
|
||||||
user = create(:user)
|
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])
|
assert_not_nil(session[:user_id])
|
||||||
|
|
||||||
delete_auth(session_path, user)
|
delete_auth(session_path, user)
|
||||||
|
@ -76,7 +76,7 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
|
|||||||
context "create action" do
|
context "create action" do
|
||||||
should "create a user" do
|
should "create a user" do
|
||||||
assert_difference(-> { User.count }, 1) 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
|
end
|
||||||
created_user = User.find(session[:user_id])
|
created_user = User.find(session[:user_id])
|
||||||
assert_equal("xxx", created_user.name)
|
assert_equal("xxx", created_user.name)
|
||||||
|
@ -22,7 +22,7 @@ class UserDeletionTest < ActiveSupport::TestCase
|
|||||||
setup do
|
setup do
|
||||||
@user = create(:admin_user)
|
@user = create(:admin_user)
|
||||||
CurrentUser.user = @user
|
CurrentUser.user = @user
|
||||||
@deletion = UserDeletion.new(@user, "password")
|
@deletion = UserDeletion.new(@user, "6cQE!wbA")
|
||||||
end
|
end
|
||||||
|
|
||||||
should "fail" do
|
should "fail" do
|
||||||
@ -43,7 +43,7 @@ class UserDeletionTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
@user.update(email: "ted@danbooru.com")
|
@user.update(email: "ted@danbooru.com")
|
||||||
|
|
||||||
@deletion = UserDeletion.new(@user, "password")
|
@deletion = UserDeletion.new(@user, "6cQE!wbA")
|
||||||
with_inline_jobs { @deletion.delete! }
|
with_inline_jobs { @deletion.delete! }
|
||||||
@user.reload
|
@user.reload
|
||||||
end
|
end
|
||||||
@ -58,7 +58,7 @@ class UserDeletionTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
should "reset the password" do
|
should "reset the password" do
|
||||||
assert_raises(BCrypt::Errors::InvalidHash) do
|
assert_raises(BCrypt::Errors::InvalidHash) do
|
||||||
User.authenticate(@user.name, "password")
|
User.authenticate(@user.name, "6cQE!wbA")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -116,8 +116,8 @@ class UserTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
should "authenticate" do
|
should "authenticate" do
|
||||||
assert(User.authenticate(@user.name, "password"), "Authentication should have succeeded")
|
assert(User.authenticate(@user.name, "6cQE!wbA"), "Authentication should have succeeded")
|
||||||
assert(!User.authenticate(@user.name, "password2"), "Authentication should not have succeeded")
|
assert_not(User.authenticate(@user.name, "password2"), "Authentication should not have succeeded")
|
||||||
end
|
end
|
||||||
|
|
||||||
should "normalize its level" do
|
should "normalize its level" do
|
||||||
@ -209,8 +209,8 @@ class UserTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
should "fail if the confirmation does not match" do
|
should "fail if the confirmation does not match" do
|
||||||
@user = create(:user)
|
@user = create(:user)
|
||||||
@user.password = "zugzug6"
|
@user.password = "6cQE!wbA"
|
||||||
@user.password_confirmation = "zugzug5"
|
@user.password_confirmation = "7cQE!wbA"
|
||||||
@user.save
|
@user.save
|
||||||
assert_equal(["Password confirmation doesn't match Password"], @user.errors.full_messages)
|
assert_equal(["Password confirmation doesn't match Password"], @user.errors.full_messages)
|
||||||
end
|
end
|
||||||
@ -220,7 +220,15 @@ class UserTest < ActiveSupport::TestCase
|
|||||||
@user.password = "x5"
|
@user.password = "x5"
|
||||||
@user.password_confirmation = "x5"
|
@user.password_confirmation = "x5"
|
||||||
@user.save
|
@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
|
end
|
||||||
|
|
||||||
# should "not change the password if the password and old password are blank" do
|
# should "not change the password if the password and old password are blank" do
|
||||||
|
@ -3716,3 +3716,8 @@ zingtouch@^1.0.6:
|
|||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/zingtouch/-/zingtouch-1.0.6.tgz#456cf2b0a69f91a5ffbd8a83b18033c671f1096d"
|
resolved "https://registry.yarnpkg.com/zingtouch/-/zingtouch-1.0.6.tgz#456cf2b0a69f91a5ffbd8a83b18033c671f1096d"
|
||||||
integrity sha512-S7jcR7cSRy28VmQBO0Tq7ZJV4pzfvvrTU9FrrL0K1QPpfBal9wm0oKhoCuifc+PPCq+hQMTJr5E9XKUQDm00VA==
|
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==
|
||||||
|
Loading…
Reference in New Issue
Block a user