Merge branch 'master' into registry-790

This commit is contained in:
Artur Beljajev 2018-07-19 17:28:56 +03:00
commit e4305e7865
86 changed files with 1329 additions and 1163 deletions

View file

@ -23,6 +23,7 @@ exclude_patterns:
- "bin/"
- "config/"
- "db/"
- "lib/"
- "vendor/"
- "test/"
- "spec/"

View file

@ -3,6 +3,13 @@ cache: bundler
env:
- DB=postgresql
bundler_args: --without development staging production
before_install:
- "wget -N http://chromedriver.storage.googleapis.com/2.40/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"
before_script:
- "cp config/application-example.yml config/application.yml"
- "cp config/database-travis.yml config/database.yml"
@ -19,3 +26,4 @@ services:
- postgresql
addons:
postgresql: "9.4"
chrome: stable

View file

@ -1,3 +1,43 @@
12.07.2018
* Implemented JavaScript testing framework to catch web UI problems [#900](https://github.com/internetee/registry/issues/900)
10.07.2018
* Nameserver bulk change returns list of affected doamins [#835](https://github.com/internetee/registry/issues/835)
26.06.2018
* Whois data is updated now on pendingUpdate status removal [#757](https://github.com/internetee/registry/issues/757)
* Portal for registrars displays control code for MID authentication [#893](https://github.com/internetee/registry/issues/893)
* Changed date format in Directo invoice XML [#890](https://github.com/internetee/registry/pull/890)
* Registrant portal UI improvements [#888](https://github.com/internetee/registry/issues/888)
* Removed unused mailer code [#882](https://github.com/internetee/registry/pull/882)
* Sprocets gem update to 3.7.2 [#892](https://github.com/internetee/registry/issues/892)
* Replased Warden test helpers with Devise [#889](https://github.com/internetee/registry/issues/889)
* Removed dev rake task [#872](https://github.com/internetee/registry/pull/872)
* Spring gem removed [#856](https://github.com/internetee/registry/issues/856)
* Dcker conf changes [#881](https://github.com/internetee/registry/pull/881)
* Fixed Estonia in the country drop-down [#877](https://github.com/internetee/registry/issues/877)
* Codeclimate conf improvements [#854](https://github.com/internetee/registry/pull/854)
* Removed codeclimate badge from README [#876](https://github.com/internetee/registry/issues/876)
* added UUID for contact objects [#873](https://github.com/internetee/registry/pull/873)
* backported Rails 5 API [#868](https://github.com/internetee/registry/issues/868)
20.06.2018
* Bulk change function for technical contact replacement [#662](https://github.com/internetee/registry/issues/662)
* Removed vatcode and totalvat elements from directo request in attempt to fix invoice sending issue [#844](https://github.com/internetee/registry/issues/844)
* Regsitrar: added credit card payment option - disabled at the moment due to contractual reaons [#419](https://github.com/internetee/registry/issues/419)
* Registrant: enabled WHOIS requests over RestWHOIS API [#852](https://github.com/internetee/registry/issues/852)
* Fixed rspec configuration that caused test failures [#858](https://github.com/internetee/registry/issues/858)
* Admin: refactored date selection in pricelist [#869](https://github.com/internetee/registry/issues/869)
* Added uglifier gem for es6 compression [#864](https://github.com/internetee/registry/issues/864)
* Removed lib folder from autoload path [#859](https://github.com/internetee/registry/issues/859)
* test environment config improvements [#860](https://github.com/internetee/registry/issues/860)
* translation fixes [#865](https://github.com/internetee/registry/issues/865)
* removed obsolete .agignore [#866](https://github.com/internetee/registry/issues/866)
* removed rubocop gem [#857](https://github.com/internetee/registry/issues/857)
* new invoice payment test fix [#863](https://github.com/internetee/registry/issues/863)
* get puma gem config from Rails 5 [#867](https://github.com/internetee/registry/issues/867)
* Rails5 API controller temporary backport [#868](https://github.com/internetee/registry/issues/868)
25.05.2018
* GDPR: updated whois templates with configurable disclaimer [#795](https://github.com/internetee/registry/issues/795)
* GDPR: email forwarding solution to contact private domain registrants without revealing their email addresses [#824](https://github.com/internetee/registry/issues/824)

View file

@ -1,23 +1,9 @@
FROM ruby:2.3
FROM internetee/ruby:2.3
MAINTAINER maciej.szlosarczyk@internet.ee
RUN apt-get update > /dev/null && apt-get install -y > /dev/null \
build-essential \
nodejs \
imagemagick \
postgresql-client
RUN apt-get install -y > /dev/null \
qt5-default \
libqt5webkit5-dev \
gstreamer1.0-plugins-base \
gstreamer1.0-tools \
qtdeclarative5-dev \
gstreamer1.0-x
RUN mkdir -p /opt/webapps/app/tmp/pids
WORKDIR /opt/webapps/app
COPY Gemfile Gemfile.lock ./
RUN gem install bundler && bundle install --jobs 20 --retry 5
EXPOSE 3000

15
Gemfile
View file

@ -55,9 +55,7 @@ gem 'liquid', '3.0.6' # for email templates
gem 'cancancan', '1.11.0' # autharization
gem 'devise', '4.4.3' # authenitcation
# rest api
gem 'grape', '0.12.0'
gem 'hashie-forbidden_attributes', '0.1.1' # For grape, https://github.com/ruby-grape/grape/tree/v0.12.0#rails
gem 'grape'
gem 'jbuilder', '2.2.16' # json api
# registry specfic
@ -102,32 +100,25 @@ gem 'jquery-ui-rails', '5.0.5'
gem 'active_model-errors_details' # Backport from Rails 5, https://github.com/rails/rails/pull/18322
group :development do
gem 'spring'
gem 'spring-commands-rspec'
# deploy
gem 'mina', '0.3.1' # for fast deployment
gem 'puma'
end
group :development, :test do
gem 'factory_bot_rails'
gem 'capybara'
gem 'rspec-rails', '~> 3.6'
gem 'poltergeist'
gem 'selenium-webdriver'
# debug
gem 'pry', '0.10.1'
gem 'bullet', '4.14.7' # for finding database optimizations
gem 'bundler-audit'
gem 'brakeman', '3.6.1', require: false # for security audit'
# tmp, otherwise conflics with breakman
# gem 'html2haml', github: 'haml/html2haml', ref: '6984f50bdbbd6291535027726a5697f28778ee8d'
gem 'html2haml', '2.1.0'
gem 'sdoc', '0.4.1' # bundle exec rake doc:rails generates the API under doc/api.
gem 'railroady', '1.3.0' # to generate database diagrams
gem 'autodoc'
gem 'puma'
end
group :staging do

View file

@ -99,8 +99,8 @@ GEM
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.5.1)
public_suffix (~> 2.0, >= 2.0.2)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
airbrake (6.0.0)
airbrake-ruby (~> 2.0)
airbrake-ruby (2.0.0)
@ -122,24 +122,21 @@ GEM
bootstrap-sass (3.3.5.1)
autoprefixer-rails (>= 5.0.0.1)
sass (>= 3.3.0)
brakeman (3.6.1)
builder (3.2.3)
bullet (4.14.7)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.9.0)
bundler-audit (0.5.0)
bundler (~> 1.2)
thor (~> 0.18)
cancancan (1.11.0)
capybara (2.14.0)
capybara (3.3.1)
addressable
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
xpath (~> 3.1)
childprocess (0.9.0)
ffi (~> 1.0, >= 1.0.11)
chronic (0.10.2)
cliver (0.3.2)
coderay (1.1.0)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
@ -184,19 +181,17 @@ GEM
factory_bot_rails (4.8.2)
factory_bot (~> 4.8.2)
railties (>= 3.0.0)
ffi (1.9.25)
figaro (1.1.1)
thor (~> 0.14)
globalid (0.4.1)
activesupport (>= 4.2.0)
grape (0.12.0)
grape (1.0.3)
activesupport
builder
hashie (>= 2.1.0)
multi_json (>= 1.3.2)
multi_xml (>= 0.5.2)
mustermann-grape (~> 1.0.0)
rack (>= 1.3.0)
rack-accept
rack-mount
virtus (>= 1.0.0)
gyoku (1.3.1)
builder (>= 2.1.2)
@ -208,10 +203,7 @@ GEM
haml (>= 4.0.6, < 5.0)
html2haml (>= 1.0.1)
railties (>= 4.0.1)
hashdiff (0.3.4)
hashie (3.5.5)
hashie-forbidden_attributes (0.1.1)
hashie (>= 3.0)
hashdiff (0.3.7)
hpricot (0.8.6)
html2haml (2.1.0)
erubis (~> 2.7.0)
@ -270,38 +262,34 @@ GEM
monetize (~> 1.6.0)
money (~> 6.8.1)
railties (>= 3.0)
multi_json (1.12.1)
multi_xml (0.6.0)
multi_json (1.13.1)
mustermann (1.0.2)
mustermann-grape (1.0.0)
mustermann (~> 1.0.0)
netrc (0.11.0)
nokogiri (1.8.2)
nokogiri (1.8.4)
mini_portile2 (~> 2.3.0)
nori (2.6.0)
open4 (1.3.4)
orm_adapter (0.5.0)
pdfkit (0.6.2)
pg (0.19.0)
poltergeist (1.14.0)
capybara (~> 2.1)
cliver (~> 0.3.1)
websocket-driver (>= 0.2.0)
polyamorous (1.3.1)
activerecord (>= 3.0)
pry (0.10.1)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
public_suffix (2.0.5)
public_suffix (3.0.2)
puma (3.8.2)
que (0.10.0)
que-web (0.4.0)
erubis
que (~> 0.8)
sinatra
rack (1.6.9)
rack (1.6.10)
rack-accept (0.4.5)
rack (>= 0.4)
rack-mount (0.8.3)
rack (>= 1.0.0)
rack-protection (1.5.5)
rack
rack-test (0.6.3)
@ -372,6 +360,7 @@ GEM
rspec-support (3.6.0)
ruby_parser (3.8.4)
sexp_processor (~> 4.1)
rubyzip (1.2.1)
safe_yaml (1.0.4)
sass (3.4.23)
sass-rails (5.0.6)
@ -394,6 +383,9 @@ GEM
select2-rails (3.5.9.3)
thor (~> 0.14)
selectize-rails (0.12.1)
selenium-webdriver (3.13.0)
childprocess (~> 0.5)
rubyzip (~> 1.2)
sexp_processor (4.8.0)
simplecov (0.15.1)
docile (~> 1.1.0)
@ -408,11 +400,7 @@ GEM
sixarm_ruby_unaccent (1.1.1)
slop (3.6.0)
socksify (1.7.1)
spring (2.0.2)
activesupport (>= 4.2)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
sprockets (3.7.1)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.1)
@ -444,17 +432,14 @@ GEM
wasabi (3.5.0)
httpi (~> 2.0)
nokogiri (>= 1.4.2)
webmock (3.0.1)
webmock (3.4.2)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
whenever (0.9.4)
chronic (>= 0.6.3)
xpath (2.0.0)
nokogiri (~> 1.3)
xpath (3.1.0)
nokogiri (~> 1.8)
PLATFORMS
ruby
@ -466,9 +451,7 @@ DEPENDENCIES
airbrake
autodoc
bootstrap-sass (= 3.3.5.1)
brakeman (= 3.6.1)
bullet (= 4.14.7)
bundler-audit
cancancan (= 1.11.0)
capybara
coderay (= 1.1.0)
@ -483,9 +466,8 @@ DEPENDENCIES
epp-xml (= 1.1.0)!
factory_bot_rails
figaro (= 1.1.1)
grape (= 0.12.0)
grape
haml-rails (= 0.9.0)
hashie-forbidden_attributes (= 0.1.1)
html2haml (= 2.1.0)
html5_validators (= 1.2.2)
isikukood
@ -501,7 +483,6 @@ DEPENDENCIES
paper_trail!
pdfkit (= 0.6.2)
pg (= 0.19.0)
poltergeist
pry (= 0.10.1)
puma
que (= 0.10.0)
@ -517,10 +498,9 @@ DEPENDENCIES
sdoc (= 0.4.1)
select2-rails (= 3.5.9.3)
selectize-rails (= 0.12.1)
selenium-webdriver
simplecov
simpleidn (= 0.0.7)
spring
spring-commands-rspec
uglifier
uuidtools (= 2.1.5)
validates_email_format_of (= 1.6.3)
@ -528,4 +508,4 @@ DEPENDENCIES
whenever (= 0.9.4)
BUNDLED WITH
1.16.1
1.16.2

View file

@ -2,7 +2,6 @@ Domain Registry
===============
[![Build Status](https://travis-ci.org/internetee/registry.svg?branch=master)](https://travis-ci.org/internetee/registry)
[![Code Climate](https://codeclimate.com/github/internetee/registry/badges/gpa.svg)](https://codeclimate.com/github/internetee/registry)
[![Issue Count](https://codeclimate.com/github/internetee/registry/badges/issue_count.svg)](https://codeclimate.com/github/internetee/registry)
[![Test Coverage](https://codeclimate.com/github/internetee/registry/badges/coverage.svg)](https://codeclimate.com/github/internetee/registry/coverage)
[![Documentation Status](https://readthedocs.org/projects/eeregistry/badge/?version=latest)](http://docs.internet.ee/en/latest/?badge=latest)
@ -31,7 +30,7 @@ Documentation
Installation
------------
### Registry app
### Registry app
Registry based on Rails 4 installation (rbenv install is under Debian build doc)
@ -57,7 +56,7 @@ Manual demo install and database setup:
tar -xzvf mod_epp-1.10.tar.gz
cd mod_epp-1.10
Patch mod_epp for Rack. Beacause Rack multipart parser expects specifically
Patch mod_epp for Rack. Beacause Rack multipart parser expects specifically
formatted content boundaries, the mod_epp needs to be modified before building:
wget https://github.com/internetee/registry/raw/master/doc/patches/mod_epp_1.10-rack-friendly.patch
@ -136,7 +135,7 @@ For Apache, registry admin goes to port 443 in production, /etc/apache2/sites-en
</VirtualHost>
```
Registrar configuration (/etc/apache2/sites-enabled/registrar.conf) is as follows:
Registrar configuration (/etc/apache2/sites-enabled/registrar.conf) is as follows:
```
<VirtualHost *:443>
ServerName your-registrar-domain
@ -179,7 +178,7 @@ Registrar configuration (/etc/apache2/sites-enabled/registrar.conf) is as follow
# for Apache verison 2.4 or newer
# Require all granted
Options -MultiViews
</Directory>
@ -220,7 +219,7 @@ Registrar configuration (/etc/apache2/sites-enabled/registrar.conf) is as follow
</VirtualHost>
```
Registrant configuration (/etc/apache2/sites-enabled/registrant.conf) is as follows:
Registrant configuration (/etc/apache2/sites-enabled/registrant.conf) is as follows:
```
<VirtualHost *:443>
ServerName your-registrant-domain
@ -263,7 +262,7 @@ Registrant configuration (/etc/apache2/sites-enabled/registrant.conf) is as foll
# for Apache verison 2.4 or newer
# Require all granted
Options -MultiViews
</Directory>
@ -271,11 +270,11 @@ Registrant configuration (/etc/apache2/sites-enabled/registrant.conf) is as foll
Allow from none
Deny from all
</Location>
<Location /registrant>
Allow from all
</Location>
<Location /assets>
Allow from all
</Location>
@ -342,8 +341,8 @@ For Apache, REPP goes to port 443 in production, /etc/apache2/sites-enabled/repp
</VirtualHost>
```
For Apache, epp goes to port 700.
Be sure to update paths to match your system configuration.
For Apache, epp goes to port 700.
Be sure to update paths to match your system configuration.
/etc/apache2/sites-enabled/epp.conf short example:
```apache
<IfModule mod_ssl.c>
@ -405,7 +404,7 @@ Be sure to update paths to match your system configuration.
EPPSessionRoot /proxy/session
EPPErrorRoot /proxy/error
EPPRawFrame raw_frame
ProxyPass /proxy/ http://localhost:8080/epp/
EPPAuthURI implicit
@ -421,7 +420,7 @@ Enable epp_ssl and restart apache
Now you should see registry admin at https://your-domain
All registry demo data can be found at:
All registry demo data can be found at:
db/seeds.rb
@ -464,4 +463,3 @@ Please follow EPP web client readme:
Please follow WHOIS server readme:
https://github.com/internetee/whois

View file

@ -29,7 +29,7 @@ module Repp
}
begin
current_user.registrar.replace_nameservers(hostname, new_attributes)
affected_domains = current_user.registrar.replace_nameservers(hostname, new_attributes)
rescue ActiveRecord::RecordInvalid => e
error!({ errors: e.record.errors.full_messages.map { |error| { title: error } } }, 400)
end
@ -37,7 +37,8 @@ module Repp
status 200
@response = { data: { type: 'nameserver',
id: params[:data][:attributes][:hostname],
attributes: params[:data][:attributes] } }
attributes: params[:data][:attributes] },
affected_domains: affected_domains }
end
end
end

View file

@ -52,10 +52,5 @@ body > .container
.text-grey
color: grey
.partially-hidden
border: 1px solid #dddddd
padding-left: 4px
padding-right: 4px
.disabled-value
padding-top: 8px

View file

@ -1,12 +1,15 @@
class Registrant::ContactsController < RegistrantController
helper_method :domain_ids
def show
@contact = Contact.where(id: contacts).find_by(id: params[:id])
@contact = Contact.where(id: contacts).find_by(id: params[:id])
@current_user = current_user
authorize! :read, @contact
end
private
def contacts
begin
DomainContact.where(domain_id: domain_ids).pluck(:contact_id) | Domain.where(id: domain_ids).pluck(:registrant_id)

View file

@ -1,6 +1,6 @@
class Registrant::DomainUpdateConfirmsController < RegistrantController
skip_before_action :authenticate_user!, only: [:show, :update]
skip_authorization_check only: [:show, :update]
skip_before_action :authenticate_user!, only: %i[show update]
skip_authorization_check only: %i[show update]
def show
return if params[:confirmed] || params[:rejected]

View file

@ -1,7 +1,6 @@
class Registrant::RegistrarsController < RegistrantController
def show
@registrar = Registrar.find(params[:id])
authorize! :read, @registrar
end
end
end

View file

@ -44,7 +44,10 @@ class Registrar
parsed_response = JSON.parse(response.body, symbolize_names: true)
if response.code == '200'
flash[:notice] = t '.replaced'
notices = [t('.replaced')]
notices << "#{t('.affected_domains')}: #{parsed_response[:affected_domains].join(', ')}"
flash[:notice] = notices
redirect_to registrar_domains_url
else
@api_errors = parsed_response[:errors]

View file

@ -91,7 +91,7 @@ class Registrar
@user = find_user_by_idc_and_allowed(response.user_id_code)
else
@user = find_user_by_idc(response.user_id_code)
end
end
if @user.persisted?
session[:user_id_code] = response.user_id_code

View file

@ -23,7 +23,9 @@ class DomainUpdateConfirmJob < Que::Job
registrant: domain.registrant).deliver_now
domain.poll_message!(:poll_pending_update_rejected_by_registrant)
domain.clean_pendings_lowlevel
domain.preclean_pendings
domain.clean_pendings!
end
destroy # it's best to destroy the job in the same transaction
end

View file

@ -45,18 +45,4 @@ class DomainMailer < ApplicationMailer
subject: "#{I18n.t(:delete_confirmation_subject,
name: @domain.name)} [#{@domain.name}]")
end
private
# app/models/DomainMailModel provides the data for mail that can be composed_from
# which ensures that values of objects are captured when they are valid, not later when this method is executed
def compose_from(params)
@params = params
return if delivery_off?(params, params[:deliver_emails])
return if whitelist_blocked?(params[:recipient])
params[:errors].map do |error|
logger.warn error
return
end
mail(to: params[:recipient], subject: params[:subject])
end
end
end

View file

@ -16,6 +16,7 @@ module Concerns::Contact::Transferable
new_contact.regenerate_code
new_contact.regenerate_auth_info
new_contact.remove_address unless self.class.address_processing?
new_contact.uuid = nil
new_contact.save(validate: false)
new_contact
end

View file

@ -447,31 +447,6 @@ class Contact < ActiveRecord::Base
domains
end
def all_registrant_domains(page: nil, per: nil, params: {}, registrant: nil)
if registrant
sorts = params.fetch(:sort, {}).first || []
sort = Domain.column_names.include?(sorts.first) ? sorts.first : "valid_to"
order = {"asc"=>"desc", "desc"=>"asc"}[sorts.second] || "desc"
domain_ids = DomainContact.distinct.where(contact_id: registrant.id).pluck(:domain_id)
domains = Domain.where(id: domain_ids).includes(:registrar).page(page).per(per)
if sorts.first == "registrar_name".freeze
domains = domains.includes(:registrar).where.not(registrars: {id: nil}).order("registrars.name #{order} NULLS LAST")
else
domains = domains.order("#{sort} #{order} NULLS LAST")
end
domain_c = Hash.new([])
registrant_domains.where(id: domains.map(&:id)).each{|d| domain_c[d.id] |= ["Registrant".freeze] }
DomainContact.where(contact_id: id, domain_id: domains.map(&:id)).each{|d| domain_c[d.domain_id] |= [d.type] }
domains.each{|d| d.roles = domain_c[d.id].uniq}
domains
end
end
def update_prohibited?
(statuses & [
CLIENT_UPDATE_PROHIBITED,

View file

@ -26,7 +26,7 @@ class Directo < ActiveRecord::Base
xml.invoice(
"SalesAgent" => Setting.directo_sales_agent,
"Number" => num,
"InvoiceDate" => invoice.created_at.strftime("%Y-%m-%dT%H:%M:%S"),
"InvoiceDate" => invoice.created_at.strftime("%Y-%m-%d"),
"PaymentTerm" => Setting.directo_receipt_payment_term,
"Currency" => invoice.currency,
"CustomerCode"=> invoice.buyer.accounting_customer_code

View file

@ -288,36 +288,6 @@ class Domain < ActiveRecord::Base
save
end
# state changes may be done low-level - no validation
# in this metod we still save PaperTrail log.
def clean_pendings_lowlevel
statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION)
statuses.delete(DomainStatus::PENDING_UPDATE)
statuses.delete(DomainStatus::PENDING_DELETE)
status_notes[DomainStatus::PENDING_UPDATE] = ''
status_notes[DomainStatus::PENDING_DELETE] = ''
hash = {
registrant_verification_token: nil,
registrant_verification_asked_at: nil,
pending_json: {},
status_notes: status_notes,
statuses: statuses.presence || [DomainStatus::OK],
# need this column in order to update PaperTrail version properly
updated_at: Time.now.utc
}
# PaperTrail
self.attributes = hash
record_update
clear_version_instance!
reset_transaction_id
update_columns(hash)
end
def pending_update!
return true if pending_update?
self.epp_pending_update = true # for epp
@ -575,10 +545,6 @@ class Domain < ActiveRecord::Base
end
end
def send_mail(action)
DomainMailer.send(action, DomainMailModel.new(self).send(action)).deliver
end
def admin_contact_names
admin_contacts.names
end

View file

@ -21,7 +21,10 @@ class DomainCron
if domain.pending_delete? || domain.pending_delete_confirmation?
DomainMailer.pending_delete_expired_notification(domain.id, true).deliver
end
domain.clean_pendings_lowlevel
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

View file

@ -1,117 +0,0 @@
class DomainMailModel
# Capture current values used in app/views/mailers/domain_mailer/* and app/mailers/domain_mailer will send later
def initialize(domain)
@domain = domain
@params = {errors: [], deliver_emails: domain.deliver_emails, id: domain.id}
end
def pending_delete_rejected_notification
registrant
subject(:pending_delete_rejected_notification_subject)
compose
end
def pending_delete_expired_notification
registrant
subject(:pending_delete_expired_notification_subject)
compose
end
def delete_confirmation
registrant
subject(:delete_confirmation_subject)
compose
end
def force_delete
admins
subject(:force_delete_subject)
compose
end
private
def registrant_old
@params[:recipient] = format Registrant.find(@domain.registrant_id_was).email
end
def registrant
@params[:recipient] = format @domain.registrant.email
end
def registrant_pending
@params[:recipient] = format @domain.pending_json['new_registrant_email']
@params[:new_registrant_name] = @domain.pending_json['new_registrant_name']
@params[:old_registrant_name] = @domain.registrant.name
end
# registrant and domain admin contacts
def admins
emails = ([@domain.registrant.email] + @domain.admin_contacts.map { |x| format(x.email) })
@params[:recipient] = emails.uniq.map { |x| format(x) }
end
# puny internet domain name, TODO: username<email>
def format(email)
return warn_no_email if email.nil?
user, host = email.split('@')
host = SimpleIDN.to_ascii(host)
"#{user}@#{host}"
end
def subject(subject)
@params[:name] = @domain.name
@params[:subject] = "#{I18n.t(subject, name: @domain.name)}, [#{@domain.name}]"
end
def confirm_update
verification_url('domain_update_confirms')
end
def compose
@params
end
def verification_url(path)
token = verification_token or return
@params[:verification_url] = "#{ENV['registrant_url']}/registrant/#{path}/#{@domain.id}?token=#{token}"
end
def verification_token
return warn_missing(:registrant_verification_token) if @domain.registrant_verification_token.blank?
return warn_missing(:registrant_verification_asked_at) if @domain.registrant_verification_asked_at.blank?
@domain.registrant_verification_token
end
def domain_info
[:name, :registrar_name,
:registrant_name, :registrant_ident, :registrant_email,
:registrant_street,:registrant_city
].each do |attr|
@params.store attr, @domain.send(attr)
end
@params.store :registrant_country, @domain.registrant_country.name
@params.store :registrant_priv, @domain.registrant.priv?
@params.store :old_registrant_name, Registrant.find(@domain.registrant_id_was).name
@params
end
def warn_no_email(item = 'email')
warn_missing item
nil
end
def warn_missing(item)
warn_not_delivered "#{item.to_s} is missing for #{@domain.name}"
end
def warn_not_delivered(reason)
message = "EMAIL NOT DELIVERED: #{reason}"
@params[:errors] << message
# Rails.logger.warn message
nil
end
end

View file

@ -132,14 +132,20 @@ class Registrar < ActiveRecord::Base
# Audit log is needed, therefore no raw SQL
def replace_nameservers(hostname, new_attributes)
transaction do
domain_list = []
nameservers.where(hostname: hostname).find_each do |original_nameserver|
new_nameserver = Nameserver.new
new_nameserver.domain = original_nameserver.domain
new_nameserver.attributes = new_attributes
new_nameserver.save!
domain_list << original_nameserver.domain.name
original_nameserver.destroy!
end
domain_list.uniq.sort
end
end

View file

@ -18,7 +18,7 @@ class SortedCountry
def quick_list
@quick_list ||=
[
%w[Estonia' EE],
%w[Estonia EE],
%w[Finland FI],
%w[Latvia LV],
%w[Lithuania LT],

View file

@ -6,9 +6,10 @@
%dt= t(:id)
%dd= @contact.code
%dt= t(:authinfo_pw)
%dt= Contact.human_attribute_name :auth_info
%dd
= text_field_tag :auth_info, @contact.auth_info, readonly: true, class: 'partially-hidden'
= tag :input, type: 'text', value: @contact.auth_info, readonly: true,
class: 'form-control input-sm'
%br

View file

@ -1,24 +1,8 @@
<% if flash[:notice] %>
<div class="alert alert-success alert-dismissible">
<button class="close" data-dismiss="alert" type=button><span>&times;</span></button>
<% if flash[:notice].respond_to?(:join) %>
<p><%= flash[:notice].join('<br>').html_safe %></p>
<% else %>
<p><%= flash[:notice] %></p>
<% end %>
<% display = (flash[:notice] || flash[:alert] || flash[:warning]) ? 'block' : 'none' %>
<div id="flash" style="display: <%= display %>;">
<% type = (flash[:notice]) ? 'bg-success' : 'bg-danger' %>
<% type = 'bg-warning' if flash[:warning] %>
<div class="<%= type %> alert">
<%= flash[:notice] || flash[:alert] || flash[:warning] %>
</div>
<% end %>
<% if flash[:alert] %>
<div class="alert alert-danger alert-dismissible">
<button class="close" data-dismiss="alert" type=button><span>&times;</span></button>
<p><%= flash[:alert] %></p>
</div>
<% end %>
<% if flash[:info] %>
<div class="alert alert-info alert-dismissible">
<button class="close" data-dismiss="alert" type=button><span>&times;</span></button>
<p><%= flash[:info] %></p>
</div>
<% end %>
</div>

View file

@ -65,7 +65,7 @@
</div>
</nav>
<div class="container">
<%= render 'shared/flash' %>
<%= render 'flash_messages' %>
<%= yield %>
</div>
<footer class="footer">

View file

@ -1,63 +1,63 @@
<!DOCTYPE html>
<html lang="<%= I18n.locale.to_s %>">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<% if content_for? :head_title %>
<%= yield :head_title %>
<% else %>
<title>
<%= t(:registrar_head_title) %>
</title>
<% end %>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'registrar-manifest', media: 'all' %>
<%= favicon_link_tag 'favicon.ico' %>
</head>
<body>
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button aria-expanded="false" class="navbar-toggle collapsed" data-target="#navbar" data-toggle="collapse" type="button">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<%= link_to registrar_root_path, class: 'navbar-brand' do %>
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<% if content_for? :head_title %>
<%= yield :head_title %>
<% else %>
<title>
<%= t(:registrar_head_title) %>
<% if unstable_env.present? %>
<div class="text-center">
<small style="color: #0074B3;">
<%= unstable_env %>
</small>
</div>
<% end %>
</title>
<% end %>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'registrar-manifest', media: 'all' %>
<%= favicon_link_tag 'favicon.ico' %>
</head>
<body>
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button aria-expanded="false" class="navbar-toggle collapsed" data-target="#navbar" data-toggle="collapse" type="button">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<%= link_to registrar_root_path, class: 'navbar-brand' do %>
<%= t(:registrar_head_title) %>
<% if unstable_env.present? %>
<div class="text-center">
<small style="color: #0074B3;">
<%= unstable_env %>
</small>
</div>
<% end %>
<% end %>
</div>
<%= render 'navbar' %>
</div>
</nav>
<div class="container">
<%= render 'flash_messages' %>
<% if depp_controller? %>
<%= render 'registrar/shared/epp_results' %>
<% end %>
<%= yield %>
</div>
<%= render 'navbar' %>
</div>
</nav>
<div class="container">
<%= render 'flash_messages' %>
<% if depp_controller? %>
<%= render 'registrar/shared/epp_results' %>
<% end %>
<%= yield %>
</div>
<footer class="footer">
<div class="container">
<div class="row">
<div class="col-md-6">
<%= image_tag 'eis-logo-et.png' %>
<footer class="footer">
<div class="container">
<div class="row">
<div class="col-md-6">
<%= image_tag 'eis-logo-et.png' %>
</div>
<div class="col-md-6 text-right">
Version
<%= CURRENT_COMMIT_HASH %>
</div>
</div>
</div>
<div class="col-md-6 text-right">
Version
<%= CURRENT_COMMIT_HASH %>
</div>
</div>
</div>
</footer>
<%= javascript_include_tag 'registrar-manifest', async: true %>
</body>
</footer>
<%= javascript_include_tag 'registrar-manifest', async: true %>
</body>
</html>

View file

@ -51,4 +51,4 @@
</div>
</footer>
</body>
</html>
</html>

View file

@ -1,23 +0,0 @@
.panel.panel-default
.panel-heading
%h3.panel-title= t(:address)
.panel-body
%dl.dl-horizontal
- if @contact.org_name.present?
%dt= t(:org_name)
%dd= @contact.org_name
%dt= t(:street)
%dd= @contact.street
%dt= t(:city)
%dd= @contact.city
%dt= t(:zip)
%dd= @contact.zip
%dt= t(:state)
%dd= @contact.state
%dt= t(:country)
%dd= @contact.country

View file

@ -1,30 +0,0 @@
- domains = contact.all_domains(page: params[:domain_page], per: 20, params: params.merge(leave_domains: domain_ids))
#contacts.panel.panel-default
.panel-heading
.pull-left
= t(:domains)
.pull-right
= form_tag request.path, method: :get do
= select_tag :domain_filter, options_for_select(%w(Registrant AdminDomainContact TechDomainContact), selected: params[:domain_filter]),
include_blank: true, class: 'form-control2 selectize2'
%button.btn.btn-primary
%span.glyphicon.glyphicon-search
.clearfix
.table-responsive
%table.table.table-hover.table-bordered.table-condensed
%thead
%tr
%th{class: 'col-xs-3'}=custom_sort_link t(:domain_name), :name
%th{class: 'col-xs-3'}=custom_sort_link t(:registrar_name), :registrar_name
%th{class: 'col-xs-3'}=custom_sort_link t(:valid_to), :valid_to
%th{class: 'col-xs-3'}=custom_sort_link Domain.human_attribute_name(:roles), :roles
%tbody
- domains.each do |x|
%tr
%td= link_to(x.name, [:registrant, x])
%td= link_to(x.registrar, [:registrant, x.registrar])
%td= l(x.valid_to, format: :short)
%td= x.roles.join(", ")
= paginate domains, param_name: :domain_page

View file

@ -1,48 +0,0 @@
.panel.panel-default
.panel-heading
%h3.panel-title= t(:general)
.panel-body
%dl.dl-horizontal
%dt= t(:id)
%dd= @contact.code
%dt= t(:name)
%dd= @contact.name
%dt= t(:password)
%dd
= text_field_tag :auth_info, @contact.auth_info, readonly: true, class: 'partially-hidden'
%br
%dt= t(:ident)
%dd= ident_for(@contact)
%dt= t(:email)
%dd= @contact.email
%dt= t(:phone)
%dd= @contact.phone
- if @contact.fax
%dt= t(:fax)
%dd= @contact.fax
%br
%dt= Contact.human_attribute_name :created_at
%dd
= l(@contact.created_at, format: :short)
by
= @contact.name
%dt= Contact.human_attribute_name :updated_at
%dd
= l(@contact.updated_at, format: :short)
by
= @contact.name
%dt= t(:registrar_name)
%dd
- if @contact.registrar.present?
= link_to(@contact.registrar, registrant_registrar_path(@contact.registrar))

View file

@ -1,6 +0,0 @@
= search_form_for [:registrant, @q] do |f|
= f.search_field :name_cont
= f.submit do
%span.glyphicon.glyphicon-search

View file

@ -1,21 +0,0 @@
- panel_class = contact.errors.messages[:statuses] ? 'panel-danger' : 'panel-default'
#contact_statuses.panel{class: panel_class}
.panel-heading.clearfix
= t(:statuses)
.table-responsive
%table.table.table-hover.table-bordered.table-condensed
%thead
%tr
%th{class: 'col-xs-6'}= t(:status)
%th{class: 'col-xs-6'}= t(:notes)
%tbody
- contact.statuses.each do |status|
%tr
%td= status
%td= contact.status_notes[status]
- if contact.errors.messages[:statuses]
%tfoot
- @domain.errors.messages[:statuses].each do |s|
%tr
%td{colspan: 4}= s

View file

@ -1,11 +0,0 @@
= render 'shared/title', name: @contact.name
.row
.col-md-6= render 'registrant/contacts/partials/general'
.col-md-6= render 'registrant/contacts/partials/address'
.row
.col-md-12= render 'registrant/contacts/partials/statuses', contact: @contact
.row
.col-md-12= render 'registrant/contacts/partials/domains', contact: @contact

View file

@ -0,0 +1,25 @@
<div class="page-header">
<h1><%= @contact.name %></h1>
</div>
<div class="row">
<div class="col-md-6">
<%= render 'registrant/contacts/show/general' %>
</div>
<div class="col-md-6">
<%= render 'registrant/contacts/show/address' %>
</div>
</div>
<div class="row">
<div class="col-md-12">
<%= render 'registrant/contacts/show/statuses', contact: @contact %>
</div>
</div>
<div class="row">
<div class="col-md-12">
<%= render 'registrant/contacts/show/domains', contact: @contact %>
</div>
</div>

View file

@ -0,0 +1,31 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
<%= t '.header' %>
</h3>
</div>
<div class="panel-body">
<dl class="dl-horizontal">
<% if @contact.org_name.present? %>
<dt><%= Contact.human_attribute_name :org_name %></dt>
<dd><%= @contact.org_name %></dd>
<% end %>
<dt><%= Contact.human_attribute_name :street %></dt>
<dd><%= @contact.street %></dd>
<dt><%= Contact.human_attribute_name :city %></dt>
<dd><%= @contact.city %></dd>
<dt><%= Contact.human_attribute_name :zip %></dt>
<dd><%= @contact.zip %></dd>
<dt><%= Contact.human_attribute_name :state %></dt>
<dd><%= @contact.state %></dd>
<dt><%= Contact.human_attribute_name :country %></dt>
<dd><%= @contact.country %></dd>
</dl>
</div>
</div>

View file

@ -0,0 +1,6 @@
<tr>
<td><%= link_to domain.name, registrant_domain_path(domain) %></td>
<td><%= link_to domain.registrar, registrant_registrar_path(domain.registrar) %></td>
<td><%= l domain.valid_to %></td>
<td><%= domain.roles.join(", ") %></td>
</tr>

View file

@ -0,0 +1,54 @@
<% domains = contact.all_domains(page: params[:domain_page], per: 20, params:
params.merge(leave_domains: domain_ids)) %>
<div class="panel panel-default">
<div class="panel-heading">
<div class="row">
<div class="col-sm-6">
<%= t '.header' %>
</div>
<div class="col-sm-6 text-right">
<%= form_tag request.path, method: :get, class: 'form-inline' do %>
<%= select_tag :domain_filter,
options_for_select(%w(Registrant AdminDomainContact TechDomainContact),
selected: params[:domain_filter]),
include_blank: t('.all'),
class: 'form-control' %>
<button class="btn btn-primary">
<span class="glyphicon glyphicon-search"></span>
</button>
<% end %>
</div>
</div>
</div>
<div class="table-responsive">
<table class="table table-hover table-bordered table-condensed">
<thead>
<tr>
<th class="col-xs-3">
<%= custom_sort_link Domain.human_attribute_name(:name), :name %>
</th>
<th class="col-xs-3">
<%= custom_sort_link Registrar.model_name.human, :registrar_name %>
</th>
<th class="col-xs-3">
<%= custom_sort_link Domain.human_attribute_name(:valid_to), :valid_to %>
</th>
<th class="col-xs-3">
<%= custom_sort_link Domain.human_attribute_name(:roles), :roles %>
</th>
</tr>
</thead>
<tbody>
<%= render partial: 'registrant/contacts/show/domain', collection: domains %>
</tbody>
</table>
</div>
<div class="panel-footer">
<%= paginate domains, param_name: :domain_page %>
</div>
</div>

View file

@ -0,0 +1,48 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
<%= t '.header' %>
</h3>
</div>
<div class="panel-body">
<dl class="dl-horizontal">
<dt><%= Contact.human_attribute_name :code %></dt>
<dd><%= @contact.code %></dd>
<dt><%= Contact.human_attribute_name :name %></dt>
<dd><%= @contact.name %></dd>
<dt><%= Contact.human_attribute_name :auth_info %></dt>
<dd>
<%= tag :input, type: 'text', value: @contact.auth_info, readonly: true,
class: 'form-control input-sm' %>
</dd>
<dt><%= Contact.human_attribute_name :ident %></dt>
<dd><%= ident_for(@contact) %></dd>
<dt><%= Contact.human_attribute_name :email %></dt>
<dd><%= @contact.email %></dd>
<dt><%= Contact.human_attribute_name :phone %></dt>
<dd><%= @contact.phone %></dd>
<% if @contact.fax %>
<dt><%= Contact.human_attribute_name :fax %></dt>
<dd><%= @contact.fax %></dd>
<% end %>
<dt><%= Contact.human_attribute_name :created_at %></dt>
<dd><%= l @contact.created_at %></dd>
<dt><%= Contact.human_attribute_name :updated_at %></dt>
<dd><%= l @contact.updated_at %></dd>
<dt><%= Registrar.model_name.human %></dt>
<dd>
<%= link_to @contact.registrar, registrant_registrar_path(@contact.registrar) %>
</dd>
</dl>
</div>
</div>

View file

@ -0,0 +1,6 @@
<%= search_form_for [:registrant, @q] do |f| %>
<%= f.search_field :name_cont %>
<%= f.submit do %>
<span class="glyphicon glyphicon-search"></span>
<% end %>
<% end %>

View file

@ -0,0 +1,25 @@
<div class="panel panel-default">
<div class="panel-heading">
<%= t '.header' %>
</div>
<div class="table-responsive">
<table class="table table-hover table-bordered table-condensed">
<thead>
<tr>
<th class="col-xs-6"><%= t '.status' %></th>
<th class="col-xs-6"><%= t '.notes' %></th>
</tr>
</thead>
<tbody>
<% contact.statuses.each do |status| %>
<tr>
<td><%= status %></td>
<td><%= contact.status_notes[status] %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>

View file

@ -6,9 +6,10 @@
%dt= t(:id)
%dd= @contact.id
%dt= t(:authinfo_pw)
%dt= Contact.human_attribute_name :auth_info
%dd
= text_field_tag :password, @contact.password, readonly: true, class: 'partially-hidden'
= tag :input, type: 'text', value: @contact.password, readonly: true,
class: 'form-control input-sm'
%br

View file

@ -1,9 +1,4 @@
#!/usr/bin/env ruby
begin
load File.expand_path('../spring', __FILE__)
rescue LoadError
'no spring'
end
APP_PATH = File.expand_path('../../config/application', __FILE__)
APP_PATH = File.expand_path('../../config/application', __FILE__)
require_relative '../config/boot'
require 'rails/commands'

View file

@ -1,9 +1,4 @@
#!/usr/bin/env ruby
begin
load File.expand_path('../spring', __FILE__)
rescue LoadError
'no spring'
end
require_relative '../config/boot'
require 'rake'
Rake.application.run

View file

@ -1,8 +1,4 @@
#!/usr/bin/env ruby
begin
load File.expand_path("../spring", __FILE__)
rescue LoadError
end
#
# This file was generated by Bundler.
#

29
bin/setup Normal file
View file

@ -0,0 +1,29 @@
#!/usr/bin/env ruby
require 'pathname'
# path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
Dir.chdir APP_ROOT do
# This script is a starting point to setup your application.
# Add necessary setup steps to this file:
puts "== Installing dependencies =="
system "gem install bundler --conservative"
system "bundle check || bundle install"
# puts "\n== Copying sample files =="
# unless File.exist?("config/database.yml")
# system "cp config/database.yml.sample config/database.yml"
# end
puts "\n== Preparing database =="
system "bin/rake db:setup"
puts "\n== Removing old logs and tempfiles =="
system "rm -f log/*"
system "rm -rf tmp/cache"
puts "\n== Restarting application server =="
system "touch tmp/restart.txt"
end

View file

@ -1,15 +0,0 @@
#!/usr/bin/env ruby
# This file loads spring without using Bundler, in order to be fast.
# It gets overwritten when you run the `spring binstub` command.
unless defined?(Spring)
require "rubygems"
require "bundler"
if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)
Gem.paths = { "GEM_PATH" => [Bundler.bundle_path.to_s, *Gem.path].uniq }
gem "spring", match[1]
require "spring/binstub"
end
end

View file

@ -2,7 +2,7 @@ Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
config.cache_classes = true
config.eager_load = true
config.eager_load = false
config.serve_static_files = true
config.static_cache_control = 'public, max-age=3600'
@ -25,4 +25,4 @@ Rails.application.configure do
config.cache_store = :memory_store
end
Que.mode = :sync
Que.mode = :sync

View file

@ -0,0 +1,18 @@
en:
registrant:
contacts:
show:
general:
header: General
address:
header: Address
statuses:
header: Statuses
status: Status
notes: Note
domains:
header: Domains
all: All roles

View file

@ -3,3 +3,4 @@ en:
nameservers:
update:
replaced: Nameserver have been successfully replaced
affected_domains: Affected domains

View file

@ -101,6 +101,8 @@ Rails.application.routes.draw do
namespace :registrant do
root 'domains#index'
resources :registrars, only: :show
resources :contacts, only: :show
resources :domains, only: %i[index show] do
collection do
get :download_list
@ -111,8 +113,8 @@ Rails.application.routes.draw do
end
end
resources :domain_update_confirms
resources :domain_delete_confirms
resources :domain_update_confirms, only: %i[show update]
resources :domain_delete_confirms, only: %i[show update]
devise_scope :user do
get 'login' => 'sessions#login'
@ -125,29 +127,8 @@ Rails.application.routes.draw do
post 'id' => 'sessions#id'
get 'logout' => '/devise/sessions#destroy'
end
resources :domains do
resources :registrant_verifications
collection do
post 'update', as: 'update'
post 'destroy', as: 'destroy'
get 'renew'
get 'edit'
get 'info'
get 'delete'
end
end
resources :registrars do
collection do
get :search
end
end
resources :contacts
end
# ADMIN ROUTES
namespace :admin do
resources :keyrelays

View file

@ -1 +0,0 @@
Spring.watch 'config/application.yml'

View file

@ -0,0 +1,5 @@
class EnablePgcryptoExt < ActiveRecord::Migration
def change
enable_extension 'pgcrypto'
end
end

View file

@ -0,0 +1,5 @@
class AddUuidToContacts < ActiveRecord::Migration
def change
add_column :contacts, :uuid, :uuid, default: 'gen_random_uuid()'
end
end

View file

@ -0,0 +1,5 @@
class AddUuidToDomains < ActiveRecord::Migration
def change
add_column :domains, :uuid, :uuid, default: 'gen_random_uuid()'
end
end

View file

@ -0,0 +1,6 @@
class ChangeContactsAndDomainsUuidToNotNull < ActiveRecord::Migration
def change
change_column_null :contacts, :uuid, false
change_column_null :domains, :uuid, false
end
end

View file

@ -0,0 +1,16 @@
# Unique constraint is needed to prevent accidental duplicate values in fixtures to appear in DB
class AddContactsAndDomainsUuidUniqConstraint < ActiveRecord::Migration
def up
execute <<-SQL
ALTER TABLE contacts ADD CONSTRAINT uniq_contact_uuid UNIQUE (uuid);
ALTER TABLE domains ADD CONSTRAINT uniq_domain_uuid UNIQUE (uuid);
SQL
end
def down
execute <<-SQL
ALTER TABLE contacts DROP CONSTRAINT uniq_contact_uuid;
ALTER TABLE domains DROP CONSTRAINT uniq_domain_uuid;
SQL
end
end

File diff suppressed because it is too large Load diff

View file

@ -34,8 +34,9 @@ Content-Type: application/json
"hostname": "new-ns1.example.com",
"ipv4": ["192.0.2.1", "192.0.2.2"],
"ipv6": ["2001:db8::1", "2001:db8::2"]
},
}
}
},
"affected_domains": ["example.com", "example.org"]
}
```

View file

@ -1,33 +0,0 @@
version: "3.2"
services:
app:
tty: true
stdin_open: true
build:
context: .
dockerfile: Dockerfile
links:
- db
environment:
- APP_DBHOST=db
volumes:
- .:/opt/webapps/app
ports:
- "3000:3000"
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails server -p 3000 -b '0.0.0.0'"
web:
image: nginx
volumes:
- ./docker/nginx.template:/etc/nginx/conf.d/nginx.template
ports:
- "80:80"
links:
- app
environment:
APP: 'app'
command: /bin/bash -c "envsubst '$$APP' < /etc/nginx/conf.d/nginx.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
db:
image: postgres:9.4

View file

@ -1,6 +0,0 @@
# /bin/sh
docker-compose down
docker-compose build
docker-compose run app rake db:setup:all
docker-compose run app rake db:migrate
docker-compose run app rake dev:prime

View file

@ -1,8 +0,0 @@
# /bin/sh
docker-compose down
docker-compose build
# Setup test database
docker-compose run app rake db:setup:all test
# Finally run tests to check if everything is in order
docker-compose run app rspec

View file

@ -1,29 +0,0 @@
log_format le_json '{ "time": "$time_iso8601", '
'"remote_addr": "$remote_addr", '
'"remote_user": "$remote_user", '
'"body_bytes_sent": "$body_bytes_sent", '
'"request_time": "$request_time", '
'"status": "$status", '
'"request": "$request", '
'"request_method": "$request_method", '
'"http_referrer": "$http_referer", '
'"http_user_agent": "$http_user_agent" }';
upstream app {
server ${APP}:3000;
}
server {
listen 80;
access_log /var/log/nginx/access.log le_json;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app;
break;
}
}

View file

@ -0,0 +1,149 @@
# frozen_string_literal: true
require "action_view"
require "action_controller"
require "action_controller/log_subscriber"
module ActionController
# API Controller is a lightweight version of <tt>ActionController::Base</tt>,
# created for applications that don't require all functionalities that a complete
# \Rails controller provides, allowing you to create controllers with just the
# features that you need for API only applications.
#
# An API Controller is different from a normal controller in the sense that
# by default it doesn't include a number of features that are usually required
# by browser access only: layouts and templates rendering, cookies, sessions,
# flash, assets, and so on. This makes the entire controller stack thinner,
# suitable for API applications. It doesn't mean you won't have such
# features if you need them: they're all available for you to include in
# your application, they're just not part of the default API controller stack.
#
# Normally, +ApplicationController+ is the only controller that inherits from
# <tt>ActionController::API</tt>. All other controllers in turn inherit from
# +ApplicationController+.
#
# A sample controller could look like this:
#
# class PostsController < ApplicationController
# def index
# posts = Post.all
# render json: posts
# end
# end
#
# Request, response, and parameters objects all work the exact same way as
# <tt>ActionController::Base</tt>.
#
# == Renders
#
# The default API Controller stack includes all renderers, which means you
# can use <tt>render :json</tt> and brothers freely in your controllers. Keep
# in mind that templates are not going to be rendered, so you need to ensure
# your controller is calling either <tt>render</tt> or <tt>redirect_to</tt> in
# all actions, otherwise it will return 204 No Content.
#
# def show
# post = Post.find(params[:id])
# render json: post
# end
#
# == Redirects
#
# Redirects are used to move from one action to another. You can use the
# <tt>redirect_to</tt> method in your controllers in the same way as in
# <tt>ActionController::Base</tt>. For example:
#
# def create
# redirect_to root_url and return if not_authorized?
# # do stuff here
# end
#
# == Adding New Behavior
#
# In some scenarios you may want to add back some functionality provided by
# <tt>ActionController::Base</tt> that is not present by default in
# <tt>ActionController::API</tt>, for instance <tt>MimeResponds</tt>. This
# module gives you the <tt>respond_to</tt> method. Adding it is quite simple,
# you just need to include the module in a specific controller or in
# +ApplicationController+ in case you want it available in your entire
# application:
#
# class ApplicationController < ActionController::API
# include ActionController::MimeResponds
# end
#
# class PostsController < ApplicationController
# def index
# posts = Post.all
#
# respond_to do |format|
# format.json { render json: posts }
# format.xml { render xml: posts }
# end
# end
# end
#
# Make sure to check the modules included in <tt>ActionController::Base</tt>
# if you want to use any other functionality that is not provided
# by <tt>ActionController::API</tt> out of the box.
class API < Metal
abstract!
# Shortcut helper that returns all the ActionController::API modules except
# the ones passed as arguments:
#
# class MyAPIBaseController < ActionController::Metal
# ActionController::API.without_modules(:ForceSSL, :UrlFor).each do |left|
# include left
# end
# end
#
# This gives better control over what you want to exclude and makes it easier
# to create an API controller class, instead of listing the modules required
# manually.
def self.without_modules(*modules)
modules = modules.map do |m|
m.is_a?(Symbol) ? ActionController.const_get(m) : m
end
MODULES - modules
end
MODULES = [
AbstractController::Rendering,
UrlFor,
Redirecting,
ApiRendering,
Renderers::All,
ConditionalGet,
BasicImplicitRender,
StrongParameters,
ForceSSL,
DataStreaming,
# Before callbacks should also be executed as early as possible, so
# also include them at the bottom.
AbstractController::Callbacks,
# Append rescue at the bottom to wrap as much as possible.
Rescue,
# Add instrumentations hooks at the bottom, to ensure they instrument
# all the methods properly.
Instrumentation,
# Params wrapper should come before instrumentation so they are
# properly showed in logs
ParamsWrapper
]
MODULES.each do |mod|
include mod
end
ActiveSupport.run_load_hooks(:action_controller_api, self)
ActiveSupport.run_load_hooks(:action_controller, self)
end
end

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
module ActionController
module ApiRendering
extend ActiveSupport::Concern
included do
include Rendering
end
def render_to_body(options = {})
_process_options(options)
super
end
end
end

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
module ActionController
module BasicImplicitRender # :nodoc:
def send_action(method, *args)
super.tap { default_render unless performed? }
end
def default_render(*args)
head :no_content
end
end
end

View file

@ -6,7 +6,7 @@ PaperTrail::Version.module_eval do
end
# Store console and rake changes in versions
if defined?(::Rails::Console) || File.basename($PROGRAM_NAME).split(' ').first == 'spring'
if defined?(::Rails::Console)
PaperTrail.whodunnit = "console-#{`whoami`.strip}"
elsif File.basename($PROGRAM_NAME) == 'rake'
# rake username does not work when spring enabled

View file

@ -0,0 +1,3 @@
require_relative 'action_controller/metal/basic_implicit_render'
require_relative 'action_controller/api/api_rendering'
require_relative 'action_controller/api'

View file

@ -1,20 +0,0 @@
require 'rails_helper'
RSpec.feature 'Mobile ID login', db: true do
given!(:api_user) { create(:api_user, identity_code: 1234) }
background do
digidoc_client = instance_double(Digidoc::Client, authenticate: OpenStruct.new(user_id_code: 1234), session_code: 1234)
allow(Digidoc::Client).to receive(:new).and_return(digidoc_client)
end
scenario 'login with phone number' do
visit registrar_login_path
click_on 'login-with-mobile-id-btn'
fill_in 'user[phone]', with: '1234'
click_button 'Login'
expect(page).to have_text('Confirmation sms was sent to your phone. Verification code is')
end
end

View file

@ -1,40 +0,0 @@
require 'rails_helper'
RSpec.describe DomainDeleteConfirmEmailJob do
describe '#run' do
let(:domain) { instance_double(Domain) }
let(:message) { instance_double(ActionMailer::MessageDelivery) }
before :example do
expect(Domain).to receive(:find).and_return(domain)
allow(domain).to receive_messages(
id: 1,
name: 'test.com',
registrant_email: 'registrant@test.com',
registrar: 'registrar',
registrant: 'registrant')
end
after :example do
domain_id = 1
described_class.enqueue(domain_id)
end
it 'creates log record' do
log_message = 'Send DomainDeleteMailer#confirm email for domain test.com (#1) to registrant@test.com'
allow(DomainDeleteMailer).to receive(:confirm).and_return(message)
allow(message).to receive(:deliver_now)
expect(Rails.logger).to receive(:info).with(log_message)
end
it 'sends email' do
expect(DomainDeleteMailer).to receive(:confirm).with(domain: domain,
registrar: 'registrar',
registrant: 'registrant')
.and_return(message)
expect(message).to receive(:deliver_now)
end
end
end

View file

@ -1,18 +0,0 @@
require 'rails_helper'
RSpec.describe DomainUpdateConfirmJob do
let(:domain) { instance_spy(Epp::Domain, registrant: registrant, errors: []) }
let(:registrant) { instance_double(Registrant) }
let(:registrant_change) { instance_spy(RegistrantChange) }
it 'confirms registrant change' do
expect(Epp::Domain).to receive(:find).and_return(domain)
expect(RegistrantChange).to receive(:new)
.with(domain: domain, old_registrant: registrant)
.and_return(registrant_change)
described_class.enqueue(domain_id = nil, action = RegistrantVerification::CONFIRMED)
expect(registrant_change).to have_received(:confirm)
end
end

View file

@ -2,7 +2,6 @@ ENV['RAILS_ENV'] ||= 'test'
require 'spec_helper'
require File.expand_path('../../config/environment', __FILE__)
require 'rspec/rails'
require 'capybara/poltergeist'
require 'paper_trail/frameworks/rspec'
require 'money-rails/test_helpers'
require 'support/requests/session_helpers'

View file

@ -4,5 +4,3 @@ RSpec.configure do |config|
config.include CapybaraViewMacros, type: :view
config.include CapybaraViewMacros, type: :presenter
end
Capybara.javascript_driver = :poltergeist

View file

@ -0,0 +1,42 @@
require 'test_helper'
require 'database_cleaner'
require 'selenium/webdriver'
class ApplicationSystemTestCase < ActionDispatch::IntegrationTest; end
class JavaScriptApplicationSystemTestCase < ApplicationSystemTestCase
self.use_transactional_fixtures = false
DatabaseCleaner.strategy = :truncation
Capybara.register_driver(:chrome) do |_app|
options = ::Selenium::WebDriver::Chrome::Options.new
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--window-size=1400,1400')
Capybara::Selenium::Driver.new(Rails.application, browser: :chrome, options: options)
end
Capybara.register_server(:silent_puma) do |app, port, _host|
require 'rack/handler/puma'
Rack::Handler::Puma.run(app, Port: port, Threads: '0:2', Silent: true)
end
def setup
DatabaseCleaner.start
super
Capybara.current_driver = :chrome
Capybara.server = :silent_puma
end
def teardown
super
DatabaseCleaner.clean
end
end

View file

@ -8,6 +8,7 @@ john:
registrar: bestnames
code: john-001
auth_info: cacb5b
uuid: eb2f2766-b44c-4e14-9f16-32ab1a7cb957
william: &william
name: William
@ -27,6 +28,7 @@ william: &william
country_code: US
statuses:
- ok
uuid: 0aa54704-d6f7-4ca9-b8ca-2827d9a4e4eb
jane:
name: Jane
@ -38,6 +40,7 @@ jane:
registrar: bestnames
code: jane-001
auth_info: 0aa09f
uuid: 9db3de62-2414-4487-bee2-d5c155567768
acme_ltd:
name: Acme Ltd
@ -49,6 +52,7 @@ acme_ltd:
ident_country_code: US
code: acme-ltd-001
auth_info: 720b3c
uuid: f1dd365c-5be9-4b3d-a44e-3fa002465e4d
jack:
name: Jack
@ -60,12 +64,14 @@ jack:
ident_country_code: US
code: jack-001
auth_info: e2c440
uuid: 28b65455-6f1a-49fd-961c-0758886dbd75
identical_to_william:
<<: *william
registrar: goodnames
code: william-002
auth_info: 5ab865
uuid: c0a191d5-3793-4f0b-8f85-491612d0293e
not_in_use:
name: Useless
@ -73,6 +79,7 @@ not_in_use:
registrar: bestnames
code: useless-001
auth_info: e75a2a
uuid: ca613cc5-a8dc-48c1-8d32-d3c6a0b6c952
invalid:
name: any
@ -80,3 +87,4 @@ invalid:
email: invalid@invalid.test
auth_info: any
registrar: bestnames
uuid: bd80c0f9-26ee-49e0-a2cb-2311d931c433

View file

@ -7,6 +7,7 @@ shop:
valid_to: 2010-07-05
period: 1
period_unit: m
uuid: 1b3ee442-e8fe-4922-9492-8fcb9dccc69c
airport:
name: airport.test
@ -17,6 +18,7 @@ airport:
valid_to: 2010-07-05
period: 1
period_unit: m
uuid: 2df2c1a1-8f6a-490a-81be-8bdf29866880
library:
name: library.test
@ -27,6 +29,7 @@ library:
valid_to: 2010-07-05
period: 1
period_unit: m
uuid: 647bcc48-8d5e-4a04-8ce5-2a3cd17b6eab
metro:
name: metro.test
@ -37,6 +40,7 @@ metro:
valid_to: 2010-07-05
period: 1
period_unit: m
uuid: ef97cb80-333b-4893-b9df-163f2b452798
invalid:
name: invalid.test
@ -44,3 +48,4 @@ invalid:
valid_to: <%= Time.zone.parse('2010-07-05').utc.to_s(:db) %>
registrar: bestnames
registrant: invalid
uuid: 3c430ead-bb17-4b5b-aaa1-caa7dde7e138

View file

@ -35,5 +35,4 @@ overdue:
for_payments_test:
<<: *DEFAULTS
total: 12.00
id: 1
number: 1

6
test/fixtures/whois_records.yml vendored Normal file
View file

@ -0,0 +1,6 @@
shop:
name: shop.test
domain: shop
body: WHOIS text
json:
name: shop.test

View file

@ -41,11 +41,12 @@ class APINameserversPutTest < ActionDispatch::IntegrationTest
assert_equal nameserver_hash, nameservers(:metro_ns1).reload.attributes
end
def test_returns_new_nameserver_record
def test_returns_new_nameserver_record_and_affected_domain
request_params = { format: :json, data: { type: 'nameserver', id: 'ns1.bestnames.test',
attributes: { hostname: 'ns55.bestnames.test',
ipv4: ['192.0.2.55'],
ipv6: ['2001:db8::55'] } } }
put '/repp/v1/registrar/nameservers', request_params, { 'HTTP_AUTHORIZATION' => http_auth_key }
assert_response 200
@ -53,7 +54,8 @@ class APINameserversPutTest < ActionDispatch::IntegrationTest
id: 'ns55.bestnames.test',
attributes: { hostname: 'ns55.bestnames.test',
ipv4: ['192.0.2.55'],
ipv6: ['2001:db8::55'] } } }),
ipv6: ['2001:db8::55'] }},
affected_domains: ["airport.test", "shop.test"] }),
JSON.parse(response.body, symbolize_names: true)
end

View file

@ -16,8 +16,8 @@ class RegistrarAreaNameserverBulkChangeTest < ActionDispatch::IntegrationTest
basic_auth: ['test_goodnames', 'testtest'])
.to_return(body: { data: [{
type: 'nameserver',
id: 'new-ns.bestnames.test'
}] }.to_json, status: 200)
id: 'new-ns.bestnames.test'}],
affected_domains: ["airport.test", "shop.test"]}.to_json, status: 200)
visit registrar_domains_url
click_link 'Bulk change'
@ -32,6 +32,7 @@ class RegistrarAreaNameserverBulkChangeTest < ActionDispatch::IntegrationTest
assert_requested request_stub
assert_current_path registrar_domains_path
assert_text 'Nameserver have been successfully replaced'
assert_text 'Affected domains: airport.test, shop.test'
end
def test_fails_gracefully

View file

@ -0,0 +1,36 @@
require "test_helper"
class DomainDeleteConfirmJobTest < ActiveSupport::TestCase
def setup
super
@domain = domains(:shop)
@new_registrant = contacts(:william)
@user = users(:api_bestnames)
@domain.update!(pending_json: { new_registrant_id: @new_registrant.id,
new_registrant_name: @new_registrant.name,
new_registrant_email: @new_registrant.email,
current_user_id: @user.id })
end
def teardown
super
end
def test_rejected_registrant_verification_polls_a_message
DomainDeleteConfirmJob.enqueue(@domain.id, RegistrantVerification::REJECTED)
last_registrar_message = @domain.registrar.messages.last
assert_equal(last_registrar_message.attached_obj_id, @domain.id)
assert_equal(last_registrar_message.body, 'Registrant rejected domain deletion: shop.test')
end
def test_accepted_registrant_verification_polls_a_message
DomainDeleteConfirmJob.enqueue(@domain.id, RegistrantVerification::CONFIRMED)
last_registrar_message = @domain.registrar.messages.last
assert_equal(last_registrar_message.attached_obj_id, @domain.id)
assert_equal(last_registrar_message.body, 'Registrant confirmed domain deletion: shop.test')
end
end

View file

@ -0,0 +1,36 @@
require "test_helper"
class DomainUpdateConfirmJobTest < ActiveSupport::TestCase
def setup
super
@domain = domains(:shop)
@new_registrant = contacts(:william)
@user = users(:api_bestnames)
@domain.update!(pending_json: { new_registrant_id: @new_registrant.id,
new_registrant_name: @new_registrant.name,
new_registrant_email: @new_registrant.email,
current_user_id: @user.id })
end
def teardown
super
end
def test_rejected_registrant_verification_polls_a_message
DomainUpdateConfirmJob.enqueue(@domain.id, RegistrantVerification::REJECTED)
last_registrar_message = @domain.registrar.messages.last
assert_equal(last_registrar_message.attached_obj_id, @domain.id)
assert_equal(last_registrar_message.body, 'Registrant rejected domain update: shop.test')
end
def test_accepted_registrant_verification_polls_a_message
DomainUpdateConfirmJob.enqueue(@domain.id, RegistrantVerification::CONFIRMED)
last_registrar_message = @domain.registrar.messages.last
assert_equal(last_registrar_message.attached_obj_id, @domain.id)
assert_equal(last_registrar_message.body, 'Registrant confirmed domain update: shop.test')
end
end

View file

@ -0,0 +1,23 @@
require 'test_helper'
class ReplaceNameserversTest < ActiveSupport::TestCase
def setup
@registrar = registrars(:bestnames)
end
def test_replace_nameservers_in_bulk_returns_sorted_domain_names
new_attributes = { hostname: 'ns-updated1.bestnames.test', ipv4: '192.0.3.1',
ipv6: '2001:db8::2' }
result = @registrar.replace_nameservers('ns1.bestnames.test', new_attributes)
assert_equal(["airport.test", "shop.test"], result)
end
def test_replace_nameservers_in_bulk_returns_empty_array_for_non_existent_base_nameserver
new_attributes = { hostname: 'ns-updated1.bestnames.test', ipv4: '192.0.3.1',
ipv6: '2001:db8::2' }
result = @registrar.replace_nameservers('ns3.bestnames.test', new_attributes)
assert_equal([], result)
end
end

View file

@ -0,0 +1,35 @@
require 'test_helper'
class RegistrarAreaSignInTest < JavaScriptApplicationSystemTestCase
def setup
super
WebMock.allow_net_connect!
@user = users(:api_bestnames)
@user.identity_code = '1234'
@user.save
end
def test_mobile_id_sign_in_page
mock_client = Minitest::Mock.new
mock_client.expect(:authenticate,
OpenStruct.new(user_id_code: '1234', challenge_id: '1234'),
[{ phone: "+3721234",
message_to_display: "Authenticating",
service_name: "Testimine" }])
mock_client.expect(:session_code, 1234)
Digidoc::Client.stub(:new, mock_client) do
visit registrar_login_path
click_on 'login-with-mobile-id-btn'
fill_in 'user[phone]', with: '1234'
click_button 'Login'
flash_message = page.find('div.bg-success')
assert_equal('Confirmation sms was sent to your phone. Verification code is 1234.',
flash_message.text)
end
end
end

View file

@ -13,6 +13,8 @@ require 'capybara/minitest'
require 'webmock/minitest'
require 'support/rails5_assetions' # Remove once upgraded to Rails 5
require 'application_system_test_case'
Setting.address_processing = false
Setting.registry_country_code = 'US'
@ -38,4 +40,4 @@ class ActionDispatch::IntegrationTest
Capybara.reset_sessions!
Capybara.use_default_driver
end
end
end