mirror of
https://github.com/discourse/discourse.git
synced 2025-05-23 14:41:25 +08:00
FEATURE: Disallow login via omniauth when user has 2FA enabled.
This commit is contained in:
@ -27,16 +27,20 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
|||||||
loggingIn: false,
|
loggingIn: false,
|
||||||
loggedIn: false,
|
loggedIn: false,
|
||||||
processingEmailLink: false,
|
processingEmailLink: false,
|
||||||
|
showLoginButtons: true,
|
||||||
|
|
||||||
canLoginLocal: setting('enable_local_logins'),
|
canLoginLocal: setting('enable_local_logins'),
|
||||||
canLoginLocalWithEmail: setting('enable_local_logins_via_email'),
|
canLoginLocalWithEmail: setting('enable_local_logins_via_email'),
|
||||||
loginRequired: Em.computed.alias('application.loginRequired'),
|
loginRequired: Em.computed.alias('application.loginRequired'),
|
||||||
|
|
||||||
resetForm: function() {
|
resetForm: function() {
|
||||||
this.set('authenticate', null);
|
this.setProperties({
|
||||||
this.set('loggingIn', false);
|
'authenticate': null,
|
||||||
this.set('loggedIn', false);
|
'loggingIn': false,
|
||||||
this.set('secondFactorRequired', false);
|
'loggedIn': false,
|
||||||
|
'secondFactorRequired': false,
|
||||||
|
'showLoginButtons': true,
|
||||||
|
});
|
||||||
$("#credentials").show();
|
$("#credentials").show();
|
||||||
$("#second-factor").hide();
|
$("#second-factor").hide();
|
||||||
},
|
},
|
||||||
@ -250,16 +254,28 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
|||||||
}).property('authenticate'),
|
}).property('authenticate'),
|
||||||
|
|
||||||
authenticationComplete(options) {
|
authenticationComplete(options) {
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
function loginError(errorMsg, className) {
|
function loginError(errorMsg, className, callback) {
|
||||||
showModal('login');
|
showModal('login');
|
||||||
Ember.run.next(function() {
|
|
||||||
|
Ember.run.next(() => {
|
||||||
|
callback();
|
||||||
self.flash(errorMsg, className || 'success');
|
self.flash(errorMsg, className || 'success');
|
||||||
self.set('authenticate', null);
|
self.set('authenticate', null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.omniauth_disallow_totp) {
|
||||||
|
return loginError(I18n.t('login.omniauth_disallow_totp'), 'error', () => {
|
||||||
|
this.setProperties({
|
||||||
|
'loginName': options.email,
|
||||||
|
'showLoginButtons': false,
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#login-account-password').focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
for (let i=0; i<AuthErrors.length; i++) {
|
for (let i=0; i<AuthErrors.length; i++) {
|
||||||
const cond = AuthErrors[i];
|
const cond = AuthErrors[i];
|
||||||
if (options[cond]) {
|
if (options[cond]) {
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
{{#login-modal screenX=lastX screenY=lastY loginName=loginName loginPassword=loginPassword loginSecondFactor=loginSecondFactor action="login"}}
|
{{#login-modal screenX=lastX screenY=lastY loginName=loginName loginPassword=loginPassword loginSecondFactor=loginSecondFactor action="login"}}
|
||||||
{{#d-modal-body title="login.title" class="login-modal"}}
|
{{#d-modal-body title="login.title" class="login-modal"}}
|
||||||
{{login-buttons
|
{{#if showLoginButtons}}
|
||||||
canLoginLocalWithEmail=canLoginLocalWithEmail
|
{{login-buttons
|
||||||
processingEmailLink=processingEmailLink
|
canLoginLocalWithEmail=canLoginLocalWithEmail
|
||||||
emailLogin='emailLogin'
|
processingEmailLink=processingEmailLink
|
||||||
externalLogin='externalLogin'}}
|
emailLogin='emailLogin'
|
||||||
|
externalLogin='externalLogin'}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#if canLoginLocal}}
|
{{#if canLoginLocal}}
|
||||||
<form id='login-form' method='post'>
|
<form id='login-form' method='post'>
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
{{#login-modal screenX=lastX screenY=lastY loginName=loginName loginPassword=loginPassword loginSecondFactor=loginSecondFactor action="login"}}
|
{{#login-modal screenX=lastX screenY=lastY loginName=loginName loginPassword=loginPassword loginSecondFactor=loginSecondFactor action="login"}}
|
||||||
{{#d-modal-body title="login.title" class="login-modal"}}
|
{{#d-modal-body title="login.title" class="login-modal"}}
|
||||||
{{login-buttons
|
{{#if showLoginButtons}}
|
||||||
canLoginLocalWithEmail=canLoginLocalWithEmail
|
{{login-buttons
|
||||||
processingEmailLink=processingEmailLink
|
canLoginLocalWithEmail=canLoginLocalWithEmail
|
||||||
emailLogin='emailLogin'
|
processingEmailLink=processingEmailLink
|
||||||
externalLogin='externalLogin'}}
|
emailLogin='emailLogin'
|
||||||
|
externalLogin='externalLogin'}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#if canLoginLocal}}
|
{{#if canLoginLocal}}
|
||||||
<form id='login-form' method='post'>
|
<form id='login-form' method='post'>
|
||||||
|
@ -114,6 +114,11 @@ class Users::OmniauthCallbacksController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def user_found(user)
|
def user_found(user)
|
||||||
|
if user.totp_enabled?
|
||||||
|
@auth_result.omniauth_disallow_totp = true
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
# automatically activate/unstage any account if a provider marked the email valid
|
# automatically activate/unstage any account if a provider marked the email valid
|
||||||
if @auth_result.email_valid && @auth_result.email == user.email
|
if @auth_result.email_valid && @auth_result.email == user.email
|
||||||
user.update!(staged: false)
|
user.update!(staged: false)
|
||||||
|
@ -1140,6 +1140,7 @@ en:
|
|||||||
not_allowed_from_ip_address: "You can't login from that IP address."
|
not_allowed_from_ip_address: "You can't login from that IP address."
|
||||||
admin_not_allowed_from_ip_address: "You can't log in as admin from that IP address."
|
admin_not_allowed_from_ip_address: "You can't log in as admin from that IP address."
|
||||||
resend_activation_email: "Click here to send the activation email again."
|
resend_activation_email: "Click here to send the activation email again."
|
||||||
|
omniauth_disallow_totp: "Your account has 2FA enabled. Please login with your password."
|
||||||
|
|
||||||
resend_title: "Resend Activation Email"
|
resend_title: "Resend Activation Email"
|
||||||
change_email: "Change Email Address"
|
change_email: "Change Email Address"
|
||||||
|
@ -4,7 +4,7 @@ class Auth::Result
|
|||||||
:awaiting_approval, :authenticated, :authenticator_name,
|
:awaiting_approval, :authenticated, :authenticator_name,
|
||||||
:requires_invite, :not_allowed_from_ip_address,
|
:requires_invite, :not_allowed_from_ip_address,
|
||||||
:admin_not_allowed_from_ip_address, :omit_username,
|
:admin_not_allowed_from_ip_address, :omit_username,
|
||||||
:skip_email_validation, :destination_url
|
:skip_email_validation, :destination_url, :omniauth_disallow_totp
|
||||||
|
|
||||||
attr_accessor(
|
attr_accessor(
|
||||||
:failed,
|
:failed,
|
||||||
@ -42,13 +42,22 @@ class Auth::Result
|
|||||||
date: I18n.l(user.suspended_till, format: :date_only), reason: user.suspend_reason)
|
date: I18n.l(user.suspended_till, format: :date_only), reason: user.suspend_reason)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
result = {
|
result =
|
||||||
authenticated: !!authenticated,
|
if omniauth_disallow_totp
|
||||||
awaiting_activation: !!awaiting_activation,
|
{
|
||||||
awaiting_approval: !!awaiting_approval,
|
omniauth_disallow_totp: !!omniauth_disallow_totp,
|
||||||
not_allowed_from_ip_address: !!not_allowed_from_ip_address,
|
email: email
|
||||||
admin_not_allowed_from_ip_address: !!admin_not_allowed_from_ip_address
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
authenticated: !!authenticated,
|
||||||
|
awaiting_activation: !!awaiting_activation,
|
||||||
|
awaiting_approval: !!awaiting_approval,
|
||||||
|
not_allowed_from_ip_address: !!not_allowed_from_ip_address,
|
||||||
|
admin_not_allowed_from_ip_address: !!admin_not_allowed_from_ip_address
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
result[:destination_url] = destination_url if authenticated && destination_url.present?
|
result[:destination_url] = destination_url if authenticated && destination_url.present?
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
@ -132,6 +132,23 @@ RSpec.describe Users::OmniauthCallbacksController do
|
|||||||
expect(user.registration_ip_address).to be_present
|
expect(user.registration_ip_address).to be_present
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when user has second factor enabled' do
|
||||||
|
before do
|
||||||
|
user.create_totp(enabled: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should return the right response' do
|
||||||
|
get "/auth/google_oauth2/callback.json"
|
||||||
|
|
||||||
|
expect(response).to be_success
|
||||||
|
|
||||||
|
response_body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(response_body["email"]).to eq(user.email)
|
||||||
|
expect(response_body["omniauth_disallow_totp"]).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when user has not verified his email' do
|
context 'when user has not verified his email' do
|
||||||
before do
|
before do
|
||||||
GoogleUserInfo.create!(google_user_id: '12345', user: user)
|
GoogleUserInfo.create!(google_user_id: '12345', user: user)
|
||||||
|
Reference in New Issue
Block a user