nexpose_thycotic 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|