diff --git a/README.md b/README.md index ca7bf3fc3..0a1747d53 100644 --- a/README.md +++ b/README.md @@ -307,3 +307,9 @@ Rollback to previous release: mina rollback # staging mina pr rollback # production + +General rake and mina tips: + + rake -T # list all rake commands + rake -T db # list all database related commands + mina -T # list all mina deploy commands diff --git a/app/helpers/epp/contacts_helper.rb b/app/helpers/epp/contacts_helper.rb index 8450441e0..20197c7f7 100644 --- a/app/helpers/epp/contacts_helper.rb +++ b/app/helpers/epp/contacts_helper.rb @@ -10,7 +10,8 @@ module Epp::ContactsHelper # FIXME: Update returns 2303 update multiple times code = params_hash['epp']['command']['update']['update'][:id] @contact = Contact.where(code: code).first - if update_rights? && stamp(@contact) && @contact.update_attributes(contact_and_address_attributes(:update)) + # if update_rights? && stamp(@contact) && @contact.update_attributes(contact_and_address_attributes(:update)) + if owner? && stamp(@contact) && @contact.update_attributes(contact_and_address_attributes(:update)) render 'epp/contacts/update' else contact_exists?(code) @@ -38,8 +39,8 @@ module Epp::ContactsHelper def info_contact handle_errors(@contact) and return unless @contact handle_errors(@contact) and return unless rights? - @disclosure = ContactDisclosure.default_values.merge(@contact.disclosure.as_hash) - @disclosure_policy = @contact.disclosure.attributes_with_flag + @disclosure = ContactDisclosure.default_values.merge(@contact.disclosure.try(:as_hash) || {}) + @disclosure_policy = @contact.disclosure.try(:attributes_with_flag) @owner = owner?(false) render 'epp/contacts/info' end @@ -67,7 +68,8 @@ module Epp::ContactsHelper def validate_contact_update_request @ph = params_hash['epp']['command']['update']['update'] update_attrs_present? - xml_attrs_present?(@ph, [['id'], %w(authInfo pw)]) + # xml_attrs_present?(@ph, [['id'], %w(authInfo pw)]) + xml_attrs_present?(@ph, [['id']]) end def contact_exists?(code) diff --git a/app/models/concerns/domain_version_observer.rb b/app/models/concerns/domain_version_observer.rb index e3d37ee3e..09175aa4d 100644 --- a/app/models/concerns/domain_version_observer.rb +++ b/app/models/concerns/domain_version_observer.rb @@ -11,13 +11,18 @@ module DomainVersionObserver name = domain_name return unless name body = snapshot - delay.update_whois(name, body) + delay.update_private_whois(name, body) + delay.update_public_whois(name, body) end - # not sure we need to pass in the params since i don't know if delayed job has access to - # all the regular attributes and stuff - def update_whois(domain_name, body) - wd = WhoisDomain.find_or_initialize_by(name: domain_name) + def update_private_whois(domain_name, body) + wd = Whois::PublicDomain.find_or_initialize_by(name: domain_name) + wd.body = body + wd.save! + end + + def update_public_whois(domain_name, body) + wd = Whois::PrivateDomain.find_or_initialize_by(name: domain_name) wd.body = body wd.save! end diff --git a/app/models/contact.rb b/app/models/contact.rb index 42bb2e78b..46156d7b9 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -9,6 +9,7 @@ class Contact < ActiveRecord::Base has_many :domain_contacts has_many :domains, through: :domain_contacts + has_many :statuses, class_name: 'ContactStatus' # TODO: remove the x_by belongs_to :created_by, class_name: 'EppUser', foreign_key: :created_by_id diff --git a/app/models/contact_status.rb b/app/models/contact_status.rb new file mode 100644 index 000000000..71b435b78 --- /dev/null +++ b/app/models/contact_status.rb @@ -0,0 +1,78 @@ +class ContactStatus < ActiveRecord::Base + include EppErrors + + belongs_to :contact + + CLIENT_DELETE_PROHIBITED = 'clientDeleteProhibited' + SERVER_DELETE_PROHIBITED = 'serverDeleteProhibited' + CLIENT_HOLD = 'clientHold' + SERVER_HOLD = 'serverHold' + CLIENT_RENEW_PROHIBITED = 'clientRenewProhibited' + SERVER_RENEW_PROHIBITED = 'serverRenewProhibited' + CLIENT_TRANSFER_PROHIBITED = 'clientTransferProhibited' + SERVER_TRANSFER_PROHIBITED = 'serverTransferProhibited' + CLIENT_UPDATE_PROHIBITED = 'clientUpdateProhibited' + SERVER_UPDATE_PROHIBITED = 'serverUpdateProhibited' + INACTIVE = 'inactive' + OK = 'ok' + PENDING_CREATE = 'pendingCreate' + PENDING_DELETE = 'pendingDelete' + PENDING_RENEW = 'pendingRenew' + PENDING_TRANSFER = 'pendingTransfer' + PENDING_UPDATE = 'pendingUpdate' + + SERVER_MANUAL_INZONE = 'serverManualInzone' + SERVER_REGISTRANT_CHANGE_PROHIBITED = 'serverRegistrantChangeProhibited' + SERVER_ADMIN_CHANGE_PROHIBITED = 'serverAdminChangeProhibited' + SERVER_TECH_CHANGE_PROHIBITED = 'serverTechChangeProhibited' + FORCE_DELETE = 'forceDelete' + DELETE_CANDIDATE = 'deleteCandidate' + EXPIRED = 'expired' + + STATUSES = [ + CLIENT_DELETE_PROHIBITED, SERVER_DELETE_PROHIBITED, CLIENT_HOLD, SERVER_HOLD, + CLIENT_RENEW_PROHIBITED, SERVER_RENEW_PROHIBITED, CLIENT_TRANSFER_PROHIBITED, + SERVER_TRANSFER_PROHIBITED, CLIENT_UPDATE_PROHIBITED, SERVER_UPDATE_PROHIBITED, + INACTIVE, OK, PENDING_CREATE, PENDING_DELETE, PENDING_RENEW, PENDING_TRANSFER, + PENDING_UPDATE, SERVER_MANUAL_INZONE, SERVER_REGISTRANT_CHANGE_PROHIBITED, + SERVER_ADMIN_CHANGE_PROHIBITED, SERVER_TECH_CHANGE_PROHIBITED, FORCE_DELETE, + DELETE_CANDIDATE, EXPIRED + ] + + CLIENT_STATUSES = [ + CLIENT_DELETE_PROHIBITED, CLIENT_HOLD, CLIENT_RENEW_PROHIBITED, CLIENT_TRANSFER_PROHIBITED, + CLIENT_UPDATE_PROHIBITED + ] + + SERVER_STATUSES = [ + SERVER_DELETE_PROHIBITED, SERVER_HOLD, SERVER_RENEW_PROHIBITED, SERVER_TRANSFER_PROHIBITED, + SERVER_UPDATE_PROHIBITED, SERVER_MANUAL_INZONE, SERVER_REGISTRANT_CHANGE_PROHIBITED, + SERVER_ADMIN_CHANGE_PROHIBITED, SERVER_TECH_CHANGE_PROHIBITED + ] + + def epp_code_map + { + '2302' => [ # Object exists + [:value, :taken, { value: { obj: 'status', val: value } }] + ] + } + end + + def server_status? + SERVER_STATUSES.include?(value) + end + + def client_status? + CLIENT_STATUSES.include?(value) + end + + class << self + def statuses_for_client + CLIENT_STATUSES.map { |x| x.sub('client', '') } + end + + def statuses_for_admin + SERVER_STATUSES.map { |x| x.sub('server', '') } + end + end +end diff --git a/app/models/whois.rb b/app/models/whois.rb new file mode 100644 index 000000000..1364d5099 --- /dev/null +++ b/app/models/whois.rb @@ -0,0 +1,2 @@ +module Whois +end diff --git a/app/models/whois/private_domain.rb b/app/models/whois/private_domain.rb new file mode 100644 index 000000000..9f3a79178 --- /dev/null +++ b/app/models/whois/private_domain.rb @@ -0,0 +1,5 @@ +module Whois + class PrivateDomain < PrivateServer + self.table_name = 'domains' + end +end diff --git a/app/models/whois/private_server.rb b/app/models/whois/private_server.rb new file mode 100644 index 000000000..51f52d0fa --- /dev/null +++ b/app/models/whois/private_server.rb @@ -0,0 +1,6 @@ +module Whois + class PrivateServer < ActiveRecord::Base + self.abstract_class = true + establish_connection :"#{Rails.env}_private_whois" + end +end diff --git a/app/models/whois/public_domain.rb b/app/models/whois/public_domain.rb new file mode 100644 index 000000000..3c019eb02 --- /dev/null +++ b/app/models/whois/public_domain.rb @@ -0,0 +1,5 @@ +module Whois + class PublicDomain < PublicServer + self.table_name = 'domains' + end +end diff --git a/app/models/whois/public_server.rb b/app/models/whois/public_server.rb new file mode 100644 index 000000000..4a5d6eb4e --- /dev/null +++ b/app/models/whois/public_server.rb @@ -0,0 +1,6 @@ +module Whois + class PublicServer < ActiveRecord::Base + self.abstract_class = true + establish_connection :"#{Rails.env}_public_whois" + end +end diff --git a/app/models/whois_domain.rb b/app/models/whois_domain.rb deleted file mode 100644 index 3a38465bd..000000000 --- a/app/models/whois_domain.rb +++ /dev/null @@ -1,3 +0,0 @@ -class WhoisDomain < WhoisServer - self.table_name = 'domains' -end diff --git a/app/models/whois_server.rb b/app/models/whois_server.rb deleted file mode 100644 index 5892f7891..000000000 --- a/app/models/whois_server.rb +++ /dev/null @@ -1,4 +0,0 @@ -class WhoisServer < ActiveRecord::Base - self.abstract_class = true - establish_connection :"#{Rails.env}_whois" -end diff --git a/app/views/epp/contacts/info.xml.builder b/app/views/epp/contacts/info.xml.builder index 8af796009..e12615c79 100644 --- a/app/views/epp/contacts/info.xml.builder +++ b/app/views/epp/contacts/info.xml.builder @@ -16,12 +16,17 @@ xml.epp_head do xml.tag!('contact:crDate', @contact.created_at) xml.tag!('contact:upID', @contact.up_id) if @contact.up_id xml.tag!('contact:upDate', @contact.updated_at) unless @contact.updated_at == @contact.created_at + xml.tag!('contact:ident', @contact.ident, type: @contact.ident_type) xml.tag!('contact:trDate', '123') if false if @owner xml.tag!('contact:authInfo') do xml.tag!('contact:pw', @contact.auth_info) # Doc says we have to return this but is it necessary? end end + # statuses + @contact.statuses.each do |cs| + xml.tag!('contact:status', s: cs.value) + end xml << render('/epp/contacts/disclosure_policy') end end diff --git a/config/database-example.yml b/config/database-example.yml index f19b20c42..7c28d927e 100644 --- a/config/database-example.yml +++ b/config/database-example.yml @@ -18,8 +18,18 @@ production: <<: *default database: registry_production -development_whois: - database: whois_development +development_private_whois: + <<: *default + database: whois_private -test_whois: - database: test_whois +development_private_whois: + <<: *default + database: whois_public + +production_public_whois: + <<: *default + database: production_whois_public + +production_private_whois: + <<: *default + database: production_whois_private diff --git a/db/migrate/20141216075056_create_contact_statuses.rb b/db/migrate/20141216075056_create_contact_statuses.rb new file mode 100644 index 000000000..0e56f9106 --- /dev/null +++ b/db/migrate/20141216075056_create_contact_statuses.rb @@ -0,0 +1,10 @@ +class CreateContactStatuses < ActiveRecord::Migration + def change + create_table :contact_statuses do |t| + t.string :value + t.string :description + t.belongs_to :contact + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index ae14d430a..e00caf67b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -59,6 +59,14 @@ ActiveRecord::Schema.define(version: 20141218154829) do t.boolean "address" end + create_table "contact_statuses", force: true do |t| + t.string "value" + t.string "description" + t.integer "contact_id" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "contact_versions", force: true do |t| t.string "item_type", null: false t.integer "item_id", null: false diff --git a/doc/ssl.md b/doc/ssl.md new file mode 100644 index 000000000..4c16ff4d7 --- /dev/null +++ b/doc/ssl.md @@ -0,0 +1,61 @@ +Setting up client-side certificate authentication +------------------------------------------------- + +This is written and tested on apache2. +Requires openSSL, tested on OpenSSL version 1.0.1f 6 Jan 2014. +First, setup openssl for use in being a certificate authority. For that you have to edit openssl.conf ( on debian based systems should be located at /etc/ssl/ ) +There are a lot of options there but some basics for example. Your policy_match will probably look different in production, also the default days (of key validity) should probably be not 10 years for production. + +``` +default_ca = CA_development + +[ CA_development ] +dir = /etc/ssl/private +database = $dir/index.txt +serial = $dir/serial +private_key = $dir/ca.key.pem +certificate = $dir/ca.crt +default_days = 3650 +default_md = md5 +new_certs_dir = $dir +policy = policy_match + +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = match +commonName = supplied +emailAddress = optional +``` +Following commands should be run in /etc/ssl/ unless you choose another location for your keys. +Now you need cert authority key which can be generated by +` openssl genrsa -out private/ca.key ` +Now generate a new certificate request with `openssl req -new -key private/ca.key -out private/ca.csr` +And sign it `openssl x509 -req -days 3650 -in private/ca.csr -signkey private/ca.key -out private/ca.crt` +Setup the first serial number for our keys, should be a 4 digit hex string `echo FAD0 > private/serial` +Create key database `touch private/index.txt` +And finally, create a cert revocation list for removing user certs `openssl ca -gencrl -out /etc/ssl/private/ca.crl -crldays 7` + +Now, we need a certificate for our webserver. Don't bother putting a password on it since you need it to start apache. If there's a good workaround for this, please let us know. +Let's generate the apache key, `openssl genrsa -out private/apache.key` +Create a certificate request for it ` openssl req -new -key apache.key -out apache.csr` and sign it `openssl ca -in private/apache.csr -cert private/ca.crt -keyfile private/ca.key -out private/apache.crt` + + +Now we are ready to setup apache to use our keys for authentication. A sample apache2 conf +``` +SSLEngine on +SSlOptions +StrictRequire +SSLCertificateFile /etc/ssl/private/apache.crt +SSLCertificateKeyFile /etc/ssl/private/apache.key +SSLCACertificateFile /etc/ssl/private/ca.crt +SSLVerifyClient require +``` +*replace the repeating lines from previous apache conf with lines from this one* + +There could be some more mojo needed to check if Certificates are expired and etc but I haven't really tested it out yet. + +Now let's create an example user certificate. +Start off with the key `openssl genrsa -des3 -out $base/users/$1/$1.key 1024` . Now a certificate signing request for that key `openssl req -new -key $base/users/$1/$1.key -out $base/users/$1/$1.csr` and finally let's sign it `openssl ca -in $base/users/$1/$1.csr -cert $base/ca.crt -keyfile $base/ca.key -out $base/users/$1/$1.crt` and we should be done. + +In real life (!development), user should generate their own key and cert request. diff --git a/doc/whois.rm b/doc/whois.rm index f04702f6a..883b61a0a 100644 --- a/doc/whois.rm +++ b/doc/whois.rm @@ -6,18 +6,20 @@ that disables whois (effects should fully be loaded after restarting the app) What whois_enabled=false does is that the whois callback does not get included. Delayed jobs migration and everything else will still be installed tho. -If you wish to use the whois module, tho, you need a whois database looking something like this +Basic whois logic - we save whois data to 2 separate databases - whois private and public. +Those two databases are streamed to appropriate whois servers. -```ruby - create_table :domains do |t| - t.string :name - t.text :body - t.timestamps - end -``` +This is still WIP and few whois details need to be sorted out. -NB! Migration with rake tasks is available in whois app repo. +Setting the databases up +__________________________________ +Make sure username and password in DATABASES at lib/tasks/whois.rb are correct ( match the ones +in config/database.yml whois_public and whois_private attributes ). We don't load them from +config/database.yml at the moment. So you might have to change them manually. +Finally set the whois app to connect to your whois_public/whois_private database and you're good to go. + +Then run `rake whois:create` to create and migrate the databases. For development, delayed_job queue can be handled by rake task `rake jobs:work` or if you wish to be fancy you can use a daemon with `bin/delayed_job start` `` diff --git a/lib/tasks/whois.rake b/lib/tasks/whois.rake new file mode 100644 index 000000000..ff18c39cf --- /dev/null +++ b/lib/tasks/whois.rake @@ -0,0 +1,53 @@ +DATABASES = [ + { database: 'whois_public', host: 'localhost', adapter: 'postgresql', encoding: 'unicode', + pool: '5', username: 'whois', password: 'test', port: '5432' }, + { database: 'whois_private', host: 'localhost', adapter: 'postgresql', encoding: 'unicode', + pool: '5', username: 'whois', password: 'test', port: '5432' } +] + +namespace :whois do + task :load_config do + require 'active_record' + require 'pg' + end + + desc 'Create whois databases' + task create: [:load_config] do + DATABASES.each do |conf| + create_database(conf) + migrate + end + end + + task 'Migrate whois databases' + task migrate: [:load_config] do + DATABASES.each do |conf| + ActiveRecord::Base.establish_connection(conf) + migrate + end + end + + def create_database(conf) + ActiveRecord::Base.establish_connection(conf.merge(database: 'postgres')) + ActiveRecord::Base.connection.create_database(conf[:database]) + ActiveRecord::Base.establish_connection(conf) + end + + def migrate + CreateWhoisBase.up + end +end + +class CreateWhoisBase < ActiveRecord::Migration + def self.up + create_table :domains do |t| + t.string :name + t.text :body + t.timestamps + end + end + + def self.down + drob_table :domains + end +end diff --git a/spec/epp/contact_spec.rb b/spec/epp/contact_spec.rb index 827eaa3a5..b9e0ae2cf 100644 --- a/spec/epp/contact_spec.rb +++ b/spec/epp/contact_spec.rb @@ -153,9 +153,7 @@ describe 'EPP Contact', epp: true do expect(response[:results][0][:msg]).to eq('Required parameter missing: add, rem or chg') expect(response[:results][1][:result_code]).to eq('2003') expect(response[:results][1][:msg]).to eq('Required parameter missing: id') - expect(response[:results][2][:result_code]).to eq('2003') - expect(response[:results][2][:msg]).to eq('Required parameter missing: pw') - expect(response[:results].count).to eq 3 + expect(response[:results].count).to eq 2 end it 'fails with wrong authentication info' do @@ -163,8 +161,8 @@ describe 'EPP Contact', epp: true do response = epp_request(update_contact_xml({ id: { value: 'sh8013' } }), :xml, :elkdata) - expect(response[:msg]).to eq('Authentication error') - expect(response[:result_code]).to eq('2200') + expect(response[:msg]).to eq('Authorization error') + expect(response[:result_code]).to eq('2201') end it 'is succesful' do