mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 09:12:45 +08:00
FEATURE: restrict admin access based on IP address
This commit is contained in:
parent
1040a88389
commit
ca5f361d0a
|
@ -21,7 +21,8 @@ Discourse.ScreenedIpAddressFormComponent = Ember.Component.extend({
|
|||
actionNames: function() {
|
||||
return [
|
||||
{id: 'block', name: I18n.t('admin.logs.screened_ips.actions.block')},
|
||||
{id: 'do_nothing', name: I18n.t('admin.logs.screened_ips.actions.do_nothing')}
|
||||
{id: 'do_nothing', name: I18n.t('admin.logs.screened_ips.actions.do_nothing')},
|
||||
{id: 'allow_admin', name: I18n.t('admin.logs.screened_ips.actions.allow_admin')}
|
||||
];
|
||||
}.property(),
|
||||
|
||||
|
|
|
@ -147,6 +147,12 @@ export default DiscourseController.extend(ModalFunctionality, {
|
|||
this.set('authenticate', null);
|
||||
return;
|
||||
}
|
||||
if (options.not_allowed_from_ip_address) {
|
||||
this.send('showLogin');
|
||||
this.flash(I18n.t('login.not_allowed_from_ip_address'), 'success');
|
||||
this.set('authenticate', null);
|
||||
return;
|
||||
}
|
||||
// Reload the page if we're authenticated
|
||||
if (options.authenticated) {
|
||||
if (window.location.pathname === Discourse.getURL('/login')) {
|
||||
|
|
|
@ -84,6 +84,11 @@ class SessionController < ApplicationController
|
|||
return
|
||||
end
|
||||
|
||||
if ScreenedIpAddress.block_login?(user, request.remote_ip)
|
||||
not_allowed_from_ip_address(user)
|
||||
return
|
||||
end
|
||||
|
||||
(user.active && user.email_confirmed?) ? login(user) : not_activated(user)
|
||||
end
|
||||
|
||||
|
@ -151,6 +156,10 @@ class SessionController < ApplicationController
|
|||
}
|
||||
end
|
||||
|
||||
def not_allowed_from_ip_address(user)
|
||||
render json: {error: I18n.t("login.not_allowed_from_ip_address", username: user.username)}
|
||||
end
|
||||
|
||||
def failed_to_login(user)
|
||||
message = user.suspend_reason ? "login.suspended_with_reason" : "login.suspended"
|
||||
|
||||
|
|
|
@ -81,8 +81,9 @@ class Users::OmniauthCallbacksController < ApplicationController
|
|||
user.toggle(:active).save
|
||||
end
|
||||
|
||||
# log on any account that is active with forum access
|
||||
if Guardian.new(user).can_access_forum? && user.active
|
||||
if ScreenedIpAddress.block_login?(user, request.remote_ip)
|
||||
@data.not_allowed_from_ip_address = true
|
||||
elsif Guardian.new(user).can_access_forum? && user.active # log on any account that is active with forum access
|
||||
log_on_user(user)
|
||||
Invite.invalidate_for_email(user.email) # invite link can't be used to log in anymore
|
||||
session[:authentication] = nil # don't carry around old auth info, perhaps move elsewhere
|
||||
|
|
|
@ -76,10 +76,19 @@ class ScreenedIpAddress < ActiveRecord::Base
|
|||
exists_for_ip_address_and_action?(ip_address, actions[:do_nothing])
|
||||
end
|
||||
|
||||
def self.exists_for_ip_address_and_action?(ip_address, action_type)
|
||||
def self.exists_for_ip_address_and_action?(ip_address, action_type, opts={})
|
||||
b = match_for_ip_address(ip_address)
|
||||
b.record_match! if b
|
||||
!!b and b.action_type == action_type
|
||||
found = (!!b and b.action_type == action_type)
|
||||
b.record_match! if found and opts[:record_match] != false
|
||||
found
|
||||
end
|
||||
|
||||
def self.block_login?(user, ip_address)
|
||||
return false if user.nil?
|
||||
return false if !user.admin?
|
||||
return false if ScreenedIpAddress.where(action_type: actions[:allow_admin]).count == 0
|
||||
return true if ip_address.nil?
|
||||
!exists_for_ip_address_and_action?(ip_address, actions[:allow_admin], record_match: false)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -601,6 +601,7 @@ en:
|
|||
awaiting_approval: "Your account has not been approved by a staff member yet. You will be sent an email when it is approved."
|
||||
requires_invite: "Sorry, access to this forum is by invite only."
|
||||
not_activated: "You can't log in yet. We previously sent an activation email to you at <b>{{sentTo}}</b>. Please follow the instructions in that email to activate your account."
|
||||
not_allowed_from_ip_address: "You can't login from that IP address."
|
||||
resend_activation_email: "Click here to send the activation email again."
|
||||
sent_activation_email_again: "We sent another activation email to you at <b>{{currentEmail}}</b>. It might take a few minutes for it to arrive; be sure to check your spam folder."
|
||||
google:
|
||||
|
@ -1799,6 +1800,7 @@ en:
|
|||
actions:
|
||||
block: "Block"
|
||||
do_nothing: "Allow"
|
||||
allow_admin: "Allow Admin"
|
||||
form:
|
||||
label: "New:"
|
||||
ip_address: "IP address"
|
||||
|
|
|
@ -1082,6 +1082,7 @@ en:
|
|||
active: "Your account is activated and ready to use."
|
||||
activate_email: "You're almost done! We sent an activation email to <b>%{email}</b>. Please follow the instructions in the email to activate your account."
|
||||
not_activated: "You can't log in yet. We sent an activation email to you. Please follow the instructions in the email to activate your account."
|
||||
not_allowed_from_ip_address: "You can't login as %{username} from that IP address."
|
||||
suspended: "You can't log in until %{date}."
|
||||
suspended_with_reason: "You can't log in until %{date}. The reason you were suspended: %{reason}"
|
||||
errors: "%{errors}"
|
||||
|
|
|
@ -2,7 +2,7 @@ class Auth::Result
|
|||
attr_accessor :user, :name, :username, :email, :user,
|
||||
:email_valid, :extra_data, :awaiting_activation,
|
||||
:awaiting_approval, :authenticated, :authenticator_name,
|
||||
:requires_invite
|
||||
:requires_invite, :not_allowed_from_ip_address
|
||||
|
||||
def session_data
|
||||
{
|
||||
|
@ -22,7 +22,8 @@ class Auth::Result
|
|||
{
|
||||
authenticated: !!authenticated,
|
||||
awaiting_activation: !!awaiting_activation,
|
||||
awaiting_approval: !!awaiting_approval
|
||||
awaiting_approval: !!awaiting_approval,
|
||||
not_allowed_from_ip_address: !!not_allowed_from_ip_address
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -27,7 +27,8 @@ module CurrentUser
|
|||
end
|
||||
|
||||
def current_user
|
||||
current_user_provider.current_user
|
||||
c = current_user_provider.current_user
|
||||
ScreenedIpAddress.block_login?(c, request.remote_ip) ? nil : c
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -3,7 +3,7 @@ module ScreeningModel
|
|||
|
||||
module ClassMethods
|
||||
def actions
|
||||
@actions ||= Enum.new(:block, :do_nothing)
|
||||
@actions ||= Enum.new(:block, :do_nothing, :allow_admin)
|
||||
end
|
||||
|
||||
def default_action(action_key)
|
||||
|
|
|
@ -291,6 +291,36 @@ describe SessionController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admins are restricted by ip address' do
|
||||
let(:permitted_ip_address) { '111.234.23.11' }
|
||||
|
||||
before do
|
||||
Fabricate(:screened_ip_address, ip_address: permitted_ip_address, action_type: ScreenedIpAddress.actions[:allow_admin])
|
||||
end
|
||||
|
||||
it 'is successful for admin at the ip address' do
|
||||
User.any_instance.stubs(:admin?).returns(true)
|
||||
ActionDispatch::Request.any_instance.stubs(:remote_ip).returns(permitted_ip_address)
|
||||
xhr :post, :create, login: user.username, password: 'myawesomepassword'
|
||||
session[:current_user_id].should == user.id
|
||||
end
|
||||
|
||||
it 'returns an error for admin not at the ip address' do
|
||||
User.any_instance.stubs(:admin?).returns(true)
|
||||
ActionDispatch::Request.any_instance.stubs(:remote_ip).returns("111.234.23.12")
|
||||
xhr :post, :create, login: user.username, password: 'myawesomepassword'
|
||||
JSON.parse(response.body)['error'].should be_present
|
||||
session[:current_user_id].should_not == user.id
|
||||
end
|
||||
|
||||
it 'is successful for non-admin not at the ip address' do
|
||||
User.any_instance.stubs(:admin?).returns(false)
|
||||
ActionDispatch::Request.any_instance.stubs(:remote_ip).returns("111.234.23.12")
|
||||
xhr :post, :create, login: user.username, password: 'myawesomepassword'
|
||||
session[:current_user_id].should == user.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when email has not been confirmed' do
|
||||
|
|
|
@ -235,4 +235,51 @@ describe ScreenedIpAddress do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#block_login?' do
|
||||
context 'no allow_admin records exist' do
|
||||
it "returns false when user is nil" do
|
||||
described_class.block_login?(nil, '123.12.12.12').should == false
|
||||
end
|
||||
|
||||
it "returns false for non-admin user" do
|
||||
described_class.block_login?(Fabricate.build(:user), '123.12.12.12').should == false
|
||||
end
|
||||
|
||||
it "returns false for admin user" do
|
||||
described_class.block_login?(Fabricate.build(:admin), '123.12.12.12').should == false
|
||||
end
|
||||
|
||||
it "returns false for admin user and ip_address arg is nil" do
|
||||
described_class.block_login?(Fabricate.build(:admin), nil).should == false
|
||||
end
|
||||
end
|
||||
|
||||
context 'allow_admin record exists' do
|
||||
before do
|
||||
@permitted_ip_address = '111.234.23.11'
|
||||
Fabricate(:screened_ip_address, ip_address: @permitted_ip_address, action_type: described_class.actions[:allow_admin])
|
||||
end
|
||||
|
||||
it "returns false when user is nil" do
|
||||
described_class.block_login?(nil, @permitted_ip_address).should == false
|
||||
end
|
||||
|
||||
it "returns false for an admin user at the allowed ip address" do
|
||||
described_class.block_login?(Fabricate.build(:admin), @permitted_ip_address).should == false
|
||||
end
|
||||
|
||||
it "returns true for an admin user at another ip address" do
|
||||
described_class.block_login?(Fabricate.build(:admin), '123.12.12.12').should == true
|
||||
end
|
||||
|
||||
it "returns false for regular user at allowed ip address" do
|
||||
described_class.block_login?(Fabricate.build(:user), @permitted_ip_address).should == false
|
||||
end
|
||||
|
||||
it "returns false for regular user at another ip address" do
|
||||
described_class.block_login?(Fabricate.build(:user), '123.12.12.12').should == false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue
Block a user