licensekit-ruby 0.1.0.alpha.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.
@@ -0,0 +1,314 @@
1
+ # Generated by scripts/generate_from_openapi.rb. Do not edit manually.
2
+
3
+ module LicenseKit
4
+ module Generated
5
+ OPERATION_SCOPES = {
6
+ "assignLicenseFeature" => {
7
+ method: "POST",
8
+ path: "/api/v1/licenses/{id}/features",
9
+ scopes: ["license:write"].freeze
10
+ },
11
+ "blacklistLicenseDevice" => {
12
+ method: "POST",
13
+ path: "/api/v1/licenses/{id}/devices/{device_id}/blacklist",
14
+ scopes: ["device:write"].freeze
15
+ },
16
+ "createAPIKey" => {
17
+ method: "POST",
18
+ path: "/api/v1/api-keys",
19
+ scopes: ["admin"].freeze
20
+ },
21
+ "createCustomer" => {
22
+ method: "POST",
23
+ path: "/api/v1/customers",
24
+ scopes: ["admin"].freeze
25
+ },
26
+ "createFeature" => {
27
+ method: "POST",
28
+ path: "/api/v1/products/{id}/features",
29
+ scopes: ["product:write"].freeze
30
+ },
31
+ "createLicense" => {
32
+ method: "POST",
33
+ path: "/api/v1/licenses",
34
+ scopes: ["license:write"].freeze
35
+ },
36
+ "createOrder" => {
37
+ method: "POST",
38
+ path: "/api/v1/products/{id}/orders",
39
+ scopes: ["product:write"].freeze
40
+ },
41
+ "createPolicy" => {
42
+ method: "POST",
43
+ path: "/api/v1/products/{id}/policies",
44
+ scopes: ["product:write"].freeze
45
+ },
46
+ "createProduct" => {
47
+ method: "POST",
48
+ path: "/api/v1/products",
49
+ scopes: ["product:write"].freeze
50
+ },
51
+ "createProductCustomFieldDefinition" => {
52
+ method: "POST",
53
+ path: "/api/v1/products/{id}/custom-fields",
54
+ scopes: ["product:write"].freeze
55
+ },
56
+ "createProductVersion" => {
57
+ method: "POST",
58
+ path: "/api/v1/products/{id}/versions",
59
+ scopes: ["product:write"].freeze
60
+ },
61
+ "createSubscription" => {
62
+ method: "POST",
63
+ path: "/api/v1/products/{id}/subscriptions",
64
+ scopes: ["product:write"].freeze
65
+ },
66
+ "createWebhookEndpoint" => {
67
+ method: "POST",
68
+ path: "/api/v1/webhooks",
69
+ scopes: ["webhook:write"].freeze
70
+ },
71
+ "deleteCustomer" => {
72
+ method: "DELETE",
73
+ path: "/api/v1/customers/{id}",
74
+ scopes: ["admin"].freeze
75
+ },
76
+ "deletePolicy" => {
77
+ method: "DELETE",
78
+ path: "/api/v1/policies/{id}",
79
+ scopes: ["product:write"].freeze
80
+ },
81
+ "deleteProduct" => {
82
+ method: "DELETE",
83
+ path: "/api/v1/products/{id}",
84
+ scopes: ["product:write"].freeze
85
+ },
86
+ "deleteProductCustomFieldDefinition" => {
87
+ method: "DELETE",
88
+ path: "/api/v1/products/{id}/custom-fields/{field_id}",
89
+ scopes: ["product:write"].freeze
90
+ },
91
+ "deleteWebhookEndpoint" => {
92
+ method: "DELETE",
93
+ path: "/api/v1/webhooks/{id}",
94
+ scopes: ["webhook:write"].freeze
95
+ },
96
+ "getCustomer" => {
97
+ method: "GET",
98
+ path: "/api/v1/customers/{id}",
99
+ scopes: ["admin"].freeze
100
+ },
101
+ "getFeature" => {
102
+ method: "GET",
103
+ path: "/api/v1/features/{id}",
104
+ scopes: ["product:read"].freeze
105
+ },
106
+ "getLicense" => {
107
+ method: "GET",
108
+ path: "/api/v1/licenses/{id}",
109
+ scopes: ["license:read"].freeze
110
+ },
111
+ "getLicenseDevice" => {
112
+ method: "GET",
113
+ path: "/api/v1/licenses/{id}/devices/{device_id}",
114
+ scopes: ["license:read"].freeze
115
+ },
116
+ "getOrder" => {
117
+ method: "GET",
118
+ path: "/api/v1/orders/{id}",
119
+ scopes: ["product:read"].freeze
120
+ },
121
+ "getPolicy" => {
122
+ method: "GET",
123
+ path: "/api/v1/policies/{id}",
124
+ scopes: ["product:read"].freeze
125
+ },
126
+ "getProduct" => {
127
+ method: "GET",
128
+ path: "/api/v1/products/{id}",
129
+ scopes: ["product:read"].freeze
130
+ },
131
+ "getProductCustomFieldDefinition" => {
132
+ method: "GET",
133
+ path: "/api/v1/products/{id}/custom-fields/{field_id}",
134
+ scopes: ["product:read"].freeze
135
+ },
136
+ "getSubscription" => {
137
+ method: "GET",
138
+ path: "/api/v1/subscriptions/{id}",
139
+ scopes: ["product:read"].freeze
140
+ },
141
+ "getWebhookEndpoint" => {
142
+ method: "GET",
143
+ path: "/api/v1/webhooks/{id}",
144
+ scopes: ["webhook:write"].freeze
145
+ },
146
+ "listAPIKeys" => {
147
+ method: "GET",
148
+ path: "/api/v1/api-keys",
149
+ scopes: ["admin"].freeze
150
+ },
151
+ "listCustomerCustomFieldValues" => {
152
+ method: "GET",
153
+ path: "/api/v1/customers/{id}/custom-fields",
154
+ scopes: ["admin"].freeze
155
+ },
156
+ "listCustomers" => {
157
+ method: "GET",
158
+ path: "/api/v1/customers",
159
+ scopes: ["admin"].freeze
160
+ },
161
+ "listEvents" => {
162
+ method: "GET",
163
+ path: "/api/v1/events",
164
+ scopes: ["event:read"].freeze
165
+ },
166
+ "listFeaturesByProduct" => {
167
+ method: "GET",
168
+ path: "/api/v1/products/{id}/features",
169
+ scopes: ["product:read"].freeze
170
+ },
171
+ "listLicenseCustomFieldValues" => {
172
+ method: "GET",
173
+ path: "/api/v1/licenses/{id}/custom-fields",
174
+ scopes: ["license:read"].freeze
175
+ },
176
+ "listLicenseDevices" => {
177
+ method: "GET",
178
+ path: "/api/v1/licenses/{id}/devices",
179
+ scopes: ["license:read"].freeze
180
+ },
181
+ "listLicenseFeatures" => {
182
+ method: "GET",
183
+ path: "/api/v1/licenses/{id}/features",
184
+ scopes: ["license:read"].freeze
185
+ },
186
+ "listLicenses" => {
187
+ method: "GET",
188
+ path: "/api/v1/licenses",
189
+ scopes: ["license:read"].freeze
190
+ },
191
+ "listPoliciesByProduct" => {
192
+ method: "GET",
193
+ path: "/api/v1/products/{id}/policies",
194
+ scopes: ["product:read"].freeze
195
+ },
196
+ "listProductCustomFieldDefinitions" => {
197
+ method: "GET",
198
+ path: "/api/v1/products/{id}/custom-fields",
199
+ scopes: ["product:read"].freeze
200
+ },
201
+ "listProductOrders" => {
202
+ method: "GET",
203
+ path: "/api/v1/products/{id}/orders",
204
+ scopes: ["product:read"].freeze
205
+ },
206
+ "listProductSubscriptions" => {
207
+ method: "GET",
208
+ path: "/api/v1/products/{id}/subscriptions",
209
+ scopes: ["product:read"].freeze
210
+ },
211
+ "listProductVersions" => {
212
+ method: "GET",
213
+ path: "/api/v1/products/{id}/versions",
214
+ scopes: ["product:read"].freeze
215
+ },
216
+ "listProducts" => {
217
+ method: "GET",
218
+ path: "/api/v1/products",
219
+ scopes: ["product:read"].freeze
220
+ },
221
+ "listWebhookEndpoints" => {
222
+ method: "GET",
223
+ path: "/api/v1/webhooks",
224
+ scopes: ["webhook:write"].freeze
225
+ },
226
+ "reinstateLicense" => {
227
+ method: "POST",
228
+ path: "/api/v1/licenses/{id}/reinstate",
229
+ scopes: ["license:write"].freeze
230
+ },
231
+ "removeLicenseFeature" => {
232
+ method: "DELETE",
233
+ path: "/api/v1/licenses/{id}/features/{feature_id}",
234
+ scopes: ["license:write"].freeze
235
+ },
236
+ "renewLicense" => {
237
+ method: "POST",
238
+ path: "/api/v1/licenses/{id}/renew",
239
+ scopes: ["license:write"].freeze
240
+ },
241
+ "resetLicenseDevice" => {
242
+ method: "POST",
243
+ path: "/api/v1/licenses/{id}/devices/{device_id}/reset",
244
+ scopes: ["device:write"].freeze
245
+ },
246
+ "resetLicenseUsage" => {
247
+ method: "POST",
248
+ path: "/api/v1/licenses/{id}/usage/reset",
249
+ scopes: ["license:write"].freeze
250
+ },
251
+ "revokeLicense" => {
252
+ method: "POST",
253
+ path: "/api/v1/licenses/{id}/revoke",
254
+ scopes: ["license:write"].freeze
255
+ },
256
+ "suspendLicense" => {
257
+ method: "POST",
258
+ path: "/api/v1/licenses/{id}/suspend",
259
+ scopes: ["license:write"].freeze
260
+ },
261
+ "transferLicense" => {
262
+ method: "POST",
263
+ path: "/api/v1/licenses/{id}/transfer",
264
+ scopes: ["license:write"].freeze
265
+ },
266
+ "updateCustomer" => {
267
+ method: "PATCH",
268
+ path: "/api/v1/customers/{id}",
269
+ scopes: ["admin"].freeze
270
+ },
271
+ "updateOrder" => {
272
+ method: "PATCH",
273
+ path: "/api/v1/orders/{id}",
274
+ scopes: ["product:write"].freeze
275
+ },
276
+ "updatePolicy" => {
277
+ method: "PATCH",
278
+ path: "/api/v1/policies/{id}",
279
+ scopes: ["product:write"].freeze
280
+ },
281
+ "updateProduct" => {
282
+ method: "PATCH",
283
+ path: "/api/v1/products/{id}",
284
+ scopes: ["product:write"].freeze
285
+ },
286
+ "updateProductCustomFieldDefinition" => {
287
+ method: "PATCH",
288
+ path: "/api/v1/products/{id}/custom-fields/{field_id}",
289
+ scopes: ["product:write"].freeze
290
+ },
291
+ "updateSubscription" => {
292
+ method: "PATCH",
293
+ path: "/api/v1/subscriptions/{id}",
294
+ scopes: ["product:write"].freeze
295
+ },
296
+ "updateWebhookEndpoint" => {
297
+ method: "PATCH",
298
+ path: "/api/v1/webhooks/{id}",
299
+ scopes: ["webhook:write"].freeze
300
+ },
301
+ "upsertCustomerCustomFieldValue" => {
302
+ method: "PUT",
303
+ path: "/api/v1/customers/{id}/custom-fields/{field_id}",
304
+ scopes: ["admin"].freeze
305
+ },
306
+ "upsertLicenseCustomFieldValue" => {
307
+ method: "PUT",
308
+ path: "/api/v1/licenses/{id}/custom-fields/{field_id}",
309
+ scopes: ["license:write"].freeze
310
+ },
311
+ }.freeze
312
+ MANAGEMENT_SCOPES = ["admin", "device:write", "event:read", "license:read", "license:write", "product:read", "product:write", "webhook:write"].freeze
313
+ end
314
+ end
@@ -0,0 +1,16 @@
1
+ module LicenseKit
2
+ MANAGEMENT_SCOPES = Generated::MANAGEMENT_SCOPES
3
+ OPERATION_SCOPES = Generated::OPERATION_SCOPES
4
+
5
+ def self.get_required_scopes(operation_id)
6
+ entry = OPERATION_SCOPES.fetch(operation_id)
7
+ entry[:scopes]
8
+ end
9
+
10
+ def self.has_required_scopes(operation_id, scopes)
11
+ granted = Array(scopes).map(&:to_s)
12
+ return true if granted.include?("admin")
13
+
14
+ get_required_scopes(operation_id).all? { |scope| granted.include?(scope) }
15
+ end
16
+ end
@@ -0,0 +1,53 @@
1
+ module LicenseKit
2
+ class RetryOptions
3
+ attr_reader :retries, :retryable_methods
4
+
5
+ def initialize(retries: 0, retryable_methods: ["GET"])
6
+ @retries = retries
7
+ @retryable_methods = Array(retryable_methods).map(&:to_s).map(&:upcase).freeze
8
+ end
9
+ end
10
+
11
+ class RequestOptions
12
+ attr_reader :headers, :timeout
13
+
14
+ def initialize(headers: nil, timeout: nil)
15
+ @headers = headers
16
+ @timeout = timeout
17
+ end
18
+ end
19
+
20
+ class RawResponse
21
+ attr_reader :status, :headers, :data, :response, :body
22
+
23
+ def initialize(status:, headers:, data:, response:, body:)
24
+ @status = status
25
+ @headers = headers
26
+ @data = data
27
+ @response = response
28
+ @body = body
29
+ end
30
+ end
31
+
32
+ class TransportRequest
33
+ attr_reader :method, :url, :headers, :body, :timeout
34
+
35
+ def initialize(method:, url:, headers:, body:, timeout:)
36
+ @method = method
37
+ @url = url
38
+ @headers = headers
39
+ @body = body
40
+ @timeout = timeout
41
+ end
42
+ end
43
+
44
+ class TransportResponse
45
+ attr_reader :status, :headers, :body
46
+
47
+ def initialize(status:, headers:, body:)
48
+ @status = Integer(status)
49
+ @headers = headers
50
+ @body = body
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,94 @@
1
+ require "base64"
2
+ require "ed25519"
3
+ require "json"
4
+
5
+ module LicenseKit
6
+ class VerificationResult
7
+ attr_reader :key
8
+
9
+ def initialize(ok:, key:)
10
+ @ok = ok
11
+ @key = key
12
+ end
13
+
14
+ def ok
15
+ @ok
16
+ end
17
+ end
18
+
19
+ class PublicKeyStore
20
+ def initialize(keys = nil)
21
+ @keys = {}
22
+ @verify_keys = {}
23
+ Array(keys).each { |key| add(key) }
24
+ end
25
+
26
+ def add(key)
27
+ @keys[key.fetch("kid")] = key
28
+ @verify_keys.delete(key.fetch("kid"))
29
+ end
30
+
31
+ def add_all(keys)
32
+ Array(keys).each { |key| add(key) }
33
+ end
34
+
35
+ def get(kid)
36
+ @keys[kid]
37
+ end
38
+
39
+ def values
40
+ @keys.values
41
+ end
42
+
43
+ def verify_key(kid)
44
+ key = get(kid)
45
+ raise TypeError, "Unknown public key kid: #{kid}" if key.nil?
46
+
47
+ @verify_keys[kid] ||= Ed25519::VerifyKey.new(LicenseKit.send(:decode_base64, key.fetch("public_key")))
48
+ end
49
+ end
50
+
51
+ def self.find_public_key(keys, kid)
52
+ if keys.is_a?(PublicKeyStore)
53
+ keys.get(kid)
54
+ else
55
+ Array(keys).find { |key| key["kid"] == kid }
56
+ end
57
+ end
58
+
59
+ def self.verify_runtime_payload(data, signature, keys)
60
+ public_key = find_public_key(keys, signature.fetch("kid"))
61
+ raise TypeError, "Unknown public key kid: #{signature.fetch('kid')}" if public_key.nil?
62
+
63
+ if public_key.fetch("algorithm") != "Ed25519" || signature.fetch("alg") != "Ed25519"
64
+ raise TypeError, "Unsupported signature algorithm: expected Ed25519, received key=#{public_key.fetch('algorithm')}, signature=#{signature.fetch('alg')}"
65
+ end
66
+
67
+ verify_key = keys.is_a?(PublicKeyStore) ? keys.verify_key(public_key.fetch("kid")) : Ed25519::VerifyKey.new(LicenseKit.send(:decode_base64, public_key.fetch("public_key")))
68
+ payload = stable_json_bytes(data)
69
+ signature_bytes = LicenseKit.send(:decode_base64, signature.fetch("value"))
70
+
71
+ begin
72
+ verify_key.verify(signature_bytes, payload)
73
+ VerificationResult.new(ok: true, key: public_key)
74
+ rescue Ed25519::VerifyError
75
+ VerificationResult.new(ok: false, key: public_key)
76
+ end
77
+ end
78
+
79
+ def self.verify_runtime_result(result, keys)
80
+ verify_runtime_payload(result.fetch("data"), result.fetch("signature"), keys)
81
+ end
82
+
83
+ def self.stable_json_bytes(data)
84
+ JSON.generate(data).encode("UTF-8")
85
+ end
86
+
87
+ def self.decode_base64(value)
88
+ Base64.strict_decode64(value.to_s)
89
+ rescue ArgumentError => e
90
+ raise TypeError, "Malformed base64 input: #{e.message}"
91
+ end
92
+
93
+ private_class_method :stable_json_bytes, :decode_base64
94
+ end
@@ -0,0 +1,3 @@
1
+ module LicenseKit
2
+ VERSION = "0.1.0.alpha.0".freeze
3
+ end
data/lib/licensekit.rb ADDED
@@ -0,0 +1,9 @@
1
+ require_relative "licensekit/version"
2
+ require_relative "licensekit/types"
3
+ require_relative "licensekit/errors"
4
+ require_relative "licensekit/generated/metadata"
5
+ require_relative "licensekit/generated/operation_scopes"
6
+ require_relative "licensekit/client"
7
+ require_relative "licensekit/generated/clients"
8
+ require_relative "licensekit/scopes"
9
+ require_relative "licensekit/verification"