Added new security question to sign up, Added notification for unconfirmed emails
• Added: - new security question to sign up - notification for unconfirmed emails - modal for describing issue with Gab emails
This commit is contained in:
parent
9c0fc47777
commit
3c07e9d63b
@ -74,8 +74,9 @@ class Api::BaseController < ApplicationController
|
||||
render json: { error: 'This method requires an authenticated user' }, status: 422
|
||||
elsif current_user.disabled?
|
||||
render json: { error: 'Your login is currently disabled' }, status: 403
|
||||
elsif !current_user.confirmed?
|
||||
render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403
|
||||
# : todo : when figure out email/catpcha, put this back
|
||||
# elsif !current_user.confirmed?
|
||||
# render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403
|
||||
elsif !current_user.approved?
|
||||
render json: { error: 'Your login is currently pending approval' }, status: 403
|
||||
else
|
||||
|
@ -18,6 +18,27 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
|
||||
render json: @account, serializer: REST::CredentialAccountSerializer
|
||||
end
|
||||
|
||||
def resend_email_confirmation
|
||||
@account = current_account
|
||||
|
||||
if !@account.user.confirmed?
|
||||
redisResult = Redis.current.get("account:#{@account.id}:last_email_confirmation_resend") || 0
|
||||
|
||||
@lastSentDate = redisResult
|
||||
if redisResult != 0
|
||||
@lastSentDate = Time.at(redisResult.to_i).utc
|
||||
end
|
||||
|
||||
if @lastSentDate == 0 || (@lastSentDate != 0 && Time.now.utc - @lastSentDate >= 1.hour)
|
||||
@user = Account.find(@account.id).user || raise(ActiveRecord::RecordNotFound)
|
||||
Redis.current.set("account:#{@account.id}:last_email_confirmation_resend", Time.now.utc.to_i)
|
||||
@user.resend_confirmation_instructions
|
||||
end
|
||||
end
|
||||
|
||||
render json: { success: true, message: 'ok' }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account_params
|
||||
@ -35,4 +56,5 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
|
||||
'setting_default_language' => source_params.fetch(:language, @account.user.setting_default_language),
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -4,6 +4,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
||||
layout :determine_layout
|
||||
|
||||
before_action :set_invite, only: [:new, :create]
|
||||
before_action :set_challenge, only: [:new]
|
||||
before_action :check_enabled_registrations, only: [:new, :create]
|
||||
before_action :configure_sign_up_params, only: [:create]
|
||||
before_action :set_sessions, only: [:edit, :update]
|
||||
@ -15,6 +16,16 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
||||
super(&:build_invite_request)
|
||||
end
|
||||
|
||||
def create
|
||||
if session[:challenge_answer].to_s == params[:user][:challenge].to_s.strip
|
||||
# Reset after, may be errors to return and this ensures its still visible
|
||||
set_challenge
|
||||
super
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
not_found
|
||||
end
|
||||
@ -96,6 +107,12 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
||||
@invite = invite&.valid_for_use? ? invite : nil
|
||||
end
|
||||
|
||||
def set_challenge
|
||||
@challenge_add_1 = rand(0...9)
|
||||
@challenge_add_2 = rand(0...9)
|
||||
session[:challenge_answer] = @challenge_add_1 + @challenge_add_2
|
||||
end
|
||||
|
||||
def determine_layout
|
||||
%w(edit update).include?(action_name) ? 'admin' : 'auth'
|
||||
end
|
||||
|
@ -1,11 +1,15 @@
|
||||
import isObject from 'lodash.isobject'
|
||||
import api from '../api'
|
||||
import { me } from '../initial_state'
|
||||
import {
|
||||
me,
|
||||
emailConfirmed,
|
||||
} from '../initial_state'
|
||||
import { importFetchedAccount } from './importer'
|
||||
|
||||
export const SAVE_USER_PROFILE_INFORMATION_FETCH_REQUEST = 'SAVE_USER_PROFILE_INFORMATION_FETCH_REQUEST'
|
||||
export const SAVE_USER_PROFILE_INFORMATION_FETCH_SUCCESS = 'SAVE_USER_PROFILE_INFORMATION_FETCH_SUCCESS'
|
||||
export const SAVE_USER_PROFILE_INFORMATION_FETCH_FAIL = 'SAVE_USER_PROFILE_INFORMATION_FETCH_FAIL'
|
||||
export const RESEND_USER_CONFIRMATION_EMAIL_SUCCESS = 'RESEND_USER_CONFIRMATION_EMAIL_SUCCESS'
|
||||
|
||||
export const saveUserProfileInformation = (data) => {
|
||||
return function (dispatch, getState) {
|
||||
@ -52,3 +56,12 @@ function saveUserProfileInformationFail(error) {
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
export const resendUserConfirmationEmail = () => (dispatch, getState) => {
|
||||
if (!me || emailConfirmed) return
|
||||
|
||||
api(getState).post('/api/v1/accounts/resend_email_confirmation').then((response) => {
|
||||
dispatch({ type: RESEND_USER_CONFIRMATION_EMAIL_SUCCESS })
|
||||
})
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import {
|
||||
me,
|
||||
meUsername,
|
||||
} from '../../initial_state'
|
||||
import Button from '../button'
|
||||
import Text from '../text'
|
||||
import ModalLayout from './modal_layout'
|
||||
|
||||
class EmailConfirmationReminderModal extends React.PureComponent {
|
||||
|
||||
render() {
|
||||
const { onClose } = this.props
|
||||
|
||||
return (
|
||||
<ModalLayout
|
||||
title='Having Email Confirmation Issues?'
|
||||
width={480}
|
||||
onClose={onClose}
|
||||
>
|
||||
<Text size='medium' weight='medium' className={_s.mb10}>
|
||||
Many email providers block Gab’s emails.
|
||||
</Text>
|
||||
<Text size='medium' color='secondary'>
|
||||
Please check your spam folder for the confirmation email. If you still do not see an email please reach out to us for help.
|
||||
</Text>
|
||||
|
||||
<div className={[_s.d, _s.flexRow, _s.pt15, _s.pb10].join(' ')}>
|
||||
<Button
|
||||
isOutline
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
href={`mailto:support@gab.com?subject=Please%20confirm%20my%20email%20(${me})&body=My%20username%20is:%20${meUsername}%20and%20account%20id%20is:%20${me}`}
|
||||
className={[_s.flexRow, _s.aiCenter, _s.jcCenter, _s.mr10].join(' ')}
|
||||
>
|
||||
<Text color='inherit' weight='medium' align='center'>
|
||||
Email Gab Support
|
||||
</Text>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</ModalLayout>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EmailConfirmationReminderModal.propTypes = {
|
||||
onClose: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default EmailConfirmationReminderModal
|
@ -16,6 +16,7 @@ import {
|
||||
MODAL_DISPLAY_OPTIONS,
|
||||
MODAL_EDIT_PROFILE,
|
||||
MODAL_EDIT_SHORTCUTS,
|
||||
MODAL_EMAIL_CONFIRMATION_REMINDER,
|
||||
MODAL_EMBED,
|
||||
MODAL_GROUP_CREATE,
|
||||
MODAL_GROUP_DELETE,
|
||||
@ -48,6 +49,7 @@ import {
|
||||
DisplayOptionsModal,
|
||||
EditProfileModal,
|
||||
EditShortcutsModal,
|
||||
EmailConfirmationReminderModal,
|
||||
EmbedModal,
|
||||
GroupCreateModal,
|
||||
GroupDeleteModal,
|
||||
@ -83,6 +85,7 @@ MODAL_COMPONENTS[MODAL_CONFIRM] = ConfirmationModal
|
||||
MODAL_COMPONENTS[MODAL_DISPLAY_OPTIONS] = DisplayOptionsModal
|
||||
MODAL_COMPONENTS[MODAL_EDIT_SHORTCUTS] = EditShortcutsModal
|
||||
MODAL_COMPONENTS[MODAL_EDIT_PROFILE] = EditProfileModal
|
||||
MODAL_COMPONENTS[MODAL_EMAIL_CONFIRMATION_REMINDER] = EmailConfirmationReminderModal
|
||||
MODAL_COMPONENTS[MODAL_EMBED] = EmbedModal
|
||||
MODAL_COMPONENTS[MODAL_GROUP_CREATE] = GroupCreateModal
|
||||
MODAL_COMPONENTS[MODAL_GROUP_DELETE] = GroupDeleteModal
|
||||
|
@ -2,8 +2,10 @@ import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import {
|
||||
CX,
|
||||
BREAKPOINT_SMALL,
|
||||
} from '../../constants'
|
||||
import { emailConfirmed } from '../../initial_state'
|
||||
import Button from '../button'
|
||||
import { openModal } from '../../actions/modal'
|
||||
import Responsive from '../../features/ui/util/responsive_component'
|
||||
@ -26,10 +28,18 @@ class SidebarLayout extends React.PureComponent {
|
||||
children,
|
||||
} = this.props
|
||||
|
||||
const innerContainerClasses = CX({
|
||||
d: 1,
|
||||
posFixed: 1,
|
||||
calcH53PX: emailConfirmed,
|
||||
calcH106PX: !emailConfirmed,
|
||||
bottom0: 1,
|
||||
})
|
||||
|
||||
return (
|
||||
<header role='banner' className={[_s.d, _s.flexGrow1, _s.z3, _s.aiEnd].join(' ')}>
|
||||
<div className={[_s.d, _s.w240PX].join(' ')}>
|
||||
<div className={[_s.d, _s.posFixed, _s.calcH53PX, _s.bottom0].join(' ')}>
|
||||
<div className={innerContainerClasses}>
|
||||
<div className={[_s.d, _s.h100PC, _s.aiStart, _s.w240PX, _s.pr15, _s.py10, _s.noScrollbar, _s.overflowYScroll].join(' ')}>
|
||||
<div className={[_s.d, _s.w100PC].join(' ')}>
|
||||
{
|
||||
|
@ -46,6 +46,7 @@ export const MODAL_CONFIRM = 'CONFIRM'
|
||||
export const MODAL_DISPLAY_OPTIONS = 'DISPLAY_OPTIONS'
|
||||
export const MODAL_EDIT_PROFILE = 'EDIT_PROFILE'
|
||||
export const MODAL_EDIT_SHORTCUTS = 'EDIT_SHORTCUTS'
|
||||
export const MODAL_EMAIL_CONFIRMATION_REMINDER = 'EMAIL_CONFIRMATION_REMINDER'
|
||||
export const MODAL_EMBED = 'EMBED'
|
||||
export const MODAL_GROUP_CREATE = 'GROUP_CREATE'
|
||||
export const MODAL_GROUP_DELETE = 'GROUP_DELETE'
|
||||
|
@ -26,5 +26,7 @@ export const lastReadNotificationId = getMeta('last_read_notification_id');
|
||||
export const monthlyExpensesComplete = getMeta('monthly_expenses_complete');
|
||||
export const favouritesCount = getMeta('favourites_count');
|
||||
export const isFirstSession = getMeta('is_first_session');
|
||||
export const emailConfirmed = getMeta('email_confirmed');
|
||||
export const meEmail = getMeta('email');
|
||||
|
||||
export default initialState;
|
||||
|
@ -2,12 +2,14 @@ import {
|
||||
SAVE_USER_PROFILE_INFORMATION_FETCH_REQUEST,
|
||||
SAVE_USER_PROFILE_INFORMATION_FETCH_SUCCESS,
|
||||
SAVE_USER_PROFILE_INFORMATION_FETCH_FAIL,
|
||||
RESEND_USER_CONFIRMATION_EMAIL_SUCCESS,
|
||||
} from '../actions/user'
|
||||
import { Map as ImmutableMap } from 'immutable'
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
emailConfirmationResends: 0,
|
||||
})
|
||||
|
||||
export default function (state = initialState, action) {
|
||||
@ -18,6 +20,8 @@ export default function (state = initialState, action) {
|
||||
return state
|
||||
case SAVE_USER_PROFILE_INFORMATION_FETCH_FAIL:
|
||||
return state.set('isError', true)
|
||||
case RESEND_USER_CONFIRMATION_EMAIL_SUCCESS:
|
||||
return state.set('emailConfirmationResends', state.get('emailConfirmationResends') + 1)
|
||||
default:
|
||||
return state
|
||||
}
|
||||
|
@ -443,6 +443,7 @@ pre {
|
||||
.topNeg50PX { top: -50px; }
|
||||
.top0 { top: 0; }
|
||||
.top120PX { top: 120px; }
|
||||
.top53PX { top: 53px; }
|
||||
.top60PC { top: 60%; }
|
||||
.top50PC { top: 50%; }
|
||||
|
||||
@ -478,12 +479,15 @@ pre {
|
||||
.maxH340PX { max-height: 340px; }
|
||||
.maxH200PX { max-height: 200px; }
|
||||
.maxH56PX { max-height: 56px; }
|
||||
.maxH56PX { max-height: 42px; }
|
||||
.calcH106PX { height: calc(100vh - 106px); }
|
||||
.calcH53PX { height: calc(100vh - 53px); }
|
||||
.calcH80VH106PX { height: calc(80vh - 106px); }
|
||||
|
||||
.minH100VH { min-height: 100vh; }
|
||||
.minH50VH { min-height: 50vh; }
|
||||
.minH200PX { min-height: 200px; }
|
||||
.minH106PX { min-height: 106px; }
|
||||
.minH98PX { min-height: 98px; }
|
||||
.minH80PX { min-height: 80px; }
|
||||
.minH58PX { min-height: 58px; }
|
||||
|
@ -253,6 +253,10 @@ class User < ApplicationRecord
|
||||
@invite_code = code
|
||||
end
|
||||
|
||||
def challenge
|
||||
#
|
||||
end
|
||||
|
||||
def password_required?
|
||||
return false if Devise.pam_authentication || Devise.ldap_authentication
|
||||
super
|
||||
|
@ -39,6 +39,8 @@ class InitialStateSerializer < ActiveModel::Serializer
|
||||
store[:monthly_expenses_complete] = Redis.current.get("monthly_funding_amount") || 0
|
||||
store[:favourites_count] = object.current_account.favourites.count.to_s
|
||||
store[:is_first_session] = is_first_session object.current_account
|
||||
store[:email_confirmed] = object.current_account.user.confirmed?
|
||||
store[:email] = object.current_account.user.confirmed? ? '[hidden]' : object.current_account.user.email
|
||||
end
|
||||
|
||||
store
|
||||
|
@ -34,6 +34,9 @@
|
||||
|
||||
= f.input :invite_code, as: :hidden
|
||||
|
||||
.fields-group
|
||||
= f.input :challenge, wrapper: :with_label, label: "Are you human? What is #{@challenge_add_1} + #{@challenge_add_2} = ", required: true, input_html: { 'aria-label' => "Are you human? What is #{@challenge_add_1} + #{@challenge_add_2}", :autocomplete => 'off' }
|
||||
|
||||
.fields-group-agreement
|
||||
= f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', about_tos_path: about_tos_path)
|
||||
|
||||
|
@ -190,7 +190,7 @@ Devise.setup do |config|
|
||||
# able to access the website for two days without confirming their account,
|
||||
# access will be blocked just in the third day. Default is 0.days, meaning
|
||||
# the user cannot access the website without confirming their account.
|
||||
# config.allow_unconfirmed_access_for = 2.days
|
||||
config.allow_unconfirmed_access_for = 10.years
|
||||
|
||||
# A period that the user is allowed to confirm their account before their
|
||||
# token becomes invalid. For example, if set to 3.days, the user can confirm
|
||||
@ -198,7 +198,7 @@ Devise.setup do |config|
|
||||
# their account can't be confirmed with the token any more.
|
||||
# Default is nil, meaning there is no restriction on how long a user can take
|
||||
# before confirming their account.
|
||||
config.confirm_within = 2.days
|
||||
config.confirm_within = 12.days
|
||||
|
||||
# If true, requires any email changes to be confirmed (exactly the same way as
|
||||
# initial account confirmation) to be applied. Requires additional unconfirmed_email
|
||||
|
@ -405,6 +405,7 @@ Rails.application.routes.draw do
|
||||
namespace :accounts do
|
||||
get :verify_credentials, to: 'credentials#show'
|
||||
patch :update_credentials, to: 'credentials#update'
|
||||
post :resend_email_confirmation, to: 'credentials#resend_email_confirmation'
|
||||
resource :search, only: :show, controller: :search
|
||||
resources :relationships, only: :index
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user