identikey 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +180 -0
- data/Rakefile +8 -0
- data/bin/console +10 -0
- data/bin/setup +8 -0
- data/identikey.gemspec +34 -0
- data/lib/identikey/administration/digipass.rb +103 -0
- data/lib/identikey/administration/session.rb +119 -0
- data/lib/identikey/administration/session_query.rb +35 -0
- data/lib/identikey/administration/user.rb +127 -0
- data/lib/identikey/administration.rb +211 -0
- data/lib/identikey/authentication.rb +56 -0
- data/lib/identikey/base.rb +272 -0
- data/lib/identikey/unsigned.rb +28 -0
- data/lib/identikey/version.rb +3 -0
- data/lib/identikey.rb +10 -0
- data/log/.keep +0 -0
- metadata +176 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d5a041647bdeb7baf1d5eee22d75c1c19039f7f2a95ed2f72919ce1a62a3c27e
|
4
|
+
data.tar.gz: 36b979cf4c32041377b4ab0b881afcca1fd880f8153ac1060447069c47658169
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1bf5928570adb64247b63c0fd9b61cd02711db99c6591fdbedea4d013693dbf8d6cd13edfe06e1c9a77680f20ac95303e4870472e55026a28daff24e2a5f36d6
|
7
|
+
data.tar.gz: 7bcea676a24e5ddecbce9895796f5525f4b1ec2f96c27ef993718e7fcc8f1879acad082f88dca88fb154199283d01d87f253760fa1f970b0bca06a4c06d3bdec
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Marcello Barnaba <m.barnaba@ifad.org>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
# Identikey
|
2
|
+
|
3
|
+
This library is a thin and incomplete wrapper of the VASCO Identikey SOAP API.
|
4
|
+
|
5
|
+
Vasco Identikey has been recently re-branded as OneSpan Authentication Server.
|
6
|
+
|
7
|
+
## Requirements
|
8
|
+
|
9
|
+
The gem requires the Vasco SDK, that is private intellectual property and
|
10
|
+
cannot be redistributed here. You have to obtain it from VASCO / OneSpan
|
11
|
+
as part of your subscription.
|
12
|
+
|
13
|
+
The gem interfaces against a running Identikey server, communicating on
|
14
|
+
port 8888/TCP the SOAP protocol over HTTPS.
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'identikey', github: 'ifad/identikey'
|
22
|
+
```
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
$ bundle
|
27
|
+
|
28
|
+
## Configuration
|
29
|
+
|
30
|
+
By default the client expects WSDL files in the current working directory,
|
31
|
+
into `./sdk/wsdl` and it connects to an Identikey API endpoint on localhost
|
32
|
+
port 8888 using TLSv1.2. Great for development, but definitely not good for
|
33
|
+
production.
|
34
|
+
|
35
|
+
To configure the client, you should at least define where your WSDL files are
|
36
|
+
and where the SOAP endpoint is. Given the WSDL file is different for the two
|
37
|
+
API sets (Authentication and Administration), you need to configure the two
|
38
|
+
classes separately.
|
39
|
+
|
40
|
+
Use the `.configure` method, that will run the block you give to it in the
|
41
|
+
context of the [Savon::Globals](http://savonrb.com/version2/globals.html)
|
42
|
+
object as such all available configuration parameters are available as
|
43
|
+
instance methods.
|
44
|
+
|
45
|
+
Example:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
Identikey::Authentication.configure do
|
49
|
+
wsdl './path/to/your/authentication.wsdl'
|
50
|
+
endpoint 'https://your-identikey.example.com:8888'
|
51
|
+
|
52
|
+
# ... more configuration options as needed ...
|
53
|
+
end
|
54
|
+
|
55
|
+
Identikey::Administration.configure do
|
56
|
+
wsdl './path/to/your/administrtaion.wsdl'
|
57
|
+
endpoint 'https://your-identikey.example.com:8888'
|
58
|
+
|
59
|
+
# ... more configuration options as needed ...
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
By default, all SOAP requests and responses are logged to `log/identikey.log`.
|
64
|
+
|
65
|
+
If you want to reduce the logging level please use:
|
66
|
+
|
67
|
+
```
|
68
|
+
Identikey::Authentication.configure do
|
69
|
+
log_level :info # or one of [:debug, :warn, :error, :fatal]
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
Or to disable it altogether (not recommended):
|
74
|
+
|
75
|
+
```
|
76
|
+
Identikey::Authentication.configure do
|
77
|
+
log false
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
The `configure` block accepts all Savon options, for which documentation
|
82
|
+
is available here: http://savonrb.com/version2/globals.html feel free to
|
83
|
+
amend it to suit your needs.
|
84
|
+
|
85
|
+
The only option whose semantics differ from the default is `filters`, as
|
86
|
+
it adds handling the faulty parameter passing design in Identikey, where
|
87
|
+
the same elements are used to transmit different business informatios.
|
88
|
+
|
89
|
+
By default, sensitive values attribute are filtered out from the logs.
|
90
|
+
Other attributes to filter out can be specified by prefixing them with
|
91
|
+
`identikey:`. Example, filter out `CREDFLD_PASSWORD` and `CREDFLD_USERID`:
|
92
|
+
|
93
|
+
Example, filter out `CREDFLD_PASSWORD` and `CREDFLD_USERID`:
|
94
|
+
|
95
|
+
```
|
96
|
+
Identikey::Authentication.configure do
|
97
|
+
filters [ 'identikey:CREDFLD_PASSWORD', 'identikey:CREDFLD_USERID' ]
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
Please note that the following attributes are filtered out by default:
|
102
|
+
|
103
|
+
* CREDFLD_PASSWORD
|
104
|
+
* CREDFLD_STATIC_PASSWORD
|
105
|
+
* CREDFLD_SESSION_ID
|
106
|
+
|
107
|
+
Please note that if you set your custom filters, these will override the
|
108
|
+
defaults and you should also take care of filtering the above parameters
|
109
|
+
in addition to the ones you want to filter out.
|
110
|
+
|
111
|
+
## Usage
|
112
|
+
|
113
|
+
This is still in alpha stage, as such there is not much documentation. Have a
|
114
|
+
look at the specs for sample usage.
|
115
|
+
|
116
|
+
* Verify an end user OTP
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
Identikey::Authentication.valid_otp?('username', 'otp')
|
120
|
+
```
|
121
|
+
|
122
|
+
* Start an administration session
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
s = Identikey::Administration::Session.new(username: 'admin', password: 'foobar')
|
126
|
+
s.logon
|
127
|
+
```
|
128
|
+
|
129
|
+
* Find a digipass
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
d = s.find_digipass('serial')
|
133
|
+
```
|
134
|
+
|
135
|
+
* Perform an OTP test
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
d = d.test_otp('1234567890')
|
139
|
+
```
|
140
|
+
|
141
|
+
* Assign a digipass to an user
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
d.assign! 'username'
|
145
|
+
```
|
146
|
+
|
147
|
+
* Unassign a digipass
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
d.unassign!
|
151
|
+
```
|
152
|
+
|
153
|
+
* End an administrative session
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
s.logoff
|
157
|
+
```
|
158
|
+
|
159
|
+
## Development
|
160
|
+
|
161
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then,
|
162
|
+
run `rake` to run the tests. You can also run `bin/console` for an interactive
|
163
|
+
prompt that will allow you to experiment.
|
164
|
+
|
165
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
166
|
+
|
167
|
+
To release a new version, update the version number in `version.rb`, and then
|
168
|
+
run `bundle exec rake release`, which will create a git tag for the version,
|
169
|
+
push git commits and tags, and push the `.gem` file to
|
170
|
+
[rubygems.org](https://rubygems.org).
|
171
|
+
|
172
|
+
## Contributing
|
173
|
+
|
174
|
+
Bug reports and pull requests are welcome on GitHub at
|
175
|
+
https://github.com/ifad/identikey.
|
176
|
+
|
177
|
+
## License
|
178
|
+
|
179
|
+
The gem is available as open source under the terms of the [MIT
|
180
|
+
License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
data/bin/setup
ADDED
data/identikey.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "identikey/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "identikey"
|
7
|
+
spec.version = Identikey::VERSION
|
8
|
+
spec.authors = ["Marcello Barnaba"]
|
9
|
+
spec.email = ["vjt@openssl.it"]
|
10
|
+
|
11
|
+
spec.summary = %q{OneSpan Authentication Server (former VASCO Identikey) wrapper for Ruby}
|
12
|
+
spec.description = %q{This gem contains a SOAP client to consume Identikey API}
|
13
|
+
spec.homepage = "https://github.com/ifad/identikey"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
# Specify which files should be added to the gem when it is released.
|
17
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
18
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
19
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
20
|
+
end
|
21
|
+
spec.bindir = "exe"
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_dependency "savon", "~> 2.0"
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
28
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
29
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
30
|
+
spec.add_development_dependency 'pry'
|
31
|
+
spec.add_development_dependency 'hirb'
|
32
|
+
spec.add_development_dependency 'byebug'
|
33
|
+
spec.add_development_dependency 'simplecov'
|
34
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Identikey
|
2
|
+
class Administration < Base
|
3
|
+
|
4
|
+
class Digipass
|
5
|
+
def self.find(session:, serial_no:)
|
6
|
+
new(session).find(serial_no)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(session, digipass = nil)
|
10
|
+
@session = session
|
11
|
+
|
12
|
+
replace(digipass) if digipass
|
13
|
+
end
|
14
|
+
|
15
|
+
def replace(digipass)
|
16
|
+
@attributes = {
|
17
|
+
serial: digipass['DIGIPASSFLD_SERNO'],
|
18
|
+
domain: digipass['DIGIPASSFLD_DOMAIN'],
|
19
|
+
ou: digipass['DIGIPASSFLD_ORGANIZATIONAL_UNIT'],
|
20
|
+
type: digipass['DIGIPASSFLD_DPTYPE'],
|
21
|
+
application: digipass['DIGIPASSFLD_ACTIVE_APPL_NAMES'],
|
22
|
+
status: digipass['DIGIPASSFLD_ASSIGN_STATUS'],
|
23
|
+
userid: digipass['DIGIPASSFLD_ASSIGNED_USERID'],
|
24
|
+
assigned_at: digipass['DIGIPASSFLD_ASSIGNED_DATE'],
|
25
|
+
grace_expires_at: digipass['DIGIPASSFLD_GRACE_PERIOD_EXPIRES'],
|
26
|
+
created_at: digipass['DIGIPASSFLD_CREATE_TIME'],
|
27
|
+
updated_at: digipass['DIGIPASSFLD_MODIFY_TIME'],
|
28
|
+
activation_count: digipass['DIGIPASSFLD_ACTIV_COUNT'],
|
29
|
+
last_activation_at: digipass['DIGIPASSFLD_LAST_ACTIV_TIME'],
|
30
|
+
bind_status: digipass['DIGIPASSFLD_BIND_STATUS'],
|
31
|
+
max_activations: digipass['DIGIPASSFLD_MAX_ACTIVATIONS'],
|
32
|
+
expired: digipass['DIGIPASSFLD_EXPIRED'],
|
33
|
+
grace_expired: digipass['DIGIPASSFLD_GRACE_PERIOD_EXPIRED']
|
34
|
+
}.freeze
|
35
|
+
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def assigned?
|
40
|
+
self.status == 'Assigned'
|
41
|
+
end
|
42
|
+
|
43
|
+
def find(serial_no)
|
44
|
+
stat, digipass, error = @session.execute(
|
45
|
+
:digipass_execute_VIEW, serial_no: serial_no)
|
46
|
+
|
47
|
+
if stat != 'STAT_SUCCESS'
|
48
|
+
raise Identikey::Error, "Find digipass failed: #{stat} - #{error}"
|
49
|
+
end
|
50
|
+
|
51
|
+
replace(digipass)
|
52
|
+
end
|
53
|
+
|
54
|
+
def reload
|
55
|
+
find(self.serial)
|
56
|
+
end
|
57
|
+
|
58
|
+
def unassign!
|
59
|
+
stat, digipass, error = @session.execute(
|
60
|
+
:digipass_execute_UNASSIGN, serial_no: self.serial)
|
61
|
+
|
62
|
+
if stat != 'STAT_SUCCESS'
|
63
|
+
raise Identikey::Error, "Assign digipass failed: #{stat} - #{error}"
|
64
|
+
end
|
65
|
+
|
66
|
+
replace(digipass)
|
67
|
+
end
|
68
|
+
|
69
|
+
def assign!(username, domain)
|
70
|
+
stat, digipass, error = @session.execute(
|
71
|
+
:digipass_execute_ASSIGN, serial_no: self.serial, username: username, domain: domain)
|
72
|
+
|
73
|
+
if stat != 'STAT_SUCCESS'
|
74
|
+
raise Identikey::Error, "Unassign digipass failed: #{stat} - #{error}"
|
75
|
+
end
|
76
|
+
|
77
|
+
replace(digipass)
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_otp(otp)
|
81
|
+
stat, appl, error = @session.execute(
|
82
|
+
:digipassappl_execute_TEST_OTP, serial_no: self.serial, appl: self.application, otp: otp)
|
83
|
+
|
84
|
+
# Stat is useless here - it reports whether the call or not has
|
85
|
+
# succeeded, not whether the OTP is valid
|
86
|
+
if stat != 'STAT_SUCCESS'
|
87
|
+
raise Identikey::Error, "Test OTP failed: #{stat} - #{error}"
|
88
|
+
end
|
89
|
+
|
90
|
+
appl['DIGIPASSAPPLFLD_RESULT_CODE'] == '0'
|
91
|
+
end
|
92
|
+
|
93
|
+
def method_missing(name, *args, &block)
|
94
|
+
if @attributes.key?(name)
|
95
|
+
@attributes.fetch(name)
|
96
|
+
else
|
97
|
+
super(name, *args, &block)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Identikey
|
2
|
+
class Administration < Base
|
3
|
+
|
4
|
+
class Session
|
5
|
+
attr_reader :username, :password, :domain
|
6
|
+
attr_reader :session_id, :product, :version
|
7
|
+
attr_reader :privileges, :location
|
8
|
+
|
9
|
+
def initialize(username:, password:, domain: 'master')
|
10
|
+
@client = Identikey::Administration.new
|
11
|
+
|
12
|
+
@username = username
|
13
|
+
@password = password
|
14
|
+
@domain = domain
|
15
|
+
end
|
16
|
+
|
17
|
+
def endpoint
|
18
|
+
@client.endpoint
|
19
|
+
end
|
20
|
+
|
21
|
+
def wsdl
|
22
|
+
@client.wsdl
|
23
|
+
end
|
24
|
+
|
25
|
+
def logon
|
26
|
+
stat, sess, error = @client.logon(username: @username, password: @password, domain: @domain)
|
27
|
+
|
28
|
+
if stat != 'STAT_SUCCESS'
|
29
|
+
raise Identikey::Error, "logon failed: #{stat} - #{error}"
|
30
|
+
end
|
31
|
+
|
32
|
+
@privileges = parse_privileges sess['CREDFLD_LOGICAL_ADMIN_PRIVILEGES']
|
33
|
+
|
34
|
+
@session_id = sess['CREDFLD_SESSION_ID'].freeze
|
35
|
+
@location = sess['CREDFLD_USER_LOCATION'].freeze
|
36
|
+
@last_logon = sess['CREDFLD_LAST_LOGON_TIME'].freeze
|
37
|
+
|
38
|
+
@product = sess['CREDFLD_PRODUCT_NAME'].freeze
|
39
|
+
@version = sess['CREDFLD_PRODUCT_VERSION'].freeze
|
40
|
+
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def logoff
|
45
|
+
require_logged_on!
|
46
|
+
|
47
|
+
stat, _, error = @client.logoff session_id: @session_id
|
48
|
+
|
49
|
+
unless stat == 'STAT_ADMIN_SESSION_STOPPED' || stat == 'STAT_SUCCESS'
|
50
|
+
raise Identikey::Error, "logoff failed: #{stat} - #{error}"
|
51
|
+
end
|
52
|
+
|
53
|
+
@privileges = nil
|
54
|
+
@session_id = nil
|
55
|
+
@product = nil
|
56
|
+
@version = nil
|
57
|
+
@last_logon = nil
|
58
|
+
|
59
|
+
stat == 'STAT_SUCCESS'
|
60
|
+
end
|
61
|
+
|
62
|
+
def alive?
|
63
|
+
return false unless logged_on?
|
64
|
+
|
65
|
+
stat, _ = @client.sessionalive session_id: @session_id
|
66
|
+
|
67
|
+
stat == 'STAT_SUCCESS'
|
68
|
+
end
|
69
|
+
|
70
|
+
def execute(command, *args)
|
71
|
+
kwargs = args.first || {}
|
72
|
+
kwargs.update(session_id: @session_id)
|
73
|
+
@client.public_send(command, kwargs)
|
74
|
+
end
|
75
|
+
|
76
|
+
def all
|
77
|
+
require_logged_on!
|
78
|
+
|
79
|
+
SessionQuery.all session: self
|
80
|
+
end
|
81
|
+
|
82
|
+
def find_digipass(serial_no)
|
83
|
+
require_logged_on!
|
84
|
+
|
85
|
+
Digipass.find session: self, serial_no: serial_no
|
86
|
+
end
|
87
|
+
|
88
|
+
def find_user(username, domain = nil)
|
89
|
+
require_logged_on!
|
90
|
+
|
91
|
+
User.find session: self, username: username, domain: domain || self.domain
|
92
|
+
end
|
93
|
+
|
94
|
+
def inspect
|
95
|
+
"#<#{self.class.name} sid=#@session_id username=#@username domain=#@domain product=#@product>"
|
96
|
+
end
|
97
|
+
|
98
|
+
alias sid session_id
|
99
|
+
|
100
|
+
def logged_on?
|
101
|
+
!@session_id.nil?
|
102
|
+
end
|
103
|
+
|
104
|
+
def require_logged_on!
|
105
|
+
unless logged_on?
|
106
|
+
raise Identikey::Error, "Session is not logged on at the moment"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def parse_privileges(privileges)
|
111
|
+
privileges.split(', ').inject({}) do |h, priv|
|
112
|
+
privilege, status = priv.split(' ')
|
113
|
+
h.update(privilege => status == 'true')
|
114
|
+
end.freeze
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Identikey
|
2
|
+
class Administration < Base
|
3
|
+
|
4
|
+
class SessionQuery
|
5
|
+
def self.all(session:)
|
6
|
+
stat, sessions, error = session.execute(:admin_session_query)
|
7
|
+
|
8
|
+
if stat != 'STAT_SUCCESS'
|
9
|
+
raise Identikey::Error, "query failed: #{stat} - #{error}"
|
10
|
+
end
|
11
|
+
|
12
|
+
sessions.map do |sess|
|
13
|
+
new(
|
14
|
+
idx: sess['ADMINSESSIONFLD_SESSION_IDX'],
|
15
|
+
username: sess['ADMINSESSIONFLD_LOGIN_NAME'],
|
16
|
+
domain: sess['ADMINSESSIONFLD_DOMAIN'],
|
17
|
+
location: sess['ADMINSESSIONFLD_LOCATION'],
|
18
|
+
start_time: sess['ADMINSESSIONFLD_START_TIME']
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :idx, :username, :domain, :location, :start_time
|
24
|
+
|
25
|
+
def initialize(idx:, username:, domain:, location:, start_time:)
|
26
|
+
@idx = idx
|
27
|
+
@username = username
|
28
|
+
@domain = domain
|
29
|
+
@location = location
|
30
|
+
@start_time = start_time
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Identikey
|
2
|
+
class Administration < Base
|
3
|
+
|
4
|
+
class User
|
5
|
+
def self.find(session:, username:, domain:)
|
6
|
+
new(session).find(username, domain)
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :username
|
10
|
+
attr_accessor :email
|
11
|
+
attr_accessor :mobile
|
12
|
+
attr_accessor :phone
|
13
|
+
attr_accessor :created_at
|
14
|
+
attr_accessor :updated_at
|
15
|
+
attr_accessor :has_digipass
|
16
|
+
attr_accessor :domain
|
17
|
+
attr_accessor :ou
|
18
|
+
attr_accessor :digipass
|
19
|
+
attr_accessor :local_auth
|
20
|
+
attr_accessor :backend_auth
|
21
|
+
attr_accessor :disabled
|
22
|
+
attr_accessor :lock_count
|
23
|
+
attr_accessor :locked
|
24
|
+
attr_accessor :last_auth_success_at
|
25
|
+
attr_accessor :expires_at
|
26
|
+
attr_accessor :expired
|
27
|
+
attr_accessor :last_auth_attempt_at
|
28
|
+
|
29
|
+
def initialize(session, user = nil)
|
30
|
+
@session = session
|
31
|
+
|
32
|
+
replace(user) if user
|
33
|
+
end
|
34
|
+
|
35
|
+
def find(username, domain)
|
36
|
+
stat, user, error = @session.execute(
|
37
|
+
:user_execute_VIEW, username: username, domain: domain)
|
38
|
+
|
39
|
+
if stat != 'STAT_SUCCESS'
|
40
|
+
raise Identikey::Error, "Find user failed: #{stat} - #{error}"
|
41
|
+
end
|
42
|
+
|
43
|
+
replace(user, persisted: true)
|
44
|
+
end
|
45
|
+
|
46
|
+
def persisted?
|
47
|
+
@persisted || false
|
48
|
+
end
|
49
|
+
|
50
|
+
def reload
|
51
|
+
find(self.username, self.domain)
|
52
|
+
end
|
53
|
+
|
54
|
+
def save!
|
55
|
+
method = persisted? ? :user_execute_UPDATE : :user_execute_CREATE
|
56
|
+
|
57
|
+
stat, user, error = @session.execute(method, attributes: {
|
58
|
+
USERFLD_BACKEND_AUTH: self.backend_auth,
|
59
|
+
USERFLD_DISABLED: self.disabled,
|
60
|
+
USERFLD_DOMAIN: self.domain,
|
61
|
+
USERFLD_EMAIL: self.email,
|
62
|
+
USERFLD_LOCAL_AUTH: self.local_auth,
|
63
|
+
USERFLD_LOCKED: self.locked,
|
64
|
+
USERFLD_MOBILE: self.mobile,
|
65
|
+
USERFLD_ORGANIZATIONAL_UNIT: self.ou,
|
66
|
+
USERFLD_PHONE: self.phone,
|
67
|
+
USERFLD_USERID: self.username
|
68
|
+
})
|
69
|
+
|
70
|
+
if stat != 'STAT_SUCCESS'
|
71
|
+
raise Identikey::Error, "Save user failed: #{stat} - #{error}"
|
72
|
+
end
|
73
|
+
|
74
|
+
replace(user, persisted: true)
|
75
|
+
end
|
76
|
+
|
77
|
+
def destroy!
|
78
|
+
unless self.persisted?
|
79
|
+
raise Identikey::Error, "User #{self.username} is not persisted"
|
80
|
+
end
|
81
|
+
|
82
|
+
unless self.username && self.domain
|
83
|
+
raise Identikey::Error, "User #{self} is missing username and/or domain"
|
84
|
+
end
|
85
|
+
|
86
|
+
stat, _, error = @session.execute(
|
87
|
+
:user_execute_DELETE, username: username, domain: domain)
|
88
|
+
|
89
|
+
if stat != 'STAT_SUCCESS'
|
90
|
+
raise Identikey::Error, "Delete user failed: #{stat} - #{error}"
|
91
|
+
end
|
92
|
+
|
93
|
+
@persisted = false
|
94
|
+
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
def replace(user, persisted: false)
|
100
|
+
self.username = user['USERFLD_USERID']
|
101
|
+
self.email = user['USERFLD_EMAIL']
|
102
|
+
self.mobile = user['USERFLD_MOBILE']
|
103
|
+
self.phone = user['USERFLD_PHONE']
|
104
|
+
self.created_at = user['USERFLD_CREATE_TIME']
|
105
|
+
self.updated_at = user['USERFLD_MODIFY_TIME']
|
106
|
+
self.has_digipass = user['USERFLD_HAS_DP'] == 'Assigned'
|
107
|
+
self.domain = user['USERFLD_DOMAIN']
|
108
|
+
self.ou = user['USERFLD_ORGANIZATIONAL_UNIT']
|
109
|
+
self.digipass = user['USERFLD_ASSIGNED_DIGIPASS']&.split(',') || [ ]
|
110
|
+
self.local_auth = user['USERFLD_LOCAL_AUTH']
|
111
|
+
self.backend_auth = user['USERFLD_BACKEND_AUTH']
|
112
|
+
self.disabled = user['USERFLD_DISABLED']
|
113
|
+
self.lock_count = user['USERFLD_LOCK_COUNT']
|
114
|
+
self.locked = user['USERFLD_LOCKED']
|
115
|
+
self.last_auth_success_at = user['USERFLD_LASTAUTH_TIME']
|
116
|
+
self.expires_at = user['USERFLD_EXPIRATION_TIME']
|
117
|
+
self.expired = user['USERFLD_EXPIRED']
|
118
|
+
self.last_auth_attempt_at = user['USERFLD_LASTAUTHREQ_TIME']
|
119
|
+
|
120
|
+
@persisted = persisted
|
121
|
+
|
122
|
+
self
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|