-
Notifications
You must be signed in to change notification settings - Fork 25
/
census_authorization_handler.rb
160 lines (129 loc) · 4.42 KB
/
census_authorization_handler.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# frozen_string_literal: true
# Checks the authorization against the census for Barcelona.
require "digest/md5"
# This class performs a check against the official census database in order
# to verify the citizen's residence.
class CensusAuthorizationHandler < Decidim::AuthorizationHandler
include ActionView::Helpers::SanitizeHelper
AVAILABLE_GENDERS = %w(man woman non_binary).freeze
attribute :document_number, String
attribute :document_type, Symbol
attribute :postal_code, String
attribute :scope_id, Integer
attribute :date_of_birth, Date
attribute :gender, String
validates :date_of_birth, presence: true
validates :document_type, inclusion: { in: [:dni, :nie, :passport] }, presence: true
validates :document_number, format: { with: /\A[A-z0-9]*\z/ }, presence: true
validates :postal_code, presence: true, format: { with: /\A[0-9]*\z/ }
validates :scope_id, presence: true
validate :document_type_valid
validate :age_limit
validate :valid_postal_code
# If you need to store any of the defined attributes in the authorization you
# can do it here.
#
# You must return a Hash that will be serialized to the authorization when
# it's created, and available though authorization.metadata
def metadata
super.merge(
date_of_birth: date_of_birth&.strftime("%Y-%m-%d"),
postal_code:,
scope: scope_name,
scope_id: scope&.id,
scope_code: scope&.code,
extras: {
gender:
}
)
end
def scope
@scope ||= Decidim::Scope.find_by(id: scope_id)
end
def census_document_types
[:dni, :nie, :passport].map do |type|
[I18n.t(type, scope: "decidim.census_authorization_handler.document_types"), type]
end
end
def unique_id
Digest::MD5.hexdigest(
"#{document_number}-#{Rails.application.secrets.secret_key_base}"
)
end
# When there's a postal code, sanitize it allowing only numbers.
def postal_code
return unless super
super.gsub(/[^0-9]/, "")
end
# When there's a document number, sanitize it allowing only numbers.
def document_number
return unless super
super.gsub(/[^A-Za-z0-9]/, "").upcase
end
private
def scope_name
return nil unless scope
scope.name["ca"]
end
def sanitized_document_type
case document_type&.to_sym
when :dni
"01"
when :passport
"02"
when :nie
"03"
end
end
def sanitized_date_of_birth
@sanitized_date_of_birth ||= date_of_birth&.strftime("%Y%m%d")
end
def document_type_valid
return nil if response.blank?
errors.add(:document_number, I18n.t("census_authorization_handler.invalid_document")) unless response.xpath("//codiRetorn").text == "01"
end
def response
return nil if document_number.blank? ||
document_type.blank? ||
postal_code.blank? ||
date_of_birth.blank?
return @response if defined?(@response)
response ||= Faraday.post Rails.application.secrets.census_url do |request|
request.headers["Content-Type"] = "text/xml"
request.body = request_body
end
@response ||= Nokogiri::XML(response.body).remove_namespaces!
end
def request_body
@request_body ||= <<~SOAP
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://es.bcn.mci.ws/cr/schemas">
<soapenv:Header/>
<soapenv:Body>
<sch:GetPersonaLocalitzaAdrecaRequest>
<sch:usuari>PAM</sch:usuari>
<sch:Dades>
<sch:tipDocument>#{sanitized_document_type}</sch:tipDocument>
<sch:docId>#{sanitize document_number}</sch:docId>
<sch:codiPostal>#{sanitize postal_code}</sch:codiPostal>
<sch:dataNaixConst>#{sanitized_date_of_birth}</sch:dataNaixConst>
</sch:Dades>
</sch:GetPersonaLocalitzaAdrecaRequest>
</soapenv:Body>
</soapenv:Envelope>
SOAP
end
def age_limit
errors.add(:date_of_birth, I18n.t("census_authorization_handler.age_under", min_age: 14)) unless age && age >= 14
end
def age
return nil if date_of_birth.blank?
now = Date.current
extra_year = (now.month > date_of_birth.month) || (
now.month == date_of_birth.month && now.day >= date_of_birth.day
)
now.year - date_of_birth.year - (extra_year ? 0 : 1)
end
def valid_postal_code
errors.add(:postal_code, :not_in_district) unless PostalCodeDistricts.valid?(postal_code, scope.code)
end
end