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
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 67b4c0932275275ba02caaba0df4638c1117ea8aaf8f7273c784984fdd59dc1c
|
|
4
|
+
data.tar.gz: 0ec91a2dc11db0b3175ce2f2bd9c6170cdab60fae9056dad335e7172e78f7c2f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5b4e155db880b75292d4b7c2a6b4ff891351c3b806dd5dc35cd621f9bbebb8850024f71a574824ac66ded3b7dc3fe9d7e2582330ded9e985f92088432badc395
|
|
7
|
+
data.tar.gz: 8a540fe81ec5abc4d271acafab92270bfc8c29c27e2fe1d1b6084b04a4b5dcf4643e04c972248da5e07028cec89effc41e5c2f300b81e1eec9c92184e23709fc
|
data/README.md
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
# evoleap-licensing
|
|
2
|
+
|
|
3
|
+
Ruby SDK for [evoleap License Manager (ELM)](https://docs.elm.io) — license your Ruby and Rails applications using evoleap's cloud licensing system.
|
|
4
|
+
|
|
5
|
+
Supports server instance registration, per-user session licensing, component checkout/checkin, feature flags, and graceful offline handling.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add to your Gemfile:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem 'evoleap-licensing'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then run:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bundle install
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### 1. Get Your Credentials
|
|
24
|
+
|
|
25
|
+
Sign up at the [ELM portal](https://elm.evoleap.com) and create a product. You'll need:
|
|
26
|
+
|
|
27
|
+
- **Product ID** (GUID) — identifies your product
|
|
28
|
+
- **Public Key** (PEM file) — used to encrypt API communication
|
|
29
|
+
- **License Key** — provided to your customers
|
|
30
|
+
|
|
31
|
+
### 2. Configure the Gem
|
|
32
|
+
|
|
33
|
+
Create a Rails initializer at `config/initializers/evoleap_licensing.rb`:
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
EvoleapLicensing.configure do |config|
|
|
37
|
+
config.host = "https://elm.evoleap.com" # default; change for self-hosted/elm Lite
|
|
38
|
+
config.timeout = 60 # HTTP timeout in seconds
|
|
39
|
+
end
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Store your public key securely (e.g., `config/elm_public_key.pem`).
|
|
43
|
+
|
|
44
|
+
### 3. Register the Server Instance
|
|
45
|
+
|
|
46
|
+
Do this once per deployment (e.g., in a Rake task or during setup):
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
public_key = File.read(Rails.root.join("config", "elm_public_key.pem"))
|
|
50
|
+
|
|
51
|
+
scm = EvoleapLicensing::ServerControlManager.new(
|
|
52
|
+
product_id: "your-product-guid",
|
|
53
|
+
version: "1.0.0",
|
|
54
|
+
public_key: public_key
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
identity = EvoleapLicensing::InstanceIdentity.from_hardware
|
|
58
|
+
result = scm.register(license_key: "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX", instance_identity: identity)
|
|
59
|
+
|
|
60
|
+
if result.success?
|
|
61
|
+
# Persist scm.server_state.to_h as JSON (database, file, etc.)
|
|
62
|
+
File.write("config/server_state.json", scm.server_state.to_h.to_json)
|
|
63
|
+
puts "Instance registered: #{scm.server_state.instance_guid}"
|
|
64
|
+
else
|
|
65
|
+
puts "Registration failed: #{result.error_message}"
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 4. Register Users
|
|
70
|
+
|
|
71
|
+
When a user first accesses your application:
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
public_key = File.read(Rails.root.join("config", "elm_public_key.pem"))
|
|
75
|
+
server_state = EvoleapLicensing::ServerState.new(JSON.parse(File.read("config/server_state.json")))
|
|
76
|
+
|
|
77
|
+
ucm = EvoleapLicensing::UserControlManager.new(
|
|
78
|
+
product_id: "your-product-guid",
|
|
79
|
+
version: "1.0.0",
|
|
80
|
+
instance_id: server_state.instance_guid,
|
|
81
|
+
public_key: public_key
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
user_identity = EvoleapLicensing::UserIdentity.new("uid" => current_user.id.to_s)
|
|
85
|
+
|
|
86
|
+
result = ucm.register(
|
|
87
|
+
user_info: { name: current_user.name, email: current_user.email },
|
|
88
|
+
user_identity: user_identity
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if result.success?
|
|
92
|
+
# Persist ucm.user_state.to_h per user (database JSON column)
|
|
93
|
+
current_user.update!(elm_user_state: ucm.user_state.to_h.to_json)
|
|
94
|
+
end
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 5. Validate Sessions
|
|
98
|
+
|
|
99
|
+
On each request (via `before_action` or middleware):
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
ucm = EvoleapLicensing::UserControlManager.new(
|
|
103
|
+
product_id: "your-product-guid",
|
|
104
|
+
version: "1.0.0",
|
|
105
|
+
instance_id: server_state.instance_guid,
|
|
106
|
+
public_key: public_key,
|
|
107
|
+
user_state: EvoleapLicensing::UserState.new(JSON.parse(current_user.elm_user_state)),
|
|
108
|
+
session_state: EvoleapLicensing::SessionState.new(session[:elm_session_state])
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
user_identity = EvoleapLicensing::UserIdentity.new("uid" => current_user.id.to_s)
|
|
112
|
+
validity = ucm.validate_session(user_identity: user_identity)
|
|
113
|
+
|
|
114
|
+
if validity.valid?
|
|
115
|
+
# Save updated state
|
|
116
|
+
current_user.update!(elm_user_state: ucm.user_state.to_h.to_json)
|
|
117
|
+
session[:elm_session_state] = ucm.session_state.to_h
|
|
118
|
+
|
|
119
|
+
# Check feature flags
|
|
120
|
+
if ucm.session_state.features.include?("premium")
|
|
121
|
+
# Enable premium features
|
|
122
|
+
end
|
|
123
|
+
else
|
|
124
|
+
# Handle invalid license — see Error Handling below
|
|
125
|
+
handle_license_failure(validity)
|
|
126
|
+
end
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Component Checkout/Checkin
|
|
130
|
+
|
|
131
|
+
Components are independently licensable features within your product (seat-based or token-based):
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
# Check out a component during an active session
|
|
135
|
+
result = ucm.check_out_components("ReportGenerator", "DataExport")
|
|
136
|
+
if result.success?
|
|
137
|
+
# Component is now checked out for this session
|
|
138
|
+
# result.components and result.component_entitlements have details
|
|
139
|
+
else
|
|
140
|
+
puts "Checkout failed: #{result.failure_reason}"
|
|
141
|
+
# :insufficient_tokens, :invalid_component, etc.
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Check in when done
|
|
145
|
+
ucm.check_in_components("ReportGenerator", "DataExport")
|
|
146
|
+
|
|
147
|
+
# Query all component statuses
|
|
148
|
+
status = ucm.components_status
|
|
149
|
+
status.components.each do |comp|
|
|
150
|
+
puts "#{comp['name']}: #{comp['license_model']}"
|
|
151
|
+
end
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## License Info
|
|
155
|
+
|
|
156
|
+
Get information about the license owner during an active session:
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
info = ucm.license_info
|
|
160
|
+
if info.success?
|
|
161
|
+
puts "Licensed to: #{info.owner_name}"
|
|
162
|
+
puts "Expires: #{info.expiry}"
|
|
163
|
+
end
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## State Persistence
|
|
167
|
+
|
|
168
|
+
The SDK uses three state objects that must be persisted between requests:
|
|
169
|
+
|
|
170
|
+
| State | Scope | Where to Store | Lifetime |
|
|
171
|
+
|-------|-------|---------------|----------|
|
|
172
|
+
| `ServerState` | Per deployment | JSON file or database | Until re-registration |
|
|
173
|
+
| `UserState` | Per user | Database (JSON column) | Until re-registration |
|
|
174
|
+
| `SessionState` | Per user session | Rails session or Redis | Until session ends |
|
|
175
|
+
|
|
176
|
+
All state objects support `to_h` for serialization and accept a Hash in their constructor:
|
|
177
|
+
|
|
178
|
+
```ruby
|
|
179
|
+
# Save
|
|
180
|
+
json = state.to_h.to_json
|
|
181
|
+
|
|
182
|
+
# Restore
|
|
183
|
+
state = EvoleapLicensing::ServerState.new(JSON.parse(json))
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Rails Database Migration Example
|
|
187
|
+
|
|
188
|
+
```ruby
|
|
189
|
+
class AddElmLicensingToUsers < ActiveRecord::Migration[7.0]
|
|
190
|
+
def change
|
|
191
|
+
add_column :users, :elm_user_state, :jsonb, default: {}
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Error Handling
|
|
197
|
+
|
|
198
|
+
### Connection Failures (Server Unreachable)
|
|
199
|
+
|
|
200
|
+
When the ELM server is unreachable, the SDK uses **grace periods** to allow continued operation:
|
|
201
|
+
|
|
202
|
+
```ruby
|
|
203
|
+
validity = ucm.validate_session(user_identity: user_identity)
|
|
204
|
+
|
|
205
|
+
if validity.valid?
|
|
206
|
+
if validity.in_validation_failure_grace_period?
|
|
207
|
+
# Working offline — warn the user
|
|
208
|
+
flash[:warning] = "License server unreachable. " \
|
|
209
|
+
"License valid until #{validity.grace_period_expiration.strftime('%Y-%m-%d %H:%M')}"
|
|
210
|
+
end
|
|
211
|
+
# Allow access
|
|
212
|
+
else
|
|
213
|
+
if validity.invalid_reason == :service_unreachable
|
|
214
|
+
# Grace period expired or never had a successful validation
|
|
215
|
+
render_service_unavailable
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Registration Failures
|
|
221
|
+
|
|
222
|
+
```ruby
|
|
223
|
+
result = scm.register(license_key: key, instance_identity: identity)
|
|
224
|
+
|
|
225
|
+
unless result.success?
|
|
226
|
+
case result.error_message
|
|
227
|
+
when /Invalid license key/i
|
|
228
|
+
# Customer entered wrong key
|
|
229
|
+
flash[:error] = "The license key is invalid. Please check and try again."
|
|
230
|
+
when /Error contacting/i
|
|
231
|
+
# Network issue during registration
|
|
232
|
+
flash[:error] = "Cannot reach the license server. Please check your connection."
|
|
233
|
+
else
|
|
234
|
+
# Other registration errors (product disabled, activation pending, etc.)
|
|
235
|
+
flash[:error] = "Registration failed: #{result.error_message}"
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Validation Failures
|
|
241
|
+
|
|
242
|
+
The `invalid_reason` on `SessionValidity` and `InstanceValidity` tells you exactly what went wrong:
|
|
243
|
+
|
|
244
|
+
```ruby
|
|
245
|
+
def handle_license_failure(validity)
|
|
246
|
+
case validity.invalid_reason
|
|
247
|
+
when :license_expired
|
|
248
|
+
redirect_to license_expired_path,
|
|
249
|
+
alert: "Your license has expired. Please renew to continue."
|
|
250
|
+
|
|
251
|
+
when :no_seats_available
|
|
252
|
+
redirect_to no_seats_path,
|
|
253
|
+
alert: "All licensed seats are in use. Please try again later or contact your admin."
|
|
254
|
+
|
|
255
|
+
when :session_revoked
|
|
256
|
+
redirect_to session_revoked_path,
|
|
257
|
+
alert: "Your session was revoked by an administrator."
|
|
258
|
+
|
|
259
|
+
when :instance_disabled, :user_disabled
|
|
260
|
+
redirect_to account_disabled_path,
|
|
261
|
+
alert: "Your license has been disabled. Please contact support."
|
|
262
|
+
|
|
263
|
+
when :inconsistent_registration, :inconsistent_user
|
|
264
|
+
# Hardware or identity changed — may need re-registration
|
|
265
|
+
redirect_to reregistration_path,
|
|
266
|
+
alert: "License identity mismatch detected. Re-registration may be required."
|
|
267
|
+
|
|
268
|
+
when :activation_pending
|
|
269
|
+
redirect_to activation_pending_path,
|
|
270
|
+
alert: "Your license is pending activation. Please check with your administrator."
|
|
271
|
+
|
|
272
|
+
when :user_tampering_detected
|
|
273
|
+
# System clock was manipulated
|
|
274
|
+
redirect_to tampering_detected_path,
|
|
275
|
+
alert: "A time inconsistency was detected. Please verify your system clock."
|
|
276
|
+
|
|
277
|
+
when :registration_required
|
|
278
|
+
redirect_to registration_path
|
|
279
|
+
|
|
280
|
+
when :service_unreachable
|
|
281
|
+
render_service_unavailable
|
|
282
|
+
|
|
283
|
+
else
|
|
284
|
+
redirect_to license_error_path,
|
|
285
|
+
alert: "A licensing error occurred. Please try again."
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Component Checkout Failures
|
|
291
|
+
|
|
292
|
+
```ruby
|
|
293
|
+
result = ucm.check_out_components("AdvancedReporting")
|
|
294
|
+
|
|
295
|
+
unless result.success?
|
|
296
|
+
case result.failure_reason
|
|
297
|
+
when :insufficient_tokens
|
|
298
|
+
flash[:error] = "Not enough tokens to use this feature."
|
|
299
|
+
when :invalid_component
|
|
300
|
+
flash[:error] = "This component is not available in your license."
|
|
301
|
+
else
|
|
302
|
+
flash[:error] = "Component checkout failed: #{result.failure_reason}"
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Grace Period Configuration
|
|
308
|
+
|
|
309
|
+
Grace periods allow your application to continue working when the license server is temporarily unreachable:
|
|
310
|
+
|
|
311
|
+
```ruby
|
|
312
|
+
strategy = EvoleapLicensing::ControlStrategy.new(
|
|
313
|
+
# Allow unregistered products to run for 7 days (trial period)
|
|
314
|
+
grace_period_for_unregistered_product: 7 * 24 * 3600,
|
|
315
|
+
|
|
316
|
+
# Automatically start a new session when the current one expires
|
|
317
|
+
start_session_for_expired_session: false
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
scm = EvoleapLicensing::ServerControlManager.new(
|
|
321
|
+
product_id: "your-product-guid",
|
|
322
|
+
version: "1.0.0",
|
|
323
|
+
public_key: public_key,
|
|
324
|
+
strategy: strategy
|
|
325
|
+
)
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
The **validation failure grace period** is set server-side when you register your product. It determines how long a previously-validated instance can operate without contacting the license server.
|
|
329
|
+
|
|
330
|
+
## Example Rails Middleware
|
|
331
|
+
|
|
332
|
+
For applications that need to check licensing on every request:
|
|
333
|
+
|
|
334
|
+
```ruby
|
|
335
|
+
# app/middleware/license_check_middleware.rb
|
|
336
|
+
class LicenseCheckMiddleware
|
|
337
|
+
SKIP_PATHS = %w[/license /health /assets].freeze
|
|
338
|
+
|
|
339
|
+
def initialize(app)
|
|
340
|
+
@app = app
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def call(env)
|
|
344
|
+
request = ActionDispatch::Request.new(env)
|
|
345
|
+
|
|
346
|
+
# Skip licensing for certain paths
|
|
347
|
+
return @app.call(env) if SKIP_PATHS.any? { |p| request.path.start_with?(p) }
|
|
348
|
+
|
|
349
|
+
# Skip if no authenticated user
|
|
350
|
+
return @app.call(env) unless request.session[:user_id]
|
|
351
|
+
|
|
352
|
+
user = User.find_by(id: request.session[:user_id])
|
|
353
|
+
return @app.call(env) unless user
|
|
354
|
+
|
|
355
|
+
ucm = build_user_control_manager(user, request.session)
|
|
356
|
+
user_identity = EvoleapLicensing::UserIdentity.new("uid" => user.id.to_s)
|
|
357
|
+
validity = ucm.validate_session(user_identity: user_identity)
|
|
358
|
+
|
|
359
|
+
if validity.valid?
|
|
360
|
+
# Persist updated state
|
|
361
|
+
user.update_column(:elm_user_state, ucm.user_state.to_h.to_json)
|
|
362
|
+
request.session[:elm_session_state] = ucm.session_state.to_h
|
|
363
|
+
@app.call(env)
|
|
364
|
+
else
|
|
365
|
+
[403, { "Content-Type" => "text/html" },
|
|
366
|
+
["License validation failed: #{validity.invalid_reason}"]]
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
private
|
|
371
|
+
|
|
372
|
+
def build_user_control_manager(user, session)
|
|
373
|
+
public_key = Rails.application.credentials.elm_public_key
|
|
374
|
+
server_state = JSON.parse(File.read(Rails.root.join("config", "server_state.json")))
|
|
375
|
+
|
|
376
|
+
EvoleapLicensing::UserControlManager.new(
|
|
377
|
+
product_id: Rails.application.credentials.elm_product_id,
|
|
378
|
+
version: Rails.application.config.app_version,
|
|
379
|
+
instance_id: server_state["instance_guid"],
|
|
380
|
+
public_key: public_key,
|
|
381
|
+
user_state: EvoleapLicensing::UserState.new(
|
|
382
|
+
user.elm_user_state.is_a?(String) ? JSON.parse(user.elm_user_state) : user.elm_user_state
|
|
383
|
+
),
|
|
384
|
+
session_state: EvoleapLicensing::SessionState.new(session[:elm_session_state])
|
|
385
|
+
)
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Add to `config/application.rb`:
|
|
391
|
+
|
|
392
|
+
```ruby
|
|
393
|
+
config.middleware.use LicenseCheckMiddleware
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Feature Flags
|
|
397
|
+
|
|
398
|
+
Features returned from session validation can be used to gate functionality:
|
|
399
|
+
|
|
400
|
+
```ruby
|
|
401
|
+
# After validate_session succeeds:
|
|
402
|
+
features = ucm.session_state.features
|
|
403
|
+
|
|
404
|
+
if features.include?("advanced_reporting")
|
|
405
|
+
# Show advanced reporting UI
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
if features.include?("api_access")
|
|
409
|
+
# Allow API access
|
|
410
|
+
end
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
## Staging vs. Production
|
|
414
|
+
|
|
415
|
+
ELM provides a free staging server for development. To switch between environments:
|
|
416
|
+
|
|
417
|
+
```ruby
|
|
418
|
+
# config/initializers/evoleap_licensing.rb
|
|
419
|
+
EvoleapLicensing.configure do |config|
|
|
420
|
+
if Rails.env.production?
|
|
421
|
+
config.host = "https://elm.evoleap.com"
|
|
422
|
+
else
|
|
423
|
+
config.host = "https://staging.elm.evoleap.com"
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
You'll need separate product IDs and public keys for staging and production. Store them in Rails credentials:
|
|
429
|
+
|
|
430
|
+
```yaml
|
|
431
|
+
# config/credentials.yml.enc
|
|
432
|
+
elm:
|
|
433
|
+
staging:
|
|
434
|
+
product_id: "staging-product-guid"
|
|
435
|
+
public_key: |
|
|
436
|
+
-----BEGIN PUBLIC KEY-----
|
|
437
|
+
...staging key...
|
|
438
|
+
-----END PUBLIC KEY-----
|
|
439
|
+
production:
|
|
440
|
+
product_id: "production-product-guid"
|
|
441
|
+
public_key: |
|
|
442
|
+
-----BEGIN PUBLIC KEY-----
|
|
443
|
+
...production key...
|
|
444
|
+
-----END PUBLIC KEY-----
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
## API Reference
|
|
448
|
+
|
|
449
|
+
### ServerControlManager
|
|
450
|
+
|
|
451
|
+
| Method | Returns | Description |
|
|
452
|
+
|--------|---------|-------------|
|
|
453
|
+
| `register(license_key:, instance_identity:)` | `RegistrationResult` | Register server instance |
|
|
454
|
+
| `validate_instance(instance_identity:)` | `InstanceValidity` | Validate instance license |
|
|
455
|
+
|
|
456
|
+
### UserControlManager
|
|
457
|
+
|
|
458
|
+
| Method | Returns | Description |
|
|
459
|
+
|--------|---------|-------------|
|
|
460
|
+
| `register(user_info:, user_identity:)` | `RegistrationResult` | Register a user |
|
|
461
|
+
| `validate_session(user_identity:, requested_duration:)` | `SessionValidity` | Begin or extend session |
|
|
462
|
+
| `end_session` | `Boolean` | End active session |
|
|
463
|
+
| `check_out_components(*names)` | `ComponentCheckoutResult` | Check out components |
|
|
464
|
+
| `check_in_components(*names)` | `ComponentCheckinResult` | Check in components |
|
|
465
|
+
| `components_status` | `ComponentsStatus` | Get all component statuses |
|
|
466
|
+
| `license_info` | `LicenseInfo` | Get license owner info |
|
|
467
|
+
|
|
468
|
+
### Result Types
|
|
469
|
+
|
|
470
|
+
- **`RegistrationResult`** — `success?`, `error_message`
|
|
471
|
+
- **`SessionValidity`** — `valid?`, `invalid_reason`, `validity_duration`, `in_validation_failure_grace_period?`, `in_unregistered_grace_period?`, `grace_period_expiration`
|
|
472
|
+
- **`InstanceValidity`** — `valid?`, `invalid_reason`, `in_validation_failure_grace_period?`, `in_unregistered_grace_period?`, `grace_period_expiration`
|
|
473
|
+
- **`ComponentCheckoutResult`** — `success?`, `failure_reason`, `components`, `component_entitlements`
|
|
474
|
+
- **`ComponentCheckinResult`** — `success?`, `failure_reason`
|
|
475
|
+
- **`ComponentsStatus`** — `success?`, `components`, `component_entitlements`, `error_message`
|
|
476
|
+
- **`LicenseInfo`** — `success?`, `owner_name`, `owner_logo_url`, `expiry`, `error_message`
|
|
477
|
+
|
|
478
|
+
## License
|
|
479
|
+
|
|
480
|
+
MIT
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EvoleapLicensing
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :host, :timeout, :api_version, :user_agent
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@host = "https://elm.evoleap.com"
|
|
9
|
+
@timeout = 60
|
|
10
|
+
@api_version = "v3.4"
|
|
11
|
+
@user_agent = "evoleap-licensing-ruby/#{EvoleapLicensing::VERSION}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def api_base_url
|
|
15
|
+
"#{@host}/api/#{@api_version}/auth"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
attr_writer :configuration
|
|
21
|
+
|
|
22
|
+
def configuration
|
|
23
|
+
@configuration ||= Configuration.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def configure
|
|
27
|
+
yield(configuration)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def reset_configuration!
|
|
31
|
+
@configuration = Configuration.new
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EvoleapLicensing
|
|
4
|
+
module ControlLogic
|
|
5
|
+
def self.first_launch_time_valid?(first_launch_time, current_time)
|
|
6
|
+
current_time >= first_launch_time
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.time_inconsistency_detected?(last_trusted_time, previous_times, current_time, current_time_is_trusted)
|
|
10
|
+
return false if last_trusted_time.nil?
|
|
11
|
+
return false if current_time_is_trusted
|
|
12
|
+
|
|
13
|
+
time_to_check = get_time_to_check(previous_times, last_trusted_time)
|
|
14
|
+
current_time < time_to_check
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.in_grace_period_for_unregistered_product?(strategy, first_launch_time, current_time)
|
|
18
|
+
return [false, nil] if strategy.grace_period_for_unregistered_product <= 0
|
|
19
|
+
|
|
20
|
+
expiration = first_launch_time + strategy.grace_period_for_unregistered_product
|
|
21
|
+
ok = current_time < expiration
|
|
22
|
+
[ok, expiration]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.in_grace_period_for_validation_failures?(grace_period, number_of_failed_attempts, last_successful_time, current_time)
|
|
26
|
+
return [false, nil] if last_successful_time.nil? || grace_period.nil? || grace_period <= 0
|
|
27
|
+
|
|
28
|
+
expiration = last_successful_time + grace_period
|
|
29
|
+
ok = number_of_failed_attempts == 0 || current_time < expiration
|
|
30
|
+
[ok, expiration]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.get_time_to_check(times, fallback)
|
|
34
|
+
times.length <= 1 ? fallback : times[1]
|
|
35
|
+
end
|
|
36
|
+
private_class_method :get_time_to_check
|
|
37
|
+
end
|
|
38
|
+
end
|