evoleap-licensing 1.0.0.12
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/README.md +480 -0
- data/lib/evoleap_licensing/configuration.rb +34 -0
- data/lib/evoleap_licensing/control_logic.rb +38 -0
- data/lib/evoleap_licensing/control_manager_helper.rb +230 -0
- data/lib/evoleap_licensing/control_strategy.rb +16 -0
- data/lib/evoleap_licensing/encryption_handler.rb +42 -0
- data/lib/evoleap_licensing/errors.rb +9 -0
- data/lib/evoleap_licensing/identity/instance_identity.rb +39 -0
- data/lib/evoleap_licensing/identity/user_identity.rb +32 -0
- data/lib/evoleap_licensing/platform_info.rb +75 -0
- data/lib/evoleap_licensing/results/component_checkin_result.rb +16 -0
- data/lib/evoleap_licensing/results/component_checkout_result.rb +18 -0
- data/lib/evoleap_licensing/results/components_status.rb +18 -0
- data/lib/evoleap_licensing/results/instance_validity.rb +46 -0
- data/lib/evoleap_licensing/results/license_info.rb +19 -0
- data/lib/evoleap_licensing/results/registration_result.rb +16 -0
- data/lib/evoleap_licensing/results/session_validity.rb +47 -0
- data/lib/evoleap_licensing/server_control_manager.rb +50 -0
- data/lib/evoleap_licensing/state/server_state.rb +78 -0
- data/lib/evoleap_licensing/state/session_state.rb +68 -0
- data/lib/evoleap_licensing/state/user_state.rb +78 -0
- data/lib/evoleap_licensing/types/component_license_model.rb +22 -0
- data/lib/evoleap_licensing/types/invalid_reason.rb +49 -0
- data/lib/evoleap_licensing/types/validation_status.rb +91 -0
- data/lib/evoleap_licensing/user_control_manager.rb +198 -0
- data/lib/evoleap_licensing/version.rb +6 -0
- data/lib/evoleap_licensing/web_client.rb +65 -0
- data/lib/evoleap_licensing/web_service.rb +168 -0
- data/lib/evoleap_licensing.rb +41 -0
- metadata +129 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "time"
|
|
4
|
+
|
|
5
|
+
module EvoleapLicensing
|
|
6
|
+
module ControlManagerHelper
|
|
7
|
+
def self.register(state)
|
|
8
|
+
initialize_state(state)
|
|
9
|
+
response = yield
|
|
10
|
+
|
|
11
|
+
if response["success"]
|
|
12
|
+
state.registered_at = Time.parse(response["first_registered_at"])
|
|
13
|
+
state.grace_period_for_validation_failures = response["validation_grace_period"]
|
|
14
|
+
state.reset_registration_failures
|
|
15
|
+
state.take_registration_params(response)
|
|
16
|
+
RegistrationResult.new(success: true)
|
|
17
|
+
else
|
|
18
|
+
state.add_registration_failure(Time.now.utc)
|
|
19
|
+
RegistrationResult.new(success: false, error_message: response["error"])
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.validate_instance(strategy, state)
|
|
24
|
+
initialize_state(state)
|
|
25
|
+
current_time = Time.now.utc
|
|
26
|
+
|
|
27
|
+
unless state.registered?
|
|
28
|
+
return get_unregistered_instance_validity(strategy, state, current_time)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
response = yield
|
|
32
|
+
|
|
33
|
+
is_server_time = false
|
|
34
|
+
if response.key?("time")
|
|
35
|
+
current_time = Time.parse(response["time"])
|
|
36
|
+
is_server_time = true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
status = response["status"]
|
|
40
|
+
|
|
41
|
+
if status == ValidationStatus::SUCCESS
|
|
42
|
+
grace_period = response["validation_grace_period"].to_i
|
|
43
|
+
state.grace_period_for_validation_failures = grace_period
|
|
44
|
+
state.features = response["features"] || []
|
|
45
|
+
else
|
|
46
|
+
grace_period = state.grace_period_for_validation_failures
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if status == ValidationStatus::SERVICE_UNREACHABLE || status == ValidationStatus::GENERAL_ERROR
|
|
50
|
+
get_server_unreachable_instance_validity(state, grace_period, current_time, is_server_time)
|
|
51
|
+
else
|
|
52
|
+
get_server_reached_instance_validity(state, current_time, response)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.validate_session(strategy, user_state, session_state, web_service_call)
|
|
57
|
+
initialize_state(user_state)
|
|
58
|
+
current_time = Time.now.utc
|
|
59
|
+
|
|
60
|
+
unless user_state.registered?
|
|
61
|
+
return get_unregistered_session_validity(strategy, user_state, current_time)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
response = web_service_call.call
|
|
65
|
+
|
|
66
|
+
is_server_time = false
|
|
67
|
+
if response.key?("time")
|
|
68
|
+
current_time = Time.parse(response["time"])
|
|
69
|
+
is_server_time = true
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
status = response["status"]
|
|
73
|
+
|
|
74
|
+
if status == ValidationStatus::SUCCESS
|
|
75
|
+
grace_period = response["validation_grace_period"].to_i
|
|
76
|
+
user_state.grace_period_for_validation_failures = grace_period
|
|
77
|
+
user_state.features = response["features"] || []
|
|
78
|
+
session_state.update_from_session_response(response) if session_state.active? == false
|
|
79
|
+
session_state.update_from_extend_response(response) if session_state.active?
|
|
80
|
+
# Handle first session setup
|
|
81
|
+
unless session_state.active?
|
|
82
|
+
session_state.update_from_session_response(response)
|
|
83
|
+
end
|
|
84
|
+
else
|
|
85
|
+
grace_period = user_state.grace_period_for_validation_failures
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
if status == ValidationStatus::SERVICE_UNREACHABLE || status == ValidationStatus::GENERAL_ERROR
|
|
89
|
+
get_server_unreachable_session_validity(user_state, grace_period, current_time, is_server_time)
|
|
90
|
+
else
|
|
91
|
+
get_server_reached_session_validity(user_state, current_time, response)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# --- Private methods ---
|
|
96
|
+
|
|
97
|
+
def self.initialize_state(state)
|
|
98
|
+
state.first_launch_time ||= Time.now.utc
|
|
99
|
+
end
|
|
100
|
+
private_class_method :initialize_state
|
|
101
|
+
|
|
102
|
+
def self.get_unregistered_instance_validity(strategy, state, current_time)
|
|
103
|
+
if !ControlLogic.first_launch_time_valid?(state.first_launch_time, current_time)
|
|
104
|
+
state.number_of_failed_validation_attempts += 1
|
|
105
|
+
state.last_validation_status = ValidationStatus::USER_TAMPERING_DETECTED
|
|
106
|
+
return InstanceValidity.invalid(ValidationStatus::USER_TAMPERING_DETECTED)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
if ControlLogic.time_inconsistency_detected?(state.first_launch_time, state.failed_registration_times, current_time, false)
|
|
110
|
+
state.number_of_failed_validation_attempts += 1
|
|
111
|
+
state.last_validation_status = ValidationStatus::USER_TAMPERING_DETECTED
|
|
112
|
+
return InstanceValidity.invalid(ValidationStatus::USER_TAMPERING_DETECTED)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
ok, expiration = ControlLogic.in_grace_period_for_unregistered_product?(strategy, state.first_launch_time, current_time)
|
|
116
|
+
return InstanceValidity.unregistered_grace_period(expiration) if ok
|
|
117
|
+
|
|
118
|
+
state.number_of_failed_validation_attempts += 1
|
|
119
|
+
state.last_validation_status = ValidationStatus::REGISTRATION_REQUIRED
|
|
120
|
+
InstanceValidity.invalid(ValidationStatus::REGISTRATION_REQUIRED)
|
|
121
|
+
end
|
|
122
|
+
private_class_method :get_unregistered_instance_validity
|
|
123
|
+
|
|
124
|
+
def self.get_server_unreachable_instance_validity(state, grace_period, current_time, is_server_time)
|
|
125
|
+
if ControlLogic.time_inconsistency_detected?(state.last_successful_validation_time, state.failed_validation_times, current_time, is_server_time)
|
|
126
|
+
state.number_of_failed_validation_attempts += 1
|
|
127
|
+
state.last_validation_status = ValidationStatus::USER_TAMPERING_DETECTED
|
|
128
|
+
return InstanceValidity.invalid(ValidationStatus::USER_TAMPERING_DETECTED)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
state.number_of_failed_validation_attempts += 1
|
|
132
|
+
state.add_validation_failure(current_time)
|
|
133
|
+
|
|
134
|
+
if state.last_successful_validation_time.nil?
|
|
135
|
+
return InstanceValidity.invalid(ValidationStatus::SERVICE_UNREACHABLE)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
if state.last_validation_status == ValidationStatus::SUCCESS
|
|
139
|
+
ok, expiration = ControlLogic.in_grace_period_for_validation_failures?(
|
|
140
|
+
grace_period,
|
|
141
|
+
state.number_of_failed_validation_attempts,
|
|
142
|
+
state.last_successful_validation_time,
|
|
143
|
+
current_time
|
|
144
|
+
)
|
|
145
|
+
if ok
|
|
146
|
+
return InstanceValidity.validation_failure_grace_period(expiration)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
InstanceValidity.invalid(ValidationStatus::SERVICE_UNREACHABLE)
|
|
151
|
+
end
|
|
152
|
+
private_class_method :get_server_unreachable_instance_validity
|
|
153
|
+
|
|
154
|
+
def self.get_server_reached_instance_validity(state, current_time, response)
|
|
155
|
+
state.reset_validation_failures
|
|
156
|
+
state.last_successful_validation_time = current_time
|
|
157
|
+
state.last_validation_status = response["status"]
|
|
158
|
+
|
|
159
|
+
if response["status"] == ValidationStatus::SUCCESS
|
|
160
|
+
InstanceValidity.valid
|
|
161
|
+
else
|
|
162
|
+
InstanceValidity.invalid(response["status"])
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
private_class_method :get_server_reached_instance_validity
|
|
166
|
+
|
|
167
|
+
def self.get_unregistered_session_validity(strategy, state, current_time)
|
|
168
|
+
if !ControlLogic.first_launch_time_valid?(state.first_launch_time, current_time)
|
|
169
|
+
state.number_of_failed_validation_attempts += 1
|
|
170
|
+
state.last_validation_status = ValidationStatus::USER_TAMPERING_DETECTED
|
|
171
|
+
return SessionValidity.invalid(ValidationStatus::USER_TAMPERING_DETECTED)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
if ControlLogic.time_inconsistency_detected?(state.first_launch_time, state.failed_registration_times, current_time, false)
|
|
175
|
+
state.number_of_failed_validation_attempts += 1
|
|
176
|
+
state.last_validation_status = ValidationStatus::USER_TAMPERING_DETECTED
|
|
177
|
+
return SessionValidity.invalid(ValidationStatus::USER_TAMPERING_DETECTED)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
ok, expiration = ControlLogic.in_grace_period_for_unregistered_product?(strategy, state.first_launch_time, current_time)
|
|
181
|
+
return SessionValidity.unregistered_grace_period(expiration) if ok
|
|
182
|
+
|
|
183
|
+
state.number_of_failed_validation_attempts += 1
|
|
184
|
+
state.last_validation_status = ValidationStatus::REGISTRATION_REQUIRED
|
|
185
|
+
SessionValidity.invalid(ValidationStatus::REGISTRATION_REQUIRED)
|
|
186
|
+
end
|
|
187
|
+
private_class_method :get_unregistered_session_validity
|
|
188
|
+
|
|
189
|
+
def self.get_server_unreachable_session_validity(state, grace_period, current_time, is_server_time)
|
|
190
|
+
if ControlLogic.time_inconsistency_detected?(state.last_successful_validation_time, state.failed_validation_times, current_time, is_server_time)
|
|
191
|
+
state.number_of_failed_validation_attempts += 1
|
|
192
|
+
state.last_validation_status = ValidationStatus::USER_TAMPERING_DETECTED
|
|
193
|
+
return SessionValidity.invalid(ValidationStatus::USER_TAMPERING_DETECTED)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
state.number_of_failed_validation_attempts += 1
|
|
197
|
+
state.add_validation_failure(current_time)
|
|
198
|
+
|
|
199
|
+
if state.last_successful_validation_time.nil?
|
|
200
|
+
return SessionValidity.invalid(ValidationStatus::SERVICE_UNREACHABLE)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
if state.last_validation_status == ValidationStatus::SUCCESS
|
|
204
|
+
ok, expiration = ControlLogic.in_grace_period_for_validation_failures?(
|
|
205
|
+
grace_period,
|
|
206
|
+
state.number_of_failed_validation_attempts,
|
|
207
|
+
state.last_successful_validation_time,
|
|
208
|
+
current_time
|
|
209
|
+
)
|
|
210
|
+
return SessionValidity.validation_failure_grace_period(expiration) if ok
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
SessionValidity.invalid(ValidationStatus::SERVICE_UNREACHABLE)
|
|
214
|
+
end
|
|
215
|
+
private_class_method :get_server_unreachable_session_validity
|
|
216
|
+
|
|
217
|
+
def self.get_server_reached_session_validity(state, current_time, response)
|
|
218
|
+
state.reset_validation_failures
|
|
219
|
+
state.last_successful_validation_time = current_time
|
|
220
|
+
state.last_validation_status = response["status"]
|
|
221
|
+
|
|
222
|
+
if response["status"] == ValidationStatus::SUCCESS
|
|
223
|
+
SessionValidity.valid(duration: response["session_duration"])
|
|
224
|
+
else
|
|
225
|
+
SessionValidity.invalid(response["status"])
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
private_class_method :get_server_reached_session_validity
|
|
229
|
+
end
|
|
230
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EvoleapLicensing
|
|
4
|
+
class ControlStrategy
|
|
5
|
+
attr_accessor :grace_period_for_unregistered_product, :start_session_for_expired_session
|
|
6
|
+
|
|
7
|
+
def initialize(grace_period_for_unregistered_product: 0, start_session_for_expired_session: false)
|
|
8
|
+
@grace_period_for_unregistered_product = grace_period_for_unregistered_product
|
|
9
|
+
@start_session_for_expired_session = start_session_for_expired_session
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.default
|
|
13
|
+
new
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
require "base64"
|
|
5
|
+
require "json"
|
|
6
|
+
|
|
7
|
+
module EvoleapLicensing
|
|
8
|
+
module EncryptionHandler
|
|
9
|
+
def self.encrypt_payload(params, rsa_cipher)
|
|
10
|
+
aes = OpenSSL::Cipher::AES.new(128, :CBC)
|
|
11
|
+
aes.encrypt
|
|
12
|
+
aes_key = aes.random_key
|
|
13
|
+
iv = aes.random_iv
|
|
14
|
+
|
|
15
|
+
encrypted_aes_key = rsa_cipher.public_encrypt(aes_key)
|
|
16
|
+
encrypted_params = aes.update(params.to_json) + aes.final
|
|
17
|
+
|
|
18
|
+
final = []
|
|
19
|
+
final += encrypted_aes_key.bytes
|
|
20
|
+
final.push(0) # padding
|
|
21
|
+
final += iv.bytes
|
|
22
|
+
final.push(0) # padding
|
|
23
|
+
final += encrypted_params.bytes
|
|
24
|
+
|
|
25
|
+
encoded = Base64.strict_encode64(final.pack("C*"))
|
|
26
|
+
[aes_key, iv, encoded]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.decrypt_response(body, aes_key, aes_iv)
|
|
30
|
+
aes = OpenSSL::Cipher::AES.new(128, :CBC)
|
|
31
|
+
aes.decrypt
|
|
32
|
+
aes.key = aes_key
|
|
33
|
+
aes.iv = aes_iv
|
|
34
|
+
bytes = Base64.decode64(body)
|
|
35
|
+
aes.update(bytes) + aes.final
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.rsa_key(public_key)
|
|
39
|
+
OpenSSL::PKey::RSA.new(public_key.strip)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EvoleapLicensing
|
|
4
|
+
class InstanceIdentity
|
|
5
|
+
attr_reader :data
|
|
6
|
+
|
|
7
|
+
def initialize(data = {})
|
|
8
|
+
@data = data.transform_keys(&:to_s)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def add(key, value)
|
|
12
|
+
@data[key.to_s] = value
|
|
13
|
+
self
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def [](key)
|
|
17
|
+
@data[key.to_s]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_api_format
|
|
21
|
+
@data.map { |k, v| { "key" => k, "value" => v.to_s } }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_h
|
|
25
|
+
@data.dup
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.from_hardware
|
|
29
|
+
identity = new
|
|
30
|
+
info = PlatformInfo.get_info
|
|
31
|
+
info.each { |k, v| identity.add(k, v.is_a?(Array) ? v.join(",") : v) }
|
|
32
|
+
identity
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.from_hash(hash)
|
|
36
|
+
new(hash)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EvoleapLicensing
|
|
4
|
+
class UserIdentity
|
|
5
|
+
attr_reader :data
|
|
6
|
+
|
|
7
|
+
def initialize(data = {})
|
|
8
|
+
@data = data.transform_keys(&:to_s)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def add(key, value)
|
|
12
|
+
@data[key.to_s] = value
|
|
13
|
+
self
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def [](key)
|
|
17
|
+
@data[key.to_s]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_api_format
|
|
21
|
+
@data.map { |k, v| { "key" => k, "value" => v.to_s } }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_h
|
|
25
|
+
@data.dup
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.from_hash(hash)
|
|
29
|
+
new(hash)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "socket"
|
|
4
|
+
|
|
5
|
+
module EvoleapLicensing
|
|
6
|
+
module PlatformInfo
|
|
7
|
+
def self.architecture
|
|
8
|
+
RUBY_PLATFORM
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.mac_addresses
|
|
12
|
+
case RUBY_PLATFORM
|
|
13
|
+
when /linux/
|
|
14
|
+
read_linux_mac_addresses
|
|
15
|
+
when /darwin/
|
|
16
|
+
read_mac_mac_addresses
|
|
17
|
+
when /mswin|mingw/
|
|
18
|
+
read_windows_mac_addresses
|
|
19
|
+
else
|
|
20
|
+
[]
|
|
21
|
+
end
|
|
22
|
+
rescue StandardError
|
|
23
|
+
[]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.hostname
|
|
27
|
+
Socket.gethostname
|
|
28
|
+
rescue StandardError
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.ip_address
|
|
33
|
+
Socket.ip_address_list
|
|
34
|
+
.select { |addr| addr.ipv4? && !addr.ipv4_loopback? }
|
|
35
|
+
.first&.ip_address
|
|
36
|
+
rescue StandardError
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.get_info
|
|
41
|
+
info = {}
|
|
42
|
+
merge_if_available(info, :architecture) { architecture }
|
|
43
|
+
merge_if_available(info, :mac_addresses) { mac_addresses }
|
|
44
|
+
merge_if_available(info, :hostname) { hostname }
|
|
45
|
+
merge_if_available(info, :ip_address) { ip_address }
|
|
46
|
+
info
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.merge_if_available(hash, key)
|
|
50
|
+
value = yield
|
|
51
|
+
hash[key] = value if value && !(value.respond_to?(:empty?) && value.empty?)
|
|
52
|
+
rescue StandardError
|
|
53
|
+
# Skip unavailable info
|
|
54
|
+
end
|
|
55
|
+
private_class_method :merge_if_available
|
|
56
|
+
|
|
57
|
+
def self.read_linux_mac_addresses
|
|
58
|
+
Dir.glob("/sys/class/net/*/address").filter_map do |path|
|
|
59
|
+
addr = File.read(path).strip
|
|
60
|
+
addr unless addr == "00:00:00:00:00:00"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
private_class_method :read_linux_mac_addresses
|
|
64
|
+
|
|
65
|
+
def self.read_mac_mac_addresses
|
|
66
|
+
`ifconfig 2>/dev/null`.scan(/ether\s+([0-9a-f:]+)/i).flatten
|
|
67
|
+
end
|
|
68
|
+
private_class_method :read_mac_mac_addresses
|
|
69
|
+
|
|
70
|
+
def self.read_windows_mac_addresses
|
|
71
|
+
`getmac /fo csv /nh 2>nul`.scan(/([0-9A-F-]{17})/i).flatten
|
|
72
|
+
end
|
|
73
|
+
private_class_method :read_windows_mac_addresses
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EvoleapLicensing
|
|
4
|
+
class ComponentCheckinResult
|
|
5
|
+
attr_reader :failure_reason
|
|
6
|
+
|
|
7
|
+
def initialize(success:, failure_reason: nil)
|
|
8
|
+
@success = success
|
|
9
|
+
@failure_reason = failure_reason
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def success?
|
|
13
|
+
@success
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EvoleapLicensing
|
|
4
|
+
class ComponentCheckoutResult
|
|
5
|
+
attr_reader :failure_reason, :components, :component_entitlements
|
|
6
|
+
|
|
7
|
+
def initialize(success:, failure_reason: nil, components: [], component_entitlements: [])
|
|
8
|
+
@success = success
|
|
9
|
+
@failure_reason = failure_reason
|
|
10
|
+
@components = components
|
|
11
|
+
@component_entitlements = component_entitlements
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def success?
|
|
15
|
+
@success
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EvoleapLicensing
|
|
4
|
+
class ComponentsStatus
|
|
5
|
+
attr_reader :components, :component_entitlements, :error_message
|
|
6
|
+
|
|
7
|
+
def initialize(success:, components: [], component_entitlements: [], error_message: nil)
|
|
8
|
+
@success = success
|
|
9
|
+
@components = components
|
|
10
|
+
@component_entitlements = component_entitlements
|
|
11
|
+
@error_message = error_message
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def success?
|
|
15
|
+
@success
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EvoleapLicensing
|
|
4
|
+
class InstanceValidity
|
|
5
|
+
attr_reader :invalid_reason, :grace_period_expiration
|
|
6
|
+
|
|
7
|
+
def initialize(valid:, invalid_reason: nil,
|
|
8
|
+
in_validation_failure_grace_period: false,
|
|
9
|
+
in_unregistered_grace_period: false,
|
|
10
|
+
grace_period_expiration: nil)
|
|
11
|
+
@valid = valid
|
|
12
|
+
@invalid_reason = invalid_reason
|
|
13
|
+
@in_validation_failure_grace_period = in_validation_failure_grace_period
|
|
14
|
+
@in_unregistered_grace_period = in_unregistered_grace_period
|
|
15
|
+
@grace_period_expiration = grace_period_expiration
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def valid?
|
|
19
|
+
@valid
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def in_validation_failure_grace_period?
|
|
23
|
+
@in_validation_failure_grace_period
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def in_unregistered_grace_period?
|
|
27
|
+
@in_unregistered_grace_period
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.invalid(reason)
|
|
31
|
+
new(valid: false, invalid_reason: InvalidReason.from_validation_status(reason))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.valid
|
|
35
|
+
new(valid: true)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.unregistered_grace_period(expiration)
|
|
39
|
+
new(valid: true, in_unregistered_grace_period: true, grace_period_expiration: expiration)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.validation_failure_grace_period(expiration)
|
|
43
|
+
new(valid: true, in_validation_failure_grace_period: true, grace_period_expiration: expiration)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EvoleapLicensing
|
|
4
|
+
class LicenseInfo
|
|
5
|
+
attr_reader :owner_name, :owner_logo_url, :expiry, :error_message
|
|
6
|
+
|
|
7
|
+
def initialize(success:, owner_name: nil, owner_logo_url: nil, expiry: nil, error_message: nil)
|
|
8
|
+
@success = success
|
|
9
|
+
@owner_name = owner_name
|
|
10
|
+
@owner_logo_url = owner_logo_url
|
|
11
|
+
@expiry = expiry
|
|
12
|
+
@error_message = error_message
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def success?
|
|
16
|
+
@success
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EvoleapLicensing
|
|
4
|
+
class RegistrationResult
|
|
5
|
+
attr_reader :error_message
|
|
6
|
+
|
|
7
|
+
def initialize(success:, error_message: nil)
|
|
8
|
+
@success = success
|
|
9
|
+
@error_message = error_message
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def success?
|
|
13
|
+
@success
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EvoleapLicensing
|
|
4
|
+
class SessionValidity
|
|
5
|
+
attr_reader :invalid_reason, :validity_duration, :grace_period_expiration
|
|
6
|
+
|
|
7
|
+
def initialize(valid:, invalid_reason: nil, validity_duration: nil,
|
|
8
|
+
in_validation_failure_grace_period: false,
|
|
9
|
+
in_unregistered_grace_period: false,
|
|
10
|
+
grace_period_expiration: nil)
|
|
11
|
+
@valid = valid
|
|
12
|
+
@invalid_reason = invalid_reason
|
|
13
|
+
@validity_duration = validity_duration
|
|
14
|
+
@in_validation_failure_grace_period = in_validation_failure_grace_period
|
|
15
|
+
@in_unregistered_grace_period = in_unregistered_grace_period
|
|
16
|
+
@grace_period_expiration = grace_period_expiration
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def valid?
|
|
20
|
+
@valid
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def in_validation_failure_grace_period?
|
|
24
|
+
@in_validation_failure_grace_period
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def in_unregistered_grace_period?
|
|
28
|
+
@in_unregistered_grace_period
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.invalid(reason)
|
|
32
|
+
new(valid: false, invalid_reason: InvalidReason.from_validation_status(reason))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.valid(duration: nil)
|
|
36
|
+
new(valid: true, validity_duration: duration)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.unregistered_grace_period(expiration)
|
|
40
|
+
new(valid: true, in_unregistered_grace_period: true, grace_period_expiration: expiration)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.validation_failure_grace_period(expiration)
|
|
44
|
+
new(valid: true, in_validation_failure_grace_period: true, grace_period_expiration: expiration)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EvoleapLicensing
|
|
4
|
+
class ServerControlManager
|
|
5
|
+
attr_reader :product_id, :version, :public_key, :strategy, :server_state
|
|
6
|
+
|
|
7
|
+
def initialize(product_id:, version:, public_key:, strategy: nil, server_state: nil)
|
|
8
|
+
@product_id = product_id
|
|
9
|
+
@version = version
|
|
10
|
+
@public_key = public_key
|
|
11
|
+
@strategy = strategy || ControlStrategy.default
|
|
12
|
+
@server_state = server_state || ServerState.new
|
|
13
|
+
@client = WebClient.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Register the server instance with the licensing service.
|
|
17
|
+
# Returns a RegistrationResult.
|
|
18
|
+
def register(license_key:, instance_identity:)
|
|
19
|
+
ControlManagerHelper.register(@server_state) do
|
|
20
|
+
WebService.register_instance(
|
|
21
|
+
product_id: @product_id,
|
|
22
|
+
license_key: license_key,
|
|
23
|
+
hardware_identity: instance_identity.to_api_format,
|
|
24
|
+
public_key: @public_key,
|
|
25
|
+
client: @client
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Validate the server instance against the licensing service.
|
|
31
|
+
# Returns an InstanceValidity.
|
|
32
|
+
def validate_instance(instance_identity:)
|
|
33
|
+
ControlManagerHelper.validate_instance(@strategy, @server_state) do
|
|
34
|
+
WebService.validate_instance(
|
|
35
|
+
product_id: @product_id,
|
|
36
|
+
instance_id: @server_state.instance_guid,
|
|
37
|
+
version: @version,
|
|
38
|
+
hardware_identity: instance_identity.to_api_format,
|
|
39
|
+
public_key: @public_key,
|
|
40
|
+
client: @client
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# For testing: allow injecting a custom web client
|
|
46
|
+
def web_client=(client)
|
|
47
|
+
@client = client
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|