securid 0.1 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3ba152af81ffc60b57fdebc419824bb5b98cf74e
4
+ data.tar.gz: 6c6527fa4fe5f43bebc9adf130b7394ff8c3c852
5
+ SHA512:
6
+ metadata.gz: 5d8d893afbe8ad4feb725d791d5d5b7ca072d45ab5e42c110908c21f20e1bc26abcae58d3e7afa01659c96ec72a86bc5ac1a1163887eaec287b3e080d7b908b3
7
+ data.tar.gz: 1a56068c8874462c4b78678e45eb2e27afe150b55c5403d14bb395acc12d8c31160ca704fb6046d3b349bb0b406603681f598839e9ffa3bc58b6be716c9f0f38
@@ -5,6 +5,7 @@ require 'mkmf'
5
5
  dir_config('aceclnt')
6
6
 
7
7
  have_header('acexport.h')
8
+ have_header('securid.h')
8
9
  have_library('aceclnt')
9
10
 
10
- create_makefile('securid')
11
+ create_makefile('securid/securid')
@@ -1,5 +1,17 @@
1
1
  #include "ruby.h"
2
2
  #include "acexport.h"
3
+ #include "status_display.h"
4
+ #include "securid.h"
5
+
6
+ #ifdef WIN32
7
+ #include <winsock.h>
8
+ #else
9
+ #include <sys/socket.h>
10
+ #include <netinet/in.h>
11
+ #include <arpa/inet.h>
12
+ #endif
13
+
14
+ #define CTEST(v) ((v) ? Qtrue : Qfalse)
3
15
 
4
16
  // module RSA
5
17
  static VALUE rb_mRSA;
@@ -10,133 +22,833 @@ static VALUE rb_mRSASecurID;
10
22
  // class RSA::SecurID::SecurIDError < StandardError
11
23
  static VALUE rb_eSecurIDError;
12
24
 
13
- // def RSA::SecurID.authenticate(username, passcode)
14
- static VALUE t_authenticate (VALUE self, VALUE username, VALUE passcode)
25
+ // class RSA::SecurID::Session
26
+ static VALUE rb_cRSASecurIDSession;
27
+
28
+ // ID used for session storage on RSA::SecurID::Session
29
+ ID securid_id_session;
30
+
31
+ // ID used for status storage on RSA::SecurID::Session
32
+ ID securid_id_session_status;
33
+
34
+ // ID used for test mode storage on RSA::SecurID::Session
35
+ ID securid_id_session_test_mode;
36
+
37
+ // symbol version of 'test_mode'
38
+ static VALUE rb_symTestMode;
39
+
40
+ // symbol version of 'resynchronize'
41
+ static VALUE rb_symResynchronize;
42
+
43
+ // symbol version of 'change_pin'
44
+ static VALUE rb_symChangePin;
45
+
46
+ // symbol version of 'denied'
47
+ static VALUE rb_symDenied;
48
+
49
+ // IDs used to identify RSA::SecurID::Session constants
50
+ ID securid_id_session_authenticated;
51
+ ID securid_id_session_denied;
52
+ ID securid_id_session_change_pin;
53
+ ID securid_id_session_resynchronize;
54
+
55
+ // symbols used to in the agent status hash
56
+ static VALUE rb_symConfigVersion; // 'config_version'
57
+ static VALUE rb_symMaxServers; // 'max_servers'
58
+ static VALUE rb_symMaxReplicas; // 'max_replicas'
59
+ static VALUE rb_symMaxRetries; // 'max_retries'
60
+ static VALUE rb_symBaseTimeout; // 'base_timeout'
61
+ static VALUE rb_symUseDES; // 'use_des'
62
+ static VALUE rb_symTrusted; // 'trusted'
63
+ static VALUE rb_symPort; // 'port'
64
+ static VALUE rb_symServiceName; // 'service_name'
65
+ static VALUE rb_symServiceProtocol; // 'service_protocol'
66
+ static VALUE rb_symServiceProtocolVersion; // 'service_protocol_version'
67
+ static VALUE rb_symServerReleaseNumber; // 'server_release_number'
68
+ static VALUE rb_symServers; // 'servers'
69
+ static VALUE rb_symMajor; // 'major'
70
+ static VALUE rb_symMinor; // 'minor'
71
+ static VALUE rb_symPatch; // 'patch'
72
+ static VALUE rb_symBuild; // 'build'
73
+ static VALUE rb_symAddress; // 'address'
74
+ static VALUE rb_symActiveAddress; // 'active_address'
75
+ static VALUE rb_symAliases; // 'aliases'
76
+ static VALUE rb_symDisplayStatus; // 'display_status'
77
+ static VALUE rb_symHostname; // 'hostname'
78
+ static VALUE rb_symPrimary; // 'primary'
79
+ static VALUE rb_symMaster; // 'master'
80
+ static VALUE rb_symSlave; // 'slave'
81
+ static VALUE rb_symSelectable; // 'selectable'
82
+ static VALUE rb_symEmergency; // 'emergency'
83
+ static VALUE rb_symSuspended; // 'suspended'
84
+ static VALUE rb_symAddressVerified; // 'address_verified'
85
+
86
+ static void securid_session_free(void *ptr)
15
87
  {
16
- // the authentication handle representing a single authentication
17
- // context, i.e. a multi-step authentication attempt
18
- SDI_HANDLE aceHdl;
19
-
20
- // a string containing the username
21
- SD_CHAR *userID = StringValuePtr(username);
22
-
23
- // a string containing the passcode
24
- SD_CHAR *pass = StringValuePtr(passcode);
25
-
26
- // a hint to the developer about how long to display the next
27
- // prompt string for the user
28
- SD_I32 respTimeout;
29
-
30
- // an indicator of the maximum number of bytes of data expected
31
- // in the next developer-supplied response
32
- SD_I32 nextRespLen;
33
-
34
- // a developer-supplied character array to be filled in by the
35
- // API with the string that the caller uses as the next message
36
- // displayed to the user
37
- SD_CHAR promptStr[512];
38
-
39
- // the size of the developer-supplied storage for the prompt
40
- // string
41
- SD_I32 promptStrLen;
42
-
43
- // a flag that is set by the API to indicate whether more data
44
- // is needed by the authentication context
45
- SD_BOOL moreData;
46
-
47
- // a flag that guides the developer as to whether the next
48
- // expected response is echoed to the screen
49
- SD_BOOL echoFlag;
50
-
51
- // the final authentication status
52
- SD_I32 authStatus;
53
-
54
- // initialize the authentication library. even though it will only do anything
55
- // the first time it is called, subsequent calls should still return true if the
56
- // initialization previously succeeded.
57
- if (!AceInitialize())
58
- {
59
- // the authentication library failed to initialize.
60
- rb_raise(rb_eSecurIDError, "Failed to initialize authentication library");
61
- }
62
-
63
- int retVal;
64
-
65
- // reset size of prompt string
66
- promptStrLen = sizeof(promptStr);
67
-
68
- // start our authentication attempt by first sending the username to
69
- // the authentication manager.
70
- retVal = AceStartAuth(&aceHdl, userID, strlen(userID), &moreData, &echoFlag, &respTimeout, &nextRespLen, promptStr, &promptStrLen);
71
-
72
- if (retVal != ACM_OK)
73
- {
74
- // the authentication attempt could not be started for some reason.
75
- rb_raise(rb_eSecurIDError, "Failed to start authentication attempt - Code %d", retVal);
76
- }
77
-
78
- if (!moreData)
79
- {
80
- // the authentication manager should have asked for a passcode
81
- AceCloseAuth(aceHdl);
82
- rb_raise(rb_eSecurIDError, "Authentication manager did not ask for a passcode");
83
- }
84
-
85
- // reset size of prompt string
86
- promptStrLen = sizeof(promptStr);
87
-
88
- // the authentication manager wants us to prompt the user for more data. because
89
- // this function is non-interactive, we assume the manager wants the passcode. since
90
- // we already have it, we'll pass it along without prompting the user.
91
- retVal = AceContinueAuth(aceHdl, pass, strlen(pass), &moreData, &echoFlag, &respTimeout, &nextRespLen, promptStr, &promptStrLen);
92
-
93
- if (retVal != ACM_OK)
94
- {
95
- // the authentication attempt could not be continued for some reason.
96
- AceCloseAuth(aceHdl);
97
- rb_raise(rb_eSecurIDError, "Failed to continue authentication attempt - Code %d", retVal);
98
- }
99
-
100
- if (moreData)
101
- {
102
- // either our assumption that the authentication manager wanted the passcode was
103
- // incorrect, or something else went wrong.
104
- AceCloseAuth(aceHdl);
105
- rb_raise(rb_eSecurIDError, "Authentication manager asked for more than a passcode");
106
- }
107
-
108
- // ask the authentication manager for the status of this authentication attempt.
109
- retVal = AceGetAuthenticationStatus(aceHdl, &authStatus);
110
-
111
- // finalize this authentication attempt by closing our handle.
112
- AceCloseAuth(aceHdl);
113
-
114
- if (retVal != ACE_SUCCESS)
115
- {
116
- // the authentication status could not be retrieved for some reason.
117
- rb_raise(rb_eSecurIDError, "Failed to retrieve authentication status - Code %d", retVal);
118
- }
119
-
120
- // check the status of the authentication attempt and return true or false.
121
- if (authStatus == ACM_OK)
122
- return Qtrue;
123
- else if (authStatus == ACM_ACCESS_DENIED)
124
- return Qfalse;
125
-
126
- rb_raise(rb_eSecurIDError, "Unexpected authentication status - Code %d", authStatus);
127
- }
128
-
129
- void Init_securid ()
88
+ securid_session_t *session = (securid_session_t *)ptr;
89
+ if (session->handle != SDI_HANDLE_NONE)
90
+ {
91
+ SD_Close(session->handle);
92
+ session->handle = SDI_HANDLE_NONE;
93
+ }
94
+ }
95
+
96
+ static void securid_session_mark(void *ptr)
130
97
  {
131
- // module RSA
132
- rb_mRSA = rb_define_module("RSA");
98
+
99
+ }
133
100
 
134
- // module RSA::SecurID
135
- rb_mRSASecurID = rb_define_module_under(rb_mRSA, "SecurID");
101
+ static size_t securid_session_size(void const *ptr)
102
+ {
103
+ return sizeof(securid_session_t);
104
+ }
136
105
 
137
- // class RSA::SecurID::SecurIDError < StandardError
138
- rb_eSecurIDError = rb_define_class_under(rb_mRSASecurID, "SecurIDError", rb_eStandardError);
106
+ struct rb_data_type_struct securid_session_data_type = {
107
+ "RSA::SecurID::Session Storage",
108
+ {
109
+ securid_session_mark, /* dmark */
110
+ securid_session_free, /* dfree */
111
+ securid_session_size, /* dsize */
112
+ {NULL, NULL}
113
+ },
114
+ NULL,
115
+ NULL,
116
+ RUBY_TYPED_FREE_IMMEDIATELY
117
+ };
118
+
119
+ /*
120
+ * @overload authenticate(username, password)
121
+ * @deprecated Use {Session#authenticate} instead.
122
+ * @param username [String] The username to authenticate with.
123
+ * @param passcode [String] The passcode to authenticate with. Passcodes are the user's pin
124
+ * concatenated with the current token value.
125
+ *
126
+ * Performs a basic non-interactive authentication attempt.
127
+ *
128
+ * @return [Boolean] +true+ on authentication succes, +false+ on authentication denial.
129
+ * @raise [SecureIDError] if anything beyond a basic rejection of the authentication
130
+ * happened such as a pin must be changed, the token needs resynchronization, the ACE
131
+ * server can't be found, etc.
132
+ */
133
+ static VALUE securid_authenticate(VALUE self, VALUE username, VALUE passcode)
134
+ {
135
+ // the authentication handle representing a single authentication
136
+ // context, i.e. a multi-step authentication attempt
137
+ SDI_HANDLE aceHdl;
138
+
139
+ // a string containing the username
140
+ SD_CHAR *userID = StringValuePtr(username);
141
+
142
+ // a string containing the passcode
143
+ SD_CHAR *pass = StringValuePtr(passcode);
144
+
145
+ // a hint to the developer about how long to display the next
146
+ // prompt string for the user
147
+ SD_I32 respTimeout;
148
+
149
+ // an indicator of the maximum number of bytes of data expected
150
+ // in the next developer-supplied response
151
+ SD_I32 nextRespLen;
152
+
153
+ // a developer-supplied character array to be filled in by the
154
+ // API with the string that the caller uses as the next message
155
+ // displayed to the user
156
+ SD_CHAR promptStr[512];
157
+
158
+ // the size of the developer-supplied storage for the prompt
159
+ // string
160
+ SD_I32 promptStrLen;
161
+
162
+ // a flag that is set by the API to indicate whether more data
163
+ // is needed by the authentication context
164
+ SD_BOOL moreData;
165
+
166
+ // a flag that guides the developer as to whether the next
167
+ // expected response is echoed to the screen
168
+ SD_BOOL echoFlag;
169
+
170
+ // the final authentication status
171
+ SD_I32 authStatus;
172
+
173
+ // initialize the authentication library. even though it will only do anything
174
+ // the first time it is called, subsequent calls should still return true if the
175
+ // initialization previously succeeded.
176
+ if (!AceInitialize())
177
+ {
178
+ // the authentication library failed to initialize.
179
+ rb_raise(rb_eSecurIDError, "Failed to initialize authentication library");
180
+ }
181
+
182
+ int retVal;
183
+
184
+ // reset size of prompt string
185
+ promptStrLen = sizeof(promptStr);
186
+
187
+ // start our authentication attempt by first sending the username to
188
+ // the authentication manager.
189
+ retVal = AceStartAuth(&aceHdl, userID, strlen(userID), &moreData, &echoFlag, &respTimeout, &nextRespLen, promptStr, &promptStrLen);
190
+
191
+ if (retVal != ACM_OK)
192
+ {
193
+ // the authentication attempt could not be started for some reason.
194
+ rb_raise(rb_eSecurIDError, "Failed to start authentication attempt - Code %d", retVal);
195
+ }
196
+
197
+ if (!moreData)
198
+ {
199
+ // the authentication manager should have asked for a passcode
200
+ AceCloseAuth(aceHdl);
201
+ rb_raise(rb_eSecurIDError, "Authentication manager did not ask for a passcode");
202
+ }
203
+
204
+ // reset size of prompt string
205
+ promptStrLen = sizeof(promptStr);
206
+
207
+ // the authentication manager wants us to prompt the user for more data. because
208
+ // this function is non-interactive, we assume the manager wants the passcode. since
209
+ // we already have it, we'll pass it along without prompting the user.
210
+ retVal = AceContinueAuth(aceHdl, pass, strlen(pass), &moreData, &echoFlag, &respTimeout, &nextRespLen, promptStr, &promptStrLen);
211
+
212
+ if (retVal != ACM_OK)
213
+ {
214
+ // the authentication attempt could not be continued for some reason.
215
+ AceCloseAuth(aceHdl);
216
+ rb_raise(rb_eSecurIDError, "Failed to continue authentication attempt - Code %d", retVal);
217
+ }
218
+
219
+ if (moreData)
220
+ {
221
+ // either our assumption that the authentication manager wanted the passcode was
222
+ // incorrect, or something else went wrong.
223
+ AceCloseAuth(aceHdl);
224
+ rb_raise(rb_eSecurIDError, "Authentication manager asked for more than a passcode");
225
+ }
226
+
227
+ // ask the authentication manager for the status of this authentication attempt.
228
+ retVal = AceGetAuthenticationStatus(aceHdl, &authStatus);
229
+
230
+ // finalize this authentication attempt by closing our handle.
231
+ AceCloseAuth(aceHdl);
232
+
233
+ if (retVal != ACE_SUCCESS)
234
+ {
235
+ // the authentication status could not be retrieved for some reason.
236
+ rb_raise(rb_eSecurIDError, "Failed to retrieve authentication status - Code %d", retVal);
237
+ }
238
+
239
+ // check the status of the authentication attempt and return true or false.
240
+ if (authStatus == ACM_OK)
241
+ return Qtrue;
242
+ else if (authStatus == ACM_ACCESS_DENIED)
243
+ return Qfalse;
244
+
245
+ rb_raise(rb_eSecurIDError, "Unexpected authentication status - Code %d", authStatus);
246
+ }
247
+
248
+ // Checks that the status of the session `self` matches the constant identified by `status_id`. Pass
249
+ // 0 for `status_id` to check if the the status of `self` is Qnil.
250
+ void securid_session_check_status(VALUE self, ID status_id)
251
+ {
252
+ VALUE current_status = rb_ivar_get(self, securid_id_session_status);
253
+ VALUE compared_status;
254
+ int invalid_state;
255
+
256
+ if (status_id)
257
+ {
258
+ compared_status = rb_const_get(rb_cRSASecurIDSession, status_id);
259
+ invalid_state = !rb_eql(current_status, compared_status);
260
+ } else
261
+ {
262
+ invalid_state = !NIL_P(current_status);
263
+ }
264
+
265
+ if (invalid_state)
266
+ {
267
+ rb_raise(rb_eSecurIDError, "Session is in an invalid state for the requested operation");
268
+ }
269
+ }
139
270
 
140
- // def RSA::SecurID.authenticate(username, passcode)
141
- rb_define_module_function(rb_mRSASecurID, "authenticate", t_authenticate, 2);
271
+ int securid_session_is_test_mode(VALUE self)
272
+ {
273
+ VALUE test_mode = rb_ivar_get(self, securid_id_session_test_mode);
274
+ return RTEST(test_mode);
275
+ }
276
+
277
+ int securid_session_is_test_mode_resynchronize(VALUE self)
278
+ {
279
+ VALUE test_mode = rb_ivar_get(self, securid_id_session_test_mode);
280
+ return rb_eql(test_mode, rb_symResynchronize);
281
+ }
282
+
283
+ int securid_session_is_test_mode_change_pin(VALUE self)
284
+ {
285
+ VALUE test_mode = rb_ivar_get(self, securid_id_session_test_mode);
286
+ return rb_eql(test_mode, rb_symChangePin);
287
+ }
288
+
289
+ int securid_session_is_test_mode_denied(VALUE self)
290
+ {
291
+ VALUE test_mode = rb_ivar_get(self, securid_id_session_test_mode);
292
+ return rb_eql(test_mode, rb_symDenied);
293
+ }
294
+
295
+ /*
296
+ * @overload initialize(options={})
297
+ * @param options [Hash{Symbol=>Symbol,Boolean}] A hash of options.
298
+ * @option options [Symbol,Boolean] :test_mode (false) Indicates if the test mode
299
+ * should be enabled, and if so, what mode to enable.
300
+ * Allowed options are: +true+, +false+, +:resynchronize+, +:change_pin+, +:denied+.
301
+ * +true+ means that all authentication steps are returned as successful.
302
+ *
303
+ * Creates a new session. The session will talk to the RSA server configured via the
304
+ * RSA configuration associated with the installed library, or skip talking to the server
305
+ * entirely if in test mode.
306
+ *
307
+ */
308
+ static VALUE securid_session_initialize(int argc, VALUE *argv, VALUE self)
309
+ {
310
+ securid_session_t *session;
311
+ VALUE session_data;
312
+ VALUE options = Qnil;
313
+ VALUE test_mode = Qfalse;
314
+
315
+ rb_scan_args(argc, argv, "0:", &options);
316
+
317
+ // Allocate a new securid_session_t and wrap it as a ruby object
318
+ session_data = TypedData_Make_Struct(rb_cData, securid_session_t, &securid_session_data_type, session);
319
+ session->handle = SDI_HANDLE_NONE;
320
+
321
+ // Stick our new securid_session_t into an instance variable on self
322
+ rb_ivar_set(self, securid_id_session, session_data);
323
+
324
+ // Initalize our status to nil
325
+ rb_ivar_set(self, securid_id_session_status, Qnil);
326
+
327
+ // Initalize our test_mode to the supplied option
328
+ if (!NIL_P(options))
329
+ {
330
+ test_mode = rb_hash_aref(options, rb_symTestMode);
331
+ }
332
+ rb_ivar_set(self, securid_id_session_test_mode, test_mode);
333
+
334
+ return self;
335
+ }
336
+
337
+ /*
338
+ * @overload authenticate(username, passcode)
339
+ * @param username [String] The username to authenticate with.
340
+ * @param passcode [String] The passcode to authenticate with. Passcodes are the user's pin concatenated
341
+ * with the current token value.
342
+ *
343
+ * Attempts to authenticate the user against the RSA ACE server using the username, pin, and token value.
344
+ * It will indicate if authentication fails due to token issue (pin change or resynchronization request),
345
+ * or a normal authentication failure (denied). The session object will have the returned status stored in
346
+ * its {#status status} attribute. A session with a status of {AUTHENTICATED} or
347
+ * {DENIED} can not be reused. A session with a status of {MUST_CHANGE_PIN} or
348
+ * {MUST_RESYNCHRONIZE} can be used to complete the required step, and then be used to reauthenticate
349
+ * with another call to {#authenticate}
350
+ *
351
+ * This method can only be called when the session state is {UNSTARTED}. Calling it in any other state
352
+ * will result in a {SecurIDError} being raised.
353
+ *
354
+ * @raise [SecurIDError] if the session is not in the {UNSTARTED} state or if an internal error occurs.
355
+ * @return [Symbol] The state of the authentication request, which is one of the constants {AUTHENTICATED},
356
+ * {DENIED}, {MUST_CHANGE_PIN}, or {MUST_RESYNCHRONIZE}.
357
+ */
358
+ static VALUE securid_session_authenticate(VALUE self, VALUE username, VALUE passcode)
359
+ {
360
+ int return_value;
361
+ VALUE session_data;
362
+ VALUE status = Qnil;
363
+ securid_session_t *session;
364
+ SD_CHAR *username_str;
365
+ SD_CHAR *passcode_str;
366
+
367
+ // Check that we are in an allowed state
368
+ securid_session_check_status(self, (ID)0);
369
+
370
+ if (!securid_session_is_test_mode(self))
371
+ {
372
+ // Fetch our securid_session_t from self
373
+ session_data = rb_ivar_get(self, securid_id_session);
374
+ TypedData_Get_Struct(session_data, securid_session_t, &securid_session_data_type, session);
375
+
376
+ // Convert our arguments to C Strings
377
+ username_str = StringValueCStr(username);
378
+ passcode_str = StringValueCStr(passcode);
379
+
380
+ // Initalize the session handler
381
+ return_value = SD_Init(&session->handle);
382
+ if (return_value != ACM_OK)
383
+ {
384
+ rb_raise(rb_eSecurIDError, "Failed to initialize session handler - code %d", return_value);
385
+ }
386
+
387
+ // Lock the username, part of the Two Step Authentication flow
388
+ return_value = SD_Lock(session->handle, username_str);
389
+ if (return_value != ACM_OK)
390
+ {
391
+ rb_raise(rb_eSecurIDError, "Failed to lock username - code %d", return_value);
392
+ }
393
+
394
+ return_value = SD_Check(session->handle, passcode_str, username_str);
395
+
396
+ if (return_value == ACM_OK)
397
+ {
398
+ // We are authenticated
399
+ status = rb_const_get(rb_cRSASecurIDSession, securid_id_session_authenticated);
400
+ } else if (return_value == ACM_ACCESS_DENIED)
401
+ {
402
+ // We are denied
403
+ status = rb_const_get(rb_cRSASecurIDSession, securid_id_session_denied);
404
+ } else if (return_value == ACM_NEXT_CODE_REQUIRED)
405
+ {
406
+ // We need the user to resynchronize the token
407
+ status = rb_const_get(rb_cRSASecurIDSession, securid_id_session_resynchronize);
408
+ } else if (return_value == ACM_NEW_PIN_REQUIRED)
409
+ {
410
+ // We need the user to enter a new pin
411
+ status = rb_const_get(rb_cRSASecurIDSession, securid_id_session_change_pin);
412
+ } else
413
+ {
414
+ // Internal error of some sort
415
+ rb_raise(rb_eSecurIDError, "Failed to authenticate the user - code %d", return_value);
416
+ }
417
+ } else
418
+ {
419
+ if (securid_session_is_test_mode_resynchronize(self))
420
+ {
421
+ // Force resynchronize in resynchronization test mode
422
+ status = rb_const_get(rb_cRSASecurIDSession, securid_id_session_resynchronize);
423
+ } else if (securid_session_is_test_mode_change_pin(self))
424
+ {
425
+ // Force pin change in pin change test mode
426
+ status = rb_const_get(rb_cRSASecurIDSession, securid_id_session_change_pin);
427
+ } else if (securid_session_is_test_mode_denied(self))
428
+ {
429
+ // Force denied in denied test mode
430
+ status = rb_const_get(rb_cRSASecurIDSession, securid_id_session_denied);
431
+ } else
432
+ {
433
+ // Force success in test mode
434
+ status = rb_const_get(rb_cRSASecurIDSession, securid_id_session_authenticated);
435
+ }
436
+ }
437
+
438
+ // Update our status
439
+ rb_ivar_set(self, securid_id_session_status, status);
440
+
441
+ return status;
442
+ }
443
+
444
+ /*
445
+ * @overload change_pin(pin)
446
+ * @param pin [String] The new pin for the token.
447
+ *
448
+ * Changes the pin associated with the token. This method can only be called when the
449
+ * session is in the {MUST_CHANGE_PIN} state. Calling it in any other state will result
450
+ * in a {SecurIDError} being raised. After calling {#change_pin}, the session state is
451
+ * reset to {UNSTARTED}. A subsequent call to {#authenticate} is needed to actually
452
+ * authenticate the user.
453
+ *
454
+ * The typical flow for chaning the pin is first call {#authenticate} with the old pin,
455
+ * see that we are in the {MUST_CHANGE_PIN} state, call {#change_pin} to with the new
456
+ * pin, then call {#authenticate} with the new pin to finally authorize the user.
457
+ *
458
+ *
459
+ * @raise [SecurIDError] if the session is not in the {MUST_CHANGE_PIN} state, if an
460
+ * internal error occurs, or the change request fails.
461
+ * @return [true] Always returns +true+ or raises an error.
462
+ */
463
+ static VALUE securid_session_change_pin(VALUE self, VALUE pin)
464
+ {
465
+ VALUE session_data;
466
+ securid_session_t *session;
467
+ SD_CHAR *pin_str;
468
+ int return_value;
469
+
470
+ // Check that we are in an allowed state
471
+ securid_session_check_status(self, securid_id_session_change_pin);
472
+
473
+ if (!securid_session_is_test_mode(self))
474
+ {
475
+ // Fetch our securid_session_t from self
476
+ session_data = rb_ivar_get(self, securid_id_session);
477
+ TypedData_Get_Struct(session_data, securid_session_t, &securid_session_data_type, session);
478
+
479
+ // Convert our arguments to C Strings
480
+ pin_str = StringValueCStr(pin);
481
+
482
+ return_value = SD_Pin(session->handle, pin_str);
483
+ if (return_value != ACM_NEW_PIN_ACCEPTED)
484
+ {
485
+ // Changing pin failed for internal reasons
486
+ rb_raise(rb_eSecurIDError, "Failed to change the pin - code %d", return_value);
487
+ }
488
+ } else
489
+ {
490
+ if (securid_session_is_test_mode_change_pin(self))
491
+ {
492
+ // exit pin change test mode for regular test mode
493
+ rb_ivar_set(self, securid_id_session_test_mode, Qtrue);
494
+ }
495
+ }
496
+
497
+ // Update our status to be unstarted.
498
+ rb_ivar_set(self, securid_id_session_status, Qnil);
499
+
500
+ return Qtrue;
501
+ }
502
+
503
+ /*
504
+ * Cancels a pin change request. On success the session state will be set back to {UNSTARTED}.
505
+ * This method can only be called when the session is in the {MUST_CHANGE_PIN} state. Calling
506
+ * it in any other state will raise a {SecurIDError}.
507
+ *
508
+ *
509
+ * @raise [SecurIDError] if the session is not in the {MUST_CHANGE_PIN} state, if an internal
510
+ * error occurs, or if the the canceling fails.
511
+ * @return [true] Always returns +true+ or raises an error.
512
+ */
513
+ static VALUE securid_session_cancel_pin(VALUE self)
514
+ {
515
+ VALUE session_data;
516
+ securid_session_t *session;
517
+ SD_CHAR *pin_str;
518
+ int return_value;
519
+
520
+ // Check that we are in an allowed state
521
+ securid_session_check_status(self, securid_id_session_change_pin);
522
+
523
+ if (!securid_session_is_test_mode(self))
524
+ {
525
+ // Fetch our securid_session_t from self
526
+ session_data = rb_ivar_get(self, securid_id_session);
527
+ TypedData_Get_Struct(session_data, securid_session_t, &securid_session_data_type, session);
528
+
529
+ return_value = SD_Pin(session->handle, NULL);
530
+ if (return_value != ACM_NEW_PIN_ACCEPTED)
531
+ {
532
+ rb_raise(rb_eSecurIDError, "Failed to cancel changing the pin - code %d", return_value);
533
+ }
534
+ }
535
+
536
+ // Update our status to be unstarted.
537
+ rb_ivar_set(self, securid_id_session_status, Qnil);
538
+
539
+ return Qtrue;
540
+ }
541
+
542
+ /*
543
+ * @overload resynchronize(passcode)
544
+ * @param passcode [String] The user's pin concatenated with the _next_ token value.
545
+ *
546
+ * Completes the token resynchronize flow. This method should be called when the server is requesting the user
547
+ * to resynchronize their token. It is not possible to initiate a resychronization request with this method. The
548
+ * session must be in the {MUST_RESYNCHRONIZE} state. Callig it in any other state will raise a {SecurIDError}.
549
+ *
550
+ *
551
+ * @raise [SecurIDError] if the session is not in the {MUST_RESYNCHRONIZE} state or the resynchronization fails
552
+ * for any reason other than the passcode not matching.
553
+ * @return [Symbol] Either {AUTHENTICATED} or {DENIED} depending on if the resynchronization was successful.
554
+ */
555
+ static VALUE securid_session_resynchronize(VALUE self, VALUE passcode)
556
+ {
557
+ int return_value;
558
+ VALUE session_data;
559
+ VALUE status;
560
+ securid_session_t *session;
561
+ SD_CHAR *passcode_str;
562
+
563
+ // Check that we are in an allowed state
564
+ securid_session_check_status(self, securid_id_session_resynchronize);
565
+
566
+ if (!securid_session_is_test_mode(self))
567
+ {
568
+ // Fetch our securid_session_t from self
569
+ session_data = rb_ivar_get(self, securid_id_session);
570
+ TypedData_Get_Struct(session_data, securid_session_t, &securid_session_data_type, session);
571
+
572
+ // Convert our arguments to C Strings
573
+ passcode_str = StringValueCStr(passcode);
574
+
575
+ // Initalize the session handler
576
+ return_value = SD_Next(session->handle, passcode_str);
577
+ if (return_value == ACM_OK)
578
+ {
579
+ // We are authenticated
580
+ status = rb_const_get(rb_cRSASecurIDSession, securid_id_session_authenticated);
581
+ } else if (return_value == ACM_ACCESS_DENIED)
582
+ {
583
+ // We are denied
584
+ status = rb_const_get(rb_cRSASecurIDSession, securid_id_session_denied);
585
+ } else {
586
+ // Internal error of some sort
587
+ rb_raise(rb_eSecurIDError, "Failed to synchronize the token - code %d", return_value);
588
+ }
589
+ } else {
590
+ if (securid_session_is_test_mode_resynchronize(self))
591
+ {
592
+ // exit resynchronization test mode for regular test mode
593
+ rb_ivar_set(self, securid_id_session_test_mode, Qtrue);
594
+ }
595
+ // Force success in test mode
596
+ status = rb_const_get(rb_cRSASecurIDSession, securid_id_session_authenticated);
597
+ }
598
+
599
+ // Update our status
600
+ rb_ivar_set(self, securid_id_session_status, status);
601
+
602
+ return status;
603
+ }
604
+
605
+
606
+ /*
607
+ * Fetches the status of the RSA agent. This includes details about the RSA ACE server
608
+ * the agent is communicating with. *NOTE:* this method can not be called in test mode
609
+ * unless there is an actual RSA agent present. The returned status hash is a ruby
610
+ * version of the +S_status_display+ struct populated by +AceAgentStatusDisplay+, and
611
+ * any questions about the meaning of its fields should be directed there.
612
+ *
613
+ * @example Returned Status Hash
614
+ * {
615
+ * config_version: 15,
616
+ * max_servers: 1,
617
+ * max_replicas: 4,
618
+ * max_retries: 5,
619
+ * base_timeout: 5,
620
+ * use_des: 1,
621
+ * trusted: 0,
622
+ * port: 5500,
623
+ * service_protocol_version: 0,
624
+ * service_name: "securid",
625
+ * service_protocol: "udp",
626
+ * server_release_number: {
627
+ * major: 0,
628
+ * minor: 0,
629
+ * patch: 0,
630
+ * build: 0
631
+ * },
632
+ * servers: [
633
+ * {
634
+ * hostname: "rsa.example.com",
635
+ * address: "192.168.0.1",
636
+ * active_address: nil,
637
+ * aliases: [],
638
+ * display_status: {
639
+ * primary: true,
640
+ * master: true,
641
+ * slave: false,
642
+ * selectable: false,
643
+ * emergency: false,
644
+ * suspended: true
645
+ * }
646
+ * },
647
+ * {
648
+ * hostname: nil,
649
+ * address: "192.168.0.2",
650
+ * active_address: nil,
651
+ * aliases: [],
652
+ * display_status: {
653
+ * primary: false,
654
+ * master: false,
655
+ * slave: false,
656
+ * selectable: false,
657
+ * emergency: false,
658
+ * suspended: true
659
+ * }
660
+ * }
661
+ * ]
662
+ * }
663
+ *
664
+ * @raise [SecurIDError] if the RSA agent failed to initialize.
665
+ * @return [Hash] The status hash, keyed by symbols.
666
+ */
667
+ static VALUE securid_agent_status(VALUE self) {
668
+ VALUE status = Qfalse;
669
+ VALUE server_release_number;
670
+ VALUE server_details;
671
+ VALUE servers;
672
+ VALUE server_aliases;
673
+ VALUE display_status;
674
+ S_status_display agent_status;
675
+ DISP_SRVR_INFO * server_info;
676
+ int return_value, i, j, str_length;
677
+ struct in_addr addr;
678
+
679
+ // Initialize the library. Safe to call multiple times.
680
+ if (AceInitialize() != SD_TRUE) {
681
+ rb_raise(rb_eSecurIDError, "Failed to initialize authentication agent");
682
+ }
683
+
684
+ // Make sure we are zero'd
685
+ memset(&agent_status, 0, sizeof(agent_status));
686
+ // Set the struct size so the SDK can identify the version used
687
+ agent_status.u32Size = (SD_U32) sizeof(agent_status);
688
+ // Fetch the agent status
689
+ return_value = AceAgentStatusDisplay(&agent_status);
690
+
691
+ if (return_value == ACE_SUCCESS) {
692
+ status = rb_hash_new();
693
+
694
+ // Populate status hash
695
+ rb_hash_aset(status, rb_symConfigVersion, INT2NUM(agent_status.config_version));
696
+ rb_hash_aset(status, rb_symMaxServers, INT2NUM(agent_status.acmmaxservers));
697
+ rb_hash_aset(status, rb_symMaxReplicas, INT2NUM(agent_status.acmmaxreplicas));
698
+ rb_hash_aset(status, rb_symMaxRetries, INT2NUM(agent_status.acmmaxretries));
699
+ rb_hash_aset(status, rb_symBaseTimeout, INT2NUM(agent_status.acmbasetimeout));
700
+ rb_hash_aset(status, rb_symUseDES, INT2NUM(agent_status.use_des));
701
+ rb_hash_aset(status, rb_symTrusted, INT2NUM(agent_status.trusted));
702
+ rb_hash_aset(status, rb_symPort, INT2NUM(agent_status.acmport));
703
+ rb_hash_aset(status, rb_symServiceProtocolVersion, INT2NUM(agent_status.server_hi_protocol));
704
+
705
+ str_length = strnlen(agent_status.acmservice, sizeof(agent_status.acmservice) / sizeof(SD_CHAR));
706
+ rb_hash_aset(status, rb_symServiceName, rb_str_new(agent_status.acmservice, str_length));
707
+
708
+ str_length = strnlen(agent_status.acmprotocol, sizeof(agent_status.acmprotocol) / sizeof(SD_CHAR));
709
+ rb_hash_aset(status, rb_symServiceProtocol, rb_str_new(agent_status.acmprotocol, str_length));
710
+
711
+ // Populate release number hash
712
+ server_release_number = rb_hash_new();
713
+ rb_hash_aset(server_release_number, rb_symMajor, INT2NUM(agent_status.server_release_from_server[0]));
714
+ rb_hash_aset(server_release_number, rb_symMinor, INT2NUM(agent_status.server_release_from_server[1]));
715
+ rb_hash_aset(server_release_number, rb_symPatch, INT2NUM(agent_status.server_release_from_server[2]));
716
+ rb_hash_aset(server_release_number, rb_symBuild, INT2NUM(agent_status.server_release_from_server[3]));
717
+ rb_hash_aset(status, rb_symServerReleaseNumber, server_release_number);
718
+
719
+ servers = rb_ary_new();
720
+
721
+ // Populate server array
722
+ for (i = 0; i < agent_status.acmmaxreplicas; ++i) {
723
+ server_info = &agent_status.acm_servers[i];
724
+
725
+ if (!(server_info->addr && server_info->hostname)) {
726
+ continue;
727
+ }
728
+
729
+ server_details = rb_hash_new();
730
+
731
+ str_length = strnlen(server_info->hostname, DISP_LENHOSTNAME);
732
+ rb_hash_aset(server_details, rb_symHostname, str_length ? rb_str_new(server_info->hostname, str_length) : Qnil);
733
+
734
+ addr.s_addr = server_info->addr;
735
+ rb_hash_aset(server_details, rb_symAddress, server_info->addr ? rb_str_new2(inet_ntoa(addr)) : Qnil);
736
+
737
+ addr.s_addr = server_info->active_addr;
738
+ rb_hash_aset(server_details, rb_symActiveAddress, server_info->active_addr ? rb_str_new2(inet_ntoa(addr)) : Qnil);
739
+
740
+ // build server aliases array
741
+ server_aliases = rb_ary_new();
742
+ for (j = 0; j < DISP_MAXALIASES; ++j) {
743
+ if (!server_info->aliases[j]) {
744
+ continue;
745
+ }
746
+ addr.s_addr = server_info->aliases[j];
747
+ rb_ary_push(server_aliases, rb_str_new2(inet_ntoa(addr)));
748
+ }
749
+ rb_hash_aset(server_details, rb_symAliases, server_aliases);
750
+
751
+ display_status = rb_hash_new();
752
+ rb_hash_aset(display_status, rb_symPrimary, CTEST(server_info->display_status & DISP_STATUS_PRIMARY));
753
+ rb_hash_aset(display_status, rb_symMaster, CTEST(server_info->display_status & DISP_MSTR_SLAVE && i == 0));
754
+ rb_hash_aset(display_status, rb_symSlave, CTEST(server_info->display_status & DISP_MSTR_SLAVE && i > 0));
755
+ rb_hash_aset(display_status, rb_symSelectable, CTEST(server_info->display_status & DISP_STATUS_SELECTABLE));
756
+ rb_hash_aset(display_status, rb_symEmergency, CTEST(server_info->display_status & DISP_STATUS_EMERGENCY));
757
+ rb_hash_aset(display_status, rb_symSuspended, CTEST(server_info->display_status & DISP_STATUS_SUSPENDED));
758
+ rb_hash_aset(server_details, rb_symDisplayStatus, display_status);
759
+
760
+ // Add server details to servers array
761
+ rb_ary_push(servers, server_details);
762
+ }
763
+
764
+ // Add servers array to status hash
765
+ rb_hash_aset(status, rb_symServers, servers);
766
+ }
767
+
768
+ return status;
769
+ }
770
+
771
+ void Init_securid()
772
+ {
773
+ securid_id_session = rb_intern("session_handler"); // hidden from the ruby runtime due to its name
774
+ securid_id_session_status = rb_intern("@status");
775
+ securid_id_session_test_mode = rb_intern("@test_mode");
776
+ securid_id_session_authenticated = rb_intern("AUTHENTICATED");
777
+ securid_id_session_denied = rb_intern("DENIED");
778
+ securid_id_session_change_pin = rb_intern("MUST_CHANGE_PIN");
779
+ securid_id_session_resynchronize = rb_intern("MUST_RESYNCHRONIZE");
780
+ rb_symTestMode = ID2SYM(rb_intern("test_mode"));
781
+ rb_symResynchronize = ID2SYM(rb_intern("resynchronize"));
782
+ rb_symChangePin = ID2SYM(rb_intern("change_pin"));
783
+ rb_symDenied = ID2SYM(rb_intern("denied"));
784
+
785
+ rb_symConfigVersion = ID2SYM(rb_intern("config_version"));
786
+ rb_symMaxServers = ID2SYM(rb_intern("max_servers"));
787
+ rb_symMaxReplicas = ID2SYM(rb_intern("max_replicas"));
788
+ rb_symMaxRetries = ID2SYM(rb_intern("max_retries"));
789
+ rb_symBaseTimeout = ID2SYM(rb_intern("base_timeout"));
790
+ rb_symUseDES = ID2SYM(rb_intern("use_des"));
791
+ rb_symTrusted = ID2SYM(rb_intern("trusted"));
792
+ rb_symPort = ID2SYM(rb_intern("port"));
793
+ rb_symServiceName = ID2SYM(rb_intern("service_name"));
794
+ rb_symServiceProtocol = ID2SYM(rb_intern("service_protocol"));
795
+ rb_symServiceProtocolVersion = ID2SYM(rb_intern("service_protocol_version"));
796
+ rb_symServerReleaseNumber = ID2SYM(rb_intern("server_release_number"));
797
+ rb_symServers = ID2SYM(rb_intern("servers"));
798
+ rb_symMajor = ID2SYM(rb_intern("major"));
799
+ rb_symMinor = ID2SYM(rb_intern("minor"));
800
+ rb_symPatch = ID2SYM(rb_intern("patch"));
801
+ rb_symBuild = ID2SYM(rb_intern("build"));
802
+ rb_symAddress = ID2SYM(rb_intern("address"));
803
+ rb_symActiveAddress = ID2SYM(rb_intern("active_address"));
804
+ rb_symAliases = ID2SYM(rb_intern("aliases"));
805
+ rb_symDisplayStatus = ID2SYM(rb_intern("display_status"));
806
+ rb_symHostname = ID2SYM(rb_intern("hostname"));
807
+ rb_symPrimary = ID2SYM(rb_intern("primary"));
808
+ rb_symMaster = ID2SYM(rb_intern("master"));
809
+ rb_symSlave = ID2SYM(rb_intern("slave"));
810
+ rb_symSelectable = ID2SYM(rb_intern("selectable"));
811
+ rb_symEmergency = ID2SYM(rb_intern("emergency"));
812
+ rb_symSuspended = ID2SYM(rb_intern("suspended"));
813
+ rb_symAddressVerified = ID2SYM(rb_intern("address_verified"));
814
+
815
+ // module RSA
816
+ rb_mRSA = rb_define_module("RSA");
817
+
818
+ // module RSA::SecurID
819
+ rb_mRSASecurID = rb_define_module_under(rb_mRSA, "SecurID");
820
+
821
+ /*
822
+ * Document-class: RSA::SecurID::SecurIDError
823
+ *
824
+ * The error class used by the {Session} class to indicate internal state or communication
825
+ * errors. Error codes found in the messages can be referenced against the RSA SDK
826
+ * documentation.
827
+ *
828
+ */
829
+ rb_eSecurIDError = rb_define_class_under(rb_mRSASecurID, "SecurIDError", rb_eStandardError);
830
+
831
+ // def RSA::SecurID.authenticate(username, passcode)
832
+ rb_define_module_function(rb_mRSASecurID, "authenticate", securid_authenticate, 2);
833
+
834
+ // def RSA::SecurID.agent_status
835
+ rb_define_module_function(rb_mRSASecurID, "agent_status", securid_agent_status, 0);
836
+
837
+ // class RSA::SecurID::Session
838
+ rb_cRSASecurIDSession = rb_define_class_under(rb_mRSASecurID, "Session", rb_cObject);
839
+
840
+ // def RSA::SecurID::Session.new
841
+ rb_define_method(rb_cRSASecurIDSession, "initialize", securid_session_initialize, -1);
842
+
843
+ // def RSA::SecurID::Session#authenticate(username, passcode)
844
+ rb_define_method(rb_cRSASecurIDSession, "authenticate", securid_session_authenticate, 2);
845
+
846
+ // def RSA::SecurID::Session#change_pin(pin)
847
+ rb_define_method(rb_cRSASecurIDSession, "change_pin", securid_session_change_pin, 1);
848
+
849
+ // def RSA::SecurID::Session#cancel_pin
850
+ rb_define_method(rb_cRSASecurIDSession, "cancel_pin", securid_session_cancel_pin, 0);
851
+
852
+ // def RSA::SecurID::Session#resynchronize
853
+ rb_define_method(rb_cRSASecurIDSession, "resynchronize", securid_session_resynchronize, 1);
142
854
  }
@@ -0,0 +1,11 @@
1
+ #ifndef SECURID_H_
2
+ #define SECURID_H_
3
+
4
+ extern ID securid_id_session;
5
+
6
+ // The internal storage used by RSA::SecurID::Session instances
7
+ typedef struct {
8
+ SDI_HANDLE handle;
9
+ } securid_session_t;
10
+
11
+ #endif
@@ -0,0 +1,56 @@
1
+ require 'securid/securid'
2
+
3
+ module RSA
4
+ module SecurID
5
+
6
+ # Manages a single authentication session against the RSA ACE server. Handles the various life cycle
7
+ # events that may occur, such as token resynchronization and pin changes. Includes a test mode that can
8
+ # simulate various responses when an ACE server is not present (for example during local development).
9
+ # Instances of this class should not be reused unless the RSA flow (and this documentation) indicates
10
+ # otherwise (for example, you can issue an {#authenticate} call after a successful {#change_pin} call).
11
+ #
12
+ # This class assumes you have an understanding of the RSA authentication flow. Timeouts on state
13
+ # transitions are enforced by the server, and are not documented here as they may change between ACE
14
+ # releases.
15
+ #
16
+ # In test mode, this class will send no network traffic and not talk to the RSA agent. The RSA SDK
17
+ # libraries do not even need to be present, just the header files. In the normal mode, the server
18
+ # configuration is imported by the agent directly.
19
+ class Session
20
+
21
+ # Returns the current state of the session, which is one of {AUTHENTICATED}, {DENIED},
22
+ # {MUST_CHANGE_PIN}, {MUST_RESYNCHRONIZE}, or {UNSTARTED}.
23
+ attr_reader :status
24
+
25
+ AUTHENTICATED = :authenticated
26
+ DENIED = :denied
27
+ MUST_CHANGE_PIN = :must_change_pin
28
+ MUST_RESYNCHRONIZE = :must_resynchronize
29
+ UNSTARTED = nil
30
+
31
+ # @return [Boolean] +true+ if the session is in the {MUST_RESYNCHRONIZE} state, +false+ otherwise.
32
+ # Checks if the session is in the {MUST_RESYNCHRONIZE} state.
33
+ def resynchronize?
34
+ @status == MUST_RESYNCHRONIZE
35
+ end
36
+
37
+ # Checks if the session is in the {MUST_CHANGE_PIN} state.
38
+ # @return [Boolean] +true+ if the session is in the {MUST_CHANGE_PIN} state, +false+ otherwise.
39
+ def change_pin?
40
+ @status == MUST_CHANGE_PIN
41
+ end
42
+
43
+ # Checks if the session is in the {AUTHENTICATED} state.
44
+ # @return [Boolean] +true+ if the session is in the {AUTHENTICATED} state, +false+ otherwise.
45
+ def authenticated?
46
+ @status == AUTHENTICATED
47
+ end
48
+
49
+ # Checks if the session is in the {DENIED} state.
50
+ # @return [Boolean] +true+ if the session is in the {DENIED} state, +false+ otherwise.
51
+ def denied?
52
+ @status == DENIED
53
+ end
54
+ end
55
+ end
56
+ end
metadata CHANGED
@@ -1,67 +1,52 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: securid
3
- version: !ruby/object:Gem::Version
4
- hash: 9
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 1
9
- version: "0.1"
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.5
10
5
  platform: ruby
11
- authors:
6
+ authors:
12
7
  - Ian Lesperance
8
+ - Edward Holets
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2010-06-10 00:00:00 -07:00
18
- default_executable:
12
+ date: 2016-03-14 00:00:00.000000000 Z
19
13
  dependencies: []
20
-
21
- description: A library for authenticating with an RSA SecurID ACE Authentication Server
14
+ description: A library for authenticating with an RSA SecurID ACE Authentication Server.
15
+ Supports synchronous authenttication with ACE Server 6.1 and greater. Supports interactive
16
+ and non-interactive flows.
22
17
  email: ilesperance@ezpublishing.com
23
18
  executables: []
24
-
25
- extensions:
19
+ extensions:
26
20
  - ext/securid/extconf.rb
27
21
  extra_rdoc_files: []
28
-
29
- files:
30
- - ext/securid/securid.c
22
+ files:
31
23
  - ext/securid/extconf.rb
32
- has_rdoc: true
33
- homepage: http://github.com/ezpub/securid
34
- licenses: []
35
-
24
+ - ext/securid/securid.c
25
+ - ext/securid/securid.h
26
+ - lib/securid.rb
27
+ homepage: http://github.com/sendstream/securid
28
+ licenses:
29
+ - MIT
30
+ metadata: {}
36
31
  post_install_message:
37
32
  rdoc_options: []
38
-
39
- require_paths:
33
+ require_paths:
40
34
  - lib
41
- required_ruby_version: !ruby/object:Gem::Requirement
42
- none: false
43
- requirements:
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
44
37
  - - ">="
45
- - !ruby/object:Gem::Version
46
- hash: 3
47
- segments:
48
- - 0
49
- version: "0"
50
- required_rubygems_version: !ruby/object:Gem::Requirement
51
- none: false
52
- requirements:
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
53
42
  - - ">="
54
- - !ruby/object:Gem::Version
55
- hash: 3
56
- segments:
57
- - 0
58
- version: "0"
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
59
45
  requirements: []
60
-
61
46
  rubyforge_project:
62
- rubygems_version: 1.3.7
47
+ rubygems_version: 2.2.2
63
48
  signing_key:
64
- specification_version: 3
49
+ specification_version: 4
65
50
  summary: A library for authenticating with an RSA SecurID ACE Authentication Server
66
51
  test_files: []
67
-
52
+ has_rdoc: