mirror of
https://github.com/internetee/registry.git
synced 2025-08-02 16:02:03 +02:00
Merge remote-tracking branch 'origin/master' into 1832-registrar-prefix-on-contact-info-and-check-requests
This commit is contained in:
commit
60f260968a
16 changed files with 238 additions and 8 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -1,3 +1,13 @@
|
|||
10.02.2021
|
||||
* Option to remove email addresses from AWS SES Supression list [#1839](https://github.com/internetee/registry/issues/1839)
|
||||
* Added separate key for bounce API [#1842](https://github.com/internetee/registry/pull/1842)
|
||||
|
||||
09.02.2021
|
||||
* Added new endpoint for WHOIS contact requests [#1794](https://github.com/internetee/registry/pull/1794)
|
||||
|
||||
05.02.2021
|
||||
* Fixed IPv4 empty string issue in case of IPv6 only entries for IP whitelist [#1833](https://github.com/internetee/registry/issues/1833)
|
||||
|
||||
02.02.2021
|
||||
* Fixed updateProhibited status not affecting bulk tech contact change operation [#1820](https://github.com/internetee/registry/pull/1820)
|
||||
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -92,3 +92,5 @@ group :test do
|
|||
gem 'webdrivers'
|
||||
gem 'webmock'
|
||||
end
|
||||
|
||||
gem 'aws-sdk-sesv2', '~> 1.16'
|
||||
|
|
14
Gemfile.lock
14
Gemfile.lock
|
@ -148,6 +148,18 @@ GEM
|
|||
attr_required (1.0.1)
|
||||
autoprefixer-rails (10.0.0.2)
|
||||
execjs
|
||||
aws-eventstream (1.1.0)
|
||||
aws-partitions (1.424.0)
|
||||
aws-sdk-core (3.112.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.239.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-sesv2 (1.16.0)
|
||||
aws-sdk-core (~> 3, >= 3.112.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sigv4 (1.2.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
bcrypt (3.1.16)
|
||||
bindata (2.4.8)
|
||||
bootsnap (1.4.8)
|
||||
|
@ -226,6 +238,7 @@ GEM
|
|||
i18n_data (0.10.0)
|
||||
isikukood (0.1.2)
|
||||
iso8601 (0.12.1)
|
||||
jmespath (1.4.0)
|
||||
jquery-rails (4.4.0)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
|
@ -484,6 +497,7 @@ DEPENDENCIES
|
|||
active_interaction (~> 3.8)
|
||||
activerecord-import
|
||||
airbrake
|
||||
aws-sdk-sesv2 (~> 1.16)
|
||||
bootsnap (>= 1.1.0)
|
||||
bootstrap-sass (~> 3.4)
|
||||
cancancan
|
||||
|
|
|
@ -11,7 +11,7 @@ module Api
|
|||
end
|
||||
|
||||
def authenticate_shared_key
|
||||
api_key = "Basic #{ENV['api_shared_key']}"
|
||||
api_key = "Basic #{ENV['rwhois_internal_api_shared_key']}"
|
||||
head(:unauthorized) unless api_key == request.authorization
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module Api
|
||||
module V1
|
||||
class BouncesController < BaseController
|
||||
before_action :authenticate_shared_key
|
||||
before_action :validate_shared_key_integrity
|
||||
|
||||
# POST api/v1/bounces/
|
||||
def create
|
||||
|
@ -20,6 +20,13 @@ module Api
|
|||
|
||||
params.require(:data)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_shared_key_integrity
|
||||
api_key = "Basic #{ENV['rwhois_bounces_api_shared_key']}"
|
||||
head(:unauthorized) unless api_key == request.authorization
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
37
app/controllers/api/v1/contact_requests_controller.rb
Normal file
37
app/controllers/api/v1/contact_requests_controller.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
module Api
|
||||
module V1
|
||||
class ContactRequestsController < BaseController
|
||||
before_action :authenticate_shared_key
|
||||
|
||||
# POST api/v1/contact_requests/
|
||||
def create
|
||||
return head(:bad_request) if contact_request_params[:email].blank?
|
||||
|
||||
contact_request = ContactRequest.save_record(contact_request_params)
|
||||
render json: contact_request, status: :created
|
||||
rescue StandardError
|
||||
head(:bad_request)
|
||||
end
|
||||
|
||||
def update
|
||||
return head(:bad_request) if params[:id].blank?
|
||||
|
||||
process_id(params[:id])
|
||||
end
|
||||
|
||||
def process_id(id)
|
||||
record = ContactRequest.find_by(id: id)
|
||||
return :not_found unless record
|
||||
|
||||
record.update_status(contact_request_params)
|
||||
render json: record, status: :ok
|
||||
rescue StandardError
|
||||
head :bad_request
|
||||
end
|
||||
|
||||
def contact_request_params
|
||||
params.require(:contact_request).permit(:email, :whois_record_id, :name, :status, :ip)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,6 @@
|
|||
class BouncedMailAddress < ApplicationRecord
|
||||
validates :email, :message_id, :bounce_type, :bounce_subtype, :action, :status, presence: true
|
||||
after_destroy :destroy_aws_suppression
|
||||
|
||||
def bounce_reason
|
||||
"#{action} (#{status} #{diagnostic})"
|
||||
|
@ -25,4 +26,20 @@ class BouncedMailAddress < ApplicationRecord
|
|||
diagnostic: bounced_record['diagnosticCode'],
|
||||
}
|
||||
end
|
||||
|
||||
def destroy_aws_suppression
|
||||
return unless BouncedMailAddress.ses_configured?
|
||||
|
||||
res = Aws::SESV2::Client.new.delete_suppressed_destination(email_address: email)
|
||||
res.successful?
|
||||
rescue Aws::SESV2::Errors::ServiceError => e
|
||||
logger.warn("Suppression not removed. #{e}")
|
||||
end
|
||||
|
||||
def self.ses_configured?
|
||||
ses ||= Aws::SESV2::Client.new
|
||||
ses.config.credentials.access_key_id.present?
|
||||
rescue Aws::Errors::MissingRegionError
|
||||
false
|
||||
end
|
||||
end
|
||||
|
|
40
app/models/contact_request.rb
Normal file
40
app/models/contact_request.rb
Normal file
|
@ -0,0 +1,40 @@
|
|||
class ContactRequest < ApplicationRecord
|
||||
establish_connection :"whois_#{Rails.env}"
|
||||
self.table_name = 'contact_requests'
|
||||
|
||||
STATUS_NEW = 'new'.freeze
|
||||
STATUS_CONFIRMED = 'confirmed'.freeze
|
||||
STATUS_SENT = 'sent'.freeze
|
||||
STATUSES = [STATUS_NEW, STATUS_CONFIRMED, STATUS_SENT].freeze
|
||||
|
||||
validates :whois_record_id, presence: true
|
||||
validates :email, presence: true
|
||||
validates :name, presence: true
|
||||
validates :status, inclusion: { in: STATUSES }
|
||||
|
||||
attr_readonly :secret,
|
||||
:valid_to
|
||||
|
||||
def self.save_record(params)
|
||||
contact_request = new(params)
|
||||
contact_request.secret = create_random_secret
|
||||
contact_request.valid_to = set_valid_to_24_hours_from_now
|
||||
contact_request.status = STATUS_NEW
|
||||
contact_request.save!
|
||||
contact_request
|
||||
end
|
||||
|
||||
def update_status(params)
|
||||
self.status = params['status']
|
||||
self.ip_address = params['ip']
|
||||
save!
|
||||
end
|
||||
|
||||
def self.create_random_secret
|
||||
SecureRandom.hex(64)
|
||||
end
|
||||
|
||||
def self.set_valid_to_24_hours_from_now
|
||||
(Time.zone.now + 24.hours)
|
||||
end
|
||||
end
|
|
@ -4,8 +4,13 @@ class WhiteIp < ApplicationRecord
|
|||
|
||||
validate :valid_ipv4?
|
||||
validate :valid_ipv6?
|
||||
|
||||
validate :validate_ipv4_and_ipv6
|
||||
before_save :normalize_blank_values
|
||||
|
||||
def normalize_blank_values
|
||||
%i[ipv4 ipv6].each { |c| self[c].present? || self[c] = nil }
|
||||
end
|
||||
|
||||
def validate_ipv4_and_ipv6
|
||||
return if ipv4.present? || ipv6.present?
|
||||
errors.add(:base, I18n.t(:ipv4_or_ipv6_must_be_present))
|
||||
|
@ -50,10 +55,10 @@ class WhiteIp < ApplicationRecord
|
|||
def ids_including(ip)
|
||||
ipv4 = ipv6 = []
|
||||
if check_ip4(ip).present?
|
||||
ipv4 = select { |white_ip| IPAddr.new(white_ip.ipv4, Socket::AF_INET) === check_ip4(ip) }
|
||||
ipv4 = select { |white_ip| check_ip4(white_ip.ipv4) === check_ip4(ip) }
|
||||
end
|
||||
if check_ip6(ip).present?
|
||||
ipv6 = select { |white_ip| IPAddr.new(white_ip.ipv6, Socket::AF_INET6) === check_ip6(ip) }
|
||||
ipv6 = select { |white_ip| check_ip6(white_ip.ipv6) === check_ip6(ip) }
|
||||
end
|
||||
(ipv4 + ipv6).pluck(:id).flatten.uniq
|
||||
end
|
||||
|
|
|
@ -87,8 +87,11 @@ sk_digi_doc_service_name: 'Testimine'
|
|||
registrant_api_base_url:
|
||||
registrant_api_auth_allowed_ips: '127.0.0.1, 0.0.0.0' #ips, separated with commas
|
||||
|
||||
# Bounces API
|
||||
api_shared_key: testkey
|
||||
# Shared key for REST-WHOIS Bounces API incl. CERT
|
||||
rwhois_bounces_api_shared_key: testkey
|
||||
|
||||
# Link to REST-WHOIS API
|
||||
rwhois_internal_api_shared_key: testkey
|
||||
|
||||
# Base URL (inc. https://) of REST registrant portal
|
||||
# Leave blank to use internal registrant portal
|
||||
|
|
4
config/initializers/aws_ses.rb
Normal file
4
config/initializers/aws_ses.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
Aws.config.update(
|
||||
region: ENV['aws_default_region'],
|
||||
credentials: Aws::Credentials.new(ENV['aws_access_key_id'], ENV['aws_secret_access_key'])
|
||||
)
|
|
@ -91,6 +91,7 @@ Rails.application.routes.draw do
|
|||
end
|
||||
|
||||
resources :auctions, only: %i[index show update], param: :uuid
|
||||
resources :contact_requests, only: %i[create update], param: :id
|
||||
resources :bounces, only: %i[create]
|
||||
end
|
||||
|
||||
|
|
8
test/fixtures/contact_requests.yml
vendored
Normal file
8
test/fixtures/contact_requests.yml
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
new:
|
||||
whois_record_id: 1
|
||||
email: aaa@bbb.com
|
||||
name: Testname
|
||||
status: new
|
||||
secret: somesecret
|
||||
valid_to: 2010-07-05
|
||||
|
|
@ -2,7 +2,7 @@ require 'test_helper'
|
|||
|
||||
class BouncesApiV1CreateTest < ActionDispatch::IntegrationTest
|
||||
def setup
|
||||
@api_key = "Basic #{ENV['api_shared_key']}"
|
||||
@api_key = "Basic #{ENV['rwhois_bounces_api_shared_key']}"
|
||||
@headers = { "Authorization": "#{@api_key}" }
|
||||
@json_body = { "data": valid_bounce_request }.as_json
|
||||
end
|
||||
|
|
68
test/integration/api/v1/contact_requests_test.rb
Normal file
68
test/integration/api/v1/contact_requests_test.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
require 'test_helper'
|
||||
|
||||
class ApiV1ContactRequestTest < ActionDispatch::IntegrationTest
|
||||
def setup
|
||||
@api_key = "Basic #{ENV['rwhois_internal_api_shared_key']}"
|
||||
@headers = { "Authorization": "#{@api_key}" }
|
||||
@json_create = { "contact_request": valid_contact_request_create }.as_json
|
||||
@json_update = { "contact_request": valid_contact_request_update }.as_json
|
||||
@contact_request = contact_requests(:new)
|
||||
end
|
||||
|
||||
def test_authorizes_api_request
|
||||
post api_v1_contact_requests_path, params: @json_create, headers: @headers
|
||||
assert_response :created
|
||||
|
||||
invalid_headers = { "Authorization": "Basic invalid_api_key" }
|
||||
post api_v1_contact_requests_path, params: @json_create, headers: invalid_headers
|
||||
assert_response :unauthorized
|
||||
end
|
||||
|
||||
def test_saves_new_contact_request
|
||||
request_body = @json_create.dup
|
||||
random_mail = "#{rand(10000..99999)}@registry.test"
|
||||
request_body['contact_request']['email'] = random_mail
|
||||
|
||||
post api_v1_contact_requests_path, params: request_body, headers: @headers
|
||||
assert_response :created
|
||||
|
||||
contact_request = ContactRequest.last
|
||||
assert_equal contact_request.email, random_mail
|
||||
assert ContactRequest::STATUS_NEW, contact_request.status
|
||||
end
|
||||
|
||||
def test_updates_existing_contact_request
|
||||
request_body = @json_update.dup
|
||||
|
||||
put api_v1_contact_request_path(@contact_request.id), params: request_body, headers: @headers
|
||||
assert_response :ok
|
||||
|
||||
@contact_request.reload
|
||||
assert ContactRequest::STATUS_CONFIRMED, @contact_request.status
|
||||
end
|
||||
|
||||
def test_not_updates_if_status_error
|
||||
request_body = @json_update.dup
|
||||
request_body['contact_request']['status'] = 'some_error_status'
|
||||
|
||||
put api_v1_contact_request_path(@contact_request.id), params: request_body, headers: @headers
|
||||
assert_response 400
|
||||
|
||||
@contact_request.reload
|
||||
assert ContactRequest::STATUS_NEW, @contact_request.status
|
||||
end
|
||||
|
||||
def valid_contact_request_create
|
||||
{
|
||||
"email": "aaa@bbb.com",
|
||||
"whois_record_id": "1",
|
||||
"name": "test"
|
||||
}.as_json
|
||||
end
|
||||
|
||||
def valid_contact_request_update
|
||||
{
|
||||
"status": "#{ContactRequest::STATUS_CONFIRMED}",
|
||||
}.as_json
|
||||
end
|
||||
end
|
|
@ -38,6 +38,20 @@ class WhiteIpTest < ActiveSupport::TestCase
|
|||
assert white_ip.valid?
|
||||
end
|
||||
|
||||
def test_validates_include_empty_ipv4
|
||||
white_ip = WhiteIp.new
|
||||
|
||||
white_ip.ipv4 = nil
|
||||
white_ip.ipv6 = '001:0db8:85a3:0000:0000:8a2e:0370:7334'
|
||||
white_ip.registrar = registrars(:bestnames)
|
||||
|
||||
assert_nothing_raised { white_ip.save }
|
||||
assert white_ip.valid?
|
||||
|
||||
assert WhiteIp.include_ip?(white_ip.ipv6)
|
||||
assert_not WhiteIp.include_ip?('192.168.1.1')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_white_ip
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue