diff --git a/app/assets/stylesheets/registrar/registrar.sass b/app/assets/stylesheets/registrar/registrar.sass index 9f046237b..601abf5ec 100644 --- a/app/assets/stylesheets/registrar/registrar.sass +++ b/app/assets/stylesheets/registrar/registrar.sass @@ -32,3 +32,12 @@ h1, h2, h3, h4 .semifooter padding: 42px 0 80px 0 + + + +.payment-form + text-align: center + input[type="submit"] + cursor: pointer + top: 50% + position: absolute \ No newline at end of file diff --git a/app/controllers/admin/contacts_controller.rb b/app/controllers/admin/contacts_controller.rb index 1388db636..a1df165d2 100644 --- a/app/controllers/admin/contacts_controller.rb +++ b/app/controllers/admin/contacts_controller.rb @@ -45,7 +45,7 @@ class Admin::ContactsController < AdminController private def set_contact - @contact = Contact.includes(domains: :registrar).find(params[:id]) + @contact = Contact.find(params[:id]) end def contact_params diff --git a/app/controllers/admin/domains_controller.rb b/app/controllers/admin/domains_controller.rb index 3240db931..562505dc1 100644 --- a/app/controllers/admin/domains_controller.rb +++ b/app/controllers/admin/domains_controller.rb @@ -46,6 +46,7 @@ class Admin::DomainsController < AdminController def update dp = ignore_empty_statuses + @domain.is_admin = true if @domain.update(dp) flash[:notice] = I18n.t('domain_updated') diff --git a/app/controllers/admin/pending_deletes_controller.rb b/app/controllers/admin/pending_deletes_controller.rb index d885b3a99..4866d65c9 100644 --- a/app/controllers/admin/pending_deletes_controller.rb +++ b/app/controllers/admin/pending_deletes_controller.rb @@ -6,6 +6,7 @@ class Admin::PendingDeletesController < AdminController authorize! :update, :pending @epp_domain = Epp::Domain.find(params[:domain_id]) # only epp domain has apply pending + @epp_domain.is_admin= true if @epp_domain.apply_pending_delete! redirect_to admin_domain_path(@domain.id), notice: t(:pending_applied) else @@ -16,6 +17,7 @@ class Admin::PendingDeletesController < AdminController def destroy authorize! :destroy, :pending + @epp_domain.is_admin= true if @domain.clean_pendings! redirect_to admin_domain_path(@domain.id), notice: t(:pending_removed) else diff --git a/app/controllers/admin/reserved_domains_controller.rb b/app/controllers/admin/reserved_domains_controller.rb index 430fb9d8a..57e4b8ed3 100644 --- a/app/controllers/admin/reserved_domains_controller.rb +++ b/app/controllers/admin/reserved_domains_controller.rb @@ -2,9 +2,9 @@ class Admin::ReservedDomainsController < AdminController load_and_authorize_resource def index - rd = ReservedDomain.first_or_initialize - rd.names = nil if rd.names.blank? - @reserved_domains = rd.names.to_yaml.gsub(/---.?\n/, '').gsub(/\.\.\..?\n/, '') + names = ReservedDomain.pluck(:names).each_with_object({}){|e_h,h| h.merge!(e_h)} + names.names = nil if names.blank? + @reserved_domains = names.to_yaml.gsub(/---.?\n/, '').gsub(/\.\.\..?\n/, '') end def create @@ -20,9 +20,27 @@ class Admin::ReservedDomainsController < AdminController render :index and return end - rd = ReservedDomain.first_or_create + result = true + ReservedDomain.transaction do + # removing old ones + existing = ReservedDomain.any_of_domains(names.keys).pluck(:id) + ReservedDomain.where.not(id: existing).delete_all - if rd.update(names: names) + #updating and adding + names.each do |name, psw| + rec = ReservedDomain.by_domain(name).first + rec ||= ReservedDomain.new + rec.names = {name => psw} + + unless rec.save + result = false + raise ActiveRecord::Rollback + end + end + end + + + if result flash[:notice] = I18n.t('record_updated') redirect_to :back else diff --git a/app/controllers/epp/domains_controller.rb b/app/controllers/epp/domains_controller.rb index a5889b8bb..8426c8ccb 100644 --- a/app/controllers/epp/domains_controller.rb +++ b/app/controllers/epp/domains_controller.rb @@ -27,8 +27,13 @@ class Epp::DomainsController < EppController @domain.valid? @domain.errors.delete(:name_dirty) if @domain.errors[:puny_label].any? handle_errors(@domain) and return if @domain.errors.any? + handle_errors and return unless balance_ok?('create') # loads pricelist in this method + + if !@domain_pricelist.try(:price)#checking if pricelist is not found + @domain.add_epp_error('2306', nil, nil, 'No price list for domain') + handle_errors(@domain) and return if @domain.errors.any? + end - handle_errors and return unless balance_ok?('create') ActiveRecord::Base.transaction do if @domain.save # TODO: Maybe use validate: false here because we have already validated the domain? current_user.registrar.debit!({ @@ -51,10 +56,6 @@ class Epp::DomainsController < EppController authorize! :update, @domain, @password begin if @domain.update(params[:parsed_frame], current_user) - - @domain.attach_legal_document(Epp::Domain.parse_legal_document_from_frame(params[:parsed_frame])) - @domain.save(validate: false) - if @domain.epp_pending_update.present? render_epp_response '/epp/domains/success_pending' else @@ -75,9 +76,6 @@ class Epp::DomainsController < EppController handle_errors(@domain) and return unless @domain.can_be_deleted? - @domain.attach_legal_document(Epp::Domain.parse_legal_document_from_frame(params[:parsed_frame])) - @domain.save(validate: false) - if @domain.epp_destroy(params[:parsed_frame], current_user.id) if @domain.epp_pending_delete.present? render_epp_response '/epp/domains/success_pending' @@ -104,6 +102,12 @@ class Epp::DomainsController < EppController period = (period_element.to_i == 0) ? 1 : period_element.to_i period_unit = Epp::Domain.parse_period_unit_from_frame(params[:parsed_frame]) || 'y' + balance_ok?('renew', period, period_unit) # loading pricelist + if !@domain_pricelist.try(:price)#checking if pricelist is not found + @domain.add_epp_error('2306', nil, nil, 'No price list for domain') + handle_errors(@domain) and return if @domain.errors.any? + end + ActiveRecord::Base.transaction do success = @domain.renew( params[:parsed_frame].css('curExpDate').text, diff --git a/app/controllers/registrar/payments_controller.rb b/app/controllers/registrar/payments_controller.rb new file mode 100644 index 000000000..931dffcf2 --- /dev/null +++ b/app/controllers/registrar/payments_controller.rb @@ -0,0 +1,46 @@ +class Registrar::PaymentsController < RegistrarController + protect_from_forgery except: :back + + skip_authorization_check # actually anyone can pay, no problems at all + skip_before_action :authenticate_user!, :check_ip, only: [:back] + before_action :check_bank + + # to handle existing model we should + # get invoice_id and then get number + # build BankTransaction without connection with right reference number + # do not connect transaction and invoice + def pay + invoice = Invoice.find(params[:invoice_id]) + @bank_link = BankLink::Request.new(params[:bank], invoice, self) + @bank_link.make_transaction + end + + + # connect invoice and transaction + # both back and IPN + def back + @bank_link = BankLink::Response.new(params[:bank], params) + if @bank_link.valid? && @bank_link.ok? + @bank_link.complete_payment + + if @bank_link.invoice.binded? + flash[:notice] = t(:pending_applied) + else + flash[:alert] = t(:something_wrong) + end + else + flash[:alert] = t(:something_wrong) + end + redirect_to registrar_invoice_path(@bank_link.invoice) + end + + private + def banks + ENV['payments_banks'].split(",").map(&:strip) + end + + def check_bank + raise StandardError.new("Not Implemented bank") unless banks.include?(params[:bank]) + end + +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7a75ff78f..098c10d9b 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -23,8 +23,13 @@ module ApplicationHelper case ident_type when 'birthday' "#{ident} [#{ident_type}]" - else - "#{ident} [#{ident_country_code} #{ident_type}]" + else + if ident.present? + "#{ident} [#{ident_country_code} #{ident_type}]" + else + "[No access]" + end + end end @@ -55,4 +60,16 @@ module ApplicationHelper username ||= '' username.split(':').last.to_s.strip end + + def custom_sort_link(title, param_name) + sort = params.fetch(:sort, {})[param_name] + order = {"asc"=>"desc", "desc"=>"asc"}[sort] || "asc" + + + if params.fetch(:sort, {}).include?(param_name) + title += (sort == "asc" ? " ▲" : " ▼") + end + + link_to(title, url_for(sort: {param_name => order}), class: "sort_link #{order}") + end end diff --git a/app/models/bank_link.rb b/app/models/bank_link.rb new file mode 100644 index 000000000..31be3e222 --- /dev/null +++ b/app/models/bank_link.rb @@ -0,0 +1,157 @@ +class BankLink + module Base + def prepend_size(value) + value = (value || "").to_s.strip + string = "" + string << sprintf("%03i", value.size) + string << value + end + end + + class Request + include Base + include ActionView::Helpers::NumberHelper + + # need controller here in order to handle random ports and domains + # I don't want to do it but has to + attr_accessor :type, :invoice, :controller + def initialize(type, invoice, controller) + @type, @invoice, @controller = type, invoice, controller + end + + def url + ENV["payments_#{type}_url"] + end + + def fields + @fields ||= (hash = {} + hash["VK_SERVICE"] = "1012" + hash["VK_VERSION"] = "008" + hash["VK_SND_ID"] = ENV["payments_#{type}_seller_account"] + hash["VK_STAMP"] = invoice.number + hash["VK_AMOUNT"] = number_with_precision(invoice.sum_cache, :precision => 2, :separator => ".") + hash["VK_CURR"] = invoice.currency + hash["VK_REF"] = "" + hash["VK_MSG"] = "Order nr. #{invoice.number}" + hash["VK_RETURN"] = controller.registrar_return_payment_with_url(type) + hash["VK_CANCEL"] = controller.registrar_return_payment_with_url(type) + hash["VK_DATETIME"] = Time.now.strftime("%Y-%m-%dT%H:%M:%S%z") + hash["VK_MAC"] = calc_mac(hash) + hash["VK_ENCODING"] = "UTF-8" + hash["VK_LANG"] = "ENG" + hash) + end + + def calc_mac(fields) + pars = %w(VK_SERVICE VK_VERSION VK_SND_ID VK_STAMP VK_AMOUNT VK_CURR VK_REF + VK_MSG VK_RETURN VK_CANCEL VK_DATETIME).freeze + data = pars.map{|e| prepend_size(fields[e]) }.join + + sign(data) + end + + def make_transaction + transaction = BankTransaction.where(description: fields["VK_MSG"]).first_or_initialize( + reference_no: invoice.reference_no, + currency: invoice.currency, + iban: invoice.seller_iban + ) + + transaction.save! + end + + private + def sign(data) + private_key = OpenSSL::PKey::RSA.new(File.read(ENV["payments_#{type}_seller_private"])) + + signed_data = private_key.sign(OpenSSL::Digest::SHA1.new, data) + signed_data = Base64.encode64(signed_data).gsub(/\n|\r/, '') + signed_data + end + end + + + + + class Response + include Base + include ActionView::Helpers::NumberHelper + + attr_accessor :type, :params, :invoice + def initialize(type, params) + @type, @params = type, params + + @invoice = Invoice.find_by(number: params["VK_STAMP"]) if params["VK_STAMP"].present? + end + + def valid? + !!validate + end + + def ok? + params["VK_SERVICE"] == "1111" + end + + def complete_payment + if valid? + transaction = BankTransaction.find_by(description: params["VK_MSG"]) + transaction.sum = BigDecimal.new(params["VK_AMOUNT"].to_s) + transaction.bank_reference = params['VK_T_NO'] + transaction.buyer_bank_code = params["VK_SND_ID"] + transaction.buyer_iban = params["VK_SND_ACC"] + transaction.buyer_name = params["VK_SND_NAME"] + transaction.paid_at = Time.parse(params["VK_T_DATETIME"]) + + transaction.autobind_invoice + end + end + + + + def validate + case params["VK_SERVICE"] + when "1111" + validate_success && validate_amount && validate_currency + when "1911" + validate_cancel + else + false + end + end + + def validate_success + pars = %w(VK_SERVICE VK_VERSION VK_SND_ID VK_REC_ID VK_STAMP VK_T_NO VK_AMOUNT VK_CURR + VK_REC_ACC VK_REC_NAME VK_SND_ACC VK_SND_NAME VK_REF VK_MSG VK_T_DATETIME).freeze + + @validate_success ||= ( + data = pars.map{|e| prepend_size(params[e]) }.join + verify_mac(data, params["VK_MAC"]) + ) + end + + def validate_cancel + pars = %w(VK_SERVICE VK_VERSION VK_SND_ID VK_REC_ID VK_STAMP VK_REF VK_MSG).freeze + @validate_cancel ||= ( + data = pars.map{|e| prepend_size(params[e]) }.join + verify_mac(data, params["VK_MAC"]) + ) + end + + def validate_amount + source = number_with_precision(BigDecimal.new(params["VK_AMOUNT"].to_s), precision: 2, separator: ".") + target = number_with_precision(invoice.sum_cache, precision: 2, separator: ".") + + source == target + end + + def validate_currency + invoice.currency == params["VK_CURR"] + end + + + def verify_mac(data, mac) + bank_public_key = OpenSSL::X509::Certificate.new(File.read(ENV["payments_#{type}_bank_certificate"])).public_key + bank_public_key.verify(OpenSSL::Digest::SHA1.new, Base64.decode64(mac), data) + end + end +end \ No newline at end of file diff --git a/app/models/contact.rb b/app/models/contact.rb index adf375041..f332dc2c4 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -349,6 +349,48 @@ class Contact < ActiveRecord::Base "#{code} #{name}" end + + # what we can do load firstly by registrant + # if total is smaller than needed, the load more + # we also need to sort by valid_to + # todo: extract to drapper. Then we can remove Domain#roles + def all_domains(page: nil, per: nil, params: {}) + # compose filter sql + filter_sql = case params[:domain_filter] + when "Registrant".freeze + %Q{select id from domains where registrant_id=#{id}} + when AdminDomainContact.to_s, TechDomainContact.to_s + %Q{select domain_id from domain_contacts where contact_id=#{id} AND type='#{params[:domain_filter]}'} + else + %Q{select domain_id from domain_contacts where contact_id=#{id} UNION select id from domains where registrant_id=#{id}} + end + + # get sorting rules + sorts = params.fetch(:sort, {}).first || [] + sort = Domain.column_names.include?(sorts.first) ? sorts.first : "valid_to" + order = {"asc"=>"desc", "desc"=>"asc"}[sorts.second] || "desc" + + + # fetch domains + domains = Domain.where("domains.id IN (#{filter_sql})").includes(:registrar).page(page).per(per) + if sorts.first == "registrar_name".freeze + # using small rails hack to generate outer join + domains = domains.includes(:registrar).where.not(registrars: {id: nil}).order("registrars.name #{order} NULLS LAST") + else + domains = domains.order("#{sort} #{order} NULLS LAST") + end + + + + # adding roles. Need here to make faster sqls + 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 + def set_linked statuses << LINKED if statuses.detect { |s| s == LINKED }.blank? end diff --git a/app/models/depp/contact.rb b/app/models/depp/contact.rb index 0fa9f777a..b4597867c 100644 --- a/app/models/depp/contact.rb +++ b/app/models/depp/contact.rb @@ -280,7 +280,7 @@ module Depp end def country_name - Country.new(country_code) + Country.new(country_code) || 'No access' end def org? diff --git a/app/models/domain.rb b/app/models/domain.rb index c9d669b7d..9845e8ac8 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -4,6 +4,8 @@ class Domain < ActiveRecord::Base include Statuses has_paper_trail class_name: "DomainVersion", meta: { children: :children_log } + attr_accessor :roles + # TODO: whois requests ip whitelist for full info for own domains and partial info for other domains # TODO: most inputs should be trimmed before validatation, probably some global logic? @@ -12,9 +14,9 @@ class Domain < ActiveRecord::Base # TODO: should we user validates_associated :registrant here? has_many :admin_domain_contacts - accepts_nested_attributes_for :admin_domain_contacts, allow_destroy: !:admin_change_prohibited?, reject_if: :admin_change_prohibited? + accepts_nested_attributes_for :admin_domain_contacts, allow_destroy: true, reject_if: :admin_change_prohibited? has_many :tech_domain_contacts - accepts_nested_attributes_for :tech_domain_contacts, allow_destroy: !:tech_change_prohibited?, reject_if: :tech_change_prohibited? + accepts_nested_attributes_for :tech_domain_contacts, allow_destroy: true, reject_if: :tech_change_prohibited? def registrant_change_prohibited? statuses.include? DomainStatus::SERVER_REGISTRANT_CHANGE_PROHIBITED @@ -116,6 +118,11 @@ class Domain < ActiveRecord::Base validate :status_is_consistant def status_is_consistant has_error = (statuses.include?(DomainStatus::SERVER_HOLD) && statuses.include?(DomainStatus::SERVER_MANUAL_INZONE)) + unless has_error + if (statuses & [DomainStatus::PENDING_DELETE_CONFIRMATION, DomainStatus::PENDING_DELETE, DomainStatus::FORCE_DELETE]).any? + has_error = statuses.include? DomainStatus::SERVER_DELETE_PROHIBITED + end + end errors.add(:domains, I18n.t(:object_status_prohibits_operation)) if has_error end @@ -299,12 +306,14 @@ class Domain < ActiveRecord::Base c = 0 Domain.where("statuses @> '{deleteCandidate}'::varchar[]").each do |x| + Whois::Record.where('domain_id = ?', x.id).try(':destroy') x.destroy STDOUT << "#{Time.zone.now.utc} Domain.destroy_delete_candidates: by deleteCandidate ##{x.id} (#{x.name})\n" unless Rails.env.test? c += 1 end Domain.where('force_delete_at <= ?', Time.zone.now).each do |x| + Whois::Record.where('domain_id = ?', x.id).try(':destroy') x.destroy STDOUT << "#{Time.zone.now.utc} Domain.destroy_delete_candidates: by force delete time ##{x.id} (#{x.name})\n" unless Rails.env.test? c += 1 @@ -399,7 +408,7 @@ class Domain < ActiveRecord::Base def clean_pendings! preclean_pendings self.pending_json = {} - statuses.delete[DomainStatus::PENDING_DELETE_CONFIRMATION] + statuses.delete(DomainStatus::PENDING_DELETE_CONFIRMATION) statuses.delete(DomainStatus::PENDING_UPDATE) statuses.delete(DomainStatus::PENDING_DELETE) status_notes[DomainStatus::PENDING_UPDATE] = '' @@ -415,7 +424,6 @@ class Domain < ActiveRecord::Base pending_json_cache = pending_json token = registrant_verification_token asked_at = registrant_verification_asked_at - changes_cache = changes new_registrant_id = registrant.id new_registrant_email = registrant.email new_registrant_name = registrant.name @@ -429,7 +437,6 @@ class Domain < ActiveRecord::Base self.registrant_verification_token = token self.registrant_verification_asked_at = asked_at set_pending_update - pending_json['domain'] = changes_cache pending_json['new_registrant_id'] = new_registrant_id pending_json['new_registrant_email'] = new_registrant_email pending_json['new_registrant_name'] = new_registrant_name @@ -443,22 +450,17 @@ class Domain < ActiveRecord::Base # rubocop: disable Metrics/CyclomaticComplexity def registrant_update_confirmable?(token) - return true if Rails.env.development? + return false if (statuses & [DomainStatus::FORCE_DELETE, DomainStatus::DELETE_CANDIDATE]).any? return false unless pending_update? - return false if registrant_verification_token.blank? - return false if registrant_verification_asked_at.blank? - return false if token.blank? - return false if registrant_verification_token != token + return false unless registrant_verification_asked? + return false unless registrant_verification_token == token true end def registrant_delete_confirmable?(token) - return true if Rails.env.development? return false unless pending_delete? - return false if registrant_verification_token.blank? - return false if registrant_verification_asked_at.blank? - return false if token.blank? - return false if registrant_verification_token != token + return false unless registrant_verification_asked? + return false unless registrant_verification_token == token true end # rubocop: enable Metrics/CyclomaticComplexity @@ -560,8 +562,8 @@ class Domain < ActiveRecord::Base def pending_registrant return '' if pending_json.blank? - return '' if pending_json['domain']['registrant_id'].blank? - Registrant.find_by(id: pending_json['domain']['registrant_id'].last) + return '' if pending_json['new_registrant_id'].blank? + Registrant.find_by(id: pending_json['new_registrant_id'].last) end def generate_auth_info @@ -693,7 +695,7 @@ class Domain < ActiveRecord::Base end def pending_delete? - statuses.include?(DomainStatus::PENDING_DELETE) && !statuses.include?(DomainStatus::FORCE_DELETE) + (statuses & [DomainStatus::PENDING_DELETE_CONFIRMATION, DomainStatus::PENDING_DELETE]).any? end def pending_delete_confirmation? @@ -753,11 +755,11 @@ class Domain < ActiveRecord::Base def children_log log = HashWithIndifferentAccess.new - log[:admin_contacts] = admin_contacts.map(&:attributes) - log[:tech_contacts] = tech_contacts.map(&:attributes) - log[:nameservers] = nameservers.map(&:attributes) - log[:registrant] = [registrant.try(:attributes)] - log[:domain_statuses] = domain_statuses.map(&:attributes) + log[:admin_contacts] = admin_contact_ids + log[:tech_contacts] = tech_contact_ids + log[:nameservers] = nameserver_ids + log[:registrant] = [registrant_id] + log[:domain_statuses] = domain_status_ids log end diff --git a/app/models/epp/domain.rb b/app/models/epp/domain.rb index 4a446d25c..ffd525d9d 100644 --- a/app/models/epp/domain.rb +++ b/app/models/epp/domain.rb @@ -439,6 +439,10 @@ class Epp::Domain < Domain at.deep_merge!(attrs_from(frame.css('chg'), current_user, 'chg')) at.deep_merge!(attrs_from(frame.css('rem'), current_user, 'rem')) + if doc = attach_legal_document(Epp::Domain.parse_legal_document_from_frame(frame)) + frame.css("legalDocument").first.content = doc.path if doc && doc.persisted? + end + at_add = attrs_from(frame.css('add'), current_user) at[:nameservers_attributes] += at_add[:nameservers_attributes] @@ -503,7 +507,7 @@ class Epp::Domain < Domain def attach_legal_document(legal_document_data) return unless legal_document_data - legal_documents.build( + legal_documents.create( document_type: legal_document_data[:type], body: legal_document_data[:body] ) @@ -512,8 +516,12 @@ class Epp::Domain < Domain def epp_destroy(frame, user_id) return false unless valid? + if doc = attach_legal_document(Epp::Domain.parse_legal_document_from_frame(frame)) + frame.css("legalDocument").first.content = doc.path if doc && doc.persisted? + end + if Setting.request_confirmation_on_domain_deletion_enabled && - frame.css('delete').attr('verified').to_s.downcase != 'yes' + frame.css('delete').children.css('delete').attr('verified').to_s.downcase != 'yes' registrant_verification_asked!(frame.to_s, user_id) self.deliver_emails = true # turn on email delivery for epp @@ -776,12 +784,12 @@ class Epp::Domain < Domain ### ABILITIES ### - # depricated -- this is redundant TODO: try to remove + def can_be_deleted? begin errors.add(:base, :domain_status_prohibits_operation) return false - end if statuses.include?(DomainStatus::CLIENT_DELETE_PROHIBITED) + end if (statuses & [DomainStatus::CLIENT_DELETE_PROHIBITED, DomainStatus::SERVER_DELETE_PROHIBITED]).any? true end diff --git a/app/models/legacy/registrar.rb b/app/models/legacy/registrar.rb index 048529d62..a0a0c251d 100644 --- a/app/models/legacy/registrar.rb +++ b/app/models/legacy/registrar.rb @@ -3,7 +3,7 @@ module Legacy self.table_name = :registrar has_many :invoices, foreign_key: :registrarid - has_one :acl, foreign_key: :registrarid, class_name: "Legacy::RegistrarAcl" + has_many :acl, foreign_key: :registrarid, class_name: "Legacy::RegistrarAcl" def account_balance invoices.sum(:credit) diff --git a/app/models/legal_document.rb b/app/models/legal_document.rb index f1a63e976..f9d4cb4eb 100644 --- a/app/models/legal_document.rb +++ b/app/models/legal_document.rb @@ -1,16 +1,19 @@ class LegalDocument < ActiveRecord::Base - include Versions # version/legal_document_version.rb - belongs_to :documentable, polymorphic: true - if ENV['legal_document_types'].present? TYPES = ENV['legal_document_types'].split(',').map(&:strip) else - TYPES = %w(pdf bdoc ddoc zip rar gz tar 7z odt doc docx) + TYPES = %w(pdf bdoc ddoc zip rar gz tar 7z odt doc docx).freeze end attr_accessor :body - before_save :save_to_filesystem + belongs_to :documentable, polymorphic: true + + before_create :add_creator + before_save :save_to_filesystem + + + def save_to_filesystem loop do rand = SecureRandom.random_number.to_s.last(4) @@ -25,4 +28,9 @@ class LegalDocument < ActiveRecord::Base File.open(path, 'wb') { |f| f.write(Base64.decode64(body)) } unless Rails.env.test? self.path = path end + + def add_creator + self.creator_str = ::PaperTrail.whodunnit + true + end end diff --git a/app/models/pricelist.rb b/app/models/pricelist.rb index cc7b898ea..42cca0126 100644 --- a/app/models/pricelist.rb +++ b/app/models/pricelist.rb @@ -3,7 +3,7 @@ class Pricelist < ActiveRecord::Base scope :valid, lambda { where( - "valid_from <= ? AND valid_to >= ? OR valid_to IS NULL", + "valid_from <= ? AND (valid_to >= ? OR valid_to IS NULL)", Time.zone.now.end_of_day, Time.zone.now.beginning_of_day ) } diff --git a/app/models/reserved_domain.rb b/app/models/reserved_domain.rb index 6684d6541..936c744bf 100644 --- a/app/models/reserved_domain.rb +++ b/app/models/reserved_domain.rb @@ -9,7 +9,15 @@ class ReservedDomain < ActiveRecord::Base class << self def pw_for(domain_name) - select("names -> '#{domain_name}' AS pw").first.try(:pw) + by_domain(domain_name).select("names -> '#{domain_name}' AS pw").first.try(:pw) + end + + def by_domain name + where("names ? '#{name}'") + end + + def any_of_domains names + where("names ?| ARRAY['#{names.join("','")}']") end end end diff --git a/app/models/version/legal_document_version.rb b/app/models/version/legal_document_version.rb deleted file mode 100644 index d812b9720..000000000 --- a/app/models/version/legal_document_version.rb +++ /dev/null @@ -1,5 +0,0 @@ -class LegalDocumentVersion < PaperTrail::Version - include VersionSession - self.table_name = :log_legal_documents - self.sequence_name = :log_legal_documents_id_seq -end diff --git a/app/views/admin/contacts/partials/_domains.haml b/app/views/admin/contacts/partials/_domains.haml index 0c319127b..97eee3d5a 100644 --- a/app/views/admin/contacts/partials/_domains.haml +++ b/app/views/admin/contacts/partials/_domains.haml @@ -1,15 +1,30 @@ +- domains = contact.all_domains(page: params[:domain_page], per: 20, params: params) #contacts.panel.panel-default - .panel-heading= t(:domains) + .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-4'}= t(:domain_name) - %th{class: 'col-xs-4'}= t(:registrar) - %th{class: 'col-xs-4'}= t(:valid_to) + %th{class: 'col-xs-3'}=custom_sort_link t(:domain_name), :name + %th{class: 'col-xs-3'}=custom_sort_link t(:registrar), :registrar_name + %th{class: 'col-xs-3'}=custom_sort_link t(:valid_to), :valid_to + %th{class: 'col-xs-3'}= t(:roles) %tbody - - @contact.registrant_domains.each do |x| + - domains.each do |x| %tr %td= link_to(x.name, [:admin, x]) %td= link_to(x.registrar, [:admin, x.registrar]) %td= l(x.valid_to, format: :short) + %td= x.roles.join(", ") + += paginate domains, param_name: :domain_page \ No newline at end of file diff --git a/app/views/admin/contacts/show.haml b/app/views/admin/contacts/show.haml index affc55cd7..c5d317ccb 100644 --- a/app/views/admin/contacts/show.haml +++ b/app/views/admin/contacts/show.haml @@ -5,10 +5,10 @@ .row .col-md-6= render 'admin/contacts/partials/general' .col-md-6= render 'admin/contacts/partials/address' -.row - .col-md-12= render 'admin/contacts/partials/domains' .row .col-md-12= render 'admin/contacts/partials/statuses', contact: @contact +.row + .col-md-12= render 'admin/contacts/partials/domains', contact: @contact - if @contact.legal_documents.present? .row diff --git a/app/views/admin/domains/form/_pending_delete.haml b/app/views/admin/domains/form/_pending_delete.haml index 6924a2b30..2e199d0b7 100644 --- a/app/views/admin/domains/form/_pending_delete.haml +++ b/app/views/admin/domains/form/_pending_delete.haml @@ -1,8 +1,8 @@ -- if (status == DomainStatus::PENDING_DELETE && f.object.pending_json.present?) - = link_to(t(:apply_pending), admin_domain_pending_delete_path(f.object.id, f.object.id), +- if (status == DomainStatus::PENDING_DELETE || status == DomainStatus::PENDING_DELETE_CONFIRMATION) + = link_to(t(:accept_delete), admin_domain_pending_delete_path(f.object.id, f.object.id), method: :patch, data: { confirm: t(:are_you_sure) }, class: 'btn btn-danger btn-xs') - = link_to(t(:delete_pending), admin_domain_pending_delete_path(f.object.id, f.object.id), + = link_to(t(:reject_delete), admin_domain_pending_delete_path(f.object.id, f.object.id), method: :delete, data: { confirm: t(:are_you_sure) }, class: 'btn btn-danger btn-xs') diff --git a/app/views/admin/shared/form/_statuses.haml b/app/views/admin/shared/form/_statuses.haml index 697abad44..27be8d032 100644 --- a/app/views/admin/shared/form/_statuses.haml +++ b/app/views/admin/shared/form/_statuses.haml @@ -9,8 +9,9 @@ .pull-left= t(:status) .pull-right - if model == 'domain' - = render 'admin/domains/form/pending_update', f: f, status: s - = render 'admin/domains/form/pending_delete', f: f, status: s + .hide-when-new + = render 'admin/domains/form/pending_update', f: f, status: s + = render 'admin/domains/form/pending_delete', f: f, status: s = link_to(t(:delete), '#', class: 'btn btn-danger btn-xs js-destroy-status', style: delete_style) .panel-body .form-group @@ -37,6 +38,7 @@ if el.find('.js-disabled-value') el.find('.js-disabled-value').remove() el.find('.js-select').show() + el.find('.hide-when-new').hide() el.find('.js-destroy-status').show() $(document).on 'click', '.js-destroy-status', (e) -> diff --git a/app/views/epp/contacts/info.xml.builder b/app/views/epp/contacts/info.xml.builder index ea6306839..18019208a 100644 --- a/app/views/epp/contacts/info.xml.builder +++ b/app/views/epp/contacts/info.xml.builder @@ -24,6 +24,15 @@ xml.epp_head do xml.tag!('contact:pc', @contact.zip) xml.tag!('contact:cc', @contact.country_code) end + else + xml.tag!('contact:org', 'No access') + xml.tag!('contact:addr') do + xml.tag!('contact:street', 'No access') + xml.tag!('contact:city', 'No access') + xml.tag!('contact:sp', 'No access') + xml.tag!('contact:pc', 'No access') + xml.tag!('contact:cc', 'No access') + end end end @@ -31,6 +40,10 @@ xml.epp_head do xml.tag!('contact:voice', @contact.phone) xml.tag!('contact:fax', @contact.fax) if @contact.fax.present? xml.tag!('contact:email', @contact.email) + else + xml.tag!('contact:voice', 'No access') + xml.tag!('contact:fax', 'No access') + xml.tag!('contact:email', 'No access') end xml.tag!('contact:clID', @contact.registrar.try(:name)) @@ -49,6 +62,10 @@ xml.epp_head do xml.tag!('contact:authInfo') do xml.tag!('contact:pw', @contact.auth_info) end + else + xml.tag!('contact:authInfo') do + xml.tag!('contact:pw', 'No access') + end end # xml << render('/epp/contacts/disclosure_policy') end diff --git a/app/views/epp/domains/info.xml.builder b/app/views/epp/domains/info.xml.builder index c4827f29f..e7962002c 100644 --- a/app/views/epp/domains/info.xml.builder +++ b/app/views/epp/domains/info.xml.builder @@ -61,19 +61,34 @@ xml.epp_head do end xml.extension do + def tag_key_data(xml, key) + xml.tag!('secDNS:keyData') do + xml.tag!('secDNS:flags', key.flags) + xml.tag!('secDNS:protocol', key.protocol) + xml.tag!('secDNS:alg', key.alg) + xml.tag!('secDNS:pubKey', key.public_key) + end + end + + def tag_ds_data(xml, key) + xml.tag!('secDNS:dsData') do + xml.tag!('secDNS:keyTag', key.ds_key_tag) + xml.tag!('secDNS:alg', key.ds_alg) + xml.tag!('secDNS:digestType', key.ds_digest_type) + xml.tag!('secDNS:digest', key.ds_digest) + tag_key_data(xml, key) if key.public_key.present? + end + end + xml.tag!('secDNS:infData', 'xmlns:secDNS' => 'urn:ietf:params:xml:ns:secDNS-1.1') do - @domain.dnskeys.sort.each do |key| - xml.tag!('secDNS:dsData') do - xml.tag!('secDNS:keyTag', key.ds_key_tag) - xml.tag!('secDNS:alg', key.ds_alg) - xml.tag!('secDNS:digestType', key.ds_digest_type) - xml.tag!('secDNS:digest', key.ds_digest) - xml.tag!('secDNS:keyData') do - xml.tag!('secDNS:flags', key.flags) - xml.tag!('secDNS:protocol', key.protocol) - xml.tag!('secDNS:alg', key.alg) - xml.tag!('secDNS:pubKey', key.public_key) - end + # might not have ds in first key? maybe check any? k.ds_digest if requirements change (DS not accepted by EIS) + if @domain.dnskeys[0].ds_digest.blank? + @domain.dnskeys.sort.each do |key| + tag_key_data(xml, key) + end + else + @domain.dnskeys.sort.each do |key| + tag_ds_data(xml, key) end end end diff --git a/app/views/registrar/contacts/show.haml b/app/views/registrar/contacts/show.haml index 6ed6100fe..96faa4ecc 100644 --- a/app/views/registrar/contacts/show.haml +++ b/app/views/registrar/contacts/show.haml @@ -9,6 +9,8 @@ .col-md-6= render 'registrar/contacts/partials/address' .row .col-md-12= render 'registrar/contacts/partials/statuses', statuses: @contact.statuses + .row + .col-md-12= render 'admin/contacts/partials/domains', contact: Contact.find_by(code: params[:id]) - else .row diff --git a/app/views/registrar/domains/partials/_dnskeys.haml b/app/views/registrar/domains/partials/_dnskeys.haml index fc5961849..46dcd0fce 100644 --- a/app/views/registrar/domains/partials/_dnskeys.haml +++ b/app/views/registrar/domains/partials/_dnskeys.haml @@ -19,7 +19,22 @@ - if x.css('digest').text.present? %dt= t(:ds_digest) %dd= x.css('digest').text + - @data.css('keyData').each do |x| + %dl.dl-horizontal + %dt= t(:flag) + %dd= x.css('flags').text + %dt= t(:protocol) + %dd= x.css('protocol').text + + %dt= t(:algorithm) + %dd= x.css('alg').text + + %dt= t(:public_key) + %dd= x.css('pubKey').text + + - @data.css('keyData').each do |x| + %dl.dl-horizontal %dt= t(:flag) %dd= x.css('flags').text @@ -27,7 +42,7 @@ %dd= x.css('protocol').text %dt= t(:algorithm) - %dd= x.css('keyData > alg').text + %dd= x.css('alg').text %dt= t(:public_key) %dd= x.css('pubKey').text diff --git a/app/views/registrar/invoices/partials/_banklinks.haml b/app/views/registrar/invoices/partials/_banklinks.haml index efd6f8564..133a8d9c0 100644 --- a/app/views/registrar/invoices/partials/_banklinks.haml +++ b/app/views/registrar/invoices/partials/_banklinks.haml @@ -1,16 +1,6 @@ %h4= t(:pay_by_bank_link) %hr -= link_to '#' do - = image_tag('swed.png') - -= link_to '#' do - = image_tag('seb.png') - -= link_to '#' do - = image_tag('nordea.png') - -= link_to '#' do - = image_tag('lhv.png') - -= link_to '#' do - = image_tag('danske.png') +- ENV['payments_banks'].split(",").each do |meth| + - meth = meth.strip + = link_to registrar_payment_with_path(meth, invoice_id: params[:id]) do + = image_tag("#{meth}.png") diff --git a/app/views/registrar/payments/pay.html.haml b/app/views/registrar/payments/pay.html.haml new file mode 100644 index 000000000..62f5fb87a --- /dev/null +++ b/app/views/registrar/payments/pay.html.haml @@ -0,0 +1,10 @@ +.payment-form + = form_tag @bank_link.url, method: :post do + - @bank_link.fields.each do |k, v| + = hidden_field_tag k, v + = submit_tag "Mine maksma" + + +:coffeescript + $(document).ready -> + $('.payment-form form').submit() \ No newline at end of file diff --git a/config/application-example.yml b/config/application-example.yml index efc3101a7..cce71e3a4 100644 --- a/config/application-example.yml +++ b/config/application-example.yml @@ -109,6 +109,22 @@ new_relic_license_key: '42d1c2ba4ed17a9cf6297c59d80e563a3dd3c4fa' secret_key_base: 'please-change-it-you-can-generate-it-with-rake-secret' devise_secret: 'please-change-it-you-can-generate-it-with-rake-secret' +payments_banks: > + seb, + swed, + lhv +payments_seb_url: 'https://www.seb.ee/cgi-bin/dv.sh/ipank.r' +payments_seb_bank_certificate: 'eyp_pub.pem' +payments_seb_seller_private: 'kaupmees_priv.pem' +payments_seb_seller_account: 'testvpos' +payments_swed_url: 'https://www.seb.ee/cgi-bin/dv.sh/ipank.r' +payments_swed_bank_certificate: 'eyp_pub.pem' +payments_swed_seller_private: 'kaupmees_priv.pem' +payments_swed_seller_account: 'testvpos' +payments_lhv_url: 'https://www.seb.ee/cgi-bin/dv.sh/ipank.r' +payments_lhv_bank_certificate: 'eyp_pub.pem' +payments_lhv_seller_private: 'kaupmees_priv.pem' +payments_lhv_seller_account: 'testvpos' # # AUTOTEST overwrites diff --git a/config/locales/en.yml b/config/locales/en.yml index 7a562afa6..937096ad6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -656,7 +656,7 @@ en: m_id: 'M-ID' pending_removed: Pending was successfully removed. pending_applied: Pending was successfully applied. - something_wrong: Not success, something went wrong + something_wrong: Sorry, something went wrong failure: Not success not_found: Not found no_connection_to_registry: Connection issue to the registry EPP or REPP server! Please try again later. diff --git a/config/routes.rb b/config/routes.rb index d8f45180e..f5b81e8b5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -91,6 +91,11 @@ Rails.application.routes.draw do get 'load_xml' end end + + + get 'pay/return/:bank' => 'payments#back', as: 'return_payment_with' + post 'pay/return/:bank' => 'payments#back' + get 'pay/go/:bank' => 'payments#pay', as: 'payment_with' end # REGISTRANT ROUTES diff --git a/db/migrate/20151112160452_add_legacy_id_to_reserved_domains.rb b/db/migrate/20151112160452_add_legacy_id_to_reserved_domains.rb new file mode 100644 index 000000000..dd3c3a5b7 --- /dev/null +++ b/db/migrate/20151112160452_add_legacy_id_to_reserved_domains.rb @@ -0,0 +1,5 @@ +class AddLegacyIdToReservedDomains < ActiveRecord::Migration + def change + add_column :reserved_domains, :legacy_id, :integer + end +end diff --git a/db/migrate/20151117081204_drop_log_legal_documents.rb b/db/migrate/20151117081204_drop_log_legal_documents.rb new file mode 100644 index 000000000..2746a5d11 --- /dev/null +++ b/db/migrate/20151117081204_drop_log_legal_documents.rb @@ -0,0 +1,11 @@ +class DropLogLegalDocuments < ActiveRecord::Migration + def up + drop_table :log_legal_documents + remove_column :legal_documents, :updated_at + remove_column :legal_documents, :updator_str + end + + def down + # we don't want it back + end +end diff --git a/doc/debian_build_doc.md b/doc/debian_build_doc.md index 8ab99aa1a..e14992994 100644 --- a/doc/debian_build_doc.md +++ b/doc/debian_build_doc.md @@ -28,7 +28,7 @@ Please install following lib, otherwise your bundler install might not be succes ### For generating pdfs - sudo apt-get install wkhtmltopdf + sudo apt-get install libxrender1 libfontconfig1 ### RBENV install diff --git a/doc/epp/domain.md b/doc/epp/domain.md index e609b934e..79b3f8ed5 100644 --- a/doc/epp/domain.md +++ b/doc/epp/domain.md @@ -20,8 +20,6 @@ Domain name mapping protocol short version: Attribute: unit="y/m/d" Default is 1 year. 1 Contact reference to the registrant - Attribute: - "verified" # optional, allowed values 'yes', 'no' 0-n Contact reference. Admin contact is required if registrant is a juridical person. Attribute: type="admin / tech" 1 diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake index 3be30c15f..6e6e0664e 100644 --- a/lib/tasks/import.rake +++ b/lib/tasks/import.rake @@ -54,6 +54,7 @@ namespace :import do Rake::Task['import:registrars'].invoke Rake::Task['import:users'].invoke Rake::Task['import:contacts'].invoke + Rake::Task['import:reserved'].invoke Rake::Task['import:domains'].invoke Rake::Task['import:zones'].invoke end @@ -127,40 +128,69 @@ namespace :import do desc 'Import users' task users: :environment do start = Time.zone.now.to_f - puts '-----> Importing users...' + puts "-----> Importing users and IP's..." + id_users = [] users = [] ips = [] + temp = [] existing_ids = ApiUser.pluck(:legacy_id) - - count = 0 + existing_ips = WhiteIp.pluck(:ipv4) Legacy::Registrar.all.each do |x| - next if existing_ids.include?(x.id) - count += 1 + x.acl.all.each do |y| - users << ApiUser.new({ - username: x.handle.try(:strip), - password: x.acl.try(:password), - registrar_id: Registrar.find_by(legacy_id: x.try(:id)).try(:id), - legacy_id: x.try(:id) - }) + next if existing_ids.include?(y.id) - if x.acl.try(:ipaddr) - ips << WhiteIp.new({ - registrar_id: Registrar.find_by(legacy_id: x.try(:id)).try(:id), - ipv4: x.acl.try(:ipaddr) - }) + if y.try(:cert) != 'pki' + + if y.try(:cert) == 'idkaart' + id_users << ApiUser.new({ + username: y.try(:password) ? y.try(:password) : y.try(:password), + password: ('a'..'z').to_a.shuffle.first(8).join, + identity_code: y.try(:password) ? y.try(:password) : y.try(:password), + registrar_id: Registrar.find_by(legacy_id: x.try(:id)).try(:id), + roles: ['billing'], + legacy_id: y.try(:id) + }) + else + temp << ApiUser.new({ + username: x.handle.try(:strip), + password: y.try(:password) ? y.try(:password) : ('a'..'z').to_a.shuffle.first(8).join, + registrar_id: Registrar.find_by(legacy_id: x.try(:id)).try(:id), + roles: ['epp'], + legacy_id: y.try(:id) + }) + end + end + temp = temp.reverse!.uniq{|u| u.username } + end + users = temp + + x.acl.all.each do |y| + next if existing_ips.include?(y.ipaddr) + if !y.ipaddr.nil? && y.ipaddr != '' + ips << WhiteIp.new({ + registrar_id: Registrar.find_by(legacy_id: x.try(:id)).try(:id), + ipv4: y.ipaddr, + interfaces: ['api', 'registrar'] + }) + end end end + ApiUser.import id_users, validate: false ApiUser.import users, validate: false + if ips WhiteIp.import ips, validate: false end - puts "-----> Imported #{count} new users in #{(Time.zone.now.to_f - start).round(2)} seconds" + + puts "-----> Imported #{id_users.count} billing users and #{users.count} epp users" + puts "-----> Imported #{ips.count} white IP's in #{(Time.zone.now.to_f - start).round(2)} seconds" + end desc 'Import contacts' @@ -256,6 +286,42 @@ namespace :import do puts "-----> Imported #{count} new contacts in #{(Time.zone.now.to_f - start).round(2)} seconds" end + desc 'Import reserved' + task reserved: :environment do + start = Time.zone.now.to_f + puts '-----> Importing reserved domains...' + + reserved_domains = [] + count = 0 + + existing_ids = ReservedDomain.pluck(:legacy_id) + + Legacy::Domain.includes( + :object_registry, + :object + ).find_each(batch_size: 1000).with_index do |x, index| + + next if existing_ids.include?(x.id) || Registrar.find_by(legacy_id: x.object.try(:clid)).try(:name) != 'eedirect' + count += 1 + + reserved_domains << ReservedDomain.new({ + created_at: x.object_registry.try(:crdate), + updated_at: x.object.read_attribute(:update).nil? ? x.object_registry.try(:crdate) : x.object.read_attribute(:update), + creator_str: x.object_registry.try(:registrar).try(:name), + updator_str: x.object.try(:registrar).try(:name) ? x.object.try(:registrar).try(:name) : x.object_registry.try(:registrar).try(:name), + names: '"' + x.object_registry.name.try(:strip) + '"=>"' + SecureRandom.hex + '"', + legacy_id: x.id + }) + + if index % 1000 == 0 && index != 0 + ReservedDomain.import reserved_domains, {validate: false, timestamps: false} + reserved_domains = [] + end + end + ReservedDomain.import reserved_domains, {validate: false, timestamps: false} + puts "-----> Imported #{count} new reserved domains in #{(Time.zone.now.to_f - start).round(2)} seconds" + end + desc 'Import domains' task domains: :environment do start = Time.zone.now.to_f @@ -338,7 +404,7 @@ namespace :import do :domain_contact_maps, nsset: { hosts: :host_ipaddr_maps } ).find_each(batch_size: 10000).with_index do |x, index| - next if existing_domain_ids.include?(x.id) + next if existing_domain_ids.include?(x.id) || Registrar.find_by(legacy_id: x.object.try(:clid)).try(:name) == 'eedirect' count += 1 begin diff --git a/spec/epp/domain_spec.rb b/spec/epp/domain_spec.rb index 473ea3cf3..8e3997649 100644 --- a/spec/epp/domain_spec.rb +++ b/spec/epp/domain_spec.rb @@ -2444,14 +2444,9 @@ describe 'EPP Domain', epp: true do }) response = epp_plain_request(xml) - response[:results][0][:msg].should == + response[:results][0][:msg].should start_with( "Element '{https://epp.tld.ee/schema/domain-eis-1.0.xsd}status', attribute 's': "\ - "[facet 'enumeration'] The value 'invalidStatus' is not an element of the set "\ - "{'clientDeleteProhibited', 'clientHold', 'clientRenewProhibited', "\ - "'clientTransferProhibited', 'clientUpdateProhibited', 'inactive', "\ - "'ok', 'pendingCreate', 'pendingDelete', 'pendingRenew', 'pendingTransfer', "\ - "'pendingUpdate', 'serverDeleteProhibited', 'serverHold', 'serverRenewProhibited', "\ - "'serverTransferProhibited', 'serverUpdateProhibited'}." + "[facet 'enumeration'] The value 'invalidStatus' is not an element of the set ") response[:results][0][:result_code].should == '2001' end