mirror of
https://github.com/internetee/registry.git
synced 2025-06-10 22:54:47 +02:00
Merge remote-tracking branch 'origin/master' into repp-domains
This commit is contained in:
commit
221e4ba578
140 changed files with 2575 additions and 731 deletions
|
@ -20,6 +20,9 @@ plugins:
|
||||||
channel: eslint-5
|
channel: eslint-5
|
||||||
fixme:
|
fixme:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
checks:
|
||||||
|
TODO:
|
||||||
|
enabled: false
|
||||||
rubocop:
|
rubocop:
|
||||||
enabled: true
|
enabled: true
|
||||||
channel: rubocop-0-74
|
channel: rubocop-0-74
|
||||||
|
|
117
.github/workflows/ruby.yml
vendored
Normal file
117
.github/workflows/ruby.yml
vendored
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
name: Github Testing
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:12
|
||||||
|
ports: ["5432:5432"]
|
||||||
|
env:
|
||||||
|
POSTGRES_PASSWORD: password
|
||||||
|
POSTGRES_USERNAME: postgres
|
||||||
|
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-18.04]
|
||||||
|
ruby: [2.6, 2.7 ]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }}
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: ${{ matrix.ruby }}
|
||||||
|
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||||
|
- name: config bundler
|
||||||
|
run: |
|
||||||
|
bundle config set without 'development staging production'
|
||||||
|
bundle config set deployment '[secure]'
|
||||||
|
bundle env
|
||||||
|
head -n1 $(which bundle)
|
||||||
|
|
||||||
|
- name: Set ENV for codeclimate (pull_request)
|
||||||
|
run: |
|
||||||
|
git fetch --no-tags --prune --depth=1 origin +refs/heads/$GITHUB_HEAD_REF:refs/remotes/origin/$GITHUB_HEAD_REF
|
||||||
|
echo "GIT_BRANCH=$GITHUB_HEAD_REF" >> $GITHUB_ENV
|
||||||
|
echo "GIT_COMMIT_SHA=$(git rev-parse origin/$GITHUB_HEAD_REF)" >> $GITHUB_ENV
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
|
||||||
|
- name: Set ENV for codeclimate (push)
|
||||||
|
run: |
|
||||||
|
echo "GIT_BRANCH=$GITHUB_REF" >> $GITHUB_ENV
|
||||||
|
echo "GIT_COMMIT_SHA=$GITHUB_SHA" >> $GITHUB_ENV
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
|
||||||
|
- name: Prepare CodeClimate
|
||||||
|
env:
|
||||||
|
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
|
||||||
|
run: |
|
||||||
|
curl -LSs 'https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64' >./cc-test-reporter;
|
||||||
|
chmod +x ./cc-test-reporter
|
||||||
|
./cc-test-reporter before-build
|
||||||
|
|
||||||
|
- name: Run Tests
|
||||||
|
env:
|
||||||
|
PG_DATABASE: postgres
|
||||||
|
PG_HOST: localhost
|
||||||
|
PG_USER: postgres
|
||||||
|
PG_PASSWORD: password
|
||||||
|
PG_PORT: ${{ job.services.postgres.ports[5432] }}
|
||||||
|
RAILS_ENV: test
|
||||||
|
COVERAGE: true
|
||||||
|
DISABLE_SPRING: 1
|
||||||
|
run: |
|
||||||
|
cp config/application.yml.sample config/application.yml
|
||||||
|
cp config/database_travis.yml config/database.yml
|
||||||
|
echo "openssl_config_path: 'test/fixtures/files/test_ca/openssl.cnf'" >> config/application.yml
|
||||||
|
echo "crl_dir: 'test/fixtures/files/test_ca/crl'" >> config/application.yml
|
||||||
|
echo "crl_path: 'test/fixtures/files/test_ca/crl/crl.pem'" >> config/application.yml
|
||||||
|
echo "ca_cert_path: 'test/fixtures/files/test_ca/certs/ca.crt.pem'" >> config/application.yml
|
||||||
|
echo "ca_key_path: 'test/fixtures/files/test_ca/private/ca.key.pem'" >> config/application.yml
|
||||||
|
echo "ca_key_password: 'password'" >> config/application.yml
|
||||||
|
bundle exec rake db:setup:all
|
||||||
|
bundle exec rails test test/*
|
||||||
|
- name: Save coverage
|
||||||
|
run: ./cc-test-reporter format-coverage --output coverage/codeclimate.${{ matrix.ruby }}.json
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: coverage-${{ matrix.ruby }}
|
||||||
|
path: coverage/codeclimate.${{ matrix.ruby }}.json
|
||||||
|
|
||||||
|
upload_coverage:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
|
||||||
|
env:
|
||||||
|
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
|
||||||
|
CC_TEST_REPORTER_URL: https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64
|
||||||
|
|
||||||
|
needs: test
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download test coverage reporter
|
||||||
|
run: curl -L $CC_TEST_REPORTER_URL > cc-test-reporter
|
||||||
|
|
||||||
|
- name: Give test coverage reporter executable permissions
|
||||||
|
run: chmod +x cc-test-reporter
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v1
|
||||||
|
with:
|
||||||
|
name: coverage-2.6
|
||||||
|
path: coverage
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v1
|
||||||
|
with:
|
||||||
|
name: coverage-2.7
|
||||||
|
path: coverage
|
||||||
|
|
||||||
|
- name: Aggregate & upload results to Code Climate
|
||||||
|
run: |
|
||||||
|
./cc-test-reporter sum-coverage coverage/codeclimate.*.json
|
||||||
|
./cc-test-reporter upload-coverage
|
||||||
|
|
35
.travis.yml
35
.travis.yml
|
@ -1,35 +0,0 @@
|
||||||
language: ruby
|
|
||||||
cache: bundler
|
|
||||||
env:
|
|
||||||
- DB=postgresql
|
|
||||||
before_install:
|
|
||||||
- "wget -N http://chromedriver.storage.googleapis.com/2.43/chromedriver_linux64.zip -P ~/"
|
|
||||||
- "unzip ~/chromedriver_linux64.zip -d ~/"
|
|
||||||
- "rm ~/chromedriver_linux64.zip"
|
|
||||||
- "sudo mv -f ~/chromedriver /usr/local/share/"
|
|
||||||
- "sudo chmod +x /usr/local/share/chromedriver"
|
|
||||||
- "sudo ln -s /usr/local/share/chromedriver /usr/local/bin/chromedriver"
|
|
||||||
- "bundle config set without 'development staging production'"
|
|
||||||
- "bundle config set deployment '[secure]'"
|
|
||||||
before_script:
|
|
||||||
- "cp config/application.yml.sample config/application.yml"
|
|
||||||
- "echo \"openssl_config_path: 'test/fixtures/files/test_ca/openssl.cnf'\" >> config/application.yml"
|
|
||||||
- "echo \"crl_dir: 'test/fixtures/files/test_ca/crl'\" >> config/application.yml"
|
|
||||||
- "echo \"crl_path: 'test/fixtures/files/test_ca/crl/crl.pem'\" >> config/application.yml"
|
|
||||||
- "echo \"ca_cert_path: 'test/fixtures/files/test_ca/certs/ca.crt.pem'\" >> config/application.yml"
|
|
||||||
- "echo \"ca_key_path: 'test/fixtures/files/test_ca/private/ca.key.pem'\" >> config/application.yml"
|
|
||||||
- "echo \"ca_key_password: 'password'\" >> config/application.yml"
|
|
||||||
- "cp config/database_travis.yml config/database.yml"
|
|
||||||
- "bundle exec rake db:setup:all"
|
|
||||||
- "curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter"
|
|
||||||
- "chmod +x ./cc-test-reporter"
|
|
||||||
- "./cc-test-reporter before-build"
|
|
||||||
after_script:
|
|
||||||
- "./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT"
|
|
||||||
script:
|
|
||||||
- "bundle exec rails test test/*"
|
|
||||||
services:
|
|
||||||
- postgresql
|
|
||||||
addons:
|
|
||||||
postgresql: "9.4"
|
|
||||||
chrome: stable
|
|
39
CHANGELOG.md
39
CHANGELOG.md
|
@ -1,3 +1,42 @@
|
||||||
|
23.12.2020
|
||||||
|
* fix for REPP logging and registrar portal communication [#1782](https://github.com/internetee/registry/pull/1782)
|
||||||
|
|
||||||
|
22.12.2020
|
||||||
|
* SSL CA verification fix for Bulk renew [#1778](https://github.com/internetee/registry/pull/1778)
|
||||||
|
|
||||||
|
21.12.2020
|
||||||
|
* Bulk renew for REPP and registrar [#1763](https://github.com/internetee/registry/issues/1763)
|
||||||
|
|
||||||
|
17.12.2020
|
||||||
|
* New API for registering bounced emails [#1687](https://github.com/internetee/registry/pull/1687)
|
||||||
|
|
||||||
|
16.12.2020
|
||||||
|
* Refactored domain delete confirmation for interactors [#1769](https://github.com/internetee/registry/issues/1769)
|
||||||
|
|
||||||
|
15.12.2020
|
||||||
|
* Improved logic for domain list request in registrant API [#1750](https://github.com/internetee/registry/pull/1750)
|
||||||
|
* Refactored Whois update job for interactors [#1771](https://github.com/internetee/registry/issues/1771)
|
||||||
|
|
||||||
|
14.12.2020
|
||||||
|
* Refactored domain cron jobs for interactors [#1767](https://github.com/internetee/registry/issues/1767)
|
||||||
|
|
||||||
|
09.12.2020
|
||||||
|
* Refactored domain update confirm for interactors [#1760](https://github.com/internetee/registry/issues/1760)
|
||||||
|
|
||||||
|
08.12.2020
|
||||||
|
* Replaced Travis-CI with GitHub Actions [#1746](https://github.com/internetee/registry/pull/1746)
|
||||||
|
* Refactored domain delete for interactors [#1755](https://github.com/internetee/registry/issues/1755)
|
||||||
|
|
||||||
|
01.12.2020
|
||||||
|
* Refactored clientHold for interactors [#1751](https://github.com/internetee/registry/issues/1751)
|
||||||
|
* Fixed internal error on removing clientHold status when not present [#1766](https://github.com/internetee/registry/issues/1766)
|
||||||
|
|
||||||
|
30.11.2020
|
||||||
|
* Refactor - interactors moved to domain space [#1762](https://github.com/internetee/registry/pull/1762)
|
||||||
|
|
||||||
|
27.11.2020
|
||||||
|
* Refactored delete confirmation for interactors [#1753](https://github.com/internetee/registry/issues/1753)
|
||||||
|
|
||||||
24.11.2020
|
24.11.2020
|
||||||
* Added subnet support for list of allowed IPs [#983](https://github.com/internetee/registry/issues/983)
|
* Added subnet support for list of allowed IPs [#983](https://github.com/internetee/registry/issues/983)
|
||||||
* Added contact endpoint to Restful EPP API [#1580](https://github.com/internetee/registry/issues/1580)
|
* Added contact endpoint to Restful EPP API [#1580](https://github.com/internetee/registry/issues/1580)
|
||||||
|
|
6
Gemfile
6
Gemfile
|
@ -73,15 +73,11 @@ gem 'e_invoice', github: 'internetee/e_invoice', branch: :master
|
||||||
gem 'lhv', github: 'internetee/lhv', branch: 'master'
|
gem 'lhv', github: 'internetee/lhv', branch: 'master'
|
||||||
gem 'domain_name'
|
gem 'domain_name'
|
||||||
gem 'haml', '~> 5.0'
|
gem 'haml', '~> 5.0'
|
||||||
|
gem 'rexml'
|
||||||
gem 'wkhtmltopdf-binary', '~> 0.12.5.1'
|
gem 'wkhtmltopdf-binary', '~> 0.12.5.1'
|
||||||
|
|
||||||
gem 'directo', github: 'internetee/directo', branch: 'master'
|
gem 'directo', github: 'internetee/directo', branch: 'master'
|
||||||
|
|
||||||
group :development do
|
|
||||||
# deploy
|
|
||||||
gem 'listen', '3.2.1'
|
|
||||||
gem 'mina', '0.3.1' # for fast deployment
|
|
||||||
end
|
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'pry', '0.10.1'
|
gem 'pry', '0.10.1'
|
||||||
|
|
14
Gemfile.lock
14
Gemfile.lock
|
@ -250,9 +250,6 @@ GEM
|
||||||
kaminari-core (= 1.2.1)
|
kaminari-core (= 1.2.1)
|
||||||
kaminari-core (1.2.1)
|
kaminari-core (1.2.1)
|
||||||
libxml-ruby (3.2.0)
|
libxml-ruby (3.2.0)
|
||||||
listen (3.2.1)
|
|
||||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
||||||
rb-inotify (~> 0.9, >= 0.9.10)
|
|
||||||
logger (1.4.2)
|
logger (1.4.2)
|
||||||
loofah (2.7.0)
|
loofah (2.7.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
|
@ -266,9 +263,6 @@ GEM
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2020.0512)
|
mime-types-data (3.2020.0512)
|
||||||
mimemagic (0.3.5)
|
mimemagic (0.3.5)
|
||||||
mina (0.3.1)
|
|
||||||
open4 (~> 1.3.4)
|
|
||||||
rake
|
|
||||||
mini_mime (1.0.2)
|
mini_mime (1.0.2)
|
||||||
mini_portile2 (2.4.0)
|
mini_portile2 (2.4.0)
|
||||||
minitest (5.14.2)
|
minitest (5.14.2)
|
||||||
|
@ -296,7 +290,6 @@ GEM
|
||||||
omniauth-rails_csrf_protection (0.1.2)
|
omniauth-rails_csrf_protection (0.1.2)
|
||||||
actionpack (>= 4.2)
|
actionpack (>= 4.2)
|
||||||
omniauth (>= 1.3.1)
|
omniauth (>= 1.3.1)
|
||||||
open4 (1.3.4)
|
|
||||||
openid_connect (1.2.0)
|
openid_connect (1.2.0)
|
||||||
activemodel
|
activemodel
|
||||||
attr_required (>= 1.0.0)
|
attr_required (>= 1.0.0)
|
||||||
|
@ -370,9 +363,6 @@ GEM
|
||||||
activesupport (>= 5.2.1)
|
activesupport (>= 5.2.1)
|
||||||
i18n
|
i18n
|
||||||
polyamorous (= 2.3.2)
|
polyamorous (= 2.3.2)
|
||||||
rb-fsevent (0.10.4)
|
|
||||||
rb-inotify (0.10.1)
|
|
||||||
ffi (~> 1.0)
|
|
||||||
rbtree3 (0.6.0)
|
rbtree3 (0.6.0)
|
||||||
regexp_parser (1.8.0)
|
regexp_parser (1.8.0)
|
||||||
request_store (1.5.0)
|
request_store (1.5.0)
|
||||||
|
@ -385,6 +375,7 @@ GEM
|
||||||
http-cookie (>= 1.0.2, < 2.0)
|
http-cookie (>= 1.0.2, < 2.0)
|
||||||
mime-types (>= 1.16, < 4.0)
|
mime-types (>= 1.16, < 4.0)
|
||||||
netrc (~> 0.8)
|
netrc (~> 0.8)
|
||||||
|
rexml (3.2.4)
|
||||||
ruby2_keywords (0.0.2)
|
ruby2_keywords (0.0.2)
|
||||||
rubyzip (2.3.0)
|
rubyzip (2.3.0)
|
||||||
sass-rails (6.0.0)
|
sass-rails (6.0.0)
|
||||||
|
@ -519,8 +510,6 @@ DEPENDENCIES
|
||||||
jquery-ui-rails (= 5.0.5)
|
jquery-ui-rails (= 5.0.5)
|
||||||
kaminari
|
kaminari
|
||||||
lhv!
|
lhv!
|
||||||
listen (= 3.2.1)
|
|
||||||
mina (= 0.3.1)
|
|
||||||
minitest (~> 5.14)
|
minitest (~> 5.14)
|
||||||
money-rails
|
money-rails
|
||||||
nokogiri
|
nokogiri
|
||||||
|
@ -537,6 +526,7 @@ DEPENDENCIES
|
||||||
rails (~> 6.0)
|
rails (~> 6.0)
|
||||||
ransack (~> 2.3)
|
ransack (~> 2.3)
|
||||||
rest-client
|
rest-client
|
||||||
|
rexml
|
||||||
sass-rails
|
sass-rails
|
||||||
select2-rails (= 3.5.9.3)
|
select2-rails (= 3.5.9.3)
|
||||||
selectize-rails (= 0.12.1)
|
selectize-rails (= 0.12.1)
|
||||||
|
|
30
app/controllers/admin/bounced_mail_addresses_controller.rb
Normal file
30
app/controllers/admin/bounced_mail_addresses_controller.rb
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
module Admin
|
||||||
|
class BouncedMailAddressesController < BaseController
|
||||||
|
before_action :set_bounced_mail_address, only: %i[show destroy]
|
||||||
|
load_and_authorize_resource
|
||||||
|
|
||||||
|
# GET /bounced_mail_addresses
|
||||||
|
def index
|
||||||
|
@bounced_mail_addresses = BouncedMailAddress.all.order(created_at: :desc)
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /bounced_mail_addresses/1
|
||||||
|
def show; end
|
||||||
|
|
||||||
|
# DELETE /bounced_mail_addresses/1
|
||||||
|
def destroy
|
||||||
|
@bounced_mail_address.destroy
|
||||||
|
redirect_to(
|
||||||
|
admin_bounced_mail_addresses_url,
|
||||||
|
notice: 'Bounced mail address was successfully destroyed.'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Use callbacks to share common setup or constraints between actions.
|
||||||
|
def set_bounced_mail_address
|
||||||
|
@bounced_mail_address = BouncedMailAddress.find(params[:id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,6 +10,11 @@ module Api
|
||||||
head :unauthorized unless ip_allowed
|
head :unauthorized unless ip_allowed
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def authenticate_shared_key
|
||||||
|
api_key = "Basic #{ENV['api_shared_key']}"
|
||||||
|
head(:unauthorized) unless api_key == request.authorization
|
||||||
|
end
|
||||||
|
|
||||||
def not_found_error
|
def not_found_error
|
||||||
uuid = params['uuid']
|
uuid = params['uuid']
|
||||||
json = { error: 'Not Found', uuid: uuid, message: 'Record not found' }
|
json = { error: 'Not Found', uuid: uuid, message: 'Record not found' }
|
||||||
|
|
25
app/controllers/api/v1/bounces_controller.rb
Normal file
25
app/controllers/api/v1/bounces_controller.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
module Api
|
||||||
|
module V1
|
||||||
|
class BouncesController < BaseController
|
||||||
|
before_action :authenticate_shared_key
|
||||||
|
|
||||||
|
# POST api/v1/bounces/
|
||||||
|
def create
|
||||||
|
return head(:bad_request) unless bounce_params[:bounce][:bouncedRecipients].any?
|
||||||
|
|
||||||
|
BouncedMailAddress.record(bounce_params)
|
||||||
|
head(:created)
|
||||||
|
rescue ActionController::ParameterMissing
|
||||||
|
head(:bad_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
def bounce_params
|
||||||
|
params.require(:data).require(:bounce).require(:bouncedRecipients).each do |r|
|
||||||
|
r.require(:emailAddress)
|
||||||
|
end
|
||||||
|
|
||||||
|
params.require(:data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -19,15 +19,16 @@ module Api
|
||||||
end
|
end
|
||||||
|
|
||||||
contacts = current_user_contacts.limit(limit).offset(offset)
|
contacts = current_user_contacts.limit(limit).offset(offset)
|
||||||
serialized_contacts = contacts.collect { |contact| serialize_contact(contact) }
|
serialized_contacts = contacts.collect { |contact| serialize_contact(contact, false) }
|
||||||
render json: serialized_contacts
|
render json: serialized_contacts
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
contact = current_user_contacts.find_by(uuid: params[:uuid])
|
contact = current_user_contacts.find_by(uuid: params[:uuid])
|
||||||
|
links = params[:links] == 'true'
|
||||||
|
|
||||||
if contact
|
if contact
|
||||||
render json: serialize_contact(contact)
|
render json: serialize_contact(contact, links)
|
||||||
else
|
else
|
||||||
render json: { errors: [{ base: ['Contact not found'] }] }, status: :not_found
|
render json: { errors: [{ base: ['Contact not found'] }] }, status: :not_found
|
||||||
end
|
end
|
||||||
|
@ -85,7 +86,7 @@ module Api
|
||||||
contact.registrar.notify(action)
|
contact.registrar.notify(action)
|
||||||
end
|
end
|
||||||
|
|
||||||
render json: serialize_contact(contact)
|
render json: serialize_contact(contact, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -96,8 +97,8 @@ module Api
|
||||||
current_registrant_user.direct_contacts
|
current_registrant_user.direct_contacts
|
||||||
end
|
end
|
||||||
|
|
||||||
def serialize_contact(contact)
|
def serialize_contact(contact, links)
|
||||||
Serializers::RegistrantApi::Contact.new(contact).to_json
|
Serializers::RegistrantApi::Contact.new(contact, links).to_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,7 @@ module Api
|
||||||
def index
|
def index
|
||||||
limit = params[:limit] || 200
|
limit = params[:limit] || 200
|
||||||
offset = params[:offset] || 0
|
offset = params[:offset] || 0
|
||||||
|
simple = params[:simple] == 'true' || false
|
||||||
|
|
||||||
if limit.to_i > 200 || limit.to_i < 1
|
if limit.to_i > 200 || limit.to_i < 1
|
||||||
render(json: { errors: [{ limit: ['parameter is out of range'] }] },
|
render(json: { errors: [{ limit: ['parameter is out of range'] }] },
|
||||||
|
@ -18,21 +19,20 @@ module Api
|
||||||
status: :bad_request) && return
|
status: :bad_request) && return
|
||||||
end
|
end
|
||||||
|
|
||||||
@domains = current_user_domains.limit(limit).offset(offset)
|
domains = current_user_domains
|
||||||
|
serialized_domains = domains.limit(limit).offset(offset).map do |item|
|
||||||
serialized_domains = @domains.map do |item|
|
serializer = Serializers::RegistrantApi::Domain.new(item, simplify: simple)
|
||||||
serializer = Serializers::RegistrantApi::Domain.new(item)
|
|
||||||
serializer.to_json
|
serializer.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
render json: serialized_domains
|
render json: { count: domains.count, domains: serialized_domains }
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@domain = current_user_domains.find_by(uuid: params[:uuid])
|
@domain = current_user_domains.find_by(uuid: params[:uuid])
|
||||||
|
|
||||||
if @domain
|
if @domain
|
||||||
serializer = Serializers::RegistrantApi::Domain.new(@domain)
|
serializer = Serializers::RegistrantApi::Domain.new(@domain, simplify: false)
|
||||||
render json: serializer.to_json
|
render json: serializer.to_json
|
||||||
else
|
else
|
||||||
render json: { errors: [{ base: ['Domain not found'] }] }, status: :not_found
|
render json: { errors: [{ base: ['Domain not found'] }] }, status: :not_found
|
||||||
|
|
|
@ -4,11 +4,38 @@ class Registrar
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize! :manage, :repp
|
authorize! :manage, :repp
|
||||||
|
@expire_date = Time.zone.now.to_date
|
||||||
render file: 'registrar/bulk_change/new', locals: { active_tab: default_tab }
|
render file: 'registrar/bulk_change/new', locals: { active_tab: default_tab }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def bulk_renew
|
||||||
|
authorize! :manage, :repp
|
||||||
|
set_form_data
|
||||||
|
|
||||||
|
if ready_to_renew?
|
||||||
|
res = ReppApi.bulk_renew(domain_ids_for_bulk_renew, params[:period],
|
||||||
|
current_registrar_user)
|
||||||
|
|
||||||
|
flash_message(JSON.parse(res))
|
||||||
|
else
|
||||||
|
flash[:notice] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
render file: 'registrar/bulk_change/new', locals: { active_tab: :bulk_renew }
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def ready_to_renew?
|
||||||
|
domain_ids_for_bulk_renew.present? && params[:renew].present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_form_data
|
||||||
|
@expire_date = params[:expire_date].to_date
|
||||||
|
@domains = domains_by_date(@expire_date)
|
||||||
|
@period = params[:period]
|
||||||
|
end
|
||||||
|
|
||||||
def available_contacts
|
def available_contacts
|
||||||
current_registrar_user.registrar.contacts.order(:name).pluck(:name, :code)
|
current_registrar_user.registrar.contacts.order(:name).pluck(:name, :code)
|
||||||
end
|
end
|
||||||
|
@ -16,5 +43,27 @@ class Registrar
|
||||||
def default_tab
|
def default_tab
|
||||||
:technical_contact
|
:technical_contact
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def domains_scope
|
||||||
|
current_registrar_user.registrar.domains
|
||||||
|
end
|
||||||
|
|
||||||
|
def domains_by_date(date)
|
||||||
|
domains_scope.where('valid_to <= ?', date)
|
||||||
|
end
|
||||||
|
|
||||||
|
def domain_ids_for_bulk_renew
|
||||||
|
params.dig('domain_ids')&.reject { |id| id.blank? }
|
||||||
|
end
|
||||||
|
|
||||||
|
def renew_task(domains)
|
||||||
|
Domains::BulkRenew::Start.run(domains: domains,
|
||||||
|
period_element: @period,
|
||||||
|
registrar: current_registrar_user.registrar)
|
||||||
|
end
|
||||||
|
|
||||||
|
def flash_message(res)
|
||||||
|
flash[:notice] = res['code'] == 1000 ? t(:bulk_renew_completed) : res['message']
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -55,10 +55,12 @@ class Registrar
|
||||||
parsed_response = JSON.parse(response.body, symbolize_names: true)
|
parsed_response = JSON.parse(response.body, symbolize_names: true)
|
||||||
|
|
||||||
if response.code == '200'
|
if response.code == '200'
|
||||||
flash[:notice] = t '.transferred', count: parsed_response[:data].size
|
failed = parsed_response[:data][:failed].each(&:domain_name).join(', ')
|
||||||
|
flash[:notice] = t('.transferred', count: parsed_response[:data][:success].size,
|
||||||
|
failed: failed)
|
||||||
redirect_to registrar_domains_url
|
redirect_to registrar_domains_url
|
||||||
else
|
else
|
||||||
@api_errors = parsed_response[:errors]
|
@api_errors = parsed_response[:message]
|
||||||
render file: 'registrar/bulk_change/new', locals: { active_tab: :bulk_transfer }
|
render file: 'registrar/bulk_change/new', locals: { active_tab: :bulk_transfer }
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
|
@ -49,12 +49,13 @@ class Registrar
|
||||||
|
|
||||||
if response.code == '200'
|
if response.code == '200'
|
||||||
notices = [t('.replaced')]
|
notices = [t('.replaced')]
|
||||||
notices << "#{t('.affected_domains')}: #{parsed_response[:affected_domains].join(', ')}"
|
notices << "#{t('.affected_domains')}: " \
|
||||||
|
"#{parsed_response[:data][:affected_domains].join(', ')}"
|
||||||
|
|
||||||
flash[:notice] = notices
|
flash[:notice] = notices.join(', ')
|
||||||
redirect_to registrar_domains_url
|
redirect_to registrar_domains_url
|
||||||
else
|
else
|
||||||
@api_errors = parsed_response[:errors]
|
@api_errors = parsed_response[:message]
|
||||||
render file: 'registrar/bulk_change/new', locals: { active_tab: :nameserver }
|
render file: 'registrar/bulk_change/new', locals: { active_tab: :nameserver }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -62,6 +62,7 @@ class Registrar
|
||||||
|
|
||||||
def find_user_by_idc_and_allowed(idc)
|
def find_user_by_idc_and_allowed(idc)
|
||||||
return User.new unless idc
|
return User.new unless idc
|
||||||
|
|
||||||
possible_users = ApiUser.where(identity_code: idc) || User.new
|
possible_users = ApiUser.where(identity_code: idc) || User.new
|
||||||
possible_users.each do |selected_user|
|
possible_users.each do |selected_user|
|
||||||
if selected_user.registrar.white_ips.registrar_area.include_ip?(request.ip)
|
if selected_user.registrar.white_ips.registrar_area.include_ip?(request.ip)
|
||||||
|
|
|
@ -43,16 +43,18 @@ class Registrar
|
||||||
if response.code == '200'
|
if response.code == '200'
|
||||||
notices = [t('.replaced')]
|
notices = [t('.replaced')]
|
||||||
|
|
||||||
notices << "#{t('.affected_domains')}: #{parsed_response[:affected_domains].join(', ')}"
|
notices << "#{t('.affected_domains')}: " \
|
||||||
|
"#{parsed_response[:data][:affected_domains].join(', ')}"
|
||||||
|
|
||||||
if parsed_response[:skipped_domains]
|
if parsed_response[:data][:skipped_domains]
|
||||||
notices << "#{t('.skipped_domains')}: #{parsed_response[:skipped_domains].join(', ')}"
|
notices << "#{t('.skipped_domains')}: " \
|
||||||
|
"#{parsed_response[:data][:skipped_domains].join(', ')}"
|
||||||
end
|
end
|
||||||
|
|
||||||
flash[:notice] = notices
|
flash[:notice] = notices.join(', ')
|
||||||
redirect_to registrar_domains_url
|
redirect_to registrar_domains_url
|
||||||
else
|
else
|
||||||
@error = parsed_response[:error]
|
@error = response.code == '404' ? 'Contact(s) not found' : parsed_response[:message]
|
||||||
render file: 'registrar/bulk_change/new', locals: { active_tab: :technical_contact }
|
render file: 'registrar/bulk_change/new', locals: { active_tab: :technical_contact }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@ module Repp
|
||||||
class BaseController < ActionController::API
|
class BaseController < ActionController::API
|
||||||
rescue_from ActiveRecord::RecordNotFound, with: :not_found_error
|
rescue_from ActiveRecord::RecordNotFound, with: :not_found_error
|
||||||
before_action :authenticate_user
|
before_action :authenticate_user
|
||||||
|
before_action :validate_webclient_ca
|
||||||
before_action :check_ip_restriction
|
before_action :check_ip_restriction
|
||||||
attr_reader :current_user
|
attr_reader :current_user
|
||||||
|
|
||||||
|
@ -93,15 +94,33 @@ module Repp
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_ip_restriction
|
def check_ip_restriction
|
||||||
allowed = @current_user.registrar.api_ip_white?(request.ip)
|
return if webclient_request?
|
||||||
|
return if @current_user.registrar.api_ip_white?(request.ip)
|
||||||
return if allowed
|
|
||||||
|
|
||||||
@response = { code: 2202,
|
@response = { code: 2202,
|
||||||
message: I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip) }
|
message: I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip) }
|
||||||
render(json: @response, status: :unauthorized)
|
render(json: @response, status: :unauthorized)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def webclient_request?
|
||||||
|
return if Rails.env.test?
|
||||||
|
|
||||||
|
ENV['webclient_ips'].split(',').map(&:strip).include?(request.ip)
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_webclient_ca
|
||||||
|
return unless webclient_request?
|
||||||
|
|
||||||
|
request_name = request.env['HTTP_SSL_CLIENT_S_DN_CN']
|
||||||
|
webclient_cn = ENV['webclient_cert_common_name'] || 'webclient'
|
||||||
|
return if request_name == webclient_cn
|
||||||
|
|
||||||
|
@response = { code: 2202,
|
||||||
|
message: I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip) }
|
||||||
|
|
||||||
|
render(json: @response, status: :unauthorized)
|
||||||
|
end
|
||||||
|
|
||||||
def not_found_error
|
def not_found_error
|
||||||
@response = { code: 2303, message: 'Object does not exist' }
|
@response = { code: 2303, message: 'Object does not exist' }
|
||||||
render(json: @response, status: :not_found)
|
render(json: @response, status: :not_found)
|
||||||
|
|
65
app/controllers/repp/v1/domains/renews_controller.rb
Normal file
65
app/controllers/repp/v1/domains/renews_controller.rb
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
module Repp
|
||||||
|
module V1
|
||||||
|
module Domains
|
||||||
|
class RenewsController < BaseController
|
||||||
|
before_action :validate_renew_period, only: [:bulk_renew]
|
||||||
|
before_action :select_renewable_domains, only: [:bulk_renew]
|
||||||
|
|
||||||
|
def bulk_renew
|
||||||
|
renew = run_bulk_renew_task(@domains, bulk_renew_params[:renew_period])
|
||||||
|
return render_success(data: { updated_domains: @domains.map(&:name) }) if renew.valid?
|
||||||
|
|
||||||
|
@epp_errors << { code: 2002,
|
||||||
|
msg: renew.errors.keys.map { |k, _v| renew.errors[k] }.join(', ') }
|
||||||
|
handle_errors
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def validate_renew_period
|
||||||
|
@epp_errors ||= []
|
||||||
|
periods = Depp::Domain::PERIODS.map { |p| p[1] }
|
||||||
|
return if periods.include? bulk_renew_params[:renew_period]
|
||||||
|
|
||||||
|
@epp_errors << { code: 2005, msg: 'Invalid renew period' }
|
||||||
|
end
|
||||||
|
|
||||||
|
def select_renewable_domains
|
||||||
|
@epp_errors ||= []
|
||||||
|
|
||||||
|
if bulk_renew_params[:domains].instance_of?(Array)
|
||||||
|
@domains = bulk_renew_domains
|
||||||
|
else
|
||||||
|
@epp_errors << { code: 2005, msg: 'Domains attribute must be an array' }
|
||||||
|
end
|
||||||
|
|
||||||
|
return handle_errors if @epp_errors.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_bulk_renew_task(domains, period)
|
||||||
|
::Domains::BulkRenew::Start.run(domains: domains, period_element: period,
|
||||||
|
registrar: current_user.registrar)
|
||||||
|
end
|
||||||
|
|
||||||
|
def bulk_renew_params
|
||||||
|
params do
|
||||||
|
params.require(%i[domains renew_period])
|
||||||
|
params.permit(:domains, :renew_period)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def bulk_renew_domains
|
||||||
|
@epp_errors ||= []
|
||||||
|
domains = []
|
||||||
|
bulk_renew_params[:domains].each do |idn|
|
||||||
|
domain = Epp::Domain.find_by(name: idn)
|
||||||
|
domains << domain if domain
|
||||||
|
@epp_errors << { code: 2304, msg: "Object does not exist: #{idn}" } unless domain
|
||||||
|
end
|
||||||
|
|
||||||
|
domains
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +0,0 @@
|
||||||
module CancelForceDeleteInteraction
|
|
||||||
class Base < ActiveInteraction::Base
|
|
||||||
object :domain,
|
|
||||||
class: Domain,
|
|
||||||
description: 'Domain to cancel ForceDelete on'
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,10 +0,0 @@
|
||||||
module CancelForceDeleteInteraction
|
|
||||||
class CancelForceDelete < Base
|
|
||||||
def execute
|
|
||||||
compose(RemoveForceDeleteStatuses, inputs)
|
|
||||||
compose(RestoreStatusesBeforeForceDelete, inputs)
|
|
||||||
compose(ClearForceDeleteData, inputs)
|
|
||||||
compose(NotifyRegistrar, inputs)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,10 +0,0 @@
|
||||||
module CancelForceDeleteInteraction
|
|
||||||
class ClearForceDeleteData < Base
|
|
||||||
def execute
|
|
||||||
domain.force_delete_data = nil
|
|
||||||
domain.force_delete_date = nil
|
|
||||||
domain.force_delete_start = nil
|
|
||||||
domain.save(validate: false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,8 +0,0 @@
|
||||||
module CancelForceDeleteInteraction
|
|
||||||
class NotifyRegistrar < Base
|
|
||||||
def execute
|
|
||||||
domain.registrar.notifications.create!(text: I18n.t('force_delete_cancelled',
|
|
||||||
domain_name: domain.name))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,11 +0,0 @@
|
||||||
module CancelForceDeleteInteraction
|
|
||||||
class RemoveForceDeleteStatuses < Base
|
|
||||||
def execute
|
|
||||||
domain.statuses.delete(DomainStatus::FORCE_DELETE)
|
|
||||||
domain.statuses.delete(DomainStatus::SERVER_RENEW_PROHIBITED)
|
|
||||||
domain.statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED)
|
|
||||||
domain.statuses.delete(DomainStatus::CLIENT_HOLD)
|
|
||||||
domain.save(validate: false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,9 +0,0 @@
|
||||||
module CancelForceDeleteInteraction
|
|
||||||
class RestoreStatusesBeforeForceDelete < Base
|
|
||||||
def execute
|
|
||||||
domain.statuses = domain.statuses_before_force_delete
|
|
||||||
domain.statuses_before_force_delete = nil
|
|
||||||
domain.save(validate: false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
62
app/interactions/domains/bulk_renew/single_domain_renew.rb
Normal file
62
app/interactions/domains/bulk_renew/single_domain_renew.rb
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
module Domains
|
||||||
|
module BulkRenew
|
||||||
|
class SingleDomainRenew < ActiveInteraction::Base
|
||||||
|
object :domain,
|
||||||
|
class: Epp::Domain
|
||||||
|
integer :period
|
||||||
|
string :unit
|
||||||
|
object :registrar
|
||||||
|
|
||||||
|
def execute
|
||||||
|
in_transaction_with_retries do
|
||||||
|
success = domain.renew(domain.valid_to, period, unit)
|
||||||
|
if success
|
||||||
|
check_balance
|
||||||
|
reduce_balance
|
||||||
|
else
|
||||||
|
add_error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_balance
|
||||||
|
compose(Domains::CheckBalance::SingleDomain,
|
||||||
|
domain: domain,
|
||||||
|
operation: 'renew',
|
||||||
|
period: period,
|
||||||
|
unit: unit)
|
||||||
|
end
|
||||||
|
|
||||||
|
def reduce_balance
|
||||||
|
domain_pricelist = domain.pricelist('renew', period, unit)
|
||||||
|
registrar.debit!(sum: domain_pricelist.price.amount,
|
||||||
|
description: "#{I18n.t('renew')} #{domain.name}",
|
||||||
|
activity_type: AccountActivity::RENEW,
|
||||||
|
price: domain_pricelist)
|
||||||
|
end
|
||||||
|
|
||||||
|
def in_transaction_with_retries
|
||||||
|
if Rails.env.test?
|
||||||
|
yield
|
||||||
|
else
|
||||||
|
transaction_wrapper { yield }
|
||||||
|
end
|
||||||
|
rescue ActiveRecord::StatementInvalid
|
||||||
|
sleep rand / 100
|
||||||
|
retry
|
||||||
|
end
|
||||||
|
|
||||||
|
def transaction_wrapper
|
||||||
|
ActiveRecord::Base.transaction(isolation: :serializable) do
|
||||||
|
yield if block_given?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def add_error
|
||||||
|
errors.add(:domain, I18n.t('domain_renew_error_for_domain', domain: domain.name))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
56
app/interactions/domains/bulk_renew/start.rb
Normal file
56
app/interactions/domains/bulk_renew/start.rb
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
module Domains
|
||||||
|
module BulkRenew
|
||||||
|
class Start < ActiveInteraction::Base
|
||||||
|
array :domains do
|
||||||
|
object class: Epp::Domain
|
||||||
|
end
|
||||||
|
string :period_element
|
||||||
|
object :registrar
|
||||||
|
|
||||||
|
def execute
|
||||||
|
if renewable?
|
||||||
|
domains.each do |domain|
|
||||||
|
task = run_task(domain)
|
||||||
|
manage_errors(task)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
manage_errors(mass_check_balance)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def renewable?
|
||||||
|
mass_check_balance.valid? && mass_check_balance.result
|
||||||
|
end
|
||||||
|
|
||||||
|
def period
|
||||||
|
period_element.to_i.zero? ? 1 : period_element.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def unit
|
||||||
|
period_element[-1] || 'y'
|
||||||
|
end
|
||||||
|
|
||||||
|
def mass_check_balance
|
||||||
|
Domains::CheckBalance::Mass.run(domains: domains,
|
||||||
|
operation: 'renew',
|
||||||
|
period: period,
|
||||||
|
unit: unit,
|
||||||
|
balance: registrar.balance)
|
||||||
|
end
|
||||||
|
|
||||||
|
def manage_errors(task)
|
||||||
|
task.errors.each { |k, v| errors.add(k, v) } unless task.valid?
|
||||||
|
errors.add(:domain, I18n.t('not_enough_funds')) unless task.result
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_task(domain)
|
||||||
|
Domains::BulkRenew::SingleDomainRenew.run(domain: domain,
|
||||||
|
period: period,
|
||||||
|
unit: unit,
|
||||||
|
registrar: registrar)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
9
app/interactions/domains/cancel_force_delete/base.rb
Normal file
9
app/interactions/domains/cancel_force_delete/base.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
module Domains
|
||||||
|
module CancelForceDelete
|
||||||
|
class Base < ActiveInteraction::Base
|
||||||
|
object :domain,
|
||||||
|
class: Domain,
|
||||||
|
description: 'Domain to cancel ForceDelete on'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,12 @@
|
||||||
|
module Domains
|
||||||
|
module CancelForceDelete
|
||||||
|
class CancelForceDelete < Base
|
||||||
|
def execute
|
||||||
|
compose(RemoveForceDeleteStatuses, inputs)
|
||||||
|
compose(RestoreStatusesBeforeForceDelete, inputs)
|
||||||
|
compose(ClearForceDeleteData, inputs)
|
||||||
|
compose(NotifyRegistrar, inputs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,12 @@
|
||||||
|
module Domains
|
||||||
|
module CancelForceDelete
|
||||||
|
class ClearForceDeleteData < Base
|
||||||
|
def execute
|
||||||
|
domain.force_delete_data = nil
|
||||||
|
domain.force_delete_date = nil
|
||||||
|
domain.force_delete_start = nil
|
||||||
|
domain.save(validate: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,10 @@
|
||||||
|
module Domains
|
||||||
|
module CancelForceDelete
|
||||||
|
class NotifyRegistrar < Base
|
||||||
|
def execute
|
||||||
|
domain.registrar.notifications.create!(text: I18n.t('force_delete_cancelled',
|
||||||
|
domain_name: domain.name))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,13 @@
|
||||||
|
module Domains
|
||||||
|
module CancelForceDelete
|
||||||
|
class RemoveForceDeleteStatuses < Base
|
||||||
|
def execute
|
||||||
|
domain.statuses.delete(DomainStatus::FORCE_DELETE)
|
||||||
|
domain.statuses.delete(DomainStatus::SERVER_RENEW_PROHIBITED)
|
||||||
|
domain.statuses.delete(DomainStatus::SERVER_TRANSFER_PROHIBITED)
|
||||||
|
domain.statuses.delete(DomainStatus::CLIENT_HOLD)
|
||||||
|
domain.save(validate: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,11 @@
|
||||||
|
module Domains
|
||||||
|
module CancelForceDelete
|
||||||
|
class RestoreStatusesBeforeForceDelete < Base
|
||||||
|
def execute
|
||||||
|
domain.statuses = domain.statuses_before_force_delete
|
||||||
|
domain.statuses_before_force_delete = nil
|
||||||
|
domain.save(validate: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
33
app/interactions/domains/check_balance/mass.rb
Normal file
33
app/interactions/domains/check_balance/mass.rb
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
module Domains
|
||||||
|
module CheckBalance
|
||||||
|
class Mass < ActiveInteraction::Base
|
||||||
|
array :domains do
|
||||||
|
object class: Epp::Domain
|
||||||
|
end
|
||||||
|
string :operation
|
||||||
|
integer :period
|
||||||
|
string :unit
|
||||||
|
float :balance
|
||||||
|
|
||||||
|
attr_accessor :total_price
|
||||||
|
|
||||||
|
def execute
|
||||||
|
calculate_total_price
|
||||||
|
|
||||||
|
balance >= @total_price
|
||||||
|
end
|
||||||
|
|
||||||
|
def calculate_total_price
|
||||||
|
@total_price = 0
|
||||||
|
domains.each do |domain|
|
||||||
|
task = Domains::CheckBalance::SingleDomain.run(domain: domain,
|
||||||
|
operation: 'renew',
|
||||||
|
period: period,
|
||||||
|
unit: unit)
|
||||||
|
|
||||||
|
task.valid? ? @total_price += task.result : errors.merge!(task.errors)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
26
app/interactions/domains/check_balance/single_domain.rb
Normal file
26
app/interactions/domains/check_balance/single_domain.rb
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
module Domains
|
||||||
|
module CheckBalance
|
||||||
|
class SingleDomain < ActiveInteraction::Base
|
||||||
|
object :domain,
|
||||||
|
class: Epp::Domain
|
||||||
|
|
||||||
|
string :operation
|
||||||
|
integer :period
|
||||||
|
string :unit
|
||||||
|
|
||||||
|
def execute
|
||||||
|
return domain_pricelist.price.amount if domain_pricelist.try(:price)
|
||||||
|
|
||||||
|
errors.add(:domain, I18n.t(:active_price_missing_for_operation_with_domain,
|
||||||
|
domain: domain.name))
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def domain_pricelist
|
||||||
|
domain.pricelist(operation, period.try(:to_i), unit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
10
app/interactions/domains/client_hold/base.rb
Normal file
10
app/interactions/domains/client_hold/base.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module Domains
|
||||||
|
module ClientHold
|
||||||
|
class Base < ActiveInteraction::Base
|
||||||
|
def to_stdout(message)
|
||||||
|
time = Time.zone.now.utc
|
||||||
|
STDOUT << "#{time} - #{message}\n" unless Rails.env.test?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
69
app/interactions/domains/client_hold/process_client_hold.rb
Normal file
69
app/interactions/domains/client_hold/process_client_hold.rb
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
module Domains
|
||||||
|
module ClientHold
|
||||||
|
class ProcessClientHold < Base
|
||||||
|
object :domain,
|
||||||
|
class: Domain,
|
||||||
|
description: 'Domain to set ClientHold on'
|
||||||
|
|
||||||
|
# rubocop:disable Metrics/AbcSize
|
||||||
|
def execute
|
||||||
|
notify_on_grace_period if should_notify_on_soft_force_delete?
|
||||||
|
|
||||||
|
return unless client_holdable?
|
||||||
|
|
||||||
|
domain.statuses << DomainStatus::CLIENT_HOLD
|
||||||
|
to_stdout("DomainCron.start_client_hold: #{domain.id} (#{domain.name}) #{domain.changes}\n")
|
||||||
|
|
||||||
|
domain.save(validate: false)
|
||||||
|
notify_client_hold
|
||||||
|
|
||||||
|
to_stdout("Successfully set client_hold on (#{domain.name})")
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_on_grace_period
|
||||||
|
domain.registrar.notifications.create!(text: I18n.t('grace_period_started_domain',
|
||||||
|
domain_name: domain.name,
|
||||||
|
date: domain.force_delete_start))
|
||||||
|
send_mail if domain.template_name.present?
|
||||||
|
domain.update(contact_notification_sent_date: Time.zone.today)
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_client_hold
|
||||||
|
domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain',
|
||||||
|
domain_name: domain.name,
|
||||||
|
outzone_date: domain.outzone_date,
|
||||||
|
purge_date: domain.purge_date))
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_mail
|
||||||
|
DomainDeleteMailer.forced(domain: domain,
|
||||||
|
registrar: domain.registrar,
|
||||||
|
registrant: domain.registrant,
|
||||||
|
template_name: domain.template_name).deliver_now
|
||||||
|
end
|
||||||
|
|
||||||
|
def should_notify_on_soft_force_delete?
|
||||||
|
domain.force_delete_scheduled? && domain.contact_notification_sent_date.blank? &&
|
||||||
|
domain.force_delete_start.to_date <= Time.zone.now.to_date &&
|
||||||
|
domain.force_delete_type.to_sym == :soft &&
|
||||||
|
!domain.statuses.include?(DomainStatus::CLIENT_HOLD)
|
||||||
|
end
|
||||||
|
# rubocop:enable Metrics/AbcSize
|
||||||
|
|
||||||
|
def client_holdable?
|
||||||
|
domain.force_delete_scheduled? &&
|
||||||
|
!domain.statuses.include?(DomainStatus::CLIENT_HOLD) &&
|
||||||
|
domain.force_delete_start.present? &&
|
||||||
|
force_delete_lte_today && force_delete_lte_valid_date
|
||||||
|
end
|
||||||
|
|
||||||
|
def force_delete_lte_today
|
||||||
|
domain.force_delete_start + Setting.expire_warning_period.days <= Time.zone.now
|
||||||
|
end
|
||||||
|
|
||||||
|
def force_delete_lte_valid_date
|
||||||
|
domain.force_delete_start + Setting.expire_warning_period.days <= domain.valid_to
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
17
app/interactions/domains/client_hold/set_client_hold.rb
Normal file
17
app/interactions/domains/client_hold/set_client_hold.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
module Domains
|
||||||
|
module ClientHold
|
||||||
|
class SetClientHold < Base
|
||||||
|
def execute
|
||||||
|
to_stdout('Setting client_hold to domains\n')
|
||||||
|
|
||||||
|
::PaperTrail.request.whodunnit = "cron - #{self.class.name}"
|
||||||
|
|
||||||
|
::Domain.force_delete_scheduled.each do |domain|
|
||||||
|
Domains::ClientHold::ProcessClientHold.run(domain: domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
to_stdout('All client_hold setting are done\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
9
app/interactions/domains/delete/base.rb
Normal file
9
app/interactions/domains/delete/base.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
module Domains
|
||||||
|
module Delete
|
||||||
|
class Base < ActiveInteraction::Base
|
||||||
|
object :domain,
|
||||||
|
class: Domain,
|
||||||
|
description: 'Domain to delete'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
13
app/interactions/domains/delete/do_delete.rb
Normal file
13
app/interactions/domains/delete/do_delete.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module Domains
|
||||||
|
module Delete
|
||||||
|
class DoDelete < Base
|
||||||
|
def execute
|
||||||
|
::PaperTrail.request.whodunnit = "interaction - #{self.class.name}"
|
||||||
|
WhoisRecord.where(domain_id: domain.id).destroy_all
|
||||||
|
|
||||||
|
domain.destroy
|
||||||
|
compose(Domains::Delete::NotifyRegistrar, inputs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
14
app/interactions/domains/delete/notify_registrar.rb
Normal file
14
app/interactions/domains/delete/notify_registrar.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
module Domains
|
||||||
|
module Delete
|
||||||
|
class NotifyRegistrar < Base
|
||||||
|
def execute
|
||||||
|
bye_bye = domain.versions.last
|
||||||
|
domain.registrar.notifications.create!(
|
||||||
|
text: "#{I18n.t(:domain_deleted)}: #{domain.name}",
|
||||||
|
attached_obj_id: bye_bye.id,
|
||||||
|
attached_obj_type: bye_bye.class.to_s
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
53
app/interactions/domains/delete_confirm/base.rb
Normal file
53
app/interactions/domains/delete_confirm/base.rb
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
module Domains
|
||||||
|
module DeleteConfirm
|
||||||
|
class Base < ActiveInteraction::Base
|
||||||
|
object :domain,
|
||||||
|
class: Domain,
|
||||||
|
description: 'Domain to confirm release'
|
||||||
|
string :action
|
||||||
|
string :initiator,
|
||||||
|
default: nil
|
||||||
|
|
||||||
|
validates :domain, :action, presence: true
|
||||||
|
validates :action, inclusion: { in: [RegistrantVerification::CONFIRMED,
|
||||||
|
RegistrantVerification::REJECTED] }
|
||||||
|
|
||||||
|
def raise_errors!(domain)
|
||||||
|
return unless domain.errors.any?
|
||||||
|
|
||||||
|
message = "domain #{domain.name} failed with errors #{domain.errors.full_messages}"
|
||||||
|
throw message
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_registrar(message_key)
|
||||||
|
domain.registrar.notifications.create!(
|
||||||
|
text: "#{I18n.t(message_key)}: #{domain.name}",
|
||||||
|
attached_obj_id: domain.id,
|
||||||
|
attached_obj_type: domain.class.to_s
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def preclean_pendings
|
||||||
|
domain.registrant_verification_token = nil
|
||||||
|
domain.registrant_verification_asked_at = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def clean_pendings!
|
||||||
|
domain.is_admin = true
|
||||||
|
domain.registrant_verification_token = nil
|
||||||
|
domain.registrant_verification_asked_at = nil
|
||||||
|
domain.pending_json = {}
|
||||||
|
clear_statuses
|
||||||
|
domain.save
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear_statuses
|
||||||
|
domain.statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION)
|
||||||
|
domain.statuses.delete(DomainStatus::PENDING_UPDATE)
|
||||||
|
domain.statuses.delete(DomainStatus::PENDING_DELETE)
|
||||||
|
domain.status_notes[DomainStatus::PENDING_UPDATE] = ''
|
||||||
|
domain.status_notes[DomainStatus::PENDING_DELETE] = ''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
17
app/interactions/domains/delete_confirm/process_action.rb
Normal file
17
app/interactions/domains/delete_confirm/process_action.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
module Domains
|
||||||
|
module DeleteConfirm
|
||||||
|
class ProcessAction < Base
|
||||||
|
def execute
|
||||||
|
::PaperTrail.request.whodunnit = "interaction - #{self.class.name} - #{action} by"\
|
||||||
|
" #{initiator}"
|
||||||
|
|
||||||
|
case action
|
||||||
|
when RegistrantVerification::CONFIRMED
|
||||||
|
compose(ProcessDeleteConfirmed, inputs)
|
||||||
|
when RegistrantVerification::REJECTED
|
||||||
|
compose(ProcessDeleteRejected, inputs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,50 @@
|
||||||
|
module Domains
|
||||||
|
module DeleteConfirm
|
||||||
|
class ProcessDeleteConfirmed < Base
|
||||||
|
def execute
|
||||||
|
notify_registrar(:poll_pending_delete_confirmed_by_registrant)
|
||||||
|
domain.apply_pending_delete!
|
||||||
|
raise_errors!(domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
def apply_pending_delete!
|
||||||
|
preclean_pendings
|
||||||
|
clean_pendings!
|
||||||
|
DomainDeleteMailer.accepted(domain).deliver_now
|
||||||
|
domain.set_pending_delete!
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_pending_delete!
|
||||||
|
unless domain.pending_deletable?
|
||||||
|
add_epp_error
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
domain.delete_date = delete_date
|
||||||
|
domain.statuses << DomainStatus::PENDING_DELETE
|
||||||
|
set_server_hold if server_holdable?
|
||||||
|
domain.save(validate: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_server_hold
|
||||||
|
domain.statuses << DomainStatus::SERVER_HOLD
|
||||||
|
domain.outzone_at = Time.current
|
||||||
|
end
|
||||||
|
|
||||||
|
def server_holdable?
|
||||||
|
return false if domain.statuses.include?(DomainStatus::SERVER_HOLD)
|
||||||
|
return false if domain.statuses.include?(DomainStatus::SERVER_MANUAL_INZONE)
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_date
|
||||||
|
Time.zone.today + Setting.redemption_grace_period.days + 1.day
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_epp_error
|
||||||
|
domain.add_epp_error('2304', nil, nil, I18n.t(:object_status_prohibits_operation))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,32 @@
|
||||||
|
module Domains
|
||||||
|
module DeleteConfirm
|
||||||
|
class ProcessDeleteRejected < Base
|
||||||
|
def execute
|
||||||
|
domain.cancel_pending_delete
|
||||||
|
notify_registrar(:poll_pending_delete_rejected_by_registrant)
|
||||||
|
domain.save(validate: false)
|
||||||
|
raise_errors!(domain)
|
||||||
|
|
||||||
|
send_domain_delete_rejected_email
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_domain_delete_rejected_email
|
||||||
|
if domain.registrant_verification_token.blank?
|
||||||
|
warn "EMAIL NOT DELIVERED: registrant_verification_token is missing for #{domain.name}"
|
||||||
|
elsif domain.registrant_verification_asked_at.blank?
|
||||||
|
warn "EMAIL NOT DELIVERED: registrant_verification_asked_at is missing for #{domain.name}"
|
||||||
|
else
|
||||||
|
send_email
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def warn(message)
|
||||||
|
Rails.logger.warn(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_email
|
||||||
|
DomainDeleteMailer.rejected(domain).deliver_now
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,24 @@
|
||||||
|
module Domains
|
||||||
|
module DeleteConfirmEmail
|
||||||
|
class SendRequest < ActiveInteraction::Base
|
||||||
|
object :domain,
|
||||||
|
class: Domain,
|
||||||
|
description: 'Domain to send delete confirmation'
|
||||||
|
|
||||||
|
def execute
|
||||||
|
log
|
||||||
|
DomainDeleteMailer.confirmation_request(domain: domain,
|
||||||
|
registrar: domain.registrar,
|
||||||
|
registrant: domain.registrant).deliver_later
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def log
|
||||||
|
message = "Send DomainDeleteMailer#confirm email for domain #{domain.name} (##{domain.id})"\
|
||||||
|
" to #{domain.registrant.email}"
|
||||||
|
Rails.logger.info(message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
10
app/interactions/domains/expire_period/base.rb
Normal file
10
app/interactions/domains/expire_period/base.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module Domains
|
||||||
|
module ExpirePeriod
|
||||||
|
class Base < ActiveInteraction::Base
|
||||||
|
def to_stdout(message)
|
||||||
|
time = Time.zone.now.utc
|
||||||
|
STDOUT << "#{time} - #{message}\n" unless Rails.env.test?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
28
app/interactions/domains/expire_period/process_expired.rb
Normal file
28
app/interactions/domains/expire_period/process_expired.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
module Domains
|
||||||
|
module ExpirePeriod
|
||||||
|
class ProcessExpired < Base
|
||||||
|
object :domain,
|
||||||
|
class: Domain,
|
||||||
|
description: 'Domain to set expiration'
|
||||||
|
|
||||||
|
def execute
|
||||||
|
set_graceful_expired
|
||||||
|
to_stdout("start_expire_period: ##{domain.id} (#{domain.name}) #{domain.changes}")
|
||||||
|
|
||||||
|
saved = domain.save(validate: false)
|
||||||
|
|
||||||
|
DomainExpireEmailJob.enqueue(domain.id, run_at: send_time) if saved
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_graceful_expired
|
||||||
|
domain.outzone_at = domain.expire_time + Domain.expire_warning_period
|
||||||
|
domain.delete_date = domain.outzone_at + Domain.redemption_grace_period
|
||||||
|
domain.statuses |= [DomainStatus::EXPIRED]
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_time
|
||||||
|
domain.valid_to + Setting.expiration_reminder_mail.to_i.days
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
19
app/interactions/domains/expire_period/start.rb
Normal file
19
app/interactions/domains/expire_period/start.rb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
module Domains
|
||||||
|
module ExpirePeriod
|
||||||
|
class Start < Base
|
||||||
|
def execute
|
||||||
|
::PaperTrail.request.whodunnit = "cron - #{self.class.name}"
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
Domain.expired.each do |domain|
|
||||||
|
next unless domain.expirable?
|
||||||
|
|
||||||
|
count += 1
|
||||||
|
Domains::ExpirePeriod::ProcessExpired.run(domain: domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
to_stdout("Successfully expired #{count}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
10
app/interactions/domains/expired_pendings/base.rb
Normal file
10
app/interactions/domains/expired_pendings/base.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module Domains
|
||||||
|
module ExpiredPendings
|
||||||
|
class Base < ActiveInteraction::Base
|
||||||
|
def to_stdout(message)
|
||||||
|
time = Time.zone.now.utc
|
||||||
|
STDOUT << "#{time} - #{message}\n" unless Rails.env.test?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
35
app/interactions/domains/expired_pendings/clean_all.rb
Normal file
35
app/interactions/domains/expired_pendings/clean_all.rb
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
module Domains
|
||||||
|
module ExpiredPendings
|
||||||
|
class CleanAll < Base
|
||||||
|
def execute
|
||||||
|
to_stdout('Clean expired domain pendings')
|
||||||
|
|
||||||
|
::PaperTrail.request.whodunnit = "cron - #{self.class.name}"
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
expired_pending_domains.each do |domain|
|
||||||
|
log_error(domain) && next unless need_to_be_cleared?(domain)
|
||||||
|
count += 1
|
||||||
|
Domains::ExpiredPendings::ProcessClean.run(domain: domain)
|
||||||
|
end
|
||||||
|
to_stdout("Successfully cancelled #{count} domain pendings")
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def need_to_be_cleared?(domain)
|
||||||
|
domain.pending_update? || domain.pending_delete? || domain.pending_delete_confirmation?
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_error(domain)
|
||||||
|
to_stdout("ISSUE: DOMAIN #{domain.id}: #{domain.name} IS IN EXPIRED PENDING LIST, "\
|
||||||
|
'but no pendingDelete/pendingUpdate state present!')
|
||||||
|
end
|
||||||
|
|
||||||
|
def expired_pending_domains
|
||||||
|
expire_at = Setting.expire_pending_confirmation.hours.ago
|
||||||
|
Domain.where('registrant_verification_asked_at <= ?', expire_at)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
60
app/interactions/domains/expired_pendings/process_clean.rb
Normal file
60
app/interactions/domains/expired_pendings/process_clean.rb
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
module Domains
|
||||||
|
module ExpiredPendings
|
||||||
|
class ProcessClean < Base
|
||||||
|
object :domain,
|
||||||
|
class: Domain
|
||||||
|
|
||||||
|
def execute
|
||||||
|
check_notify
|
||||||
|
clean_pendings
|
||||||
|
|
||||||
|
to_stdout("DomainCron.clean_expired_pendings: ##{domain.id} (#{domain.name})")
|
||||||
|
UpdateWhoisRecordJob.enqueue domain.name, 'domain'
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def notify_pending_update
|
||||||
|
RegistrantChangeMailer.expired(domain: domain,
|
||||||
|
registrar: domain.registrar,
|
||||||
|
registrant: domain.registrant).deliver_later
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_pending_delete
|
||||||
|
DomainDeleteMailer.expired(domain).deliver_later
|
||||||
|
end
|
||||||
|
|
||||||
|
def clean_pendings
|
||||||
|
clean_verification_data
|
||||||
|
domain.pending_json = {}
|
||||||
|
clean_statuses
|
||||||
|
domain.save
|
||||||
|
end
|
||||||
|
|
||||||
|
def statuses_to_clean
|
||||||
|
[DomainStatus::PENDING_DELETE_CONFIRMATION,
|
||||||
|
DomainStatus::PENDING_UPDATE,
|
||||||
|
DomainStatus::PENDING_DELETE]
|
||||||
|
end
|
||||||
|
|
||||||
|
def clean_statuses
|
||||||
|
domain.statuses = domain.statuses - statuses_to_clean
|
||||||
|
domain.status_notes[DomainStatus::PENDING_UPDATE] = ''
|
||||||
|
domain.status_notes[DomainStatus::PENDING_DELETE] = ''
|
||||||
|
end
|
||||||
|
|
||||||
|
def clean_verification_data
|
||||||
|
domain.registrant_verification_token = nil
|
||||||
|
domain.registrant_verification_asked_at = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_notify
|
||||||
|
notify_pending_update if domain.pending_update?
|
||||||
|
|
||||||
|
return unless domain.pending_delete? || domain.pending_delete_confirmation?
|
||||||
|
|
||||||
|
notify_pending_delete
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
17
app/interactions/domains/force_delete/base.rb
Normal file
17
app/interactions/domains/force_delete/base.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
module Domains
|
||||||
|
module ForceDelete
|
||||||
|
class Base < ActiveInteraction::Base
|
||||||
|
object :domain,
|
||||||
|
class: Domain,
|
||||||
|
description: 'Domain to set ForceDelete on'
|
||||||
|
symbol :type,
|
||||||
|
default: :fast_track,
|
||||||
|
description: 'Force delete type, might be :fast_track or :soft'
|
||||||
|
boolean :notify_by_email,
|
||||||
|
default: false,
|
||||||
|
description: 'Do we need to send email notification'
|
||||||
|
|
||||||
|
validates :type, inclusion: { in: %i[fast_track soft] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
12
app/interactions/domains/force_delete/check_discarded.rb
Normal file
12
app/interactions/domains/force_delete/check_discarded.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module Domains
|
||||||
|
module ForceDelete
|
||||||
|
class CheckDiscarded < Base
|
||||||
|
def execute
|
||||||
|
return true unless domain.discarded?
|
||||||
|
|
||||||
|
message = 'Force delete procedure cannot be scheduled while a domain is discarded'
|
||||||
|
errors.add(:domain, message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
app/interactions/domains/force_delete/notify_by_email.rb
Normal file
23
app/interactions/domains/force_delete/notify_by_email.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
module Domains
|
||||||
|
module ForceDelete
|
||||||
|
class NotifyByEmail < Base
|
||||||
|
def execute
|
||||||
|
return unless notify_by_email
|
||||||
|
|
||||||
|
if type == :fast_track
|
||||||
|
send_email
|
||||||
|
domain.update(contact_notification_sent_date: Time.zone.today)
|
||||||
|
else
|
||||||
|
domain.update(template_name: domain.notification_template)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_email
|
||||||
|
DomainDeleteMailer.forced(domain: domain,
|
||||||
|
registrar: domain.registrar,
|
||||||
|
registrant: domain.registrant,
|
||||||
|
template_name: domain.notification_template).deliver_now
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
12
app/interactions/domains/force_delete/notify_registrar.rb
Normal file
12
app/interactions/domains/force_delete/notify_registrar.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module Domains
|
||||||
|
module ForceDelete
|
||||||
|
class NotifyRegistrar < Base
|
||||||
|
def execute
|
||||||
|
domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain',
|
||||||
|
domain_name: domain.name,
|
||||||
|
outzone_date: domain.outzone_date,
|
||||||
|
purge_date: domain.purge_date))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
19
app/interactions/domains/force_delete/post_set_process.rb
Normal file
19
app/interactions/domains/force_delete/post_set_process.rb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
module Domains
|
||||||
|
module ForceDelete
|
||||||
|
class PostSetProcess < Base
|
||||||
|
def execute
|
||||||
|
statuses = domain.statuses
|
||||||
|
# Stop all pending actions
|
||||||
|
statuses.delete(DomainStatus::PENDING_UPDATE)
|
||||||
|
statuses.delete(DomainStatus::PENDING_TRANSFER)
|
||||||
|
statuses.delete(DomainStatus::PENDING_RENEW)
|
||||||
|
statuses.delete(DomainStatus::PENDING_CREATE)
|
||||||
|
|
||||||
|
# Allow deletion
|
||||||
|
statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED)
|
||||||
|
statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED)
|
||||||
|
domain.save(validate: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
15
app/interactions/domains/force_delete/prepare_domain.rb
Normal file
15
app/interactions/domains/force_delete/prepare_domain.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
module Domains
|
||||||
|
module ForceDelete
|
||||||
|
class PrepareDomain < Base
|
||||||
|
STATUSES_TO_SET = [DomainStatus::FORCE_DELETE,
|
||||||
|
DomainStatus::SERVER_RENEW_PROHIBITED,
|
||||||
|
DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze
|
||||||
|
|
||||||
|
def execute
|
||||||
|
domain.statuses_before_force_delete = domain.statuses
|
||||||
|
domain.statuses |= STATUSES_TO_SET
|
||||||
|
domain.save(validate: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
14
app/interactions/domains/force_delete/set_force_delete.rb
Normal file
14
app/interactions/domains/force_delete/set_force_delete.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
module Domains
|
||||||
|
module ForceDelete
|
||||||
|
class SetForceDelete < Base
|
||||||
|
def execute
|
||||||
|
compose(CheckDiscarded, inputs)
|
||||||
|
compose(PrepareDomain, inputs)
|
||||||
|
compose(SetStatus, inputs)
|
||||||
|
compose(PostSetProcess, inputs)
|
||||||
|
compose(NotifyRegistrar, inputs)
|
||||||
|
compose(NotifyByEmail, inputs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
40
app/interactions/domains/force_delete/set_status.rb
Normal file
40
app/interactions/domains/force_delete/set_status.rb
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
module Domains
|
||||||
|
module ForceDelete
|
||||||
|
class SetStatus < Base
|
||||||
|
def execute
|
||||||
|
domain.force_delete_type = type
|
||||||
|
type == :fast_track ? force_delete_fast_track : force_delete_soft
|
||||||
|
domain.save(validate: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def force_delete_fast_track
|
||||||
|
domain.force_delete_date = Time.zone.today +
|
||||||
|
expire_warning_period_days +
|
||||||
|
redemption_grace_period_days
|
||||||
|
domain.force_delete_start = Time.zone.today + 1.day
|
||||||
|
end
|
||||||
|
|
||||||
|
def force_delete_soft
|
||||||
|
years = (domain.valid_to.to_date - Time.zone.today).to_i / 365
|
||||||
|
soft_forcedelete_dates(years) if years.positive?
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def soft_forcedelete_dates(years)
|
||||||
|
domain.force_delete_start = domain.valid_to - years.years
|
||||||
|
domain.force_delete_date = domain.force_delete_start +
|
||||||
|
Setting.expire_warning_period.days +
|
||||||
|
Setting.redemption_grace_period.days
|
||||||
|
end
|
||||||
|
|
||||||
|
def redemption_grace_period_days
|
||||||
|
Setting.redemption_grace_period.days + 1.day
|
||||||
|
end
|
||||||
|
|
||||||
|
def expire_warning_period_days
|
||||||
|
Setting.expire_warning_period.days
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
10
app/interactions/domains/redemption_grace_period/base.rb
Normal file
10
app/interactions/domains/redemption_grace_period/base.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module Domains
|
||||||
|
module RedemptionGracePeriod
|
||||||
|
class Base < ActiveInteraction::Base
|
||||||
|
def to_stdout(message)
|
||||||
|
time = Time.zone.now.utc
|
||||||
|
STDOUT << "#{time} - #{message}\n" unless Rails.env.test?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,20 @@
|
||||||
|
module Domains
|
||||||
|
module RedemptionGracePeriod
|
||||||
|
class ProcessGracePeriod < Base
|
||||||
|
object :domain,
|
||||||
|
class: Domain
|
||||||
|
|
||||||
|
def execute
|
||||||
|
domain.statuses << DomainStatus::SERVER_HOLD
|
||||||
|
to_stdout(process_msg)
|
||||||
|
domain.save(validate: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def process_msg
|
||||||
|
"start_redemption_grace_period: #{domain.id} (#{domain.name}) #{domain.changes}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
20
app/interactions/domains/redemption_grace_period/start.rb
Normal file
20
app/interactions/domains/redemption_grace_period/start.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
module Domains
|
||||||
|
module RedemptionGracePeriod
|
||||||
|
class Start < Base
|
||||||
|
def execute
|
||||||
|
to_stdout('Setting server_hold to domains')
|
||||||
|
|
||||||
|
::PaperTrail.request.whodunnit = "cron - #{self.class.name}"
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
Domain.outzone_candidates.each do |domain|
|
||||||
|
next unless domain.server_holdable?
|
||||||
|
|
||||||
|
count += 1
|
||||||
|
Domains::RedemptionGracePeriod::ProcessGracePeriod.run(domain: domain)
|
||||||
|
end
|
||||||
|
to_stdout("Successfully set server_hold to #{count} of domains")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
53
app/interactions/domains/update_confirm/base.rb
Normal file
53
app/interactions/domains/update_confirm/base.rb
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
module Domains
|
||||||
|
module UpdateConfirm
|
||||||
|
class Base < ActiveInteraction::Base
|
||||||
|
object :domain,
|
||||||
|
class: Domain,
|
||||||
|
description: 'Domain to confirm update'
|
||||||
|
string :action
|
||||||
|
string :initiator,
|
||||||
|
default: nil
|
||||||
|
|
||||||
|
validates :domain, :action, presence: true
|
||||||
|
validates :action, inclusion: { in: [RegistrantVerification::CONFIRMED,
|
||||||
|
RegistrantVerification::REJECTED] }
|
||||||
|
|
||||||
|
def raise_errors!(domain)
|
||||||
|
return unless domain.errors.any?
|
||||||
|
|
||||||
|
message = "domain #{domain.name} failed with errors #{domain.errors.full_messages}"
|
||||||
|
throw message
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_registrar(message_key)
|
||||||
|
domain.registrar.notifications.create!(
|
||||||
|
text: "#{I18n.t(message_key)}: #{domain.name}",
|
||||||
|
attached_obj_id: domain.id,
|
||||||
|
attached_obj_type: domain.class.to_s
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def preclean_pendings
|
||||||
|
domain.registrant_verification_token = nil
|
||||||
|
domain.registrant_verification_asked_at = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def clean_pendings!
|
||||||
|
domain.is_admin = true
|
||||||
|
domain.registrant_verification_token = nil
|
||||||
|
domain.registrant_verification_asked_at = nil
|
||||||
|
domain.pending_json = {}
|
||||||
|
clear_statuses
|
||||||
|
domain.save
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear_statuses
|
||||||
|
domain.statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION)
|
||||||
|
domain.statuses.delete(DomainStatus::PENDING_UPDATE)
|
||||||
|
domain.statuses.delete(DomainStatus::PENDING_DELETE)
|
||||||
|
domain.status_notes[DomainStatus::PENDING_UPDATE] = ''
|
||||||
|
domain.status_notes[DomainStatus::PENDING_DELETE] = ''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
17
app/interactions/domains/update_confirm/process_action.rb
Normal file
17
app/interactions/domains/update_confirm/process_action.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
module Domains
|
||||||
|
module UpdateConfirm
|
||||||
|
class ProcessAction < Base
|
||||||
|
def execute
|
||||||
|
::PaperTrail.request.whodunnit = "interaction - #{self.class.name} - #{action} by"\
|
||||||
|
" #{initiator}"
|
||||||
|
|
||||||
|
case action
|
||||||
|
when RegistrantVerification::CONFIRMED
|
||||||
|
compose(ProcessUpdateConfirmed, inputs)
|
||||||
|
when RegistrantVerification::REJECTED
|
||||||
|
compose(ProcessUpdateRejected, inputs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,37 @@
|
||||||
|
module Domains
|
||||||
|
module UpdateConfirm
|
||||||
|
class ProcessUpdateConfirmed < Base
|
||||||
|
def execute
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
old_registrant = domain.registrant
|
||||||
|
notify_registrar(:poll_pending_update_confirmed_by_registrant)
|
||||||
|
|
||||||
|
apply_pending_update!
|
||||||
|
raise_errors!(domain)
|
||||||
|
RegistrantChange.new(domain: domain, old_registrant: old_registrant).confirm
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def apply_pending_update!
|
||||||
|
preclean_pendings
|
||||||
|
update_domain
|
||||||
|
clean_pendings!
|
||||||
|
|
||||||
|
WhoisRecord.find_by(domain_id: domain.id).save # need to reload model
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_domain
|
||||||
|
user = ApiUser.find(domain.pending_json['current_user_id'])
|
||||||
|
frame = domain.pending_json['frame'] ? domain.pending_json['frame'].with_indifferent_access : {}
|
||||||
|
|
||||||
|
#self.statuses.delete(DomainStatus::PENDING_UPDATE)
|
||||||
|
domain.upid = user.registrar.id if user.registrar
|
||||||
|
domain.up_date = Time.zone.now
|
||||||
|
|
||||||
|
Actions::DomainUpdate.new(domain, frame, true).call
|
||||||
|
|
||||||
|
#save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,18 @@
|
||||||
|
module Domains
|
||||||
|
module UpdateConfirm
|
||||||
|
class ProcessUpdateRejected < Base
|
||||||
|
def execute
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
RegistrantChangeMailer.rejected(domain: domain,
|
||||||
|
registrar: domain.registrar,
|
||||||
|
registrant: domain.registrant).deliver_now
|
||||||
|
|
||||||
|
notify_registrar(:poll_pending_update_rejected_by_registrant)
|
||||||
|
|
||||||
|
preclean_pendings
|
||||||
|
clean_pendings!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,16 +0,0 @@
|
||||||
module ForceDeleteInteraction
|
|
||||||
class Base < ActiveInteraction::Base
|
|
||||||
object :domain,
|
|
||||||
class: Domain,
|
|
||||||
description: 'Domain to set ForceDelete on'
|
|
||||||
symbol :type,
|
|
||||||
default: :fast_track,
|
|
||||||
description: 'Force delete type, might be :fast_track or :soft'
|
|
||||||
boolean :notify_by_email,
|
|
||||||
default: false,
|
|
||||||
description: 'Do we need to send email notification'
|
|
||||||
|
|
||||||
validates :type, inclusion: { in: %i[fast_track soft] }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
module ForceDeleteInteraction
|
|
||||||
class CheckDiscarded < Base
|
|
||||||
def execute
|
|
||||||
return true unless domain.discarded?
|
|
||||||
|
|
||||||
message = 'Force delete procedure cannot be scheduled while a domain is discarded'
|
|
||||||
errors.add(:domain, message)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
module ForceDeleteInteraction
|
|
||||||
class NotifyByEmail < Base
|
|
||||||
def execute
|
|
||||||
return unless notify_by_email
|
|
||||||
|
|
||||||
if type == :fast_track
|
|
||||||
send_email
|
|
||||||
domain.update(contact_notification_sent_date: Time.zone.today)
|
|
||||||
else
|
|
||||||
domain.update(template_name: domain.notification_template)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def send_email
|
|
||||||
DomainDeleteMailer.forced(domain: domain,
|
|
||||||
registrar: domain.registrar,
|
|
||||||
registrant: domain.registrant,
|
|
||||||
template_name: domain.notification_template).deliver_now
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,10 +0,0 @@
|
||||||
module ForceDeleteInteraction
|
|
||||||
class NotifyRegistrar < Base
|
|
||||||
def execute
|
|
||||||
domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain',
|
|
||||||
domain_name: domain.name,
|
|
||||||
outzone_date: domain.outzone_date,
|
|
||||||
purge_date: domain.purge_date))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,17 +0,0 @@
|
||||||
module ForceDeleteInteraction
|
|
||||||
class PostSetProcess < Base
|
|
||||||
def execute
|
|
||||||
statuses = domain.statuses
|
|
||||||
# Stop all pending actions
|
|
||||||
statuses.delete(DomainStatus::PENDING_UPDATE)
|
|
||||||
statuses.delete(DomainStatus::PENDING_TRANSFER)
|
|
||||||
statuses.delete(DomainStatus::PENDING_RENEW)
|
|
||||||
statuses.delete(DomainStatus::PENDING_CREATE)
|
|
||||||
|
|
||||||
# Allow deletion
|
|
||||||
statuses.delete(DomainStatus::CLIENT_DELETE_PROHIBITED)
|
|
||||||
statuses.delete(DomainStatus::SERVER_DELETE_PROHIBITED)
|
|
||||||
domain.save(validate: false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,13 +0,0 @@
|
||||||
module ForceDeleteInteraction
|
|
||||||
class PrepareDomain < Base
|
|
||||||
STATUSES_TO_SET = [DomainStatus::FORCE_DELETE,
|
|
||||||
DomainStatus::SERVER_RENEW_PROHIBITED,
|
|
||||||
DomainStatus::SERVER_TRANSFER_PROHIBITED].freeze
|
|
||||||
|
|
||||||
def execute
|
|
||||||
domain.statuses_before_force_delete = domain.statuses
|
|
||||||
domain.statuses |= STATUSES_TO_SET
|
|
||||||
domain.save(validate: false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,12 +0,0 @@
|
||||||
module ForceDeleteInteraction
|
|
||||||
class SetForceDelete < Base
|
|
||||||
def execute
|
|
||||||
compose(CheckDiscarded, inputs)
|
|
||||||
compose(PrepareDomain, inputs)
|
|
||||||
compose(SetStatus, inputs)
|
|
||||||
compose(PostSetProcess, inputs)
|
|
||||||
compose(NotifyRegistrar, inputs)
|
|
||||||
compose(NotifyByEmail, inputs)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,38 +0,0 @@
|
||||||
module ForceDeleteInteraction
|
|
||||||
class SetStatus < Base
|
|
||||||
def execute
|
|
||||||
domain.force_delete_type = type
|
|
||||||
type == :fast_track ? force_delete_fast_track : force_delete_soft
|
|
||||||
domain.save(validate: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
def force_delete_fast_track
|
|
||||||
domain.force_delete_date = Time.zone.today +
|
|
||||||
expire_warning_period_days +
|
|
||||||
redemption_grace_period_days
|
|
||||||
domain.force_delete_start = Time.zone.today + 1.day
|
|
||||||
end
|
|
||||||
|
|
||||||
def force_delete_soft
|
|
||||||
years = (domain.valid_to.to_date - Time.zone.today).to_i / 365
|
|
||||||
soft_forcedelete_dates(years) if years.positive?
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def soft_forcedelete_dates(years)
|
|
||||||
domain.force_delete_start = domain.valid_to - years.years
|
|
||||||
domain.force_delete_date = domain.force_delete_start +
|
|
||||||
Setting.expire_warning_period.days +
|
|
||||||
Setting.redemption_grace_period.days
|
|
||||||
end
|
|
||||||
|
|
||||||
def redemption_grace_period_days
|
|
||||||
Setting.redemption_grace_period.days + 1.day
|
|
||||||
end
|
|
||||||
|
|
||||||
def expire_warning_period_days
|
|
||||||
Setting.expire_warning_period.days
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
48
app/interactions/whois/delete_record.rb
Normal file
48
app/interactions/whois/delete_record.rb
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
module Whois
|
||||||
|
class DeleteRecord < ActiveInteraction::Base
|
||||||
|
string :name
|
||||||
|
string :type
|
||||||
|
|
||||||
|
validates :type, inclusion: { in: %w[reserved blocked domain disputed zone] }
|
||||||
|
|
||||||
|
def execute
|
||||||
|
send "delete_#{type}", name
|
||||||
|
end
|
||||||
|
|
||||||
|
# 1. deleting own
|
||||||
|
# 2. trying to regenerate reserved in order domain is still in the list
|
||||||
|
def delete_domain(name)
|
||||||
|
WhoisRecord.where(name: name).destroy_all
|
||||||
|
|
||||||
|
BlockedDomain.find_by(name: name).try(:generate_data)
|
||||||
|
ReservedDomain.find_by(name: name).try(:generate_data)
|
||||||
|
Dispute.active.find_by(domain_name: name).try(:generate_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_reserved(name)
|
||||||
|
remove_status_from_whois(domain_name: name, domain_status: 'Reserved')
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_blocked(name)
|
||||||
|
delete_reserved(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_disputed(name)
|
||||||
|
return if Dispute.active.find_by(domain_name: name).present?
|
||||||
|
|
||||||
|
remove_status_from_whois(domain_name: name, domain_status: 'disputed')
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_zone(name)
|
||||||
|
WhoisRecord.where(name: name).destroy_all
|
||||||
|
Whois::Record.where(name: name).destroy_all
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_status_from_whois(domain_name:, domain_status:)
|
||||||
|
Whois::Record.where(name: domain_name).each do |r|
|
||||||
|
r.json['status'] = r.json['status'].delete_if { |status| status == domain_status }
|
||||||
|
r.json['status'].blank? ? r.destroy : r.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
39
app/interactions/whois/update.rb
Normal file
39
app/interactions/whois/update.rb
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
module Whois
|
||||||
|
class Update < ActiveInteraction::Base
|
||||||
|
array :names
|
||||||
|
string :type
|
||||||
|
|
||||||
|
validates :type, inclusion: { in: %w[reserved blocked domain disputed zone] }
|
||||||
|
|
||||||
|
def execute
|
||||||
|
::PaperTrail.request.whodunnit = "job - #{self.class.name} - #{type}"
|
||||||
|
|
||||||
|
klass = determine_class
|
||||||
|
|
||||||
|
Array(names).each do |name|
|
||||||
|
record = find_record(klass, name)
|
||||||
|
if record
|
||||||
|
Whois::UpdateRecord.run(record: record, type: type)
|
||||||
|
else
|
||||||
|
Whois::DeleteRecord.run(name: name, type: type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def determine_class
|
||||||
|
case type
|
||||||
|
when 'reserved' then ReservedDomain
|
||||||
|
when 'blocked' then BlockedDomain
|
||||||
|
when 'domain' then Domain
|
||||||
|
when 'disputed' then Dispute.active
|
||||||
|
else DNS::Zone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_record(klass, name)
|
||||||
|
klass == DNS::Zone ? klass.find_by(origin: name) : klass.find_by(name: name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
32
app/interactions/whois/update_record.rb
Normal file
32
app/interactions/whois/update_record.rb
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
module Whois
|
||||||
|
class UpdateRecord < ActiveInteraction::Base
|
||||||
|
interface :record
|
||||||
|
string :type
|
||||||
|
|
||||||
|
validates :type, inclusion: { in: %w[reserved blocked domain disputed zone] }
|
||||||
|
|
||||||
|
def execute
|
||||||
|
send "update_#{type}", record
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_domain(domain)
|
||||||
|
domain.whois_record ? domain.whois_record.save : domain.create_whois_record
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_reserved(record)
|
||||||
|
record.generate_data
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_blocked(record)
|
||||||
|
update_reserved(record)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_disputed(record)
|
||||||
|
update_reserved(record)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_zone(record)
|
||||||
|
update_reserved(record)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
2
app/jobs/application_job.rb
Normal file
2
app/jobs/application_job.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
class ApplicationJob < ActiveJob::Base
|
||||||
|
end
|
|
@ -1,22 +0,0 @@
|
||||||
class DomainDeleteConfirmEmailJob < Que::Job
|
|
||||||
def run(domain_id)
|
|
||||||
domain = Domain.find(domain_id)
|
|
||||||
|
|
||||||
log(domain)
|
|
||||||
DomainDeleteMailer.confirmation_request(domain: domain,
|
|
||||||
registrar: domain.registrar,
|
|
||||||
registrant: domain.registrant).deliver_now
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def log(domain)
|
|
||||||
message = "Send DomainDeleteMailer#confirm email for domain #{domain.name} (##{domain.id})" \
|
|
||||||
" to #{domain.registrant.email}"
|
|
||||||
logger.info(message)
|
|
||||||
end
|
|
||||||
|
|
||||||
def logger
|
|
||||||
Rails.logger
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,39 +1,11 @@
|
||||||
class DomainDeleteConfirmJob < Que::Job
|
class DomainDeleteConfirmJob < ApplicationJob
|
||||||
def run(domain_id, action, initiator = nil)
|
queue_as :default
|
||||||
::PaperTrail.request.whodunnit = "job - #{self.class.name} - #{action} by #{initiator}"
|
|
||||||
# it's recommended to keep transaction against job table as short as possible.
|
|
||||||
ActiveRecord::Base.transaction do
|
|
||||||
domain = Epp::Domain.find(domain_id)
|
|
||||||
|
|
||||||
case action
|
def perform(domain_id, action, initiator = nil)
|
||||||
when RegistrantVerification::CONFIRMED
|
domain = Epp::Domain.find(domain_id)
|
||||||
domain.notify_registrar(:poll_pending_delete_confirmed_by_registrant)
|
|
||||||
domain.apply_pending_delete!
|
|
||||||
raise_errors!(domain)
|
|
||||||
|
|
||||||
when RegistrantVerification::REJECTED
|
Domains::DeleteConfirm::ProcessAction.run(domain: domain,
|
||||||
domain.statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION)
|
action: action,
|
||||||
domain.notify_registrar(:poll_pending_delete_rejected_by_registrant)
|
initiator: initiator)
|
||||||
|
|
||||||
domain.cancel_pending_delete
|
|
||||||
domain.save(validate: false)
|
|
||||||
raise_errors!(domain)
|
|
||||||
|
|
||||||
if domain.registrant_verification_token.blank?
|
|
||||||
Rails.logger.warn "EMAIL NOT DELIVERED: registrant_verification_token is missing for #{domain.name}"
|
|
||||||
elsif domain.registrant_verification_asked_at.blank?
|
|
||||||
Rails.logger.warn "EMAIL NOT DELIVERED: registrant_verification_asked_at is missing for #{domain.name}"
|
|
||||||
else
|
|
||||||
DomainDeleteMailer.rejected(domain).deliver_now
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
destroy # it's best to destroy the job in the same transaction
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def raise_errors!(domain)
|
|
||||||
throw "domain #{domain.name} failed with errors #{domain.errors.full_messages}" if domain.errors.any?
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,15 +3,6 @@ class DomainDeleteJob < Que::Job
|
||||||
def run(domain_id)
|
def run(domain_id)
|
||||||
domain = Domain.find(domain_id)
|
domain = Domain.find(domain_id)
|
||||||
|
|
||||||
::PaperTrail.request.whodunnit = "job - #{self.class.name}"
|
Domains::Delete::DoDelete.run(domain: domain)
|
||||||
WhoisRecord.where(domain_id: domain.id).destroy_all
|
|
||||||
|
|
||||||
domain.destroy
|
|
||||||
bye_bye = domain.versions.last
|
|
||||||
domain.registrar.notifications.create!(
|
|
||||||
text: "#{I18n.t(:domain_deleted)}: #{domain.name}",
|
|
||||||
attached_obj_id: bye_bye.id,
|
|
||||||
attached_obj_type: bye_bye.class.to_s
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,37 +1,10 @@
|
||||||
class DomainUpdateConfirmJob < Que::Job
|
class DomainUpdateConfirmJob < ApplicationJob
|
||||||
def run(domain_id, action, initiator = nil)
|
queue_as :default
|
||||||
::PaperTrail.request.whodunnit = "job - #{self.class.name} - #{action} by #{initiator}"
|
|
||||||
# it's recommended to keep transaction against job table as short as possible.
|
|
||||||
ActiveRecord::Base.transaction do
|
|
||||||
domain = Epp::Domain.find(domain_id)
|
|
||||||
domain.is_admin = true
|
|
||||||
case action
|
|
||||||
when RegistrantVerification::CONFIRMED
|
|
||||||
old_registrant = domain.registrant
|
|
||||||
domain.notify_registrar(:poll_pending_update_confirmed_by_registrant)
|
|
||||||
raise_errors!(domain)
|
|
||||||
|
|
||||||
domain.apply_pending_update!
|
def perform(domain_id, action, initiator = nil)
|
||||||
raise_errors!(domain)
|
domain = Epp::Domain.find(domain_id)
|
||||||
|
Domains::UpdateConfirm::ProcessAction.run(domain: domain,
|
||||||
domain.clean_pendings!
|
action: action,
|
||||||
raise_errors!(domain)
|
initiator: initiator)
|
||||||
RegistrantChange.new(domain: domain, old_registrant: old_registrant).confirm
|
|
||||||
when RegistrantVerification::REJECTED
|
|
||||||
RegistrantChangeMailer.rejected(domain: domain,
|
|
||||||
registrar: domain.registrar,
|
|
||||||
registrant: domain.registrant).deliver_now
|
|
||||||
|
|
||||||
domain.notify_registrar(:poll_pending_update_rejected_by_registrant)
|
|
||||||
|
|
||||||
domain.preclean_pendings
|
|
||||||
domain.clean_pendings!
|
|
||||||
end
|
|
||||||
destroy # it's best to destroy the job in the same transaction
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def raise_errors!(domain)
|
|
||||||
throw "domain #{domain.name} failed with errors #{domain.errors.full_messages}" if domain.errors.any?
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,87 +1,5 @@
|
||||||
class UpdateWhoisRecordJob < Que::Job
|
class UpdateWhoisRecordJob < Que::Job
|
||||||
|
|
||||||
def run(names, type)
|
def run(names, type)
|
||||||
::PaperTrail.request.whodunnit = "job - #{self.class.name} - #{type}"
|
Whois::Update.run(names: [names].flatten, type: type)
|
||||||
|
|
||||||
klass = determine_class(type)
|
|
||||||
|
|
||||||
Array(names).each do |name|
|
|
||||||
record = find_record(klass, name)
|
|
||||||
if record
|
|
||||||
send "update_#{type}", record
|
|
||||||
else
|
|
||||||
send "delete_#{type}", name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_record(klass, name)
|
|
||||||
klass == DNS::Zone ? klass.find_by(origin: name) : klass.find_by(name: name)
|
|
||||||
end
|
|
||||||
|
|
||||||
def determine_class(type)
|
|
||||||
case type
|
|
||||||
when 'reserved' then ReservedDomain
|
|
||||||
when 'blocked' then BlockedDomain
|
|
||||||
when 'domain' then Domain
|
|
||||||
when 'disputed' then Dispute.active
|
|
||||||
when 'zone' then DNS::Zone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_domain(domain)
|
|
||||||
domain.whois_record ? domain.whois_record.save : domain.create_whois_record
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_reserved(record)
|
|
||||||
record.generate_data
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_blocked(record)
|
|
||||||
update_reserved(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_disputed(record)
|
|
||||||
update_reserved(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_zone(record)
|
|
||||||
update_reserved(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
# 1. deleting own
|
|
||||||
# 2. trying to regenerate reserved in order domain is still in the list
|
|
||||||
def delete_domain(name)
|
|
||||||
WhoisRecord.where(name: name).destroy_all
|
|
||||||
|
|
||||||
BlockedDomain.find_by(name: name).try(:generate_data)
|
|
||||||
ReservedDomain.find_by(name: name).try(:generate_data)
|
|
||||||
Dispute.active.find_by(domain_name: name).try(:generate_data)
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete_reserved(name)
|
|
||||||
remove_status_from_whois(domain_name: name, domain_status: 'Reserved')
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete_blocked(name)
|
|
||||||
delete_reserved(name)
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete_disputed(name)
|
|
||||||
return if Dispute.active.find_by(domain_name: name).present?
|
|
||||||
|
|
||||||
remove_status_from_whois(domain_name: name, domain_status: 'disputed')
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete_zone(name)
|
|
||||||
WhoisRecord.where(name: name).destroy_all
|
|
||||||
Whois::Record.where(name: name).destroy_all
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_status_from_whois(domain_name:, domain_status:)
|
|
||||||
Whois::Record.where(name: domain_name).each do |r|
|
|
||||||
r.json['status'] = r.json['status'].delete_if { |status| status == domain_status }
|
|
||||||
r.json['status'].blank? ? r.destroy : r.save
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -109,6 +109,7 @@ class Ability
|
||||||
can :destroy, :pending
|
can :destroy, :pending
|
||||||
can :create, :zonefile
|
can :create, :zonefile
|
||||||
can :access, :settings_menu
|
can :access, :settings_menu
|
||||||
|
can :manage, BouncedMailAddress
|
||||||
end
|
end
|
||||||
|
|
||||||
def static_registrant
|
def static_registrant
|
||||||
|
|
|
@ -169,16 +169,19 @@ module Actions
|
||||||
invalid = false
|
invalid = false
|
||||||
params[:statuses].each do |s|
|
params[:statuses].each do |s|
|
||||||
unless DomainStatus::CLIENT_STATUSES.include?(s[:status])
|
unless DomainStatus::CLIENT_STATUSES.include?(s[:status])
|
||||||
domain.add_epp_error('2303', 'status', s[:status], %i[domain_statuses not_found])
|
domain.add_epp_error('2303', 'status', s[:status], %i[statuses not_found])
|
||||||
invalid = true
|
invalid = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return if invalid
|
||||||
|
|
||||||
params[:statuses].select { |s| s[:action] == 'rem' }.each do |s|
|
params[:statuses].select { |s| s[:action] == 'rem' }.each do |s|
|
||||||
if domain.statuses.include?(s[:status])
|
if domain.statuses.include?(s[:status])
|
||||||
rem << s[:status]
|
rem << s[:status]
|
||||||
else
|
else
|
||||||
domain.add_epp_error('2303', 'status', s[:status], %i[domain_statuses not_found])
|
STDOUT << 'AAAAAH'
|
||||||
|
domain.add_epp_error('2303', 'status', s[:status], %i[statuses not_found])
|
||||||
invalid = true
|
invalid = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
28
app/models/bounced_mail_address.rb
Normal file
28
app/models/bounced_mail_address.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
class BouncedMailAddress < ApplicationRecord
|
||||||
|
validates :email, :message_id, :bounce_type, :bounce_subtype, :action, :status, presence: true
|
||||||
|
|
||||||
|
def bounce_reason
|
||||||
|
"#{action} (#{status} #{diagnostic})"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.record(json)
|
||||||
|
bounced_records = json['bounce']['bouncedRecipients']
|
||||||
|
bounced_records.each do |record|
|
||||||
|
bounce_record = BouncedMailAddress.new(params_from_json(json, record))
|
||||||
|
|
||||||
|
bounce_record.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.params_from_json(json, bounced_record)
|
||||||
|
{
|
||||||
|
email: bounced_record['emailAddress'],
|
||||||
|
message_id: json['mail']['messageId'],
|
||||||
|
bounce_type: json['bounce']['bounceType'],
|
||||||
|
bounce_subtype: json['bounce']['bounceSubType'],
|
||||||
|
action: bounced_record['action'],
|
||||||
|
status: bounced_record['status'],
|
||||||
|
diagnostic: bounced_record['diagnosticCode'],
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -20,6 +20,6 @@ module Concerns::Domain::Deletable
|
||||||
end
|
end
|
||||||
|
|
||||||
def deletion_deadline
|
def deletion_deadline
|
||||||
delete_date + 24.hours
|
(delete_date || Time.zone.now) + 24.hours
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,33 +33,14 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength
|
||||||
statuses.include?(DomainStatus::FORCE_DELETE)
|
statuses.include?(DomainStatus::FORCE_DELETE)
|
||||||
end
|
end
|
||||||
|
|
||||||
def should_notify_on_soft_force_delete?
|
|
||||||
force_delete_scheduled? && contact_notification_sent_date.blank? &&
|
|
||||||
force_delete_start.to_date <= Time.zone.now.to_date && force_delete_type.to_sym == :soft &&
|
|
||||||
!statuses.include?(DomainStatus::CLIENT_HOLD)
|
|
||||||
end
|
|
||||||
|
|
||||||
def client_holdable?
|
|
||||||
force_delete_scheduled? && !statuses.include?(DomainStatus::CLIENT_HOLD) &&
|
|
||||||
force_delete_start.present? && force_delete_lte_today && force_delete_lte_valid_date
|
|
||||||
end
|
|
||||||
|
|
||||||
def force_delete_lte_today
|
|
||||||
force_delete_start + Setting.expire_warning_period.days <= Time.zone.now
|
|
||||||
end
|
|
||||||
|
|
||||||
def force_delete_lte_valid_date
|
|
||||||
force_delete_start + Setting.expire_warning_period.days <= valid_to
|
|
||||||
end
|
|
||||||
|
|
||||||
def schedule_force_delete(type: :fast_track, notify_by_email: false)
|
def schedule_force_delete(type: :fast_track, notify_by_email: false)
|
||||||
ForceDeleteInteraction::SetForceDelete.run(domain: self,
|
Domains::ForceDelete::SetForceDelete.run(domain: self,
|
||||||
type: type,
|
type: type,
|
||||||
notify_by_email: notify_by_email)
|
notify_by_email: notify_by_email)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cancel_force_delete
|
def cancel_force_delete
|
||||||
CancelForceDeleteInteraction::CancelForceDelete.run(domain: self)
|
Domains::CancelForceDelete::CancelForceDelete.run(domain: self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def outzone_date
|
def outzone_date
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
module Concerns
|
|
||||||
module Job
|
|
||||||
module ForceDelete
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
|
|
||||||
class_methods do
|
|
||||||
def start_client_hold
|
|
||||||
log_prepare_client_hold
|
|
||||||
|
|
||||||
::PaperTrail.request.whodunnit = "cron - #{__method__}"
|
|
||||||
|
|
||||||
::Domain.force_delete_scheduled.each do |domain|
|
|
||||||
proceed_client_hold(domain: domain)
|
|
||||||
end
|
|
||||||
|
|
||||||
log_end_end_force_delete_job
|
|
||||||
end
|
|
||||||
|
|
||||||
def proceed_client_hold(domain:)
|
|
||||||
notify_on_grace_period(domain) if domain.should_notify_on_soft_force_delete?
|
|
||||||
return unless domain.client_holdable?
|
|
||||||
|
|
||||||
domain.statuses << DomainStatus::CLIENT_HOLD
|
|
||||||
log_start_client_hold(domain)
|
|
||||||
|
|
||||||
domain.save(validate: false)
|
|
||||||
notify_client_hold(domain)
|
|
||||||
|
|
||||||
log_end_end_client_hold(domain)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,34 +0,0 @@
|
||||||
module Concerns
|
|
||||||
module Job
|
|
||||||
module ForceDeleteLogging
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
|
|
||||||
class_methods do
|
|
||||||
def log_prepare_client_hold
|
|
||||||
return if Rails.env.test?
|
|
||||||
|
|
||||||
STDOUT << "#{Time.zone.now.utc} - Setting client_hold to domains\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def log_start_client_hold(domain)
|
|
||||||
return if Rails.env.test?
|
|
||||||
|
|
||||||
STDOUT << "#{Time.zone.now.utc} DomainCron.start_client_hold: ##{domain.id} "\
|
|
||||||
"(#{domain.name}) #{domain.changes}\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def log_end_end_client_hold(domain)
|
|
||||||
return if Rails.env.test?
|
|
||||||
|
|
||||||
STDOUT << "#{Time.zone.now.utc} - Successfully set client_hold on (#{domain.name})"
|
|
||||||
end
|
|
||||||
|
|
||||||
def log_end_end_force_delete_job
|
|
||||||
return if Rails.env.test?
|
|
||||||
|
|
||||||
STDOUT << "#{Time.zone.now.utc} - All client_hold setting are done\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,31 +0,0 @@
|
||||||
module Concerns
|
|
||||||
module Job
|
|
||||||
module ForceDeleteNotify
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
|
|
||||||
class_methods do
|
|
||||||
def notify_client_hold(domain)
|
|
||||||
domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain',
|
|
||||||
domain_name: domain.name,
|
|
||||||
outzone_date: domain.outzone_date,
|
|
||||||
purge_date: domain.purge_date))
|
|
||||||
end
|
|
||||||
|
|
||||||
def notify_on_grace_period(domain)
|
|
||||||
domain.registrar.notifications.create!(text: I18n.t('grace_period_started_domain',
|
|
||||||
domain_name: domain.name,
|
|
||||||
date: domain.force_delete_start))
|
|
||||||
send_mail(domain) if domain.template_name.present?
|
|
||||||
domain.update(contact_notification_sent_date: Time.zone.today)
|
|
||||||
end
|
|
||||||
|
|
||||||
def send_mail(domain)
|
|
||||||
DomainDeleteMailer.forced(domain: domain,
|
|
||||||
registrar: domain.registrar,
|
|
||||||
registrant: domain.registrant,
|
|
||||||
template_name: domain.template_name).deliver_now
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -347,19 +347,24 @@ class Contact < ApplicationRecord
|
||||||
@desc = {}
|
@desc = {}
|
||||||
|
|
||||||
registrant_domains.each do |dom|
|
registrant_domains.each do |dom|
|
||||||
@desc[dom.name] ||= []
|
@desc[dom.name] ||= { id: dom.uuid, roles: [] }
|
||||||
@desc[dom.name] << :registrant
|
@desc[dom.name][:roles] << :registrant
|
||||||
end
|
end
|
||||||
|
|
||||||
domain_contacts.each do |dc|
|
domain_contacts.each do |dc|
|
||||||
@desc[dc.domain.name] ||= []
|
@desc[dc.domain.name] ||= { id: dc.domain.uuid, roles: [] }
|
||||||
@desc[dc.domain.name] << dc.name.downcase.to_sym
|
@desc[dc.domain.name][:roles] << dc.name.downcase.to_sym
|
||||||
@desc[dc.domain.name] = @desc[dc.domain.name].compact
|
@desc[dc.domain.name] = @desc[dc.domain.name].compact
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc
|
@desc
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def related_domains
|
||||||
|
a = related_domain_descriptions
|
||||||
|
a.keys.map { |d| { name: d, id: a[d][:id], roles: a[d][:roles] } }
|
||||||
|
end
|
||||||
|
|
||||||
def status_notes_array=(notes)
|
def status_notes_array=(notes)
|
||||||
self.status_notes = {}
|
self.status_notes = {}
|
||||||
notes ||= []
|
notes ||= []
|
||||||
|
|
|
@ -327,6 +327,7 @@ class Domain < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def notify_registrar(message_key)
|
def notify_registrar(message_key)
|
||||||
|
# TODO: To be deleted with DomainDeleteConfirm refactoring
|
||||||
registrar.notifications.create!(
|
registrar.notifications.create!(
|
||||||
text: "#{I18n.t(message_key)}: #{name}",
|
text: "#{I18n.t(message_key)}: #{name}",
|
||||||
attached_obj_id: id,
|
attached_obj_id: id,
|
||||||
|
@ -335,11 +336,13 @@ class Domain < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def preclean_pendings
|
def preclean_pendings
|
||||||
|
# TODO: To be deleted with refactoring
|
||||||
self.registrant_verification_token = nil
|
self.registrant_verification_token = nil
|
||||||
self.registrant_verification_asked_at = nil
|
self.registrant_verification_asked_at = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def clean_pendings!
|
def clean_pendings!
|
||||||
|
# TODO: To be deleted with refactoring
|
||||||
preclean_pendings
|
preclean_pendings
|
||||||
self.pending_json = {}
|
self.pending_json = {}
|
||||||
statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION)
|
statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION)
|
||||||
|
@ -418,7 +421,7 @@ class Domain < ApplicationRecord
|
||||||
pending_delete_confirmation!
|
pending_delete_confirmation!
|
||||||
save(validate: false) # should check if this did succeed
|
save(validate: false) # should check if this did succeed
|
||||||
|
|
||||||
DomainDeleteConfirmEmailJob.enqueue(id)
|
Domains::DeleteConfirmEmail::SendRequest.run(domain: self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cancel_pending_delete
|
def cancel_pending_delete
|
||||||
|
@ -482,12 +485,6 @@ class Domain < ApplicationRecord
|
||||||
Registrant.find_by(id: pending_json['new_registrant_id'])
|
Registrant.find_by(id: pending_json['new_registrant_id'])
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_graceful_expired
|
|
||||||
self.outzone_at = expire_time + self.class.expire_warning_period
|
|
||||||
self.delete_date = outzone_at + self.class.redemption_grace_period
|
|
||||||
self.statuses |= [DomainStatus::EXPIRED]
|
|
||||||
end
|
|
||||||
|
|
||||||
def pending_update?
|
def pending_update?
|
||||||
statuses.include?(DomainStatus::PENDING_UPDATE)
|
statuses.include?(DomainStatus::PENDING_UPDATE)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,84 +1,17 @@
|
||||||
class DomainCron
|
class DomainCron
|
||||||
include Concerns::Job::ForceDelete
|
|
||||||
include Concerns::Job::ForceDeleteLogging
|
|
||||||
include Concerns::Job::ForceDeleteNotify
|
|
||||||
|
|
||||||
def self.clean_expired_pendings
|
def self.clean_expired_pendings
|
||||||
STDOUT << "#{Time.zone.now.utc} - Clean expired domain pendings\n" unless Rails.env.test?
|
Domains::ExpiredPendings::CleanAll.run!
|
||||||
|
|
||||||
::PaperTrail.request.whodunnit = "cron - #{__method__}"
|
|
||||||
expire_at = Setting.expire_pending_confirmation.hours.ago
|
|
||||||
count = 0
|
|
||||||
expired_pending_domains = Domain.where('registrant_verification_asked_at <= ?', expire_at)
|
|
||||||
expired_pending_domains.each do |domain|
|
|
||||||
unless domain.pending_update? || domain.pending_delete? || domain.pending_delete_confirmation?
|
|
||||||
msg = "#{Time.zone.now.utc} - ISSUE: DOMAIN #{domain.id}: #{domain.name} IS IN EXPIRED PENDING LIST, " \
|
|
||||||
"but no pendingDelete/pendingUpdate state present!\n"
|
|
||||||
STDOUT << msg unless Rails.env.test?
|
|
||||||
next
|
|
||||||
end
|
|
||||||
count += 1
|
|
||||||
if domain.pending_update?
|
|
||||||
RegistrantChangeExpiredEmailJob.enqueue(domain.id)
|
|
||||||
end
|
|
||||||
if domain.pending_delete? || domain.pending_delete_confirmation?
|
|
||||||
DomainDeleteMailer.expired(domain).deliver_now
|
|
||||||
end
|
|
||||||
|
|
||||||
domain.preclean_pendings
|
|
||||||
domain.clean_pendings!
|
|
||||||
|
|
||||||
unless Rails.env.test?
|
|
||||||
STDOUT << "#{Time.zone.now.utc} DomainCron.clean_expired_pendings: ##{domain.id} (#{domain.name})\n"
|
|
||||||
end
|
|
||||||
UpdateWhoisRecordJob.enqueue domain.name, 'domain'
|
|
||||||
end
|
|
||||||
STDOUT << "#{Time.zone.now.utc} - Successfully cancelled #{count} domain pendings\n" unless Rails.env.test?
|
|
||||||
count
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.start_expire_period
|
def self.start_expire_period
|
||||||
::PaperTrail.request.whodunnit = "cron - #{__method__}"
|
Domains::ExpirePeriod::Start.run!
|
||||||
domains = Domain.expired
|
|
||||||
marked = 0
|
|
||||||
real = 0
|
|
||||||
|
|
||||||
domains.each do |domain|
|
|
||||||
next unless domain.expirable?
|
|
||||||
real += 1
|
|
||||||
domain.set_graceful_expired
|
|
||||||
STDOUT << "#{Time.zone.now.utc} DomainCron.start_expire_period: ##{domain.id} (#{domain.name}) #{domain.changes}\n" unless Rails.env.test?
|
|
||||||
|
|
||||||
send_time = domain.valid_to + Setting.expiration_reminder_mail.to_i.days
|
|
||||||
saved = domain.save(validate: false)
|
|
||||||
|
|
||||||
if saved
|
|
||||||
DomainExpireEmailJob.enqueue(domain.id, run_at: send_time)
|
|
||||||
marked += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
STDOUT << "#{Time.zone.now.utc} - Successfully expired #{marked} of #{real} domains\n" unless Rails.env.test?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.start_redemption_grace_period
|
def self.start_redemption_grace_period
|
||||||
STDOUT << "#{Time.zone.now.utc} - Setting server_hold to domains\n" unless Rails.env.test?
|
Domains::RedemptionGracePeriod::Start.run!
|
||||||
|
end
|
||||||
|
|
||||||
::PaperTrail.request.whodunnit = "cron - #{__method__}"
|
def self.start_client_hold
|
||||||
|
Domains::ClientHold::SetClientHold.run!
|
||||||
domains = Domain.outzone_candidates
|
|
||||||
marked = 0
|
|
||||||
real = 0
|
|
||||||
|
|
||||||
domains.each do |domain|
|
|
||||||
next unless domain.server_holdable?
|
|
||||||
real += 1
|
|
||||||
domain.statuses << DomainStatus::SERVER_HOLD
|
|
||||||
STDOUT << "#{Time.zone.now.utc} DomainCron.start_redemption_grace_period: ##{domain.id} (#{domain.name}) #{domain.changes}\n" unless Rails.env.test?
|
|
||||||
domain.save(validate: false) and marked += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
STDOUT << "#{Time.zone.now.utc} - Successfully set server_hold to #{marked} of #{real} domains\n" unless Rails.env.test?
|
|
||||||
marked
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,24 +18,24 @@ class RegistrantVerification < ApplicationRecord
|
||||||
def domain_registrant_change_confirm!(initiator)
|
def domain_registrant_change_confirm!(initiator)
|
||||||
self.action_type = DOMAIN_REGISTRANT_CHANGE
|
self.action_type = DOMAIN_REGISTRANT_CHANGE
|
||||||
self.action = CONFIRMED
|
self.action = CONFIRMED
|
||||||
DomainUpdateConfirmJob.enqueue domain.id, CONFIRMED, initiator if save
|
DomainUpdateConfirmJob.perform_later domain.id, CONFIRMED, initiator if save
|
||||||
end
|
end
|
||||||
|
|
||||||
def domain_registrant_change_reject!(initiator)
|
def domain_registrant_change_reject!(initiator)
|
||||||
self.action_type = DOMAIN_REGISTRANT_CHANGE
|
self.action_type = DOMAIN_REGISTRANT_CHANGE
|
||||||
self.action = REJECTED
|
self.action = REJECTED
|
||||||
DomainUpdateConfirmJob.run domain.id, REJECTED, initiator if save
|
DomainUpdateConfirmJob.perform_later domain.id, REJECTED, initiator if save
|
||||||
end
|
end
|
||||||
|
|
||||||
def domain_registrant_delete_confirm!(initiator)
|
def domain_registrant_delete_confirm!(initiator)
|
||||||
self.action_type = DOMAIN_DELETE
|
self.action_type = DOMAIN_DELETE
|
||||||
self.action = CONFIRMED
|
self.action = CONFIRMED
|
||||||
DomainDeleteConfirmJob.enqueue domain.id, CONFIRMED, initiator if save
|
DomainDeleteConfirmJob.perform_later domain.id, CONFIRMED, initiator if save
|
||||||
end
|
end
|
||||||
|
|
||||||
def domain_registrant_delete_reject!(initiator)
|
def domain_registrant_delete_reject!(initiator)
|
||||||
self.action_type = DOMAIN_DELETE
|
self.action_type = DOMAIN_DELETE
|
||||||
self.action = REJECTED
|
self.action = REJECTED
|
||||||
DomainDeleteConfirmJob.enqueue domain.id, REJECTED, initiator if save
|
DomainDeleteConfirmJob.perform_later domain.id, REJECTED, initiator if save
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
33
app/models/repp_api.rb
Normal file
33
app/models/repp_api.rb
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
class ReppApi
|
||||||
|
def self.bulk_renew(domains, period, registrar)
|
||||||
|
payload = { domains: domains, renew_period: period }
|
||||||
|
uri = URI.parse("#{ENV['repp_url']}domains/renew/bulk")
|
||||||
|
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||||
|
req.body = payload.to_json
|
||||||
|
|
||||||
|
ReppApi.request(req, uri, registrar: registrar).body
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.request(request, uri, registrar:)
|
||||||
|
request.basic_auth(registrar.username, registrar.plain_text_password) if registrar
|
||||||
|
client_cert = Rails.env.test? ? nil : File.read(ENV['cert_path'])
|
||||||
|
client_key = Rails.env.test? ? nil : File.read(ENV['key_path'])
|
||||||
|
params = ReppApi.compose_ca_auth_params(uri, client_cert, client_key)
|
||||||
|
|
||||||
|
Net::HTTP.start(uri.hostname, uri.port, params) do |http|
|
||||||
|
http.request(request)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.compose_ca_auth_params(uri, client_cert, client_key)
|
||||||
|
params = { use_ssl: (uri.scheme == 'https') }
|
||||||
|
params[:verify_mode] = OpenSSL::SSL::VERIFY_NONE if Rails.env.test? || Rails.env.development?
|
||||||
|
|
||||||
|
unless Rails.env.test?
|
||||||
|
params[:cert] = OpenSSL::X509::Certificate.new(client_cert)
|
||||||
|
params[:key] = OpenSSL::PKey::RSA.new(client_key)
|
||||||
|
end
|
||||||
|
|
||||||
|
params
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,19 +5,20 @@ class TechDomainContact < DomainContact
|
||||||
skipped_domains = []
|
skipped_domains = []
|
||||||
tech_contacts = where(contact: current_contact)
|
tech_contacts = where(contact: current_contact)
|
||||||
|
|
||||||
transaction do
|
tech_contacts.each do |tech_contact|
|
||||||
tech_contacts.each do |tech_contact|
|
if tech_contact.domain.discarded?
|
||||||
if tech_contact.domain.discarded?
|
skipped_domains << tech_contact.domain.name
|
||||||
skipped_domains << tech_contact.domain.name
|
next
|
||||||
next
|
end
|
||||||
end
|
begin
|
||||||
|
|
||||||
tech_contact.contact = new_contact
|
tech_contact.contact = new_contact
|
||||||
tech_contact.save!
|
tech_contact.save!
|
||||||
affected_domains << tech_contact.domain.name
|
affected_domains << tech_contact.domain.name
|
||||||
|
rescue ActiveRecord::RecordNotUnique
|
||||||
|
skipped_domains << tech_contact.domain.name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return affected_domains.sort, skipped_domains.sort
|
[affected_domains.sort, skipped_domains.sort]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
%li= link_to t('.blocked_domains'), admin_blocked_domains_path
|
%li= link_to t('.blocked_domains'), admin_blocked_domains_path
|
||||||
%li= link_to t('.reserved_domains'), admin_reserved_domains_path
|
%li= link_to t('.reserved_domains'), admin_reserved_domains_path
|
||||||
%li= link_to t('.disputed_domains'), admin_disputes_path
|
%li= link_to t('.disputed_domains'), admin_disputes_path
|
||||||
|
%li= link_to t('.bounced_email_addresses'), admin_bounced_mail_addresses_path
|
||||||
%li= link_to t('.epp_log'), admin_epp_logs_path(created_after: 'today')
|
%li= link_to t('.epp_log'), admin_epp_logs_path(created_after: 'today')
|
||||||
%li= link_to t('.repp_log'), admin_repp_logs_path(created_after: 'today')
|
%li= link_to t('.repp_log'), admin_repp_logs_path(created_after: 'today')
|
||||||
%li= link_to t('.que'), '/admin/que'
|
%li= link_to t('.que'), '/admin/que'
|
||||||
|
|
36
app/views/admin/bounced_mail_addresses/index.html.erb
Normal file
36
app/views/admin/bounced_mail_addresses/index.html.erb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<h1>Bounced Mail Addresses</h1>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover table-bordered table-condensed">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Action</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Diagnostic</th>
|
||||||
|
<th>Tracked</th>
|
||||||
|
<th colspan="2">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<% @bounced_mail_addresses.each do |mail_addr| %>
|
||||||
|
<tr>
|
||||||
|
<td><%= mail_addr.email %></td>
|
||||||
|
<td><%= mail_addr.action %></td>
|
||||||
|
<td><%= mail_addr.status %></td>
|
||||||
|
<td><%= mail_addr.diagnostic %></td>
|
||||||
|
<td><%= mail_addr.created_at %></td>
|
||||||
|
<td><%= link_to 'Detailed', admin_bounced_mail_address_path(mail_addr) %></td>
|
||||||
|
<td><%= link_to 'Destroy', admin_bounced_mail_address_path(mail_addr), method: :delete, data: { confirm: 'Are you sure?' } %></td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
27
app/views/admin/bounced_mail_addresses/show.html.erb
Normal file
27
app/views/admin/bounced_mail_addresses/show.html.erb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<p>
|
||||||
|
<strong>Email:</strong>
|
||||||
|
<%= @bounced_mail_address.email %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Bounced message ID:</strong>
|
||||||
|
<%= @bounced_mail_address.message_id %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Overall bounce type:</strong>
|
||||||
|
<%= @bounced_mail_address.bounce_type %> (<%= @bounced_mail_address.bounce_subtype %> )
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Bounced recipient status:</strong>
|
||||||
|
<%= @bounced_mail_address.action %> (<%= @bounced_mail_address.status %>)
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Bounced recipient diagnostic:</strong>
|
||||||
|
<pre><%= @bounced_mail_address.diagnostic %></pre>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<%= link_to 'Back', admin_bounced_mail_addresses_path %>
|
||||||
|
<%= link_to 'Destroy', admin_bounced_mail_address_path(@bounced_mail_address), method: :delete, data: { confirm: 'Are you sure?' } %></td>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue