nexpose_thycotic 0.1.0 → 0.2.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 +4 -4
- data/README.md +5 -0
- data/bin/nexpose_thycotic +5 -2
- data/lib/nexpose_thycotic.rb +64 -33
- data/lib/nexpose_thycotic/config/nexpose_thycotic.config +4 -0
- data/lib/nexpose_thycotic/operations.rb +109 -59
- data/lib/nexpose_thycotic/utilities/nx_logger.rb +14 -9
- data/lib/nexpose_thycotic/version.rb +1 -1
- metadata +6 -47
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: def1949052dd9744a7b1803cf32153ca840c3e04
|
4
|
+
data.tar.gz: 616f386251fa0515e8d0df1a4b6a67b88eaa7a0b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb7b619bf897b98bc5910ee284711366dc3be9cbe4dde8653c9e23fc981d9373cd54c0fb96bf43f04ece34f5e2fd76f1bccafdeddc9979fe73a8044996686ce6
|
7
|
+
data.tar.gz: 4922dcd8ee3930d85c0ddffb277fb38251c943bce86d906b6e54093e8b14cbfe9090a7357b13786fb98f2707ccbf64cbfe56273099b77ab89185ab660fee3b57
|
data/README.md
CHANGED
@@ -63,6 +63,11 @@ The gem is available as open source under the terms of the [MIT License](http://
|
|
63
63
|
|
64
64
|
## Changelog
|
65
65
|
|
66
|
+
### 0.2.0
|
67
|
+
- User may configure the gem to delete or preserve existing credentials.
|
68
|
+
- A customisable comment is now attached to password retrieval requests.
|
69
|
+
- Multiple credentials for the same address may be imported.
|
70
|
+
|
66
71
|
### 0.0.7
|
67
72
|
- User now has the option to configure the gem using a configuration file as well as with environment variables. Nexpose and Thycotic options have been added to the configuration file.
|
68
73
|
- Added an encryption configuration file. Usernames and passwords within the configuration file are now encrypted when the application runs.
|
data/bin/nexpose_thycotic
CHANGED
@@ -24,15 +24,18 @@ options = settings[:options]
|
|
24
24
|
|
25
25
|
vault_options = { url: thycotic_options[:thycotic_url],
|
26
26
|
username: thycotic_options[:thycotic_username],
|
27
|
-
password: thycotic_options[:thycotic_password]
|
27
|
+
password: thycotic_options[:thycotic_password],
|
28
|
+
comment: thycotic_options[:comment] }
|
28
29
|
|
29
30
|
nexpose_options = { nexpose_ip: nexpose_options[:nexpose_url],
|
30
31
|
nexpose_username: nexpose_options[:nexpose_username],
|
31
32
|
nexpose_password: nexpose_options[:nexpose_password],
|
32
33
|
nexpose_port: nexpose_options[:nexpose_port],
|
33
34
|
sites: options[:sites],
|
35
|
+
clear_creds: options[:clear_creds],
|
34
36
|
logging_enabled: options[:logging_enabled],
|
35
37
|
log_level: options[:log_level],
|
36
|
-
log_console: options[:log_console] }
|
38
|
+
log_console: options[:log_console] }
|
39
|
+
|
37
40
|
NexposeThycotic.update_credentials(vault_options, nexpose_options)
|
38
41
|
|
data/lib/nexpose_thycotic.rb
CHANGED
@@ -3,55 +3,86 @@ require 'nexpose_thycotic/operations'
|
|
3
3
|
require 'nexpose_thycotic/utilities/nx_logger'
|
4
4
|
require 'nexpose'
|
5
5
|
|
6
|
-
module NexposeThycotic
|
6
|
+
module NexposeThycotic
|
7
7
|
def self.update_credentials(vault_options, nexpose_options = nil)
|
8
|
+
#TODO: Should we add the logging alias' from the SCCM gem?
|
8
9
|
log = NexposeThycotic::NxLogger.instance
|
9
|
-
log.setup_statistics_collection(NexposeThycotic::VENDOR,
|
10
|
-
NexposeThycotic::PRODUCT,
|
10
|
+
log.setup_statistics_collection(NexposeThycotic::VENDOR,
|
11
|
+
NexposeThycotic::PRODUCT,
|
11
12
|
NexposeThycotic::VERSION)
|
12
|
-
log.setup_logging(true,
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
log.setup_logging(true,
|
14
|
+
nexpose_options[:log_level],
|
15
|
+
nexpose_options[:log_console])
|
16
|
+
log.info('Starting integration.')
|
17
|
+
|
18
|
+
ss = ThycoticOperations.new(vault_options[:url], vault_options[:comment])
|
19
|
+
log.info("Logging into Thycotic at #{vault_options[:url]}")
|
17
20
|
token = ss.authenticate(vault_options[:username], vault_options[:password])
|
18
|
-
|
19
|
-
@nx = NexposeOperations.new(nexpose_options[:nexpose_ip],
|
20
|
-
nexpose_options[:nexpose_username],
|
21
|
-
nexpose_options[:nexpose_password],
|
21
|
+
|
22
|
+
@nx = NexposeOperations.new(nexpose_options[:nexpose_ip],
|
23
|
+
nexpose_options[:nexpose_username],
|
24
|
+
nexpose_options[:nexpose_password],
|
22
25
|
nexpose_options[:nexpose_port])
|
23
|
-
log.
|
26
|
+
log.info('Processing sites')
|
24
27
|
|
25
28
|
nexpose_options[:sites].each do |site_id|
|
26
29
|
log.log_debug_message("Processing site #{site_id}")
|
27
|
-
|
30
|
+
addresses = @nx.get_device_addresses(site_id)
|
31
|
+
|
28
32
|
site_credentials = []
|
33
|
+
if nexpose_options[:clear_creds]
|
34
|
+
log.debug('Clearing existing credentials.')
|
35
|
+
else
|
36
|
+
log.debug('Preserving existing credentials.')
|
37
|
+
site_credentials = @nx.get_existing_credentials(site_id)
|
38
|
+
end
|
39
|
+
|
40
|
+
addresses.each do |addr|
|
41
|
+
log.debug("Getting credentials for #{addr}")
|
42
|
+
summaries = ss.get_secret_summaries(token, addr)
|
43
|
+
next if summaries.empty?
|
44
|
+
|
45
|
+
log.debug("Discovered #{summaries.count} credentials for #{addr}")
|
46
|
+
|
47
|
+
summaries.each do |summary|
|
48
|
+
cred = self.create_credential(ss, token, summary, addr)
|
29
49
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
# Gets OS
|
37
|
-
asset_data = {}
|
38
|
-
asset_data[:ip] = ip_from
|
39
|
-
res = ss.get_secret_simple(token, secret_summary[:secret_id])
|
40
|
-
asset_data[:username] = res[:username]
|
41
|
-
asset_data[:password] = res[:password]
|
42
|
-
credential = Nexpose::Credential.for_service(ss.check_type(secret_summary[:secret_type]),
|
43
|
-
asset_data[:username],
|
44
|
-
asset_data[:password],
|
45
|
-
nil,
|
46
|
-
ip.from)
|
47
|
-
site_credentials.push(credential)
|
50
|
+
site_credentials.reject! do |c|
|
51
|
+
c.host_restriction == cred.host_restriction &&
|
52
|
+
c.user_name == cred.user_name && c.service == cred.service
|
53
|
+
end
|
54
|
+
|
55
|
+
site_credentials.push(cred)
|
48
56
|
end
|
49
57
|
end
|
58
|
+
|
50
59
|
@nx.save_site(site_id, site_credentials)
|
51
|
-
log.
|
60
|
+
log.info("Finished processing #{site_id}")
|
52
61
|
end
|
53
62
|
end
|
54
63
|
|
64
|
+
def self.create_credential(connection, token, secret_summary, address)
|
65
|
+
# Get the kind of credential e.g. ssh, cifs
|
66
|
+
service = connection.check_type(secret_summary[:secret_type])
|
67
|
+
|
68
|
+
# Define the credential's base properties
|
69
|
+
cred_name = secret_summary[:secret_name]
|
70
|
+
cred_desc = "Thycotic imported Credential for #{address}"
|
71
|
+
|
72
|
+
credential = Nexpose::SiteCredentials.for_service(cred_name,
|
73
|
+
-1,
|
74
|
+
cred_desc,
|
75
|
+
address,
|
76
|
+
nil,
|
77
|
+
service)
|
78
|
+
# Retrieve and store the credentials
|
79
|
+
res = connection.get_secret(token, secret_summary[:secret_id])
|
80
|
+
credential.user_name = res[:username]
|
81
|
+
credential.password = res[:password]
|
82
|
+
|
83
|
+
credential
|
84
|
+
end
|
85
|
+
|
55
86
|
def self.set_variables(options)
|
56
87
|
settings = {}
|
57
88
|
options.each_key do |key|
|
@@ -13,6 +13,8 @@
|
|
13
13
|
# Filters to specific sites one per line, leave empty to generate for all sites.
|
14
14
|
:sites:
|
15
15
|
- '1'
|
16
|
+
# Delete existing credentials for the site before importing
|
17
|
+
:clear_creds: true
|
16
18
|
:nexpose_options:
|
17
19
|
# (M) Nexpose IP address
|
18
20
|
:nexpose_url: 127.0.0.1
|
@@ -29,6 +31,8 @@
|
|
29
31
|
:thycotic_username: user
|
30
32
|
# (M) The password for the above user
|
31
33
|
:thycotic_password: password
|
34
|
+
# (M) The comment used when retrieving each password
|
35
|
+
:comment: 'Retrieved via Thycotic gem.'
|
32
36
|
:encryption_options:
|
33
37
|
# (M) Path to the encryption.config file
|
34
38
|
:directory: ../../config/encryption.config
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'nexpose_thycotic/utilities/nx_logger'
|
2
|
+
require 'set'
|
3
|
+
|
1
4
|
module NexposeThycotic
|
2
5
|
class NexposeOperations
|
3
6
|
require 'nexpose'
|
@@ -7,28 +10,58 @@ module NexposeThycotic
|
|
7
10
|
def initialize(ip, user, pass, port = 3780)
|
8
11
|
@nsc = Connection.new(ip, user, pass, port)
|
9
12
|
@nsc.login
|
10
|
-
|
13
|
+
|
14
|
+
@logger = NexposeThycotic::NxLogger.instance
|
15
|
+
@logger.on_connect(ip, port, @nsc.session_id, '{}')
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_site(site_id)
|
19
|
+
Site.load(@nsc, site_id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_devices_from_site(site_id)
|
23
|
+
@nsc.list_devices(site_id)
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_device_addresses(site_id)
|
27
|
+
site = get_site(site_id)
|
28
|
+
included_targets = site.included_addresses
|
29
|
+
|
30
|
+
# Get a list of known IP addresses from a site
|
31
|
+
devices = get_devices_from_site(site_id)
|
32
|
+
device_ips = devices.map { |d| d.address }
|
33
|
+
|
34
|
+
hosts = []
|
35
|
+
ips = device_ips.to_set
|
36
|
+
included_targets.each do |address|
|
37
|
+
if address.instance_of?(Nexpose::HostName)
|
38
|
+
hosts << address.host
|
39
|
+
elsif address.to.nil?
|
40
|
+
ips.add(address.from)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
@logger.debug("Discovered #{hosts.count} hosts and #{ips.count} IPs.")
|
45
|
+
ips.to_a + hosts
|
11
46
|
end
|
12
47
|
|
13
|
-
def
|
14
|
-
site =
|
15
|
-
site.
|
48
|
+
def get_existing_credentials(site_id)
|
49
|
+
site = get_site(site_id)
|
50
|
+
site.site_credentials
|
16
51
|
end
|
17
52
|
|
18
53
|
def save_site(site_id, credentials)
|
19
|
-
site =
|
20
|
-
site.
|
54
|
+
site = get_site(site_id)
|
55
|
+
site.site_credentials = credentials
|
21
56
|
site.save(@nsc)
|
22
57
|
end
|
23
58
|
|
24
59
|
def delete_site_credentials(site_id)
|
25
|
-
|
26
|
-
site.credentials = []
|
27
|
-
site.save(@nsc)
|
60
|
+
save_site(site_id, [])
|
28
61
|
end
|
29
62
|
|
30
63
|
def start_scan(site_id)
|
31
|
-
site =
|
64
|
+
site = get_site(site_id)
|
32
65
|
site.scan(@nsc)
|
33
66
|
end
|
34
67
|
|
@@ -40,11 +73,12 @@ module NexposeThycotic
|
|
40
73
|
class ThycoticOperations
|
41
74
|
require 'savon'
|
42
75
|
attr_accessor :client
|
43
|
-
def initialize(url = nil)
|
76
|
+
def initialize(url = nil, comment = '')
|
44
77
|
# log: true, log_level: :info
|
45
|
-
@client = Savon.client(
|
46
|
-
|
47
|
-
|
78
|
+
@client = Savon.client(wsdl: url, ssl_verify_mode: :none)
|
79
|
+
|
80
|
+
# Comment used when retrieving passwords
|
81
|
+
@comment = comment
|
48
82
|
end
|
49
83
|
|
50
84
|
def operations
|
@@ -70,67 +104,83 @@ module NexposeThycotic
|
|
70
104
|
end
|
71
105
|
|
72
106
|
def authenticate(username, password)
|
73
|
-
|
74
|
-
|
75
|
-
auth_result =
|
76
|
-
|
107
|
+
operation = :authenticate
|
108
|
+
message = { username: username, password: password }
|
109
|
+
auth_result = get_secret_result(operation, message)
|
110
|
+
check_for_errors(auth_result)
|
77
111
|
@token = auth_result[:token]
|
78
112
|
end
|
79
113
|
|
80
|
-
def parse_field(secret_response_result,
|
81
|
-
items = secret_response_result[:secret][:items]
|
82
|
-
|
83
|
-
|
84
|
-
return item[:value]
|
85
|
-
end
|
86
|
-
end
|
114
|
+
def parse_field(secret_response_result, field_name)
|
115
|
+
items = secret_response_result[:secret][:items][:secret_item]
|
116
|
+
item = items.find { |i| i[:field_display_name].casecmp(field_name) == 0 }
|
117
|
+
item[:value]
|
87
118
|
end
|
88
119
|
|
89
|
-
def
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
secret_summary = secret_result[:envelope][:body][:search_secrets_by_field_value_response][:search_secrets_by_field_value_result][:secret_summaries][:secret_summary]
|
94
|
-
secret_info = { secret_id: secret_summary[:secret_id], secret_type: secret_summary[:secret_type_name] }
|
95
|
-
end
|
120
|
+
def get_secret_result(operation, message)
|
121
|
+
secret = @client.call(operation, message: message)
|
122
|
+
resp = secret.hash[:envelope][:body]["#{operation}_response".to_sym]
|
123
|
+
resp["#{operation}_result".to_sym]
|
96
124
|
end
|
97
125
|
|
98
|
-
def
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
126
|
+
def get_secret_summaries(token, ip)
|
127
|
+
operation = :search_secrets_by_field_value
|
128
|
+
message = {
|
129
|
+
token: token,
|
130
|
+
fieldName: 'machine',
|
131
|
+
searchTerm: ip,
|
132
|
+
showDeleted: true,
|
133
|
+
showRestricted: true
|
134
|
+
}
|
135
|
+
secret_result = get_secret_result(operation, message)
|
136
|
+
|
137
|
+
secrets = []
|
138
|
+
unless secret_result[:secret_summaries].nil?
|
139
|
+
summaries = secret_result[:secret_summaries][:secret_summary]
|
140
|
+
|
141
|
+
# Ensure summaries is iterable
|
142
|
+
summaries = [summaries] if summaries.is_a?(Hash)
|
143
|
+
|
144
|
+
summaries.each do |secret|
|
145
|
+
secret_info = {
|
146
|
+
secret_id: secret[:secret_id],
|
147
|
+
secret_type: secret[:secret_type_name],
|
148
|
+
secret_name: secret[:secret_name]
|
149
|
+
}
|
150
|
+
secrets << secret_info
|
151
|
+
end
|
152
|
+
end
|
106
153
|
|
154
|
+
secrets
|
107
155
|
end
|
108
156
|
|
109
157
|
def get_secret(token, secret_id)
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
username = parse_field(
|
124
|
-
password = parse_field(
|
158
|
+
operation = :get_secret
|
159
|
+
message = { token: token,
|
160
|
+
secretId: secret_id,
|
161
|
+
loadSettingsAndPermissions: false,
|
162
|
+
"codeResponses" => {"CodeResponse" =>
|
163
|
+
[{
|
164
|
+
"ErrorCode" => "COMMENT",
|
165
|
+
"Comment" => @comment
|
166
|
+
}]
|
167
|
+
}}
|
168
|
+
secret_result = get_secret_result(operation, message)
|
169
|
+
|
170
|
+
check_for_errors(secret_result)
|
171
|
+
username = parse_field(secret_result, 'Username')
|
172
|
+
password = parse_field(secret_result, 'Password')
|
173
|
+
|
174
|
+
{ username: username, password: password }
|
125
175
|
end
|
126
176
|
|
127
|
-
def
|
177
|
+
def check_for_errors(result)
|
128
178
|
errors = result[:errors]
|
129
|
-
|
179
|
+
unless errors.blank?
|
130
180
|
puts errors
|
181
|
+
#TODO: Logging
|
131
182
|
raise Exception.new(errors)
|
132
183
|
end
|
133
|
-
|
134
184
|
end
|
135
185
|
end
|
136
|
-
end
|
186
|
+
end
|
@@ -31,7 +31,11 @@ module NexposeThycotic
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
def setup_logging(enabled,
|
34
|
+
def setup_logging(enabled,
|
35
|
+
log_level = 'info',
|
36
|
+
stdout=false,
|
37
|
+
rotation='weekly',
|
38
|
+
log_size=nil)
|
35
39
|
@stdout = stdout
|
36
40
|
|
37
41
|
log_message('Logging disabled.') unless enabled || @log.nil?
|
@@ -46,9 +50,9 @@ module NexposeThycotic
|
|
46
50
|
io = IO.for_fd(IO.sysopen(@logger_file, 'a'), 'a')
|
47
51
|
io.autoclose = false
|
48
52
|
io.sync = true
|
49
|
-
@log = Logger.new(io,
|
50
|
-
@log.level = if log_level.to_s.casecmp('info') == 0
|
51
|
-
Logger::INFO
|
53
|
+
@log = Logger.new(io, rotation, log_size)
|
54
|
+
@log.level = if log_level.to_s.casecmp('info') == 0
|
55
|
+
Logger::INFO
|
52
56
|
else
|
53
57
|
Logger::DEBUG
|
54
58
|
end
|
@@ -58,11 +62,12 @@ module NexposeThycotic
|
|
58
62
|
def create_calls
|
59
63
|
levels = [:info, :debug, :error, :warn]
|
60
64
|
levels.each do |level|
|
61
|
-
method_name =
|
62
|
-
define_singleton_method(
|
65
|
+
method_name = "log_#{level.to_s}_message"
|
66
|
+
define_singleton_method(method_name) do |message|
|
63
67
|
puts message if @stdout
|
64
68
|
@log.send(level, message) unless !@enabled || @log.nil?
|
65
69
|
end
|
70
|
+
singleton_class.send(:alias_method, level, method_name.to_sym)
|
66
71
|
end
|
67
72
|
end
|
68
73
|
|
@@ -93,8 +98,8 @@ module NexposeThycotic
|
|
93
98
|
end
|
94
99
|
|
95
100
|
def get_product(product, version)
|
96
|
-
return nil if ((product.nil? || product.empty?) ||
|
97
|
-
|
101
|
+
return nil if ((product.nil? || product.empty?) ||
|
102
|
+
(version.nil? || version.empty?))
|
98
103
|
|
99
104
|
product.gsub!('-', '_')
|
100
105
|
product.slice! product.rindex('_') until product.count('_') <= 1
|
@@ -148,7 +153,7 @@ module NexposeThycotic
|
|
148
153
|
log_stat_message('Statistics collection not enabled.')
|
149
154
|
return
|
150
155
|
end
|
151
|
-
|
156
|
+
|
152
157
|
begin
|
153
158
|
payload = generate_payload value
|
154
159
|
send(nexpose_address, nexpose_port, session_id, payload)
|
metadata
CHANGED
@@ -1,43 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nexpose_thycotic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Damian Finol, Adam Robinson
|
8
|
+
- David Valente
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date:
|
12
|
+
date: 2018-04-06 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.5'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.5'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
14
|
- !ruby/object:Gem::Dependency
|
42
15
|
name: savon
|
43
16
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,34 +25,20 @@ dependencies:
|
|
52
25
|
- - ">="
|
53
26
|
- !ruby/object:Gem::Version
|
54
27
|
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rubyntlm
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :runtime
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
28
|
- !ruby/object:Gem::Dependency
|
70
29
|
name: nexpose
|
71
30
|
requirement: !ruby/object:Gem::Requirement
|
72
31
|
requirements:
|
73
32
|
- - "~>"
|
74
33
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
34
|
+
version: '7.2'
|
76
35
|
type: :runtime
|
77
36
|
prerelease: false
|
78
37
|
version_requirements: !ruby/object:Gem::Requirement
|
79
38
|
requirements:
|
80
39
|
- - "~>"
|
81
40
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
41
|
+
version: '7.2'
|
83
42
|
- !ruby/object:Gem::Dependency
|
84
43
|
name: symmetric-encryption
|
85
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -143,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
102
|
version: '0'
|
144
103
|
requirements: []
|
145
104
|
rubyforge_project:
|
146
|
-
rubygems_version: 2.
|
105
|
+
rubygems_version: 2.6.14
|
147
106
|
signing_key:
|
148
107
|
specification_version: 4
|
149
108
|
summary: Nexpose Thycotic Gem Integration
|