nemid 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +70 -0
- data/LICENSE.txt +21 -0
- data/README.md +174 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/nemid.rb +12 -0
- data/lib/nemid/authentication.rb +7 -0
- data/lib/nemid/authentication/params.rb +52 -0
- data/lib/nemid/authentication/response.rb +93 -0
- data/lib/nemid/crypto.rb +53 -0
- data/lib/nemid/errors.rb +17 -0
- data/lib/nemid/errors/app.rb +49 -0
- data/lib/nemid/errors/auth.rb +180 -0
- data/lib/nemid/errors/can.rb +97 -0
- data/lib/nemid/errors/capp.rb +105 -0
- data/lib/nemid/errors/lib.rb +19 -0
- data/lib/nemid/errors/lock.rb +48 -0
- data/lib/nemid/errors/oces.rb +85 -0
- data/lib/nemid/errors/srv.rb +72 -0
- data/lib/nemid/errors/validation.rb +31 -0
- data/lib/nemid/ocsp.rb +81 -0
- data/lib/nemid/ocsp/errors.rb +29 -0
- data/lib/nemid/pid_cpr.rb +68 -0
- data/lib/nemid/version.rb +3 -0
- data/lib/nemid/xmldsig.rb +35 -0
- data/lib/nemid/xmldsig/document.rb +75 -0
- data/nemid.gemspec +32 -0
- metadata +139 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
module NemID
|
2
|
+
module Authentication
|
3
|
+
class Response
|
4
|
+
PID_REGEX = /\APID:([0-9-]+)\Z/.freeze
|
5
|
+
RID_REGEX = /\ARID:([0-9-]+)\Z/.freeze
|
6
|
+
|
7
|
+
def initialize(string)
|
8
|
+
if string.match?(/\A[A-Za-z0-9+\/\r\n]+={0,2}\z/)
|
9
|
+
decoded_string = Base64.decode64(string)
|
10
|
+
if decoded_string.start_with? '<?xml'
|
11
|
+
@doc = NemID::XMLDSig::Document.new(decoded_string)
|
12
|
+
else
|
13
|
+
raise error(decoded_string)
|
14
|
+
end
|
15
|
+
elsif string.start_with? '<?xml'
|
16
|
+
@doc = NemID::XMLDSig::Document.new(string)
|
17
|
+
else
|
18
|
+
raise NemID::Errors::ResponseError
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def extract_pid
|
23
|
+
if has_pid?
|
24
|
+
serial_number.match(PID_REGEX)[1]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def extract_rid
|
29
|
+
if has_rid?
|
30
|
+
serial_number.match(RID_REGEX)[1]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def extract_pid_or_rid
|
35
|
+
serial_number
|
36
|
+
end
|
37
|
+
|
38
|
+
def has_pid?
|
39
|
+
serial_number.match?(PID_REGEX)
|
40
|
+
end
|
41
|
+
|
42
|
+
def has_rid?
|
43
|
+
serial_number.match?(RID_REGEX)
|
44
|
+
end
|
45
|
+
|
46
|
+
def user_certificate_expired?
|
47
|
+
@doc.user_certificate_expired?
|
48
|
+
end
|
49
|
+
|
50
|
+
def user_certificate_revoked?
|
51
|
+
@doc.user_certificate_revoked?
|
52
|
+
end
|
53
|
+
|
54
|
+
def valid_certificate_chain?
|
55
|
+
@doc.validate_certificate_chain
|
56
|
+
end
|
57
|
+
|
58
|
+
def validate_response
|
59
|
+
raise NemID::Errors::InvalidSignature if not valid_signature?
|
60
|
+
raise NemID::Errors::InvalidCertificateChain if not valid_certificate_chain?
|
61
|
+
raise NemID::Errors::UserCertificateExpired if user_certificate_expired?
|
62
|
+
raise NemID::Errors::UserCertificateRevoked if user_certificate_revoked?
|
63
|
+
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
def valid_signature?
|
68
|
+
@doc.validate_signature
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
def class_exists?(class_name)
|
73
|
+
klass = Module.const_get(class_name)
|
74
|
+
return klass.is_a?(Class)
|
75
|
+
rescue NameError
|
76
|
+
return false
|
77
|
+
end
|
78
|
+
|
79
|
+
def error(str)
|
80
|
+
klass = "NemID::Errors::#{str}Error"
|
81
|
+
if class_exists?(klass)
|
82
|
+
return eval(klass)
|
83
|
+
else
|
84
|
+
return NemID::Errors::ResponseError
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def serial_number
|
89
|
+
@serial_number ||= @doc.extract_pid_or_rid
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/nemid/crypto.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module NemID
|
4
|
+
class Crypto
|
5
|
+
|
6
|
+
def initialize(certificate, pass)
|
7
|
+
certificate = read_file(certificate)
|
8
|
+
@pkcs12 = read_pkcs12(certificate, pass)
|
9
|
+
@rsa_instance = rsa_keypair(@pkcs12, pass)
|
10
|
+
end
|
11
|
+
|
12
|
+
def base64_encoded_der_representation
|
13
|
+
Base64.strict_encode64(@pkcs12.certificate.to_der)
|
14
|
+
end
|
15
|
+
|
16
|
+
def base64_encoded_digest_representation(data)
|
17
|
+
Base64.strict_encode64(digest(data))
|
18
|
+
end
|
19
|
+
|
20
|
+
def base64_encoded_rsa_signature(data)
|
21
|
+
Base64.strict_encode64(sign(data))
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_certificate
|
25
|
+
@pkcs12.certificate
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_key
|
29
|
+
@pkcs12.key
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def digest(data)
|
34
|
+
OpenSSL::Digest::SHA256.new.digest(data)
|
35
|
+
end
|
36
|
+
|
37
|
+
def read_file(certificate)
|
38
|
+
File.read(certificate)
|
39
|
+
end
|
40
|
+
|
41
|
+
def read_pkcs12(certificate, pass)
|
42
|
+
OpenSSL::PKCS12::new(certificate, pass)
|
43
|
+
end
|
44
|
+
|
45
|
+
def rsa_keypair(pkcs12, passphrase)
|
46
|
+
OpenSSL::PKey::RSA.new(pkcs12.key, passphrase)
|
47
|
+
end
|
48
|
+
|
49
|
+
def sign(data)
|
50
|
+
@rsa_instance.sign(OpenSSL::Digest::SHA256.new, data)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/nemid/errors.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module NemID
|
2
|
+
module Errors
|
3
|
+
class ResponseError < StandardError
|
4
|
+
attr_reader :da, :en
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require_relative "errors/app"
|
10
|
+
require_relative "errors/auth"
|
11
|
+
require_relative "errors/can"
|
12
|
+
require_relative "errors/capp"
|
13
|
+
require_relative "errors/lib"
|
14
|
+
require_relative "errors/lock"
|
15
|
+
require_relative "errors/oces"
|
16
|
+
require_relative "errors/srv"
|
17
|
+
require_relative "errors/validation"
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module NemID
|
2
|
+
module Errors
|
3
|
+
class APPError < ResponseError
|
4
|
+
def initialize(msg='', sp='support')
|
5
|
+
@da = "Der er opstået en teknisk fejl. Forsøg igen. Kontakt #{sp}, hvis " \
|
6
|
+
"problemet fortsætter."
|
7
|
+
@en = "A technical error has occurred. Please try again. Contact #{sp} if " \
|
8
|
+
"the problem persists"
|
9
|
+
super(msg)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class APP001Error < APPError
|
14
|
+
def initialize(msg="Fix the integration issue. [JS Client: See tool at " \
|
15
|
+
"/developers/validateparameters.jsp]")
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class APP002Error < APPError
|
21
|
+
def initialize(msg="Correct the sign text. [JS Client: See tool at " \
|
22
|
+
"/developers/signtextviewer.jsp]")
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class APP003Error < APPError; end
|
28
|
+
|
29
|
+
class APP004Error < APPError; end
|
30
|
+
|
31
|
+
class APP007Error < APPError
|
32
|
+
def initialize(msg="Fix the integration issue. [JS Client: See tool at " \
|
33
|
+
"/developers/validateparameters.jsp]")
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class APP008Error < APPError
|
39
|
+
def initialize(msg="Fix the integration issue. [JS Client: See tool at " \
|
40
|
+
"/developers/validateparameters.jsp]")
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class APP009Error < APPError; end
|
46
|
+
|
47
|
+
class APP010Error < APPError; end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
module NemID
|
2
|
+
module Errors
|
3
|
+
class AUTHError < ResponseError
|
4
|
+
DA_SUPPORT_URL = '[https://www.nemid.nu/dk-da/support/faa_hjaelp_til_nemid/kontakt/]'
|
5
|
+
EN_SUPPORT_URL = '[https://www.nemid.nu/dk-en/support/contact/]'
|
6
|
+
|
7
|
+
def initialize(msg="The service provider is recommended to refer the user " \
|
8
|
+
"to NemID support.")
|
9
|
+
super(msg)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class AUTH001Error < AUTHError
|
14
|
+
def initialize
|
15
|
+
@da = "Dit NemID er spærret. Kontakt NemID support " \
|
16
|
+
"#{DA_SUPPORT_URL}."
|
17
|
+
@en = "Your NemID is blocked. Please contact NemID support. " \
|
18
|
+
"#{EN_SUPPORT_URL}"
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class AUTH003Error < AUTHError
|
24
|
+
def initialize(msg='')
|
25
|
+
@da = "Login er gennemført korrekt, men du har ikke en bankaftale. " \
|
26
|
+
"Kontakt din bank for at høre nærmere."
|
27
|
+
@en = "Login succeeded but you have no bank agreement. Please contact " \
|
28
|
+
"your bank for more details"
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class AUTH004Error < AUTHError
|
34
|
+
def initialize
|
35
|
+
@da = "Dit NemID er midlertidigt låst i 8 timer og du kan ikke logge på " \
|
36
|
+
"før spærringen er ophævet"
|
37
|
+
@en = "Your NemID is temporarily locked and you cannot log on until the " \
|
38
|
+
"8 hour time lock has been lifted."
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class AUTH005Error < AUTHError
|
44
|
+
def initialize
|
45
|
+
@da = "Dit NemID er spærret. Kontakt NemID support " \
|
46
|
+
"#{DA_SUPPORT_URL}."
|
47
|
+
@en = "Your NemID has been blocked. Please contact NemID support. " \
|
48
|
+
"#{EN_SUPPORT_URL}"
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class AUTH006Error < AUTHError
|
54
|
+
def initialize
|
55
|
+
@da = "Du har brugt alle nøgler på nøglekortet. " \
|
56
|
+
"Du kan bestille et nyt på siden Mistet nøglekort. " \
|
57
|
+
"[https://service.nemid.nu/dk-da/nemid/noeglekort/mistet_noeglekort/]"
|
58
|
+
@en = "You have used all the codes on your code card. " \
|
59
|
+
"You can order a new code card on the Lost code card page. " \
|
60
|
+
"[https://service.nemid.nu/dk-en/nemid/code_cards/lost_code_card/]"
|
61
|
+
super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class AUTH007Error < AUTHError
|
66
|
+
def initialize
|
67
|
+
@da = "Din NemID-adgangskode er spærret på grund af for mange fejlede forsøg. " \
|
68
|
+
"Kontakt NemID support #{DA_SUPPORT_URL}."
|
69
|
+
@en = "Your NemID password is blocked due to too many failed password attempts. " \
|
70
|
+
"Please contact NemID support. #{EN_SUPPORT_URL}"
|
71
|
+
super
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class AUTH008Error < AUTHError
|
76
|
+
def initialize
|
77
|
+
@da = "Dit NemID er ikke aktivt og du skal bestille en ny midlertidig " \
|
78
|
+
"adgangskode til aktivering hos support. Ring til NemID support " \
|
79
|
+
"#{DA_SUPPORT_URL}."
|
80
|
+
@en = "Your NemID is not active and you need support to issue a new " \
|
81
|
+
"activation password to activate. Please call NemID support. " \
|
82
|
+
"#{EN_SUPPORT_URL}"
|
83
|
+
super
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class AUTH009Error < AUTHError
|
88
|
+
def initialize(msg="The service provider is advised to perform a reload of " \
|
89
|
+
"the client so the user can re-authenticate and try again. Also, if the " \
|
90
|
+
"problem persists, refer to Support")
|
91
|
+
@da = "Der er opstået en teknisk fejl. Forsøg igen."
|
92
|
+
@en = "A technical error has occurred. Please try again."
|
93
|
+
super(msg)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class AUTH010Error < AUTHError
|
98
|
+
def initialize(msg="The service provider is advised to perform a reload of " \
|
99
|
+
"the client so the user can re-authenticate and try again.")
|
100
|
+
@da = "Der er opstået en teknisk fejl. Tjek at kun ét NemID login er " \
|
101
|
+
"aktivt og forsøg igen."
|
102
|
+
@en = "A technical error has occurred. Please try again, and ensure that " \
|
103
|
+
"only one NemID login is running."
|
104
|
+
super(msg)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class AUTH011Error < AUTHError
|
109
|
+
def initialize(msg='')
|
110
|
+
@da = "NemID på mobil understøtter ikke brug af midlertidig adgangskode. " \
|
111
|
+
"Kontakt NemID support for atfå en ny kode udstedt. " \
|
112
|
+
"#{DA_SUPPORT_URL}" \
|
113
|
+
"Prøv derefter igen."
|
114
|
+
@en = "NemID login on mobile does not support authentication using a " \
|
115
|
+
"temporary password. Please contact NemID support to have a new temporary " \
|
116
|
+
"password issued. #{EN_SUPPORT_URL} " \
|
117
|
+
"Thereafter, please try again."
|
118
|
+
super
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class AUTH012Error < AUTHError
|
123
|
+
def initialize(msg="The service provider is advised to perform a reload of " \
|
124
|
+
"the client so the user can re-authenticate and try again")
|
125
|
+
@da = "Der er opstået en teknisk fejl. Forsøg igen."
|
126
|
+
@en = "A technical error has occurred. Please try again."
|
127
|
+
super
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class AUTH013Error < AUTHError
|
132
|
+
def initialize(msg="The service provider is advised to perform a reload of " \
|
133
|
+
"the client so the user can re-authenticate using regular 2-factor authentication.")
|
134
|
+
@da = "Der er opstået en teknisk fejl. Forsøg igen."
|
135
|
+
@en = "A technical error has occurred. Please try again."
|
136
|
+
super
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class AUTH017Error < AUTHError
|
141
|
+
def initialize(msg="Consider displaying information suggesting how to avoid " \
|
142
|
+
"known environmental problems.")
|
143
|
+
@da = "En teknisk fejl i browseren gør at NemID ikke kan starte. Forsøg " \
|
144
|
+
"at slå unødige plug-ins fra, eller prøv igen med en anden browser."
|
145
|
+
@en = "Something in the browser environment has caused NemID to stop " \
|
146
|
+
"working. This could be because of an incompatible plug-in, too " \
|
147
|
+
"restrictive privacy settings or other environment factors. " \
|
148
|
+
"Please try deactivating plugins, resetting your browser settings " \
|
149
|
+
"or try using a different browser."
|
150
|
+
super
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class AUTH018Error < AUTHError
|
155
|
+
def initialize(msg='')
|
156
|
+
@da = "Din nøgleapp er spærret. For at bruge den igen skal den genaktiveres."
|
157
|
+
@en = "Your code app is revoked. To use it again please reactivate it."
|
158
|
+
super
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class AUTH019Error < AUTHError
|
163
|
+
def initialize(msg='')
|
164
|
+
@da = "Det er ikke muligt at logge ind med nøglekort, brug anden løsning " \
|
165
|
+
"nøgleapp eller nøgleviser."
|
166
|
+
@en = "It is not possible to login with a code card, please use a code app " \
|
167
|
+
"or code token."
|
168
|
+
super
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
class AUTH020Error < AUTHError
|
173
|
+
def initialize(msg='Retry with 2-factor login for user.')
|
174
|
+
@da = "Kunne ikke logge ind med 1-faktor, prøv med 2-faktor login."
|
175
|
+
@en = "Unable to login with 1-factor, please try with 2-factor login"
|
176
|
+
super
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module NemID
|
2
|
+
module Errors
|
3
|
+
class CANError < ResponseError
|
4
|
+
def initialize(msg='')
|
5
|
+
super(msg)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class CAN001Error < CANError
|
10
|
+
def initialize(msg="Redirect the user to a sensible place, taking into " \
|
11
|
+
"account where the user is in the flow.")
|
12
|
+
@da = "Du har afbrudt aktiveringen efter du har brugt den midlertidige adgangskode. " \
|
13
|
+
"Din midlertidige adgangskode er ikke længere gyldig, og du skal bestille " \
|
14
|
+
"en ny midlertidig adgangskode, før du kan aktivere og bruge NemID. " \
|
15
|
+
"Den nye, midlertidige adgangskode kan du bestille via NemID-support " \
|
16
|
+
"[https://www.nemid.nu/dk-da/support/faa_hjaelp_til_nemid/kontakt/]."
|
17
|
+
@en = "You have cancelled the activation of NemID after submitting the activation password. " \
|
18
|
+
"Your activation password is no longer valid, and you must request a new " \
|
19
|
+
"activation password before you can activate and use NemID. " \
|
20
|
+
"You can order your new activation password via NemID Support " \
|
21
|
+
"[https://www.nemid.nu/dk-en/support/contact/]"
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class CAN002Error < CANError
|
27
|
+
def initialize(msg="The service provider must send the user to a sensible " \
|
28
|
+
"place, taking into account where the user is in the flow.")
|
29
|
+
@da = "Du har afbrudt login."
|
30
|
+
@en = "You have cancelled login."
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class CAN003Error < CANError
|
36
|
+
def initialize
|
37
|
+
@da = "Forbindelsen til applikationen er timet ud eller er blevet " \
|
38
|
+
"afbrudt af en anden app. Forsøg igen."
|
39
|
+
@en = "The connection to the application has timed out or has been " \
|
40
|
+
"interrupted by another app. Please try again."
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class CAN004Error < CANError
|
46
|
+
def initialize
|
47
|
+
@da = "Sessionen er afbrudt. Forsøg igen."
|
48
|
+
@en = "The session is cancelled. Please try again."
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class CAN005Error < CANError
|
54
|
+
def initialize
|
55
|
+
@da = "Det tog for lang tid, før du godkendte den anmodning, du havde " \
|
56
|
+
"sendt til din nøgleapp"
|
57
|
+
@en = "You took too long to authenticate the request you had sent to " \
|
58
|
+
"your code app."
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class CAN006Error < CANError
|
64
|
+
def initialize
|
65
|
+
@da = "Du kan højst have ##MAXACTIVENMAS## aktive nøgleapps ad gangen. " \
|
66
|
+
"Hvis du vil aktivere en ny nøgleapp, skal du først spærre en af dine " \
|
67
|
+
"nuværende på nemid.nu eller ved at kontakte NemID support eller din bank"
|
68
|
+
@en = "The maximum number of active code apps you can have at any time " \
|
69
|
+
"is ##MAXACTIVENMAS##. If you wish to activate another code app, you must " \
|
70
|
+
"first block one of your current code apps at nemid.nu or by contacting NemID " \
|
71
|
+
"Support or your bank."
|
72
|
+
super
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class CAN007Error < CANError
|
77
|
+
def initialize
|
78
|
+
@da = "Du har afvist din anmodning om godkendelse i din nøgleapp. Hvis " \
|
79
|
+
"det var en fejl, kan du sende en ny anmodning, når du har afsluttet ved " \
|
80
|
+
"at klikke på \"Ok\"."
|
81
|
+
@en = "You rejected your code app authentication request. If this was " \
|
82
|
+
"incorrect, you can submit a new request after clicking \"OK\" to finish."
|
83
|
+
super
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class CAN008Error < CANError
|
88
|
+
def initialize(msg="User should be notified and could have a choice to restart transaction")
|
89
|
+
@da = "Du har sendt en ny anmodning til godkendelse i din nøgleapp, som " \
|
90
|
+
"overskriver en eksisterende."
|
91
|
+
@en = "You sent a new authentication request to your code app overwriting " \
|
92
|
+
"an existing one."
|
93
|
+
super
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|