Merge pull request #1473 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
This commit is contained in:
commit
cbe9b0640b
17 changed files with 104 additions and 7 deletions
|
@ -1,13 +1,17 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class AboutController < ApplicationController
|
class AboutController < ApplicationController
|
||||||
|
include RegistrationSpamConcern
|
||||||
|
|
||||||
before_action :set_pack
|
before_action :set_pack
|
||||||
|
|
||||||
layout 'public'
|
layout 'public'
|
||||||
|
|
||||||
before_action :require_open_federation!, only: [:show, :more]
|
before_action :require_open_federation!, only: [:show, :more]
|
||||||
before_action :set_body_classes, only: :show
|
before_action :set_body_classes, only: :show
|
||||||
before_action :set_instance_presenter
|
before_action :set_instance_presenter
|
||||||
before_action :set_expires_in, only: [:show, :more, :terms]
|
before_action :set_expires_in, only: [:more, :terms]
|
||||||
|
before_action :set_registration_form_time, only: :show
|
||||||
|
|
||||||
skip_before_action :require_functional!, only: [:more, :terms]
|
skip_before_action :require_functional!, only: [:more, :terms]
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class Auth::RegistrationsController < Devise::RegistrationsController
|
class Auth::RegistrationsController < Devise::RegistrationsController
|
||||||
include Devise::Controllers::Rememberable
|
include Devise::Controllers::Rememberable
|
||||||
|
include RegistrationSpamConcern
|
||||||
|
|
||||||
layout :determine_layout
|
layout :determine_layout
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
||||||
before_action :set_body_classes, only: [:new, :create, :edit, :update]
|
before_action :set_body_classes, only: [:new, :create, :edit, :update]
|
||||||
before_action :require_not_suspended!, only: [:update]
|
before_action :require_not_suspended!, only: [:update]
|
||||||
before_action :set_cache_headers, only: [:edit, :update]
|
before_action :set_cache_headers, only: [:edit, :update]
|
||||||
|
before_action :set_registration_form_time, only: :new
|
||||||
|
|
||||||
skip_before_action :require_functional!, only: [:edit, :update]
|
skip_before_action :require_functional!, only: [:edit, :update]
|
||||||
|
|
||||||
|
@ -46,16 +48,17 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
||||||
def build_resource(hash = nil)
|
def build_resource(hash = nil)
|
||||||
super(hash)
|
super(hash)
|
||||||
|
|
||||||
resource.locale = I18n.locale
|
resource.locale = I18n.locale
|
||||||
resource.invite_code = params[:invite_code] if resource.invite_code.blank?
|
resource.invite_code = params[:invite_code] if resource.invite_code.blank?
|
||||||
resource.sign_up_ip = request.remote_ip
|
resource.registration_form_time = session[:registration_form_time]
|
||||||
|
resource.sign_up_ip = request.remote_ip
|
||||||
|
|
||||||
resource.build_account if resource.account.nil?
|
resource.build_account if resource.account.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
def configure_sign_up_params
|
def configure_sign_up_params
|
||||||
devise_parameter_sanitizer.permit(:sign_up) do |u|
|
devise_parameter_sanitizer.permit(:sign_up) do |u|
|
||||||
u.permit({ account_attributes: [:username], invite_request_attributes: [:text] }, :email, :password, :password_confirmation, :invite_code, :agreement)
|
u.permit({ account_attributes: [:username], invite_request_attributes: [:text] }, :email, :password, :password_confirmation, :invite_code, :agreement, :website, :confirm_password)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
9
app/controllers/concerns/registration_spam_concern.rb
Normal file
9
app/controllers/concerns/registration_spam_concern.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module RegistrationSpamConcern
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
def set_registration_form_time
|
||||||
|
session[:registration_form_time] = Time.now.utc
|
||||||
|
end
|
||||||
|
end
|
|
@ -37,10 +37,16 @@ const messages = defineMessages({
|
||||||
markAsRead : { id: 'notifications.mark_as_read', defaultMessage: 'Mark every notification as read' },
|
markAsRead : { id: 'notifications.mark_as_read', defaultMessage: 'Mark every notification as read' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getExcludedTypes = createSelector([
|
||||||
|
state => state.getIn(['settings', 'notifications', 'shows']),
|
||||||
|
], (shows) => {
|
||||||
|
return ImmutableList(shows.filter(item => !item).keys());
|
||||||
|
});
|
||||||
|
|
||||||
const getNotifications = createSelector([
|
const getNotifications = createSelector([
|
||||||
state => state.getIn(['settings', 'notifications', 'quickFilter', 'show']),
|
state => state.getIn(['settings', 'notifications', 'quickFilter', 'show']),
|
||||||
state => state.getIn(['settings', 'notifications', 'quickFilter', 'active']),
|
state => state.getIn(['settings', 'notifications', 'quickFilter', 'active']),
|
||||||
state => ImmutableList(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()),
|
getExcludedTypes,
|
||||||
state => state.getIn(['notifications', 'items']),
|
state => state.getIn(['notifications', 'items']),
|
||||||
], (showFilterBar, allowedType, excludedTypes, notifications) => {
|
], (showFilterBar, allowedType, excludedTypes, notifications) => {
|
||||||
if (!showFilterBar || allowedType === 'all') {
|
if (!showFilterBar || allowedType === 'all') {
|
||||||
|
|
|
@ -151,6 +151,17 @@ function main() {
|
||||||
target.style.display = 'block';
|
target.style.display = 'block';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Empty the honeypot fields in JS in case something like an extension
|
||||||
|
// automatically filled them.
|
||||||
|
delegate(document, '#registration_new_user,#new_user', 'submit', () => {
|
||||||
|
['user_website', 'user_confirm_password', 'registration_user_website', 'registration_user_confirm_password'].forEach(id => {
|
||||||
|
const field = document.getElementById(id);
|
||||||
|
if (field) {
|
||||||
|
field.value = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadPolyfills()
|
loadPolyfills()
|
||||||
|
|
|
@ -345,6 +345,7 @@ code {
|
||||||
input[type=number],
|
input[type=number],
|
||||||
input[type=email],
|
input[type=email],
|
||||||
input[type=password],
|
input[type=password],
|
||||||
|
input[type=url],
|
||||||
textarea {
|
textarea {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
@ -985,3 +986,10 @@ code {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input.user_confirm_password,
|
||||||
|
.input.user_website {
|
||||||
|
&:not(.field_with_errors) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -32,10 +32,16 @@ const messages = defineMessages({
|
||||||
markAsRead : { id: 'notifications.mark_as_read', defaultMessage: 'Mark every notification as read' },
|
markAsRead : { id: 'notifications.mark_as_read', defaultMessage: 'Mark every notification as read' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getExcludedTypes = createSelector([
|
||||||
|
state => state.getIn(['settings', 'notifications', 'shows']),
|
||||||
|
], (shows) => {
|
||||||
|
return ImmutableList(shows.filter(item => !item).keys());
|
||||||
|
});
|
||||||
|
|
||||||
const getNotifications = createSelector([
|
const getNotifications = createSelector([
|
||||||
state => state.getIn(['settings', 'notifications', 'quickFilter', 'show']),
|
state => state.getIn(['settings', 'notifications', 'quickFilter', 'show']),
|
||||||
state => state.getIn(['settings', 'notifications', 'quickFilter', 'active']),
|
state => state.getIn(['settings', 'notifications', 'quickFilter', 'active']),
|
||||||
state => ImmutableList(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()),
|
getExcludedTypes,
|
||||||
state => state.getIn(['notifications', 'items']),
|
state => state.getIn(['notifications', 'items']),
|
||||||
], (showFilterBar, allowedType, excludedTypes, notifications) => {
|
], (showFilterBar, allowedType, excludedTypes, notifications) => {
|
||||||
if (!showFilterBar || allowedType === 'all') {
|
if (!showFilterBar || allowedType === 'all') {
|
||||||
|
|
|
@ -155,6 +155,17 @@ function main() {
|
||||||
target.style.display = 'block';
|
target.style.display = 'block';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Empty the honeypot fields in JS in case something like an extension
|
||||||
|
// automatically filled them.
|
||||||
|
delegate(document, '#registration_new_user,#new_user', 'submit', () => {
|
||||||
|
['user_website', 'user_confirm_password', 'registration_user_website', 'registration_user_confirm_password'].forEach(id => {
|
||||||
|
const field = document.getElementById(id);
|
||||||
|
if (field) {
|
||||||
|
field.value = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadPolyfills()
|
loadPolyfills()
|
||||||
|
|
|
@ -354,6 +354,7 @@ code {
|
||||||
input[type=number],
|
input[type=number],
|
||||||
input[type=email],
|
input[type=email],
|
||||||
input[type=password],
|
input[type=password],
|
||||||
|
input[type=url],
|
||||||
textarea {
|
textarea {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
@ -994,3 +995,10 @@ code {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input.user_confirm_password,
|
||||||
|
.input.user_website {
|
||||||
|
&:not(.field_with_errors) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -89,6 +89,13 @@ class User < ApplicationRecord
|
||||||
validates_with EmailMxValidator, if: :validate_email_dns?
|
validates_with EmailMxValidator, if: :validate_email_dns?
|
||||||
validates :agreement, acceptance: { allow_nil: false, accept: [true, 'true', '1'] }, on: :create
|
validates :agreement, acceptance: { allow_nil: false, accept: [true, 'true', '1'] }, on: :create
|
||||||
|
|
||||||
|
# Those are honeypot/antispam fields
|
||||||
|
attr_accessor :registration_form_time, :website, :confirm_password
|
||||||
|
|
||||||
|
validates_with RegistrationFormTimeValidator, on: :create
|
||||||
|
validates :website, absence: true, on: :create
|
||||||
|
validates :confirm_password, absence: true, on: :create
|
||||||
|
|
||||||
scope :recent, -> { order(id: :desc) }
|
scope :recent, -> { order(id: :desc) }
|
||||||
scope :pending, -> { where(approved: false) }
|
scope :pending, -> { where(approved: false) }
|
||||||
scope :approved, -> { where(approved: true) }
|
scope :approved, -> { where(approved: true) }
|
||||||
|
|
9
app/validators/registration_form_time_validator.rb
Normal file
9
app/validators/registration_form_time_validator.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RegistrationFormTimeValidator < ActiveModel::Validator
|
||||||
|
REGISTRATION_FORM_MIN_TIME = 3.seconds.freeze
|
||||||
|
|
||||||
|
def validate(user)
|
||||||
|
user.errors.add(:base, I18n.t('auth.too_fast')) if user.registration_form_time.present? && user.registration_form_time > REGISTRATION_FORM_MIN_TIME.ago
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,6 +10,9 @@
|
||||||
= f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, hint: false, disabled: closed_registrations?
|
= f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, hint: false, disabled: closed_registrations?
|
||||||
= f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
|
= f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
|
||||||
|
|
||||||
|
= f.input :confirm_password, as: :string, placeholder: t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
|
||||||
|
= f.input :website, as: :url, placeholder: t('simple_form.labels.defaults.honeypot', label: 'Website'), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: 'Website'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
|
||||||
|
|
||||||
- if approved_registrations?
|
- if approved_registrations?
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.simple_fields_for :invite_request do |invite_request_fields|
|
= f.simple_fields_for :invite_request do |invite_request_fields|
|
||||||
|
|
|
@ -24,6 +24,9 @@
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }
|
= f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }
|
||||||
|
= f.input :confirm_password, as: :string, wrapper: :with_label, label: t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), :autocomplete => 'off' }
|
||||||
|
|
||||||
|
= f.input :website, as: :url, wrapper: :with_label, label: t('simple_form.labels.defaults.honeypot', label: 'Website'), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: 'Website'), :autocomplete => 'off' }
|
||||||
|
|
||||||
- if approved_registrations? && !@invite.present?
|
- if approved_registrations? && !@invite.present?
|
||||||
.fields-group
|
.fields-group
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
- if object.errors.any?
|
- if object.errors.any?
|
||||||
.flash-message.alert#error_explanation
|
.flash-message.alert#error_explanation
|
||||||
%strong= t('generic.validation_errors', count: object.errors.count)
|
%strong= t('generic.validation_errors', count: object.errors.count)
|
||||||
|
- object.errors[:base].each do |error|
|
||||||
|
.flash-message.alert
|
||||||
|
%strong= error
|
||||||
|
|
|
@ -751,6 +751,7 @@ en:
|
||||||
functional: Your account is fully operational.
|
functional: Your account is fully operational.
|
||||||
pending: Your application is pending review by our staff. This may take some time. You will receive an e-mail if your application is approved.
|
pending: Your application is pending review by our staff. This may take some time. You will receive an e-mail if your application is approved.
|
||||||
redirecting_to: Your account is inactive because it is currently redirecting to %{acct}.
|
redirecting_to: Your account is inactive because it is currently redirecting to %{acct}.
|
||||||
|
too_fast: Form submitted too fast, try again.
|
||||||
trouble_logging_in: Trouble logging in?
|
trouble_logging_in: Trouble logging in?
|
||||||
use_security_key: Use security key
|
use_security_key: Use security key
|
||||||
authorize_follow:
|
authorize_follow:
|
||||||
|
|
|
@ -126,6 +126,7 @@ en:
|
||||||
expires_in: Expire after
|
expires_in: Expire after
|
||||||
fields: Profile metadata
|
fields: Profile metadata
|
||||||
header: Header
|
header: Header
|
||||||
|
honeypot: "%{label} (do not fill in)"
|
||||||
inbox_url: URL of the relay inbox
|
inbox_url: URL of the relay inbox
|
||||||
irreversible: Drop instead of hide
|
irreversible: Drop instead of hide
|
||||||
locale: Interface language
|
locale: Interface language
|
||||||
|
|
|
@ -82,6 +82,10 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
|
||||||
describe 'POST #create' do
|
describe 'POST #create' do
|
||||||
let(:accept_language) { Rails.application.config.i18n.available_locales.sample.to_s }
|
let(:accept_language) { Rails.application.config.i18n.available_locales.sample.to_s }
|
||||||
|
|
||||||
|
before do
|
||||||
|
session[:registration_form_time] = 5.seconds.ago
|
||||||
|
end
|
||||||
|
|
||||||
around do |example|
|
around do |example|
|
||||||
current_locale = I18n.locale
|
current_locale = I18n.locale
|
||||||
example.run
|
example.run
|
||||||
|
|
Loading…
Reference in a new issue