Merge pull request #85 from internetee/105846070-merge-with-105842700-arireg-for-registrant-port

105846070 merge with 105842700 arireg for registrant port
This commit is contained in:
Timo Võhmar 2016-02-29 17:48:17 +02:00
commit b60dc1d833
45 changed files with 2197 additions and 30 deletions

View file

@ -16,7 +16,7 @@ class Ability
@user.roles.each { |role| send(role) } if @user.roles
when 'ApiUser'
@user.roles.each { |role| send(role) } if @user.roles
when 'RegistrantUser'
when 'RegistrantUser'
static_registrant
end
@ -117,9 +117,11 @@ class Ability
end
def static_registrant
customer_service
can :manage, :registrant_domains
can :manage, :registrant_whois
can :manage, Depp::Domain
can :manage, Domain
end
def user

View file

@ -0,0 +1,74 @@
=begin
The portal for registrants has to offer an overview of the domains the user is related to directly or through an organisation.
Personal relation is defined by matching the personal identification code associated with a domain and the one acquired on
authentication using electronic ID. Association through a business organisation requires a query to business registry.
* when user logs in the personal identification code is sent to business registry (using XML service)
* business registry returns the list of business registry codes the user is a board member of
* the list is cached for two days (configurable)
* during that time no new queries are made to business registry for that personal identification code
and the cached organisation code listing is used
* user sees the listing of domains that are associated with him/her directly or through registered organisation
* UI of the portal displays the list of organisation codes and names used to fetch additional domains for the user
(currently by clicking on a username in top right corner of the screen).
Also time and date of the query to the business registry is displayed with the list of organisations.
* if the query to the business registry fails for any reason the list of
domains associated directly with the user is still displayed with an error message indicating a problem
with receiving current list business entities. Outdated list of organisations cannot be used.
=end
class BusinessRegistryCache < ActiveRecord::Base
# 1. load domains by business
# 2. load domains by person
def associated_domains
domains = []
contact_ids = Contact.where(ident_type: 'org', ident: associated_businesses, ident_country_code: 'EE').pluck(:id)
contact_ids += Contact.where(ident_type: 'priv', ident: ident, ident_country_code: ident_country_code).pluck(:id)
unless contact_ids.blank?
domains = DomainContact.distinct.where(contact_id: contact_ids).pluck(:domain_id)
end
Domain.includes(:registrar, :registrant).where(id: domains)
end
class << self
def fetch_associated_domains(ident_code, ident_cc)
fetch_by_ident_and_cc(ident_code, ident_cc).associated_domains
end
def fetch_by_ident_and_cc(ident_code, ident_cc)
cache = BusinessRegistryCache.where(ident: ident_code, ident_country_code: ident_cc).first_or_initialize
msg_start = "[Ariregister] #{ident_cc}-#{ident_code}:"
# fetch new data if cache is expired
if cache.retrieved_on && cache.retrieved_on > (Time.zone.now - Setting.days_to_keep_business_registry_cache.days)
Rails.logger.info("#{msg_start} Info loaded from cache")
return cache
end
cache.attributes = business_registry.associated_businesses(ident_code, ident_cc)
Rails.logger.info("#{msg_start} Info loaded from server")
cache.save
cache
end
def business_registry
Soap::Arireg.new
end
def purge
STDOUT << "#{Time.zone.now.utc} - Starting Purge of old BusinessRegistry data from cache\n" unless Rails.env.test?
purged = 0
BusinessRegistryCache.where('retrieved_on < ?',
Time.zone.now < Setting.days_to_keep_business_registry_cache.days).each do |br|
br.destroy and purged += 1
end
STDOUT << "#{Time.zone.now.utc} - Finished purging #{purged} old BusinessRegistry cache items\n" unless Rails.env.test?
end
end
end

View file

@ -754,5 +754,19 @@ class Domain < ActiveRecord::Base
DomainMailer.send(action, DomainMailModel.new(self).send(action)).deliver
end
def self.to_csv
CSV.generate do |csv|
csv << column_names
all.each do |domain|
csv << domain.attributes.values_at(*column_names)
end
end
end
def self.pdf(html)
kit = PDFKit.new(html)
kit.to_pdf
end
end
# rubocop: enable Metrics/ClassLength

View file

@ -1,4 +1,5 @@
class RegistrantUser < User
ACCEPTED_ISSUER = 'AS Sertifitseerimiskeskus'
attr_accessor :idc_data
def ability
@ -6,6 +7,19 @@ class RegistrantUser < User
end
delegate :can?, :cannot?, to: :ability
def ident
registrant_ident.to_s.split("-").last
end
def domains
ident_cc, ident = registrant_ident.to_s.split '-'
Domain.includes(:registrar, :registrant).where(contacts: {
ident_type: 'priv',
ident: ident, #identity_code,
ident_country_code: ident_cc #country_code
})
end
def to_s
username
end
@ -13,11 +27,9 @@ class RegistrantUser < User
class << self
def find_or_create_by_idc_data(idc_data, issuer_organization)
return false if idc_data.blank?
return false if issuer_organization != 'AS Sertifitseerimiskeskus'
return false if issuer_organization != ACCEPTED_ISSUER
idc_data.force_encoding('UTF-8')
logger.error(idc_data)
logger.error(idc_data.encoding)
identity_code = idc_data.scan(/serialNumber=(\d+)/).flatten.first
country = idc_data.scan(/^\/C=(.{2})/).flatten.first
first_name = idc_data.scan(%r{/GN=(.+)/serialNumber}).flatten.first

219
app/models/soap/arireg.rb Normal file
View file

@ -0,0 +1,219 @@
# coding: utf-8
require 'savon'
=begin
Estonian Business registry provides information about registered companies via xml (SOAP over HTTPS).
Note:
The SSL endpoint certificate is self signed.
Documentation:
http://www.rik.ee/et/e-ariregister/xml-teenus
Specifications are in Eng and Est
User contract required
Testing:
https://demo-ariregxml.rik.ee:447/testariport/?wsdl
http://demo-ariregxml.rik.ee:81
https://demo-ariregxml.rik.ee:447
Live service:
https://ariregxml.rik.ee/ariport/?wsdl
https://ariregxml.rik.ee/
Implements Soap::Arireg # associated_businesses
8. arireg.paringesindus_v4
Rights of representation of all persons related to the company (newer)
http://www2.rik.ee/schemas/xtee/arireg/live/paringesindus_v4.xsd
expects personal id code, to fetch list of registered business id codes
returning {ident: person, ident_country_code: ... associated_businesses: [...id_codes...]}
=end
# do some SSL set up?
# ssl_version
# ssl_verify_mode
# ssl_cert_key_file
# ssl_cert_key
# ssl_cert_key_password
# ssl_cert_file
# ssl_cert
# ssl_ca_cert_file
# ssl_ca_cert
module Soap
class Arireg
class NotAvailableError < StandardError
attr_accessor :json
def initialize(params)
params[:message] = "#{I18n.t(:business_registry_service_not_available)}" unless params.key? :message
@json = params
super(params)
end
end
class << self
attr_accessor :wsdl, :host, :username, :password
end
def initialize
if self.class.username.nil?
if Rails.application.secrets.key?(:arireg)
arireg = Rails.application.secrets[:arireg].with_indifferent_access
self.class.username = arireg[:username]
self.class.password = arireg[:password]
if self.class.wsdl.nil? # no override of config/environments/* ?
self.class.wsdl = arireg[:wsdl]
self.class.host = arireg[:host]
end
else
self.class.username = ENV['arireg_username']
self.class.password = ENV['arireg_password']
end
end
if self.class.wsdl.nil?
self.class.wsdl = ENV['arireg_wsdl']
self.class.host = ENV['arireg_host']
end
# note Savon has error if https w/non-standard port,
# use non-standard force to pre-set endpoint
@client = Savon.client(wsdl: self.class.wsdl,
host: self.class.host,
endpoint: "#{self.class.host}/cgi-bin/consumer_proxy")
@session = nil
end
# retrieve business id codes for business that a person has a legal role
def associated_businesses(ident, ident_cc = 'EST')
begin
msg = {
'fyysilise_isiku_kood' => ident,
'fyysilise_isiku_koodi_riik' => country_code_3(ident_cc)
}
Rails.logger.info "[Ariregister] Request sent with data: #{msg.inspect}"
response = @client.call :paringesindus_v4, message: body(msg)
content = extract response, :paringesindus_v4_response
Rails.logger.info "[Ariregister] Got response with data: #{content.inspect}"
if content.present? && content[:ettevotjad].key?(:item)
business_ident = items(content, :ettevotjad).map{|item| item[:ariregistri_kood]}
else
business_ident = []
end
{
ident: ident,
ident_country_code: ident_cc,
# ident_type: 'priv',
retrieved_on: Time.now,
associated_businesses: business_ident
}
rescue Savon::SOAPFault => fault
Rails.logger.error "[Ariregister] #{fault} Äriregister arireg #{self.class.username} at #{self.class.host }"
raise NotAvailableError.new(exception: fault)
rescue HTTPI::SSLError => ssl_error
Rails.logger.error "[Ariregister] #{ssl_error} at #{self.class.host}"
raise NotAvailableError.new(exception: ssl_error)
rescue SocketError => sock
Rails.logger.error "[Ariregister] #{sock}"
raise NotAvailableError.new(exception: sock)
end
end
def debug
@client.globals.log_level :debug
@client.globals.log true
@client.globals.pretty_print_xml true
@debug = true
@client
end
private
# add required elements to request
def body(args)
if @session.nil?
args['ariregister_kasutajanimi'] = self.class.username
args['ariregister_parool'] = self.class.password
else
args['ariregister_sessioon'] = @session
end
{keha: args}
end
# TLA --- three letter acronym required not two letter acronym, transform
def country_code_3(code)
if code.length == 2
code = CC2X3[code]
raise NotAvailableError.new(message: 'Unrecognized Country') if code.nil?
end
code
end
def extract(response, element)
# response envelope body has again header/body under element; header is user and password returned
response.hash[:envelope][:body][element][:keha]
end
def items(content, parent)
items = content[parent][:item]
items.is_a?(Array) ? items : [items]
end
CC2X3 = {"AF"=>"AFG", "AX"=>"ALA", "AL"=>"ALB", "DZ"=>"DZA", "AS"=>"ASM",
"AD"=>"AND", "AO"=>"AGO", "AI"=>"AIA", "AQ"=>"ATA", "AG"=>"ATG",
"AR"=>"ARG", "AM"=>"ARM", "AW"=>"ABW", "AU"=>"AUS", "AT"=>"AUT",
"AZ"=>"AZE", "BS"=>"BHS", "BH"=>"BHR", "BD"=>"BGD", "BB"=>"BRB",
"BY"=>"BLR", "BE"=>"BEL", "BZ"=>"BLZ", "BJ"=>"BEN", "BM"=>"BMU",
"BT"=>"BTN", "BO"=>"BOL", "BQ"=>"BES", "BA"=>"BIH", "BW"=>"BWA",
"BV"=>"BVT", "BR"=>"BRA", "IO"=>"IOT", "BN"=>"BRN", "BG"=>"BGR",
"BF"=>"BFA", "BI"=>"BDI", "CV"=>"CPV", "KH"=>"KHM", "CM"=>"CMR",
"CA"=>"CAN", "KY"=>"CYM", "CF"=>"CAF", "TD"=>"TCD", "CL"=>"CHL",
"CN"=>"CHN", "CX"=>"CXR", "CC"=>"CCK", "CO"=>"COL", "KM"=>"COM",
"CD"=>"COD", "CG"=>"COG", "CK"=>"COK", "CR"=>"CRI", "CI"=>"CIV",
"HR"=>"HRV", "CU"=>"CUB", "CW"=>"CUW", "CY"=>"CYP", "CZ"=>"CZE",
"DK"=>"DNK", "DJ"=>"DJI", "DM"=>"DMA", "DO"=>"DOM", "EC"=>"ECU",
"EG"=>"EGY", "SV"=>"SLV", "GQ"=>"GNQ", "ER"=>"ERI", "EE"=>"EST",
"ET"=>"ETH", "FK"=>"FLK", "FO"=>"FRO", "FJ"=>"FJI", "FI"=>"FIN",
"FR"=>"FRA", "GF"=>"GUF", "PF"=>"PYF", "TF"=>"ATF", "GA"=>"GAB",
"GM"=>"GMB", "GE"=>"GEO", "DE"=>"DEU", "GH"=>"GHA", "GI"=>"GIB",
"GR"=>"GRC", "GL"=>"GRL", "GD"=>"GRD", "GP"=>"GLP", "GU"=>"GUM",
"GT"=>"GTM", "GG"=>"GGY", "GN"=>"GIN", "GW"=>"GNB", "GY"=>"GUY",
"HT"=>"HTI", "HM"=>"HMD", "VA"=>"VAT", "HN"=>"HND", "HK"=>"HKG",
"HU"=>"HUN", "IS"=>"ISL", "IN"=>"IND", "ID"=>"IDN", "IR"=>"IRN",
"IQ"=>"IRQ", "IE"=>"IRL", "IM"=>"IMN", "IL"=>"ISR", "IT"=>"ITA",
"JM"=>"JAM", "JP"=>"JPN", "JE"=>"JEY", "JO"=>"JOR", "KZ"=>"KAZ",
"KE"=>"KEN", "KI"=>"KIR", "KP"=>"PRK", "KR"=>"KOR", "KW"=>"KWT",
"KG"=>"KGZ", "LA"=>"LAO", "LV"=>"LVA", "LB"=>"LBN", "LS"=>"LSO",
"LR"=>"LBR", "LY"=>"LBY", "LI"=>"LIE", "LT"=>"LTU", "LU"=>"LUX",
"MO"=>"MAC", "MK"=>"MKD", "MG"=>"MDG", "MW"=>"MWI", "MY"=>"MYS",
"MV"=>"MDV", "ML"=>"MLI", "MT"=>"MLT", "MH"=>"MHL", "MQ"=>"MTQ",
"MR"=>"MRT", "MU"=>"MUS", "YT"=>"MYT", "MX"=>"MEX", "FM"=>"FSM",
"MD"=>"MDA", "MC"=>"MCO", "MN"=>"MNG", "ME"=>"MNE", "MS"=>"MSR",
"MA"=>"MAR", "MZ"=>"MOZ", "MM"=>"MMR", "NA"=>"NAM", "NR"=>"NRU",
"NP"=>"NPL", "NL"=>"NLD", "NC"=>"NCL", "NZ"=>"NZL", "NI"=>"NIC",
"NE"=>"NER", "NG"=>"NGA", "NU"=>"NIU", "NF"=>"NFK", "MP"=>"MNP",
"NO"=>"NOR", "OM"=>"OMN", "PK"=>"PAK", "PW"=>"PLW", "PS"=>"PSE",
"PA"=>"PAN", "PG"=>"PNG", "PY"=>"PRY", "PE"=>"PER", "PH"=>"PHL",
"PN"=>"PCN", "PL"=>"POL", "PT"=>"PRT", "PR"=>"PRI", "QA"=>"QAT",
"RE"=>"REU", "RO"=>"ROU", "RU"=>"RUS", "RW"=>"RWA", "BL"=>"BLM",
"SH"=>"SHN", "KN"=>"KNA", "LC"=>"LCA", "MF"=>"MAF", "PM"=>"SPM",
"VC"=>"VCT", "WS"=>"WSM", "SM"=>"SMR", "ST"=>"STP", "SA"=>"SAU",
"SN"=>"SEN", "RS"=>"SRB", "SC"=>"SYC", "SL"=>"SLE", "SG"=>"SGP",
"SX"=>"SXM", "SK"=>"SVK", "SI"=>"SVN", "SB"=>"SLB", "SO"=>"SOM",
"ZA"=>"ZAF", "GS"=>"SGS", "SS"=>"SSD", "ES"=>"ESP", "LK"=>"LKA",
"SD"=>"SDN", "SR"=>"SUR", "SJ"=>"SJM", "SZ"=>"SWZ", "SE"=>"SWE",
"CH"=>"CHE", "SY"=>"SYR", "TW"=>"TWN", "TJ"=>"TJK", "TZ"=>"TZA",
"TH"=>"THA", "TL"=>"TLS", "TG"=>"TGO", "TK"=>"TKL", "TO"=>"TON",
"TT"=>"TTO", "TN"=>"TUN", "TR"=>"TUR", "TM"=>"TKM", "TC"=>"TCA",
"TV"=>"TUV", "UG"=>"UGA", "UA"=>"UKR", "AE"=>"ARE", "GB"=>"GBR",
"UM"=>"UMI", "US"=>"USA", "UY"=>"URY", "UZ"=>"UZB", "VU"=>"VUT",
"VE"=>"VEN", "VN"=>"VNM", "VG"=>"VGB", "VI"=>"VIR", "WF"=>"WLF",
"EH"=>"ESH", "YE"=>"YEM", "ZM"=>"ZMB", "ZW"=>"ZWE"}
end
end