onlyoffice-docs_integration_sdk 0.1.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 +7 -0
- data/lib/onlyoffice/docs_integration_sdk/document_editor/config.rb +1479 -0
- data/lib/onlyoffice/docs_integration_sdk/document_editor/config_test.rb +1713 -0
- data/lib/onlyoffice/docs_integration_sdk/document_editor.rb +29 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client/command.rb +417 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client/command_test.rb +672 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client/conversion.rb +477 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client/conversion_test.rb +682 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client/healthcheck.rb +101 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client/healthcheck_test.rb +209 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client/jwt.rb +116 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client/jwt_test.rb +70 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client/response.rb +73 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client/response_test.rb +49 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client/service.rb +44 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client/ua.rb +31 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client/ua_test.rb +35 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client.rb +321 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server/client_test.rb +1259 -0
- data/lib/onlyoffice/docs_integration_sdk/document_server.rb +29 -0
- data/lib/onlyoffice/docs_integration_sdk/document_storage/callback.rb +276 -0
- data/lib/onlyoffice/docs_integration_sdk/document_storage/callback_test.rb +291 -0
- data/lib/onlyoffice/docs_integration_sdk/document_storage.rb +29 -0
- data/lib/onlyoffice/docs_integration_sdk/jwt.rb +448 -0
- data/lib/onlyoffice/docs_integration_sdk/jwt_test.rb +598 -0
- data/lib/onlyoffice/docs_integration_sdk/test_test.rb +113 -0
- data/lib/onlyoffice/docs_integration_sdk/version.rb +26 -0
- data/lib/onlyoffice/docs_integration_sdk/version_test.rb +33 -0
- data/lib/onlyoffice/docs_integration_sdk.rb +31 -0
- data/lib/onlyoffice.rb +21 -0
- metadata +283 -0
@@ -0,0 +1,448 @@
|
|
1
|
+
#
|
2
|
+
# (c) Copyright Ascensio System SIA 2025
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
# typed: strict
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
require "json"
|
21
|
+
require "jwt"
|
22
|
+
require "sorbet-runtime"
|
23
|
+
require "uri"
|
24
|
+
|
25
|
+
module Onlyoffice
|
26
|
+
module DocsIntegrationSdk
|
27
|
+
# JwtEncoding is an interface that describes methods that must be
|
28
|
+
# implemented to be considered a JWT encoder.
|
29
|
+
#
|
30
|
+
# @since 0.1.0
|
31
|
+
module JwtEncoding
|
32
|
+
extend T::Sig
|
33
|
+
extend T::Helpers
|
34
|
+
interface!
|
35
|
+
|
36
|
+
# @param u The URI to encode.
|
37
|
+
# @return An encoded URI.
|
38
|
+
# @since 0.1.0
|
39
|
+
sig {abstract.params(u: URI::HTTP).returns(URI::HTTP)}
|
40
|
+
def encode_uri(u); end
|
41
|
+
|
42
|
+
# @param p The payload to encode.
|
43
|
+
# @return An encoded header.
|
44
|
+
# @since 0.1.0
|
45
|
+
sig {abstract.params(p: T.untyped).returns(String)}
|
46
|
+
def encode_header(p); end
|
47
|
+
|
48
|
+
# @param p The payload to encode.
|
49
|
+
# @return An encoded body.
|
50
|
+
# @since 0.1.0
|
51
|
+
sig {abstract.params(p: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped])}
|
52
|
+
def encode_body(p); end
|
53
|
+
|
54
|
+
# @param p The payload to encode.
|
55
|
+
# @return An encoded token.
|
56
|
+
# @since 0.1.0
|
57
|
+
sig {abstract.params(p: T::Hash[T.untyped, T.untyped]).returns(String)}
|
58
|
+
def encode(p); end
|
59
|
+
end
|
60
|
+
|
61
|
+
# JwtDecoding is an interface that describes methods that must be
|
62
|
+
# implemented to be considered a JWT decoder.
|
63
|
+
#
|
64
|
+
# @since 0.1.0
|
65
|
+
module JwtDecoding
|
66
|
+
extend T::Sig
|
67
|
+
extend T::Helpers
|
68
|
+
interface!
|
69
|
+
|
70
|
+
# @param u The URI to decode.
|
71
|
+
# @return A decoded URI.
|
72
|
+
# @since 0.1.0
|
73
|
+
sig {abstract.params(u: URI::HTTP).returns(URI::HTTP)}
|
74
|
+
def decode_uri(u); end
|
75
|
+
|
76
|
+
# @param h The header to decode.
|
77
|
+
# @return A decoded header.
|
78
|
+
# @since 0.1.0
|
79
|
+
sig {abstract.params(h: String).returns(T.untyped)}
|
80
|
+
def decode_header(h); end
|
81
|
+
|
82
|
+
# @param b The body to decode.
|
83
|
+
# @return A decoded body.
|
84
|
+
# @since 0.1.0
|
85
|
+
sig {abstract.params(b: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped])}
|
86
|
+
def decode_body(b); end
|
87
|
+
|
88
|
+
# @param t The token to decode.
|
89
|
+
# @return A decoded token.
|
90
|
+
# @since 0.1.0
|
91
|
+
sig {abstract.params(t: String).returns(T::Hash[T.untyped, T.untyped])}
|
92
|
+
def decode(t); end
|
93
|
+
end
|
94
|
+
|
95
|
+
# JwtCoding is an interface that describes methods that must be
|
96
|
+
# implemented to be considered a JWT encoder and decoder.
|
97
|
+
#
|
98
|
+
# @since 0.1.0
|
99
|
+
module JwtCoding
|
100
|
+
extend T::Sig
|
101
|
+
extend T::Helpers
|
102
|
+
include JwtEncoding
|
103
|
+
include JwtDecoding
|
104
|
+
interface!
|
105
|
+
end
|
106
|
+
|
107
|
+
# Jwt is an implementation of the {JwtCoding} interface. Under the hood, it
|
108
|
+
# uses Ruby JWT library to encode and decode JSON Web Tokens.
|
109
|
+
#
|
110
|
+
# This class has its own set of known claims. If an unfamiliar claim is
|
111
|
+
# passed to the class during its initialization, it will not be considered.
|
112
|
+
#
|
113
|
+
# Before the encoding process by Ruby JWT, this class forms a claims list.
|
114
|
+
# The {ExpClaim} will be included in the list only if its {ExpClaim.ttl} is
|
115
|
+
# not zero, otherwise it will be ignored.
|
116
|
+
#
|
117
|
+
# Before the decoding process by Ruby JWT, this class forms a claims list.
|
118
|
+
# The {ExpClaim} will be included in the list only if its {ExpClaim.leeway}
|
119
|
+
# is not zero, otherwise it will be ignored.
|
120
|
+
#
|
121
|
+
# [RFC 7519 Reference](https://datatracker.ietf.org/doc/html/rfc7519/),
|
122
|
+
# [Ruby JWT Reference](https://github.com/jwt/ruby-jwt/)
|
123
|
+
#
|
124
|
+
# @since 0.1.0
|
125
|
+
class Jwt
|
126
|
+
extend T::Sig
|
127
|
+
include JwtCoding
|
128
|
+
|
129
|
+
# Claim is an interface for claims that can be added to a JWT.
|
130
|
+
#
|
131
|
+
# [RFC 7519 Reference](https://datatracker.ietf.org/doc/html/rfc7519/#section-4.1)
|
132
|
+
#
|
133
|
+
# @since 0.1.0
|
134
|
+
module Claim
|
135
|
+
extend T::Sig
|
136
|
+
extend T::Helpers
|
137
|
+
include Kernel
|
138
|
+
interface!
|
139
|
+
end
|
140
|
+
|
141
|
+
# ExpClaim is a claim that represents the expiration time of a JWT.
|
142
|
+
#
|
143
|
+
# [RFC 7519 Reference](https://datatracker.ietf.org/doc/html/rfc7519/#section-4.1.4)
|
144
|
+
#
|
145
|
+
# @since 0.1.0
|
146
|
+
class ExpClaim
|
147
|
+
extend T::Sig
|
148
|
+
include Claim
|
149
|
+
|
150
|
+
# ttl is the time allowed for the token to be valid, measured in
|
151
|
+
# seconds.
|
152
|
+
#
|
153
|
+
# @since 0.1.0
|
154
|
+
sig {returns(Integer)}
|
155
|
+
attr_reader :ttl
|
156
|
+
|
157
|
+
# leeway is the time allowed to account for clock skew, measured in
|
158
|
+
# seconds.
|
159
|
+
#
|
160
|
+
# @since 0.1.0
|
161
|
+
sig {returns(Integer)}
|
162
|
+
attr_reader :leeway
|
163
|
+
|
164
|
+
# initialize initializes a new ExpClaim instance.
|
165
|
+
#
|
166
|
+
# @param ttl
|
167
|
+
# The time allowed for the token to be valid, measured in seconds.
|
168
|
+
# @param leeway
|
169
|
+
# The time allowed to account for clock skew, measured in seconds.
|
170
|
+
#
|
171
|
+
# @since 0.1.0
|
172
|
+
sig {params(ttl: Integer, leeway: Integer).void}
|
173
|
+
def initialize(ttl: 300, leeway: 30)
|
174
|
+
@ttl = ttl
|
175
|
+
@leeway = leeway
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# IatClaim is a claim that represents the time at which the JWT was
|
180
|
+
# issued.
|
181
|
+
#
|
182
|
+
# [RFC 7519 Reference](https://datatracker.ietf.org/doc/html/rfc7519/#section-4.1.6)
|
183
|
+
#
|
184
|
+
# @since 0.1.0
|
185
|
+
class IatClaim
|
186
|
+
extend T::Sig
|
187
|
+
include Claim
|
188
|
+
|
189
|
+
# initialize initializes a new IatClaim instance.
|
190
|
+
#
|
191
|
+
# @since 0.1.0
|
192
|
+
sig {void}
|
193
|
+
def initialize; end
|
194
|
+
end
|
195
|
+
|
196
|
+
# secret is the secret key used to encode and decode JWTs.
|
197
|
+
#
|
198
|
+
# @since 0.1.0
|
199
|
+
sig {returns(String)}
|
200
|
+
attr_reader :secret
|
201
|
+
|
202
|
+
# algorithm is the algorithm used to encode and decode JWTs.
|
203
|
+
#
|
204
|
+
# @since 0.1.0
|
205
|
+
sig {returns(String)}
|
206
|
+
attr_reader :algorithm
|
207
|
+
|
208
|
+
# claims is the list of claims to add to the JWT.
|
209
|
+
#
|
210
|
+
# @since 0.1.0
|
211
|
+
sig {returns(T::Array[Claim])}
|
212
|
+
attr_reader :claims
|
213
|
+
|
214
|
+
# initialize initializes a new Jwt instance. It makes a shallow copy of
|
215
|
+
# the claims list.
|
216
|
+
#
|
217
|
+
# @param secret The secret key used to encode and decode JWTs.
|
218
|
+
# @param algorithm The algorithm used to encode and decode JWTs.
|
219
|
+
# @param claims The list of claims to add to the JWT.
|
220
|
+
# @since 0.1.0
|
221
|
+
sig {params(secret: String, algorithm: String, claims: T::Array[Claim]).void}
|
222
|
+
def initialize(secret:, algorithm: "HS256", claims: [ExpClaim.new, IatClaim.new])
|
223
|
+
@secret = secret
|
224
|
+
@algorithm = algorithm
|
225
|
+
@claims = T.let(claims.clone, T::Array[Claim])
|
226
|
+
end
|
227
|
+
|
228
|
+
# encode_uri encodes a URI by adding a token to the query string. This
|
229
|
+
# method returns a shallow copy of the URI with the token added. It does
|
230
|
+
# not modify the original URI.
|
231
|
+
#
|
232
|
+
# @example
|
233
|
+
# uri = jwt.encode_uri(uri)
|
234
|
+
#
|
235
|
+
# @param u The URI to encode.
|
236
|
+
# @return An encoded URI.
|
237
|
+
# @raise Inherited from {encode}.
|
238
|
+
# @since 0.1.0
|
239
|
+
sig {override.params(u: URI::HTTP).returns(URI::HTTP)}
|
240
|
+
def encode_uri(u)
|
241
|
+
u = u.clone
|
242
|
+
|
243
|
+
q = u.query
|
244
|
+
if q.nil?
|
245
|
+
q = ""
|
246
|
+
end
|
247
|
+
|
248
|
+
f = URI.decode_www_form(q)
|
249
|
+
|
250
|
+
t = encode({"url" => u.to_s})
|
251
|
+
f.append(["token", t])
|
252
|
+
|
253
|
+
u.query = URI.encode_www_form(f)
|
254
|
+
|
255
|
+
u
|
256
|
+
end
|
257
|
+
|
258
|
+
# encode_header encodes a payload by adding a token to the header.
|
259
|
+
#
|
260
|
+
# @example
|
261
|
+
# req["Authorization"] = "Bearer #{jwt.encode_header(payload)}"
|
262
|
+
#
|
263
|
+
# @param p The payload to encode.
|
264
|
+
# @return An encoded header.
|
265
|
+
# @raise Inherited from {encode}.
|
266
|
+
# @since 0.1.0
|
267
|
+
sig {override.params(p: T.untyped).returns(String)}
|
268
|
+
def encode_header(p)
|
269
|
+
encode({"payload" => p})
|
270
|
+
end
|
271
|
+
|
272
|
+
# encode_body encodes a payload by adding a token to the body. This method
|
273
|
+
# returns a shallow copy of the payload with the token added. It does not
|
274
|
+
# modify the original payload.
|
275
|
+
#
|
276
|
+
# @example
|
277
|
+
# req.body = jwt.encode_body(payload).to_json
|
278
|
+
#
|
279
|
+
# @param p The payload to encode.
|
280
|
+
# @return An encoded body.
|
281
|
+
# @raise Inherited from {encode}.
|
282
|
+
# @since 0.1.0
|
283
|
+
sig {override.params(p: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped])}
|
284
|
+
def encode_body(p)
|
285
|
+
p = p.clone
|
286
|
+
p["token"] = encode(p)
|
287
|
+
p
|
288
|
+
end
|
289
|
+
|
290
|
+
# encode encodes a payload.
|
291
|
+
#
|
292
|
+
# @example
|
293
|
+
# token = jwt.encode(payload)
|
294
|
+
#
|
295
|
+
# @param p The payload to encode
|
296
|
+
# @return An encoded token.
|
297
|
+
# @raise Inherited from JWT.encode of Ruby JWT.
|
298
|
+
# @since 0.1.0
|
299
|
+
sig {override.params(p: T::Hash[T.untyped, T.untyped]).returns(String)}
|
300
|
+
def encode(p)
|
301
|
+
p = p.clone
|
302
|
+
|
303
|
+
t = Time.now.utc.to_i
|
304
|
+
|
305
|
+
for c in @claims
|
306
|
+
if c.is_a?(ExpClaim) && c.ttl != 0
|
307
|
+
p["exp"] = t + c.ttl
|
308
|
+
next
|
309
|
+
end
|
310
|
+
|
311
|
+
if c.is_a?(IatClaim)
|
312
|
+
p["iat"] = t
|
313
|
+
next
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
JWT.encode(p, @secret, @algorithm)
|
318
|
+
end
|
319
|
+
|
320
|
+
# decode_uri decodes a URI by extracting a token from the query string.
|
321
|
+
# This method returns a shallow copy of the URI with the token removed. It
|
322
|
+
# does not modify the original URI.
|
323
|
+
#
|
324
|
+
# @example
|
325
|
+
# uri = jwt.decode_uri(uri)
|
326
|
+
#
|
327
|
+
# @param u The URI to decode
|
328
|
+
# @return A decoded URI
|
329
|
+
# @raise [ArgumentError] If the URI does not have a query string.
|
330
|
+
# @raise [KeyError] If the query string does not have a 'token' key.
|
331
|
+
# @raise [KeyError] If the decoded token does not have a 'url' key.
|
332
|
+
# @raise [TypeError] If the 'url' key is not a String.
|
333
|
+
# @raise [TypeError] If the 'url' key is not a URI::HTTP.
|
334
|
+
# @raise Inherited from {decode}.
|
335
|
+
# @since 0.1.0
|
336
|
+
sig {override.params(u: URI::HTTP).returns(URI::HTTP)}
|
337
|
+
def decode_uri(u)
|
338
|
+
q = u.query
|
339
|
+
if q.nil?
|
340
|
+
raise ArgumentError, "Expected the URI to have a query string, but it did not"
|
341
|
+
end
|
342
|
+
|
343
|
+
f = URI.decode_www_form(q)
|
344
|
+
|
345
|
+
c = f.assoc("token")
|
346
|
+
if !c
|
347
|
+
raise KeyError, "Expected the query string to have a 'token' key, but it did not"
|
348
|
+
end
|
349
|
+
|
350
|
+
t = c[1]
|
351
|
+
|
352
|
+
d = decode(t)
|
353
|
+
if !d.key?("url")
|
354
|
+
raise KeyError, "Expected the decoded token to have a 'url' key, but it did not"
|
355
|
+
end
|
356
|
+
|
357
|
+
s = d["url"]
|
358
|
+
if !s.is_a?(String)
|
359
|
+
raise TypeError, "Expected the 'url' key to be a String, but it was a #{s.class}"
|
360
|
+
end
|
361
|
+
|
362
|
+
g = URI.parse(s)
|
363
|
+
if !g.is_a?(URI::HTTP)
|
364
|
+
raise TypeError, "Expected the 'url' key to be a URI::HTTP, but it was a #{g.class}"
|
365
|
+
end
|
366
|
+
|
367
|
+
g
|
368
|
+
end
|
369
|
+
|
370
|
+
# decode_header decodes a header.
|
371
|
+
#
|
372
|
+
# @example
|
373
|
+
# header = "Bearer ***"
|
374
|
+
# payload = jwt.decode_header(header[7..])
|
375
|
+
#
|
376
|
+
# @param h The header to decode
|
377
|
+
# @return A decoded header
|
378
|
+
# @raise [KeyError] If the decoded header does not have a 'payload' key.
|
379
|
+
# @raise Inherited from {decode}.
|
380
|
+
sig {override.params(h: String).returns(T.untyped)}
|
381
|
+
def decode_header(h)
|
382
|
+
d = decode(h)
|
383
|
+
if !d.key?("payload")
|
384
|
+
raise KeyError, "Expected the decoded header to have a 'payload' key, but it did not"
|
385
|
+
end
|
386
|
+
|
387
|
+
d["payload"]
|
388
|
+
end
|
389
|
+
|
390
|
+
# decode_body decodes a body by extracting a token from the body. This
|
391
|
+
# method returns a shallow copy of the body with the token removed. It
|
392
|
+
# does not modify the original body.
|
393
|
+
#
|
394
|
+
# @example
|
395
|
+
# json = JSON.parse(res.body)
|
396
|
+
# payload = jwt.decode_body(json)
|
397
|
+
#
|
398
|
+
# @param b The body to decode.
|
399
|
+
# @return A decoded body.
|
400
|
+
# @raise [KeyError] If the body does not have a 'token' key.
|
401
|
+
# @raise [TypeError] If the 'token' key is not a String.
|
402
|
+
# @raise Inherited from {decode}.
|
403
|
+
sig {override.params(b: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped])}
|
404
|
+
def decode_body(b)
|
405
|
+
if !b.key?("token")
|
406
|
+
raise KeyError, "Expected the body to have a 'token' key, but it did not"
|
407
|
+
end
|
408
|
+
|
409
|
+
t = b["token"]
|
410
|
+
if !t.is_a?(String)
|
411
|
+
raise TypeError, "Expected the 'token' key to be a String, but it was a #{t.class}"
|
412
|
+
end
|
413
|
+
|
414
|
+
decode(t)
|
415
|
+
end
|
416
|
+
|
417
|
+
# decode decodes a token.
|
418
|
+
#
|
419
|
+
# @example
|
420
|
+
# payload = jwt.decode(token)
|
421
|
+
#
|
422
|
+
# @param t The token to decode.
|
423
|
+
# @return A decoded token.
|
424
|
+
# @raise Inherited from JWT.decode of Ruby JWT.
|
425
|
+
sig {override.params(t: String).returns(T::Hash[T.untyped, T.untyped])}
|
426
|
+
def decode(t)
|
427
|
+
o = T.let({}, T::Hash[Symbol, T.untyped])
|
428
|
+
|
429
|
+
o[:algorithm] = @algorithm
|
430
|
+
|
431
|
+
for c in @claims
|
432
|
+
if c.is_a?(ExpClaim) && c.leeway != 0
|
433
|
+
o[:exp_leeway] = c.leeway
|
434
|
+
next
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
d, _ = JWT.decode(t, @secret, true, o)
|
439
|
+
d = T.cast(d, T::Hash[T.untyped, T.untyped])
|
440
|
+
|
441
|
+
d.delete("exp")
|
442
|
+
d.delete("iat")
|
443
|
+
|
444
|
+
d
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
|
+
end
|