From 35afbf1f8ce923b3bb5d6a354e7d616fa07108cb Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Wed, 4 Oct 2017 01:03:32 +0300 Subject: [PATCH] Refactor IP registrar restriction #600 --- app/api/repp/api.rb | 2 +- app/controllers/registrar/base_controller.rb | 49 +++++----- .../registrar/sessions_controller.rb | 23 +++-- app/models/authorization/restricted_ip.rb | 25 +++++ app/models/registrar.rb | 5 - app/models/white_ip.rb | 7 +- app/views/registrar/sessions/denied.haml | 1 - config/locales/api/authorization.en.yml | 4 + config/locales/en.yml | 2 - config/locales/registrar/authorization.en.yml | 4 + doc/controllers_complete.svg | 5 - spec/features/registrar/sessions/new_spec.rb | 42 +++++++++ .../authorization/restricted_ip_spec.rb | 94 +++++++++++++++++++ spec/models/white_ip_spec.rb | 28 ++++++ spec/requests/registrar/sessions_spec.rb | 67 +++++++++++++ 15 files changed, 304 insertions(+), 54 deletions(-) create mode 100644 app/models/authorization/restricted_ip.rb delete mode 100644 app/views/registrar/sessions/denied.haml create mode 100644 config/locales/api/authorization.en.yml create mode 100644 config/locales/registrar/authorization.en.yml create mode 100644 spec/features/registrar/sessions/new_spec.rb create mode 100644 spec/models/authorization/restricted_ip_spec.rb create mode 100644 spec/requests/registrar/sessions_spec.rb diff --git a/app/api/repp/api.rb b/app/api/repp/api.rb index 27d0322b0..394edfdad 100644 --- a/app/api/repp/api.rb +++ b/app/api/repp/api.rb @@ -15,7 +15,7 @@ module Repp before do webclient_request = ENV['webclient_ips'].split(',').map(&:strip).include?(request.ip) unless webclient_request - error! I18n.t('ip_is_not_whitelisted'), 401 unless @current_user.registrar.api_ip_white?(request.ip) + error! I18n.t('api.authorization.ip_not_allowed', ip: request.ip), 401 unless @current_user.registrar.api_ip_white?(request.ip) end if @current_user.cannot?(:view, :repp) diff --git a/app/controllers/registrar/base_controller.rb b/app/controllers/registrar/base_controller.rb index d2b2edd69..0455b2a2e 100644 --- a/app/controllers/registrar/base_controller.rb +++ b/app/controllers/registrar/base_controller.rb @@ -1,40 +1,37 @@ class Registrar class BaseController < ApplicationController - before_action :authenticate_user!, :check_ip - include Registrar::ApplicationHelper + before_action :authenticate_user! + before_action :check_ip_restriction helper_method :depp_controller? - - def depp_controller? - false - end - - def check_ip - return unless current_user - unless current_user.is_a? ApiUser - sign_out(current_user) - return - end - - registrar_ip_whitelisted = current_user.registrar.registrar_ip_white?(request.ip) - - return if registrar_ip_whitelisted - flash[:alert] = t('ip_is_not_whitelisted') - sign_out(current_user) - redirect_to registrar_login_path and return - end - helper_method :head_title_sufix - def head_title_sufix - t(:registrar_head_title_sufix) - end - protected def current_ability @current_ability ||= Ability.new(current_user, request.remote_ip) end + + private + + def check_ip_restriction + ip_restriction = Authorization::RestrictedIP.new(request.ip) + allowed = ip_restriction.can_access_registrar_area?(current_user.registrar) + + unless allowed + flash[:alert] = t('registrar.authorization.ip_not_allowed', ip: request.ip) + sign_out current_user + redirect_to registrar_login_url + end + end + + def depp_controller? + false + end + + def head_title_sufix + t(:registrar_head_title_sufix) + end end end diff --git a/app/controllers/registrar/sessions_controller.rb b/app/controllers/registrar/sessions_controller.rb index 6bf31b03a..9f6189c71 100644 --- a/app/controllers/registrar/sessions_controller.rb +++ b/app/controllers/registrar/sessions_controller.rb @@ -1,13 +1,8 @@ class Registrar class SessionsController < Devise::SessionsController + before_action :check_ip_restriction helper_method :depp_controller? - def depp_controller? - false - end - - before_action :check_ip - def login @depp_user = Depp::User.new end @@ -157,16 +152,24 @@ class Registrar # rubocop: enable Metrics/CyclomaticComplexity # rubocop: enable Metrics/MethodLength + private + + def depp_controller? + false + end + def find_user_by_idc(idc) return User.new unless idc ApiUser.find_by(identity_code: idc) || User.new end - private + def check_ip_restriction + ip_restriction = Authorization::RestrictedIP.new(request.ip) + allowed = ip_restriction.can_access_registrar_area_sign_in_page? - def check_ip - return if WhiteIp.registrar_ip_white?(request.ip) - render :denied, :layout => false, status: :forbidden, :locals => { :ip => request.ip } and return + unless allowed + render text: t('registrar.authorization.ip_not_allowed', ip: request.ip), status: :forbidden + end end end end diff --git a/app/models/authorization/restricted_ip.rb b/app/models/authorization/restricted_ip.rb new file mode 100644 index 000000000..b3c7b7cdb --- /dev/null +++ b/app/models/authorization/restricted_ip.rb @@ -0,0 +1,25 @@ +module Authorization + class RestrictedIP + def initialize(ip) + @ip = ip + end + + def self.enabled? + Setting.registrar_ip_whitelist_enabled + end + + def can_access_registrar_area?(registrar) + return true unless self.class.enabled? + registrar.white_ips.registrar_area.include_ip?(ip) + end + + def can_access_registrar_area_sign_in_page? + return true unless self.class.enabled? + WhiteIp.registrar_area.include_ip?(ip) + end + + private + + attr_reader :ip + end +end diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 734e68898..b906beee3 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -162,9 +162,4 @@ class Registrar < ActiveRecord::Base return true unless Setting.api_ip_whitelist_enabled white_ips.api.pluck(:ipv4, :ipv6).flatten.include?(ip) end - - def registrar_ip_white?(ip) - return true unless Setting.registrar_ip_whitelist_enabled - white_ips.registrar.pluck(:ipv4, :ipv6).flatten.include?(ip) - end end diff --git a/app/models/white_ip.rb b/app/models/white_ip.rb index 918315004..f11606610 100644 --- a/app/models/white_ip.rb +++ b/app/models/white_ip.rb @@ -18,16 +18,15 @@ class WhiteIp < ActiveRecord::Base INTERFACES = [API, REGISTRAR] scope :api, -> { where("interfaces @> ?::varchar[]", "{#{API}}") } - scope :registrar, -> { where("interfaces @> ?::varchar[]", "{#{REGISTRAR}}") } + scope :registrar_area, -> { where("interfaces @> ?::varchar[]", "{#{REGISTRAR}}") } def interfaces=(interfaces) super(interfaces.reject(&:blank?)) end class << self - def registrar_ip_white?(ip) - return true unless Setting.registrar_ip_whitelist_enabled - WhiteIp.where(ipv4: ip).registrar.any? + def include_ip?(ip) + where("#{table_name}.ipv4 = '#{ip}' OR #{table_name}.ipv6 = '#{ip}'").any? end end end diff --git a/app/views/registrar/sessions/denied.haml b/app/views/registrar/sessions/denied.haml deleted file mode 100644 index a1aea470b..000000000 --- a/app/views/registrar/sessions/denied.haml +++ /dev/null @@ -1 +0,0 @@ -#{t('access_denied')} from #{ip} diff --git a/config/locales/api/authorization.en.yml b/config/locales/api/authorization.en.yml new file mode 100644 index 000000000..53c49d146 --- /dev/null +++ b/config/locales/api/authorization.en.yml @@ -0,0 +1,4 @@ +en: + api: + authorization: + ip_not_allowed: Access denied from IP %{ip} diff --git a/config/locales/en.yml b/config/locales/en.yml index 2627b112a..a9b54c210 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -797,7 +797,6 @@ en: domain_delete_rejected_title: 'Domain deletion rejection has been received successfully' domain_delete_rejected_body: 'You have rejected pending domain deletion. You will receive confirmation by email.' no_permission: 'No permission' - access_denied: 'Access denied' common_name: 'Common name' md5: 'Md5' interface: 'Interface' @@ -815,7 +814,6 @@ en: create_bank_statement: 'Create bank statement' create_bank_transaction: 'Create bank transaction' create_new_invoice: 'Create new invoice' - ip_is_not_whitelisted: 'IP is not whitelisted' billing_settings: 'Billing settings' registry_settings: 'Registry settings' registry_billing_email: 'Billing e-mail' diff --git a/config/locales/registrar/authorization.en.yml b/config/locales/registrar/authorization.en.yml new file mode 100644 index 000000000..1e4395cb3 --- /dev/null +++ b/config/locales/registrar/authorization.en.yml @@ -0,0 +1,4 @@ +en: + registrar: + authorization: + ip_not_allowed: Access denied from IP %{ip} diff --git a/doc/controllers_complete.svg b/doc/controllers_complete.svg index f48433540..1c9bc94ec 100644 --- a/doc/controllers_complete.svg +++ b/doc/controllers_complete.svg @@ -136,9 +136,6 @@ RegistrarController -check_ip -depp_controller? -head_title_sufix _layout @@ -491,8 +488,6 @@ Registrar::SessionsController create -depp_controller? -find_user_by_idc id login login_mid diff --git a/spec/features/registrar/sessions/new_spec.rb b/spec/features/registrar/sessions/new_spec.rb new file mode 100644 index 000000000..feec6cae1 --- /dev/null +++ b/spec/features/registrar/sessions/new_spec.rb @@ -0,0 +1,42 @@ +require 'rails_helper' + +RSpec.feature 'Registrar area ip restriction', settings: false do + context 'when enabled' do + background do + Setting.registrar_ip_whitelist_enabled = true + end + + context 'when ip is allowed' do + given!(:white_ip) { create(:white_ip, + ipv4: '127.0.0.1', + interfaces: [WhiteIp::REGISTRAR]) } + + it 'does not show error message' do + visit registrar_login_path + expect(page).to_not have_text(error_message) + end + end + + context 'when ip is not allowed' do + it 'shows error message' do + visit registrar_login_path + expect(page).to have_text(error_message) + end + end + end + + context 'when disabled' do + background do + Setting.registrar_ip_whitelist_enabled = false + end + + it 'does not show error message' do + visit registrar_login_path + expect(page).to_not have_text(error_message) + end + end + + def error_message + t('registrar.authorization.ip_not_allowed', ip: '127.0.0.1') + end +end diff --git a/spec/models/authorization/restricted_ip_spec.rb b/spec/models/authorization/restricted_ip_spec.rb new file mode 100644 index 000000000..6fba76657 --- /dev/null +++ b/spec/models/authorization/restricted_ip_spec.rb @@ -0,0 +1,94 @@ +require 'rails_helper' + +RSpec.describe Authorization::RestrictedIP do + describe '#enabled?', db: true, settings: false do + context 'when "registrar_ip_whitelist_enabled" is true' do + before do + Setting.registrar_ip_whitelist_enabled = true + end + + specify do + expect(described_class).to be_enabled + end + end + + context 'when "registrar_ip_whitelist_enabled" is false' do + before do + Setting.registrar_ip_whitelist_enabled = false + end + + specify do + expect(described_class).to_not be_enabled + end + end + end + + describe '#can_access_registrar_area?', db: true do + let(:registrar) { create(:registrar) } + subject(:allowed) { described_class.new('127.0.0.1').can_access_registrar_area?(registrar) } + + context 'when enabled' do + before do + allow(described_class).to receive(:enabled?).and_return(true) + end + + context 'when ip is whitelisted', db: true do + let!(:white_ip) { create(:white_ip, registrar: registrar, ipv4: '127.0.0.1', interfaces: [WhiteIp::REGISTRAR]) } + + specify do + expect(allowed).to be true + end + end + + context 'when ip is not whitelisted' do + specify do + expect(allowed).to be false + end + end + end + + context 'when disabled' do + before do + allow(described_class).to receive(:enabled?).and_return(false) + end + + specify do + expect(allowed).to be true + end + end + end + + describe '#can_access_registrar_area_sign_in_page?' do + subject(:allowed) { described_class.new('127.0.0.1').can_access_registrar_area_sign_in_page? } + + context 'when enabled' do + before do + allow(described_class).to receive(:enabled?).and_return(true) + end + + context 'when ip is whitelisted', db: true do + let!(:white_ip) { create(:white_ip, ipv4: '127.0.0.1', interfaces: [WhiteIp::REGISTRAR]) } + + specify do + expect(allowed).to be true + end + end + + context 'when ip is not whitelisted' do + specify do + expect(allowed).to be false + end + end + end + + context 'when disabled' do + before do + allow(described_class).to receive(:enabled?).and_return(false) + end + + specify do + expect(allowed).to be true + end + end + end +end diff --git a/spec/models/white_ip_spec.rb b/spec/models/white_ip_spec.rb index 931431c1e..bb286c0ca 100644 --- a/spec/models/white_ip_spec.rb +++ b/spec/models/white_ip_spec.rb @@ -38,4 +38,32 @@ describe WhiteIp do end end end + + describe '#include_ip?' do + context 'when given ip v4 exists' do + before do + create(:white_ip, ipv4: '127.0.0.1') + end + + specify do + expect(described_class.include_ip?('127.0.0.1')).to be true + end + end + + context 'when given ip v6 exists' do + before do + create(:white_ip, ipv6: '::1') + end + + specify do + expect(described_class.include_ip?('::1')).to be true + end + end + + context 'when given ip does not exists', db: false do + specify do + expect(described_class.include_ip?('127.0.0.1')).to be false + end + end + end end diff --git a/spec/requests/registrar/sessions_spec.rb b/spec/requests/registrar/sessions_spec.rb new file mode 100644 index 000000000..674cd8f0c --- /dev/null +++ b/spec/requests/registrar/sessions_spec.rb @@ -0,0 +1,67 @@ +require 'rails_helper' + +RSpec.describe 'Registrar session management', db: false do + describe 'GET /registrar/login' do + context 'when ip is allowed' do + let(:restricted_ip) { instance_double(Authorization::RestrictedIP, + can_access_registrar_area_sign_in_page?: true) } + + before do + allow(Authorization::RestrictedIP).to receive(:new).and_return(restricted_ip) + end + + specify do + get registrar_login_path + expect(response).to be_success + end + end + + context 'when ip is not allowed' do + let(:restricted_ip) { instance_double(Authorization::RestrictedIP, + can_access_registrar_area_sign_in_page?: false) } + + before do + allow(Authorization::RestrictedIP).to receive(:new).and_return(restricted_ip) + end + + specify do + get registrar_login_path + expect(response).to be_forbidden + end + end + end + + describe 'POST /registrar/sessions' do + context 'when ip is allowed' do + let(:restricted_ip) { instance_double(Authorization::RestrictedIP, + can_access_registrar_area_sign_in_page?: true) } + + before do + allow(Authorization::RestrictedIP).to receive(:new).and_return(restricted_ip) + end + + specify do + make_request + expect(response).to be_success + end + end + + context 'when ip is not allowed' do + let(:restricted_ip) { instance_double(Authorization::RestrictedIP, + can_access_registrar_area_sign_in_page?: false) } + + before do + allow(Authorization::RestrictedIP).to receive(:new).and_return(restricted_ip) + end + + specify do + make_request + expect(response).to be_forbidden + end + end + + def make_request + post registrar_sessions_path, depp_user: { tag: 'test', password: 'test' } + end + end +end