identikey 0.3.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 +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
|