Merge branch 'master' into registry-791

# Conflicts:
#	db/structure.sql
This commit is contained in:
Artur Beljajev 2018-07-19 20:40:45 +03:00
commit 106592e53e
58 changed files with 991 additions and 1005 deletions

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,26 @@
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)

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
@ -104,14 +102,13 @@ gem 'active_model-errors_details' # Backport from Rails 5, https://github.com/ra
group :development do
# 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'
@ -121,6 +118,7 @@ group :development, :test do
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)
@ -127,15 +127,16 @@ GEM
activesupport (>= 3.0.0)
uniform_notifier (~> 1.9.0)
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)
@ -180,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)
@ -204,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)
@ -266,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)
@ -368,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)
@ -390,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)
@ -404,7 +400,7 @@ GEM
sixarm_ruby_unaccent (1.1.1)
slop (3.6.0)
socksify (1.7.1)
sprockets (3.7.1)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.1)
@ -436,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
@ -473,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
@ -491,7 +483,6 @@ DEPENDENCIES
paper_trail!
pdfkit (= 0.6.2)
pg (= 0.19.0)
poltergeist
pry (= 0.10.1)
puma
que (= 0.10.0)
@ -507,6 +498,7 @@ DEPENDENCIES
sdoc (= 0.4.1)
select2-rails (= 3.5.9.3)
selectize-rails (= 0.12.1)
selenium-webdriver
simplecov
simpleidn (= 0.0.7)
uglifier

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,5 +1,4 @@
class Registrant::RegistrarsController < RegistrantController
def show
@registrar = Registrar.find(params[:id])
authorize! :read, @registrar

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

@ -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

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

@ -294,36 +294,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
@ -581,10 +551,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

@ -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

@ -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

@ -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

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,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

@ -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'