identikey 0.7.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ddf98e9f2ab268487c380bab954e3fefebcdd729e81f74a5f1267ada748d8b8
4
- data.tar.gz: 43bb94510b8cb9e6176da71ba4742891419e4591053ded611d753538d545e895
3
+ metadata.gz: 8b12db6aa0435e14bd281a73d2ab1c50edc993ecb41cfe8933d180607bb22c36
4
+ data.tar.gz: 322412ff65c22664df05aa4e6e7c69d9da9a7851e827b97519a19b22162ab387
5
5
  SHA512:
6
- metadata.gz: 386ea4bc06bb5258ce0e3fa928880c3e3faae70198744c5c562c1ac8ab44b1961dfd82724e905e610432715c2ef105ff751055002489f41ea4d595652821a4b4
7
- data.tar.gz: 3b7081b38c22f4da4bd74360467b6b4e9951d69e376b49ca47e314a6e2185257a00357fb131a5769b94ece4b39929d741baaba7e6e1330ba334e1c85ba08d5b7
6
+ metadata.gz: d71931f68cbca537fdecbe4771d1aa4ef393d365a1654cfc1864ea6e3a43926d80a2dfee770f7d493eb211bbfeef8010e6b4f0bdad15c47c53d527abce3d2340
7
+ data.tar.gz: f42408a94e658295522ddbfcfc9ffc90668c5e8a1d30cc0beac1f3463e2ecc12d1ade779df010236dbe9e682c476270d983f6b85774188f3db2eb095010713bb
@@ -17,6 +17,13 @@ end
17
17
 
18
18
  puts "Configured Admin WSDL #{ENV.fetch('IK_WSDL_ADMIN')} against #{ENV.fetch('IK_HOST')}"
19
19
 
20
+ Identikey::Provisioning.configure do
21
+ wsdl ENV.fetch('IK_WSDL_PROVN')
22
+ endpoint ENV.fetch('IK_HOST')
23
+ end
24
+
25
+ puts "Configured Provisioning WSDL #{ENV.fetch('IK_WSDL_PROVN')} against #{ENV.fetch('IK_HOST')}"
26
+
20
27
  $ik = Identikey::Administration::Session.new(
21
28
  username: ENV.fetch('IK_USER'),
22
29
  password: ENV.fetch('IK_PASS'),
@@ -18,14 +18,13 @@ Gem::Specification.new do |spec|
18
18
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
19
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
20
  end
21
- spec.bindir = "exe"
22
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
21
  spec.require_paths = ["lib"]
24
22
 
25
23
  spec.add_dependency "savon", "~> 2.0"
24
+ spec.add_dependency "wasabi", "~> 3.5.0"
26
25
 
27
26
  spec.add_development_dependency "bundler", "~> 2.0"
28
- spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rake", ">= 12.3.3"
29
28
  spec.add_development_dependency "rspec", "~> 3.0"
30
29
  spec.add_development_dependency 'pry'
31
30
  spec.add_development_dependency 'hirb'
@@ -5,3 +5,4 @@ require 'identikey/error'
5
5
  require 'identikey/unsigned'
6
6
  require 'identikey/authentication'
7
7
  require 'identikey/administration'
8
+ require 'identikey/provisioning'
@@ -18,6 +18,10 @@ module Identikey
18
18
  :admin_session_query, :user_execute, :user_query,
19
19
  :digipass_execute, :digipass_query, :digipassappl_execute
20
20
 
21
+ ###
22
+ ## LOGON/LOGOFF/PING
23
+ ###
24
+
21
25
  def logon(username:, password:, domain:)
22
26
  resp = super(message: {
23
27
  attributeSet: {
@@ -95,6 +99,10 @@ module Identikey
95
99
  parse_response resp, :admin_session_query_response
96
100
  end
97
101
 
102
+ ###
103
+ ## USER_EXECUTE
104
+ ###
105
+
98
106
  def user_execute(session_id:, cmd:, attributes: [])
99
107
  resp = super(message: {
100
108
  sessionID: session_id,
@@ -180,6 +188,10 @@ module Identikey
180
188
  )
181
189
  end
182
190
 
191
+ ###
192
+ ## USER_QUERY
193
+ ###
194
+
183
195
  # Executes a userQuery command that searches users. By default, it doesn't
184
196
  # log anywhere. To enable logging to a specific destination, pass a logger
185
197
  # as the log: option. To log to the default destination, pass `true` as
@@ -199,6 +211,9 @@ module Identikey
199
211
  end
200
212
  end
201
213
 
214
+ ###
215
+ ## DIGIPASS_EXECUTE
216
+ ###
202
217
 
203
218
  def digipass_execute(session_id:, cmd:, attributes: [])
204
219
  resp = super(message: {
@@ -245,6 +260,9 @@ module Identikey
245
260
  )
246
261
  end
247
262
 
263
+ ###
264
+ ## DIGIPASS_QUERY
265
+ ###
248
266
 
249
267
  def digipass_query(session_id:, attributes:, query_options:)
250
268
  resp = super(message: {
@@ -258,6 +276,9 @@ module Identikey
258
276
  parse_response resp, :digipass_query_response
259
277
  end
260
278
 
279
+ ###
280
+ ## DIGIPASSAPPL_EXECUTE
281
+ ###
261
282
 
262
283
  def digipassappl_execute(session_id:, cmd:, attributes:)
263
284
  resp = super(message: {
@@ -314,6 +335,5 @@ module Identikey
314
335
  client.globals[:log] = old_log
315
336
  end
316
337
 
317
-
318
338
  end
319
339
  end
@@ -6,12 +6,21 @@ module Identikey
6
6
  attr_reader :session_id, :product, :version
7
7
  attr_reader :privileges, :location
8
8
 
9
- def initialize(username:, password:, domain: 'master')
9
+ def initialize(username:, password: nil, apikey: nil, domain: 'master')
10
+ if password.nil? && apikey.nil?
11
+ raise Identikey::UsageError, "Either a password or an API Key is required"
12
+ end
13
+
10
14
  @client = Identikey::Administration.new
11
15
 
12
16
  @username = username
13
17
  @password = password
14
18
  @domain = domain
19
+
20
+ if apikey
21
+ @service_user = true
22
+ @session_id = "Apikey #{username}:#{apikey}"
23
+ end
15
24
  end
16
25
 
17
26
  def endpoint
@@ -23,6 +32,8 @@ module Identikey
23
32
  end
24
33
 
25
34
  def logon
35
+ require_classic_user!
36
+
26
37
  stat, sess, error = @client.logon(username: @username, password: @password, domain: @domain)
27
38
 
28
39
  if stat != 'STAT_SUCCESS'
@@ -42,6 +53,7 @@ module Identikey
42
53
  end
43
54
 
44
55
  def logoff
56
+ require_classic_user!
45
57
  require_logged_on!
46
58
 
47
59
  stat, _, error = @client.logoff session_id: @session_id
@@ -60,6 +72,8 @@ module Identikey
60
72
  end
61
73
 
62
74
  def alive?(log: true)
75
+ require_classic_user!
76
+
63
77
  return false unless logged_on?
64
78
 
65
79
  stat, _ = @client.ping session_id: @session_id, log: log
@@ -108,7 +122,17 @@ module Identikey
108
122
  end
109
123
 
110
124
  def inspect
111
- "#<#{self.class.name} sid=#@session_id username=#@username domain=#@domain product=#@product>"
125
+ descr = if service_user?
126
+ "SERVICE USER"
127
+ else
128
+ "domain=#@domain product=#@product"
129
+ end
130
+
131
+ "#<#{self.class.name} sid=#@session_id username=#@username #{descr}>"
132
+ end
133
+
134
+ def service_user?
135
+ !!@service_user
112
136
  end
113
137
 
114
138
  alias sid session_id
@@ -123,6 +147,12 @@ module Identikey
123
147
  end
124
148
  end
125
149
 
150
+ def require_classic_user!
151
+ if service_user?
152
+ raise Identikey::UsageError, "This command is not supported with Service users"
153
+ end
154
+ end
155
+
126
156
  def parse_privileges(privileges)
127
157
  privileges.split(', ').inject({}) do |h, priv|
128
158
  privilege, status = priv.split(' ')
@@ -161,6 +161,16 @@ module Identikey
161
161
  true
162
162
  end
163
163
 
164
+ def set_local_auth!(value)
165
+ ensure_persisted!
166
+
167
+ self.local_auth = value
168
+
169
+ self.save!
170
+
171
+ self
172
+ end
173
+
164
174
  def unlock!
165
175
  ensure_persisted!
166
176
 
@@ -6,11 +6,13 @@ module Identikey
6
6
 
7
7
  operations :auth_user
8
8
 
9
- def auth_user(user, domain, otp)
9
+ def auth_user(user, domain, otp, client = nil)
10
+ client ||= 'Administration Program'
11
+
10
12
  resp = super(message: {
11
13
  credentialAttributeSet: {
12
14
  attributes: typed_attributes_list_from(
13
- CREDFLD_COMPONENT_TYPE: 'Administration Program',
15
+ CREDFLD_COMPONENT_TYPE: client,
14
16
  CREDFLD_USERID: user,
15
17
  CREDFLD_DOMAIN: domain,
16
18
  CREDFLD_PASSWORD_FORMAT: Unsigned(0),
@@ -22,18 +24,18 @@ module Identikey
22
24
  parse_response resp, :auth_user_response
23
25
  end
24
26
 
25
- def self.valid_otp?(user, domain, otp)
26
- status, result, _ = new.auth_user(user, domain, otp)
27
+ def self.valid_otp?(user, domain, otp, client = nil)
28
+ status, result, _ = new.auth_user(user, domain, otp, client)
27
29
  return otp_validated_ok?(status, result)
28
30
  end
29
31
 
30
- def self.validate!(user, domain, otp)
31
- status, result, error_stack = new.auth_user(user, domain, otp)
32
+ def self.validate!(user, domain, otp, client = nil)
33
+ status, result, error_stack = new.auth_user(user, domain, otp, client)
32
34
 
33
35
  if otp_validated_ok?(status, result)
34
36
  return true
35
37
  else
36
- error_message = result['CREDFLD_STATUS_MESSAGE']
38
+ error_message = result ? result['CREDFLD_STATUS_MESSAGE'] : 'no status returned'
37
39
  raise Identikey::OperationFailed.new("OTP Validation error (#{status}): #{error_message}", error_stack)
38
40
  end
39
41
  end
@@ -52,5 +54,16 @@ module Identikey
52
54
  def self.otp_validated_ok?(status, result)
53
55
  status == 'STAT_SUCCESS' && !result.key?('CREDFLD_STATUS_MESSAGE')
54
56
  end
57
+
58
+
59
+ protected
60
+ def parse_result_root(body, root_element)
61
+ root = super
62
+
63
+ # The authentication API wraps the results with another element
64
+ #
65
+ results_key = root_element.to_s.sub(/_response$/, '_results').to_sym
66
+ return root[results_key]
67
+ end
55
68
  end
56
69
  end
@@ -100,7 +100,7 @@ module Identikey
100
100
 
101
101
  # Parse the generic response types that the API returns.
102
102
  #
103
- # The returned attributes (up to now...) are always:
103
+ # The returned attributes in the Administration API are:
104
104
  #
105
105
  # - The given root element, whose name is derived from the SOAP command
106
106
  # that was invoked
@@ -111,6 +111,11 @@ module Identikey
111
111
  # or multiple ones.
112
112
  # - :error_stack, a list of error that occurred
113
113
  #
114
+ # The authentication API wraps the results element in another one
115
+ #
116
+ # The provisioning API uses a status element for the result code and
117
+ # error stack, and a result element for the results.
118
+ #
114
119
  # The returned value is a three-elements array, containing:
115
120
  #
116
121
  # [Response code, Attribute(s) list, Errors list]
@@ -129,11 +134,19 @@ module Identikey
129
134
  # formats. TODO maybe create a separate class for errors, that includes
130
135
  # the error code.
131
136
  #
132
- # TODO refactor and split in separate methods
133
- #
134
137
  def parse_response(resp, root_element)
135
- body = resp.body
138
+ root = parse_result_root(resp.body, root_element)
139
+
140
+ results = parse_result_element(root, root_element)
141
+
142
+ result_code = parse_result_code(results, root_element)
143
+ result_attributes = parse_result_attributes(results, root_element)
144
+ result_errors = parse_result_errors(results, root_element)
136
145
 
146
+ return result_code, result_attributes, result_errors
147
+ end
148
+
149
+ def parse_result_root(body, root_element)
137
150
  if body.size.zero?
138
151
  raise Identikey::ParseError, "Empty response received"
139
152
  end
@@ -144,40 +157,35 @@ module Identikey
144
157
 
145
158
  # The root results element
146
159
  #
147
- root = body[root_element]
148
-
149
- # ... that the authentication API wraps with another element
150
- #
151
- results_key = root_element.to_s.sub(/_response$/, '_results').to_sym
152
- if root.keys.size == 1 && root.key?(results_key)
153
- root = root[results_key]
154
- end
160
+ return body[root_element]
161
+ end
155
162
 
163
+ def parse_result_element(root, root_element)
156
164
  # The results element
157
165
  #
158
166
  unless root.key?(:results)
159
167
  raise Identikey::ParseError, "Results element not found below #{root_element}"
160
168
  end
161
169
 
162
- results = root[:results]
170
+ return root[:results]
171
+ end
163
172
 
164
- # Result code
165
- #
173
+ def parse_result_code(results, root_element)
166
174
  unless results.key?(:result_codes)
167
175
  raise Identikey::ParseError, "Result codes not found below #{root_element}"
168
176
  end
169
177
 
170
- result_code = results[:result_codes][:status_code_enum] || 'STAT_UNKNOWN'
178
+ results[:result_codes][:status_code_enum] || 'STAT_UNKNOWN'
179
+ end
171
180
 
172
- # Result attributes
173
- #
181
+ def parse_result_attributes(results, root_element)
174
182
  unless results.key?(:result_attribute)
175
183
  raise Identikey::ParseError, "Result attribute not found below #{root_element}"
176
184
  end
177
185
 
178
186
  results_attr = results[:result_attribute]
179
187
 
180
- result_attributes = if results_attr.key?(:attributes)
188
+ if results_attr.key?(:attributes)
181
189
  entries = [ results_attr[:attributes] ].flatten
182
190
  parse_attributes entries
183
191
 
@@ -193,16 +201,14 @@ module Identikey
193
201
  else
194
202
  nil
195
203
  end
204
+ end
196
205
 
197
- # Errors
198
- #
199
- errors = if results[:error_stack].key?(:errors)
206
+ def parse_result_errors(results, root_element)
207
+ if results[:error_stack].key?(:errors)
200
208
  parse_errors results[:error_stack][:errors]
201
209
  else
202
210
  nil
203
211
  end
204
-
205
- return result_code, result_attributes, errors
206
212
  end
207
213
 
208
214
  def parse_attributes(attributes)
@@ -0,0 +1,150 @@
1
+ require 'identikey/base'
2
+
3
+ module Identikey
4
+ # This class wraps the Provisioning API.
5
+ #
6
+ class Provisioning < Base
7
+ client wsdl: './sdk/wsdl/provisioning.wsdl'
8
+
9
+ operations :provisioning_execute, :dsapp_srp_register
10
+
11
+ ###
12
+ ## PROVISIONING_EXECUTE
13
+ ###
14
+
15
+ def provisioning_execute(cmd:, attributes:)
16
+ resp = super(message: {
17
+ cmd: cmd,
18
+ attributeSet: {
19
+ attributes: attributes
20
+ }
21
+ })
22
+
23
+ parse_response resp, :provisioning_execute_response
24
+ end
25
+
26
+ def provisioning_execute_MDL_REGISTER(component:, user:, domain:, password:)
27
+ provisioning_execute(
28
+ cmd: 'PROVISIONCMD_MDL_REGISTER',
29
+ attributes: typed_attributes_list_from(
30
+ PROVFLD_USERID: user,
31
+ PROVFLD_DOMAIN: domain,
32
+ PROVFLD_COMPONENT_TYPE: component,
33
+ PROVFLD_STATIC_PASSWORD: password,
34
+ PROVFLD_ACTIVATION_TYPE: Unsigned(0),
35
+ )
36
+ )
37
+ end
38
+
39
+ ###
40
+ ## dsappSRPRegister
41
+ ###
42
+
43
+ def dsapp_srp_register(component:, user:, domain:, password:)
44
+ resp = super(message: {
45
+ componentType: component,
46
+ user: {
47
+ userID: user,
48
+ domain: domain,
49
+ },
50
+ credential: {
51
+ staticPassword: password
52
+ }
53
+ })
54
+
55
+ parse_response resp, :dsapp_srp_register_response
56
+ end
57
+
58
+ ###
59
+ ## Wraps dsapp_srp_register and returns directly the activation
60
+ ## message through which a CRONTO image can be generated, to be
61
+ ## used for push notifications setups in combination with a MDC
62
+ ## configured on your OneSpan control panel and on Identikey.
63
+ ##
64
+ ## You may want to look into https://github.com/ifad/cronto for
65
+ ## a generator to produce PNGs to deliver to your clients.
66
+ ####
67
+ def self.cronto_code_for_srp_registration(gateway:, **kwargs)
68
+ status, result, error = new.dsapp_srp_register(**kwargs)
69
+
70
+ if status != 'STAT_SUCCESS'
71
+ raise Identikey::OperationFailed, "Error while assigning DAL: #{status} - #{[error].flatten.join('; ')}"
72
+ end
73
+
74
+ # Compose proprietary string
75
+ message = '01;01;%s;%s;%s;%s;%s' % [
76
+ result[:user][:user_id],
77
+ result[:user][:domain],
78
+ result[:registration_id],
79
+ result[:activation_password],
80
+ gateway
81
+ ]
82
+
83
+ # Encode it as hex
84
+ return message.split(//).map {|c| '%x' % c.ord}.join
85
+ end
86
+
87
+ protected
88
+ # The provisioningExecute command has the same
89
+ # design as the rest of the API, with a single
90
+ # multi-purpose `results` element that carries
91
+ # key-value results.
92
+ #
93
+ # Instead, dsappSRPRegister and other commands
94
+ # use a different design with return types and
95
+ # values predefined in the WSDL.
96
+ #
97
+ # So if we have a `results` element, this is a
98
+ # old-style response, and we delegate parsing
99
+ # to the parent class, otherwise if we detect
100
+ # the `result` and `status` elements, we parse
101
+ # in this class, basically just returning the
102
+ # values that Savon parsed for us.
103
+ #
104
+ def parse_result_element(root, root_element)
105
+ if root.key?(:results)
106
+ root[:results]
107
+ else
108
+ root
109
+ end
110
+ end
111
+
112
+ def parse_result_code(root, root_element)
113
+ if root.key?(:status)
114
+ super root[:status], root_element
115
+ else
116
+ super
117
+ end
118
+ end
119
+
120
+ # This may be an old or a new style response.
121
+ #
122
+ # New style responses may have both `result`
123
+ # and `status` elements, or only a `status`
124
+ # element.
125
+ #
126
+ # If there is no `result` but there is a
127
+ # `status`, then consider this a new style
128
+ # response and return an empty result. Else,
129
+ # pass along to the superclass to parse the
130
+ # old style response.
131
+ #
132
+ def parse_result_attributes(root, root_element)
133
+ if root.key?(:result)
134
+ root[:result]
135
+ elsif root.key?(:status)
136
+ nil
137
+ else
138
+ super
139
+ end
140
+ end
141
+
142
+ def parse_result_errors(root, root_element)
143
+ if root.key?(:status)
144
+ super root[:status], root_element
145
+ else
146
+ super
147
+ end
148
+ end
149
+ end
150
+ end
@@ -1,3 +1,3 @@
1
1
  module Identikey
2
- VERSION = "0.7.0"
2
+ VERSION = "0.9.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: identikey
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcello Barnaba
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-14 00:00:00.000000000 Z
11
+ date: 2020-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: savon
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: wasabi
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.5.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.5.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -42,16 +56,16 @@ dependencies:
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - "~>"
59
+ - - ">="
46
60
  - !ruby/object:Gem::Version
47
- version: '10.0'
61
+ version: 12.3.3
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - "~>"
66
+ - - ">="
53
67
  - !ruby/object:Gem::Version
54
- version: '10.0'
68
+ version: 12.3.3
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rspec
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +220,7 @@ files:
206
220
  - lib/identikey/authentication.rb
207
221
  - lib/identikey/base.rb
208
222
  - lib/identikey/error.rb
223
+ - lib/identikey/provisioning.rb
209
224
  - lib/identikey/unsigned.rb
210
225
  - lib/identikey/version.rb
211
226
  - log/.keep