libkeycloak 1.0.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/keycloak.rb +583 -0
- metadata +152 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '092425c0716ee45f8777708c27b2898b5a7af43edbe9c5cc194aa5998de2833d'
|
4
|
+
data.tar.gz: 75c900a8efc01cab2af35a0ce53e40435ff0b1e3c8870c7a7991c48cfe36efd3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 73f0c5a082c94a0e51fa439d1182e20f9d8f8218374ad499c7a7b7591e66da0cd41c21fdc3a8ae6748fe7a39a05d9cdfacea6cf3ec901a2b41d2ceb2ff22f362
|
7
|
+
data.tar.gz: 48917e889a59329faa173044761c206463f0196fc55e9362eefaab15b46132c1c4b6c4cdb85c27f76833b0866b61beb09d57f67725d6374bb98211647501dd75
|
data/keycloak.rb
ADDED
@@ -0,0 +1,583 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require 'ffi/libc'
|
3
|
+
|
4
|
+
module Keycloak_FFI
|
5
|
+
extend FFI::Library
|
6
|
+
|
7
|
+
ffi_lib 'keycloak'
|
8
|
+
|
9
|
+
# Error
|
10
|
+
enum :KeycloakErrorCode, [
|
11
|
+
:OK, 0,
|
12
|
+
:JSON_Parse,
|
13
|
+
:JSON_Field,
|
14
|
+
:No_JSON_Field,
|
15
|
+
:CURL,
|
16
|
+
:HTTP,
|
17
|
+
:JWTDecode,
|
18
|
+
:JWTInvalidClaimKey,
|
19
|
+
:OutOfMemory,
|
20
|
+
]
|
21
|
+
|
22
|
+
class KeycloakErrorData < FFI::Union
|
23
|
+
layout :str, :string,
|
24
|
+
:code, :ulong
|
25
|
+
end
|
26
|
+
|
27
|
+
class KeycloakError < FFI::Struct
|
28
|
+
layout :errcode, :KeycloakErrorCode,
|
29
|
+
:data, KeycloakErrorData.val
|
30
|
+
end
|
31
|
+
|
32
|
+
attach_function :keycloak_errmsg, [KeycloakError.val, :pointer], :void
|
33
|
+
|
34
|
+
# Client
|
35
|
+
class KeycloakRealm < FFI::Struct
|
36
|
+
layout :_json, :pointer,
|
37
|
+
:public_key, :string,
|
38
|
+
:token_service, :string,
|
39
|
+
:account_service, :string
|
40
|
+
|
41
|
+
def public_key
|
42
|
+
self[:public_key]
|
43
|
+
end
|
44
|
+
|
45
|
+
def token_service
|
46
|
+
self[:token_service]
|
47
|
+
end
|
48
|
+
|
49
|
+
def account_service
|
50
|
+
self[:account_service]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class KeycloakClient < FFI::ManagedStruct
|
55
|
+
layout :_json, :pointer,
|
56
|
+
:realm, :string,
|
57
|
+
:resource, :string,
|
58
|
+
:auth_server_url, :string,
|
59
|
+
:secret, :string,
|
60
|
+
:realm_info, KeycloakRealm.val
|
61
|
+
|
62
|
+
def self.release ptr
|
63
|
+
unless ptr.nil?
|
64
|
+
Keycloak_FFI.keycloak_destroy_client(ptr)
|
65
|
+
FFI::LibC.free(ptr)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
attach_function :keycloak_create_client, [
|
71
|
+
KeycloakClient.ptr,
|
72
|
+
:string, # jsonstr
|
73
|
+
:char # options
|
74
|
+
], KeycloakError.val
|
75
|
+
|
76
|
+
attach_function :keycloak_destroy_client, [:pointer], :void
|
77
|
+
|
78
|
+
# Token service
|
79
|
+
|
80
|
+
class KeycloakToken < FFI::Struct
|
81
|
+
layout :token, :string,
|
82
|
+
:expiration, :int
|
83
|
+
|
84
|
+
def token
|
85
|
+
self[:token]
|
86
|
+
end
|
87
|
+
|
88
|
+
def expiration
|
89
|
+
self[:token]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class KeycloakTokens < FFI::ManagedStruct
|
94
|
+
layout :_json, :pointer,
|
95
|
+
:access_token, KeycloakToken.val,
|
96
|
+
:refresh_token, KeycloakToken.val,
|
97
|
+
:token_type, :string,
|
98
|
+
:not_before_policy, :int,
|
99
|
+
:session_state, :string,
|
100
|
+
:scope, :string
|
101
|
+
|
102
|
+
def self.release ptr
|
103
|
+
unless ptr.nil?
|
104
|
+
Keycloak_FFI.keycloak_destroy_tokens(ptr)
|
105
|
+
FFI::LibC.free(ptr)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def access_token
|
110
|
+
return self[:access_token]
|
111
|
+
end
|
112
|
+
|
113
|
+
def refresh_token
|
114
|
+
return self[:refresh_token]
|
115
|
+
end
|
116
|
+
|
117
|
+
def token_type
|
118
|
+
return self[:token_type]
|
119
|
+
end
|
120
|
+
|
121
|
+
def not_before_policy
|
122
|
+
return self[:not_before_policy]
|
123
|
+
end
|
124
|
+
|
125
|
+
def session_state
|
126
|
+
return self[:session_state]
|
127
|
+
end
|
128
|
+
|
129
|
+
def scope
|
130
|
+
return self[:scope]
|
131
|
+
end
|
132
|
+
|
133
|
+
def scopes
|
134
|
+
return self.scope.split " "
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
attach_function :keycloak_destroy_tokens, [:pointer], :void
|
139
|
+
|
140
|
+
attach_function :keycloak_get_token, [
|
141
|
+
KeycloakClient.ptr,
|
142
|
+
:string, # user
|
143
|
+
:string, # pass
|
144
|
+
:pointer, # scopes
|
145
|
+
:int, # scopes_len
|
146
|
+
KeycloakTokens.ptr, # out tokens
|
147
|
+
:pointer # out err_response
|
148
|
+
], KeycloakError.val
|
149
|
+
|
150
|
+
attach_function :keycloak_refresh_token, [
|
151
|
+
KeycloakClient.ptr,
|
152
|
+
KeycloakToken.val, # refresh token
|
153
|
+
:pointer, # scopes
|
154
|
+
:int, # scopes len
|
155
|
+
KeycloakTokens.ptr, # out tokens
|
156
|
+
:pointer # out err_response
|
157
|
+
], KeycloakError.val
|
158
|
+
|
159
|
+
class KeycloakUserinfo < FFI::ManagedStruct
|
160
|
+
layout :_json, :pointer,
|
161
|
+
:sub, :string,
|
162
|
+
:email_verified, :bool,
|
163
|
+
:name, :string,
|
164
|
+
:preferred_username, :string,
|
165
|
+
:given_name, :string,
|
166
|
+
:family_name, :string,
|
167
|
+
:email, :string
|
168
|
+
|
169
|
+
def sub
|
170
|
+
self[:sub]
|
171
|
+
end
|
172
|
+
|
173
|
+
def email_verified
|
174
|
+
self[:email_verified]
|
175
|
+
end
|
176
|
+
|
177
|
+
def name
|
178
|
+
self[:name]
|
179
|
+
end
|
180
|
+
|
181
|
+
def preferred_username
|
182
|
+
self[:preferred_username]
|
183
|
+
end
|
184
|
+
|
185
|
+
def given_name
|
186
|
+
self[:given_name]
|
187
|
+
end
|
188
|
+
|
189
|
+
def family_name
|
190
|
+
self[:family_name]
|
191
|
+
end
|
192
|
+
|
193
|
+
def email
|
194
|
+
self[:email]
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.release ptr
|
198
|
+
unless ptr.nil?
|
199
|
+
Keycloak_FFI.keycloak_destroy_userinfo(ptr)
|
200
|
+
FFI::LibC.free(ptr)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
attach_function :keycloak_destroy_userinfo, [:pointer], :void
|
206
|
+
|
207
|
+
attach_function :keycloak_get_userinfo, [
|
208
|
+
KeycloakClient.ptr,
|
209
|
+
KeycloakTokens.ptr,
|
210
|
+
KeycloakUserinfo.ptr, # out
|
211
|
+
:pointer # out err_response
|
212
|
+
], KeycloakError.val
|
213
|
+
|
214
|
+
KeycloakJWTValidationResult = enum :VALID, 0,
|
215
|
+
:ISS_FAILURE, 1 << 0,
|
216
|
+
:SUB_FAILURE, 1 << 1,
|
217
|
+
:AUD_FAILURE, 1 << 2,
|
218
|
+
:JTI_FAILURE, 1 << 3,
|
219
|
+
:EXP_FAILURE, 1 << 4,
|
220
|
+
:NBF_FAILURE, 1 << 5,
|
221
|
+
:IAT_FAILURE, 1 << 6,
|
222
|
+
:SIGNATURE_VERIFICATION_FAILURE, 1 << 7,
|
223
|
+
:TYP_FAILURE, 1 << 8
|
224
|
+
|
225
|
+
class KeycloakJWT < FFI::ManagedStruct
|
226
|
+
layout :data, :pointer,
|
227
|
+
:len, :long
|
228
|
+
|
229
|
+
def [](claim_name)
|
230
|
+
claim = KeycloakJWTClaim.new
|
231
|
+
Keycloak_FFI.keycloak_jwt_get_claim(
|
232
|
+
self,
|
233
|
+
claim_name.to_s,
|
234
|
+
claim
|
235
|
+
)
|
236
|
+
return Keycloak._jwt_claim_value(claim)
|
237
|
+
end
|
238
|
+
|
239
|
+
def self.release ptr
|
240
|
+
unless ptr.nil?
|
241
|
+
Keycloak_FFI.keycloak_destroy_jwt(ptr)
|
242
|
+
FFI::LibC.free(ptr)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
attach_function :keycloak_destroy_jwt, [:pointer], :void
|
248
|
+
|
249
|
+
enum :KeycloakClaimType, [
|
250
|
+
:String, 0,
|
251
|
+
:Int, 1,
|
252
|
+
:Double, 2,
|
253
|
+
:Bool, 3,
|
254
|
+
:Null, 4,
|
255
|
+
:Array, 5,
|
256
|
+
:Object, 6,
|
257
|
+
:Other, 7,
|
258
|
+
]
|
259
|
+
|
260
|
+
class KeycloakJWTClaimValue < FFI::Union
|
261
|
+
layout :stringvalue, :string,
|
262
|
+
:intvalue, :int,
|
263
|
+
:doublevalue, :double,
|
264
|
+
:boolvalue, :bool,
|
265
|
+
:datavalue, :pointer
|
266
|
+
end
|
267
|
+
|
268
|
+
class KeycloakJWTClaim < FFI::Struct
|
269
|
+
layout :key, :string,
|
270
|
+
:value, KeycloakJWTClaimValue.val,
|
271
|
+
:type, :KeycloakClaimType
|
272
|
+
end
|
273
|
+
|
274
|
+
attach_function :keycloak_jwt_validation_reason_string, [KeycloakJWTValidationResult], :string
|
275
|
+
|
276
|
+
attach_function :keycloak_validate_jwt_ex, [
|
277
|
+
KeycloakClient.ptr,
|
278
|
+
KeycloakToken.ptr,
|
279
|
+
:string, #:validate_iss,
|
280
|
+
:int, #:validate_iss_length,
|
281
|
+
:string, #:validate_sub,
|
282
|
+
:int, #:validate_sub_length,
|
283
|
+
:string, #:validate_aud,
|
284
|
+
:int, #:validate_aud_length,
|
285
|
+
:string, #:validate_jti,
|
286
|
+
:int, #:validate_jti_length,
|
287
|
+
:string, #:validate_typ,
|
288
|
+
:int, #:validate_typ_length,
|
289
|
+
:bool, #:validate_exp,
|
290
|
+
:int, #:exp_tolerance_seconds,
|
291
|
+
:bool, #:validate_nbf,
|
292
|
+
:int, #:nbf_tolerance_seconds,
|
293
|
+
:bool, #:validate_iat,
|
294
|
+
:int, #:iat_tolerance_seconds,
|
295
|
+
:pointer # out validation result
|
296
|
+
], KeycloakError.val
|
297
|
+
|
298
|
+
attach_function :keycloak_decode_and_validate_jwt_ex, [
|
299
|
+
KeycloakClient.ptr,
|
300
|
+
KeycloakToken.ptr,
|
301
|
+
:string, # validate_iss,
|
302
|
+
:int, # validate_iss_length,
|
303
|
+
:string, # validate_sub,
|
304
|
+
:int, # validate_sub_length,
|
305
|
+
:string, # validate_aud,
|
306
|
+
:int, # validate_aud_length,
|
307
|
+
:string, # validate_jti,
|
308
|
+
:int, # validate_jti_length,
|
309
|
+
:string, # validate_typ,
|
310
|
+
:int, # validate_typ_length,
|
311
|
+
:bool, # validate_exp,
|
312
|
+
:int, # exp_tolerance_seconds,
|
313
|
+
:bool, # validate_nbf,
|
314
|
+
:int, # nbf_tolerance_seconds,
|
315
|
+
:bool, # validate_iat,
|
316
|
+
:int, # iat_tolerance_seconds,
|
317
|
+
:pointer, # KeycloakJWTValidationResult.ptr, # out
|
318
|
+
KeycloakJWT.ptr # out
|
319
|
+
], KeycloakError.val
|
320
|
+
|
321
|
+
attach_function :keycloak_jwt_get_claim, [
|
322
|
+
KeycloakJWT.ptr,
|
323
|
+
:string, # claim key
|
324
|
+
KeycloakJWTClaim.ptr # out claim value
|
325
|
+
], KeycloakError.val
|
326
|
+
end
|
327
|
+
|
328
|
+
module Keycloak
|
329
|
+
class Error < StandardError
|
330
|
+
def initialize(err, str)
|
331
|
+
str = FFI::MemoryPointer.new(:string, 1024) # TODO: ERR_MAX macro
|
332
|
+
Keycloak_FFI.keycloak_errmsg(err, str)
|
333
|
+
super("Error " + err[:errcode].to_s + ": " + str.read_string)
|
334
|
+
@ptr = err
|
335
|
+
end
|
336
|
+
|
337
|
+
def errcode
|
338
|
+
return @ptr[:errcode]
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
Tokens = Keycloak_FFI::KeycloakTokens
|
343
|
+
Token = Keycloak_FFI::KeycloakToken
|
344
|
+
UserInfo = Keycloak_FFI::KeycloakUserinfo
|
345
|
+
|
346
|
+
# TODO: try adding functions to ffi class
|
347
|
+
class Client
|
348
|
+
def initialize(json, options: 0)
|
349
|
+
client_ptr = FFI::LibC.malloc(Keycloak_FFI::KeycloakClient.size)
|
350
|
+
@ptr = Keycloak_FFI::KeycloakClient.new(client_ptr)
|
351
|
+
err = Keycloak_FFI.keycloak_create_client(@ptr, json, options)
|
352
|
+
if err[:errcode] != :OK
|
353
|
+
raise Keycloak::Error, err
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
def realm
|
358
|
+
@ptr[:realm]
|
359
|
+
end
|
360
|
+
|
361
|
+
def resource
|
362
|
+
@ptr[:resource]
|
363
|
+
end
|
364
|
+
|
365
|
+
def auth_server_url
|
366
|
+
@ptr[:auth_server_url]
|
367
|
+
end
|
368
|
+
|
369
|
+
def secret
|
370
|
+
@ptr[:secret]
|
371
|
+
end
|
372
|
+
|
373
|
+
def realm_info
|
374
|
+
@ptr[:realm_info]
|
375
|
+
end
|
376
|
+
|
377
|
+
def get_token(user, pass, scopes: nil)
|
378
|
+
tokens_ptr = FFI::LibC.malloc(Keycloak_FFI::KeycloakTokens.size)
|
379
|
+
tokens = Keycloak_FFI::KeycloakTokens.new(tokens_ptr)
|
380
|
+
err_response_ptr = FFI::MemoryPointer.new(:string)
|
381
|
+
scopes_ptr = nil
|
382
|
+
scopes_len = 0
|
383
|
+
unless scopes.nil?
|
384
|
+
scopes_len = scopes.size
|
385
|
+
scopes_ptr = FFI::MemoryPointer.new(:pointer, scopes_len)
|
386
|
+
ptr_arr = scopes.map do |scope, i|
|
387
|
+
ptr = FFI::MemoryPointer.from_string scope
|
388
|
+
end
|
389
|
+
scopes_ptr.put_array_of_pointer 0, ptr_arr
|
390
|
+
end
|
391
|
+
err = Keycloak_FFI.keycloak_get_token(
|
392
|
+
@ptr,
|
393
|
+
user, pass,
|
394
|
+
scopes_ptr, scopes_len,
|
395
|
+
tokens,
|
396
|
+
err_response_ptr
|
397
|
+
)
|
398
|
+
|
399
|
+
if err[:errcode] == :HTTP
|
400
|
+
err = Keycloak::Error.new(err, err_response_ptr)
|
401
|
+
raise err
|
402
|
+
elsif err[:errcode] != :OK
|
403
|
+
raise Keycloak::Error, err
|
404
|
+
end
|
405
|
+
|
406
|
+
return tokens
|
407
|
+
end
|
408
|
+
|
409
|
+
def refresh_token(refresh_token, scopes: nil)
|
410
|
+
tokens_ptr = FFI::LibC.malloc(Keycloak_FFI::KeycloakTokens.size)
|
411
|
+
tokens = Keycloak_FFI::KeycloakTokens.new(tokens_ptr)
|
412
|
+
err_response_ptr = FFI::MemoryPointer.new(:string)
|
413
|
+
scopes_ptr = nil
|
414
|
+
scopes_len = 0
|
415
|
+
unless scopes.nil?
|
416
|
+
scopes_len = scopes.size
|
417
|
+
scopes_ptr = FFI::MemoryPointer.new(:pointer, scopes_len)
|
418
|
+
ptr_arr = scopes.map do |scope, i|
|
419
|
+
ptr = FFI::MemoryPointer.from_string scope
|
420
|
+
end
|
421
|
+
scopes_ptr.put_array_of_pointer 0, ptr_arr
|
422
|
+
end
|
423
|
+
err = Keycloak_FFI.keycloak_refresh_token(
|
424
|
+
@ptr,
|
425
|
+
refresh_token,
|
426
|
+
scopes_ptr, scopes_len,
|
427
|
+
tokens,
|
428
|
+
err_response_ptr
|
429
|
+
)
|
430
|
+
|
431
|
+
if err[:errcode] == :HTTP
|
432
|
+
err = Keycloak::Error.new(err, err_response_ptr)
|
433
|
+
raise err
|
434
|
+
elsif err[:errcode] != :OK
|
435
|
+
raise Keycloak::Error, err
|
436
|
+
end
|
437
|
+
|
438
|
+
return tokens
|
439
|
+
end
|
440
|
+
|
441
|
+
def get_userinfo(tokens)
|
442
|
+
userinfo_ptr = FFI::LibC.malloc(Keycloak_FFI::KeycloakUserinfo.size)
|
443
|
+
userinfo = Keycloak_FFI::KeycloakUserinfo.new(userinfo_ptr)
|
444
|
+
|
445
|
+
err_response_ptr = FFI::MemoryPointer.new(:string)
|
446
|
+
|
447
|
+
err = Keycloak_FFI.keycloak_get_userinfo(
|
448
|
+
@ptr,
|
449
|
+
tokens,
|
450
|
+
userinfo,
|
451
|
+
err_response_ptr
|
452
|
+
)
|
453
|
+
|
454
|
+
if err[:errcode] == :HTTP
|
455
|
+
err = Keycloak::Error.new(err, err_response_ptr)
|
456
|
+
raise err
|
457
|
+
elsif err[:errcode] != :OK
|
458
|
+
raise Keycloak::Error, err
|
459
|
+
end
|
460
|
+
|
461
|
+
return userinfo
|
462
|
+
end
|
463
|
+
|
464
|
+
def validate_jwt(
|
465
|
+
token,
|
466
|
+
# strings
|
467
|
+
iss: nil,
|
468
|
+
sub: nil,
|
469
|
+
aud: nil,
|
470
|
+
jti: nil,
|
471
|
+
typ: nil,
|
472
|
+
# booleans
|
473
|
+
validate_exp: false,
|
474
|
+
# Tolerance in seconds
|
475
|
+
exp_tolerance: 0,
|
476
|
+
validate_nbf: false,
|
477
|
+
nbf_tolerance: 0,
|
478
|
+
validate_iat: false,
|
479
|
+
iat_tolerance: 0
|
480
|
+
)
|
481
|
+
valid = FFI::MemoryPointer.new(:uint8)
|
482
|
+
err = Keycloak_FFI.keycloak_validate_jwt_ex(
|
483
|
+
@ptr,
|
484
|
+
token,
|
485
|
+
iss, iss.nil? ? 0 : iss.size,
|
486
|
+
sub, sub.nil? ? 0 : sub.size,
|
487
|
+
aud, aud.nil? ? 0 : aud.size,
|
488
|
+
jti, jti.nil? ? 0 : jti.size,
|
489
|
+
typ, typ.nil? ? 0 : typ.size,
|
490
|
+
validate_exp, exp_tolerance,
|
491
|
+
validate_nbf, nbf_tolerance,
|
492
|
+
validate_iat, iat_tolerance,
|
493
|
+
valid
|
494
|
+
)
|
495
|
+
|
496
|
+
if err[:errcode] != :OK
|
497
|
+
raise err
|
498
|
+
end
|
499
|
+
|
500
|
+
reason = Keycloak_FFI::KeycloakJWTValidationResult[valid.read(:uint8).to_i]
|
501
|
+
return JWTValidationResult.new(
|
502
|
+
reason == :VALID,
|
503
|
+
reason
|
504
|
+
)
|
505
|
+
end
|
506
|
+
|
507
|
+
def decode_and_validate_jwt(
|
508
|
+
token,
|
509
|
+
iss: nil,
|
510
|
+
sub: nil,
|
511
|
+
aud: nil,
|
512
|
+
jti: nil,
|
513
|
+
typ: nil,
|
514
|
+
validate_exp: false,
|
515
|
+
exp_tolerance: 0,
|
516
|
+
validate_nbf: false,
|
517
|
+
nbf_tolerance: 0,
|
518
|
+
validate_iat: false,
|
519
|
+
iat_tolerance: 0
|
520
|
+
)
|
521
|
+
valid = FFI::MemoryPointer.new(:uint8)
|
522
|
+
jwt_ptr = FFI::LibC.malloc(Keycloak_FFI::KeycloakJWT.size)
|
523
|
+
jwt = Keycloak_FFI::KeycloakJWT.new(jwt_ptr)
|
524
|
+
err = Keycloak_FFI.keycloak_decode_and_validate_jwt_ex(
|
525
|
+
@ptr,
|
526
|
+
token,
|
527
|
+
iss, iss.nil? ? 0 : iss.size,
|
528
|
+
sub, sub.nil? ? 0 : sub.size,
|
529
|
+
aud, aud.nil? ? 0 : aud.size,
|
530
|
+
jti, jti.nil? ? 0 : jti.size,
|
531
|
+
typ, typ.nil? ? 0 : typ.size,
|
532
|
+
validate_exp, exp_tolerance,
|
533
|
+
validate_nbf, nbf_tolerance,
|
534
|
+
validate_iat, iat_tolerance,
|
535
|
+
valid,
|
536
|
+
jwt
|
537
|
+
)
|
538
|
+
|
539
|
+
if err[:errcode] != :OK
|
540
|
+
raise err
|
541
|
+
end
|
542
|
+
|
543
|
+
reason = Keycloak_FFI::KeycloakJWTValidationResult[valid.read(:uint8).to_i]
|
544
|
+
return JWTDecodeResult.new(
|
545
|
+
JWTValidationResult.new(
|
546
|
+
reason == :VALID,
|
547
|
+
reason
|
548
|
+
),
|
549
|
+
jwt
|
550
|
+
)
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
JWTValidationResult = Struct.new(:valid, :reason) do
|
555
|
+
def reason_string
|
556
|
+
return Keycloak_FFI.keycloak_jwt_validation_reason_string(self.reason)
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
JWTDecodeResult = Struct.new(:valid, :jwt)
|
561
|
+
|
562
|
+
def self._jwt_claim_value claim
|
563
|
+
case claim[:type]
|
564
|
+
when :String
|
565
|
+
return claim[:value][:stringvalue]
|
566
|
+
when :Int
|
567
|
+
return claim[:value][:intvalue]
|
568
|
+
when :Double
|
569
|
+
return claim[:value][:doublevalue]
|
570
|
+
when :Bool
|
571
|
+
return claim[:value][:boolvalue]
|
572
|
+
when :Null
|
573
|
+
return nil
|
574
|
+
# TODO
|
575
|
+
when :Array
|
576
|
+
return claim[:value][:datavalue]
|
577
|
+
when :Object
|
578
|
+
return claim[:value][:datavalue]
|
579
|
+
when :Other
|
580
|
+
return claim[:value][:datavalue]
|
581
|
+
end
|
582
|
+
end
|
583
|
+
end
|
metadata
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: libkeycloak
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jonas Everaert
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-10-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.17'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.17'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ffi-libc
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.1'
|
41
|
+
description: |
|
42
|
+
# Keycloak C
|
43
|
+
|
44
|
+
A wrapper for the Keycloak API in C. Includes some bindings for different languages
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
|
48
|
+
### Installation
|
49
|
+
|
50
|
+
#### C
|
51
|
+
|
52
|
+
see [Building](#building).
|
53
|
+
|
54
|
+
#### Ruby
|
55
|
+
|
56
|
+
- Install the C library as a [dynamic library](#dynamic-library)
|
57
|
+
- Include the ruby file at `bindings/ruby/keycloak.rb` in your project
|
58
|
+
|
59
|
+
<!-- - OR: `gem install keycloak-api` -->
|
60
|
+
|
61
|
+
## Building
|
62
|
+
|
63
|
+
You need Ruby and the colorize gem (`gem install colorize`)
|
64
|
+
|
65
|
+
### Static library
|
66
|
+
|
67
|
+
```sh
|
68
|
+
ruby build.rb build static
|
69
|
+
```
|
70
|
+
|
71
|
+
There is only one header to be included, located at src/keycloak.h
|
72
|
+
|
73
|
+
### Dynamic library
|
74
|
+
|
75
|
+
```sh
|
76
|
+
ruby build.rb build dynamic
|
77
|
+
```
|
78
|
+
|
79
|
+
The dynamc libraries are now located in the `build` folder. Copy them to a folder
|
80
|
+
in `echo $LD_LIBRARY_PATH`.
|
81
|
+
|
82
|
+
## Keycloak.json
|
83
|
+
|
84
|
+
The file `keycloak.json` is required by the library, you can obtain it from going to a Client in the Keycloak console
|
85
|
+
and clicking "action" > "Download adapter config" and choose JSON.
|
86
|
+
|
87
|
+
In settings, make sure "Client authentication" is turned on.
|
88
|
+
|
89
|
+
## Examples
|
90
|
+
|
91
|
+
To run the examples, you need a keycloak.json. You can obtain it from going to a Client in the Keycloak console
|
92
|
+
and clicking "action" > "Download adapter config" and choose JSON.
|
93
|
+
|
94
|
+
Paste the file in the root of this repository to try out the examples.
|
95
|
+
|
96
|
+
All the example code can be found in the root of this repository as `example.[lang]`.
|
97
|
+
|
98
|
+
The `[user]` and `[pass]` arguments are used to log in to a user in the keycloak realm specified by the `keycloak.json`.
|
99
|
+
|
100
|
+
### C
|
101
|
+
|
102
|
+
```sh
|
103
|
+
ruby build.rb test
|
104
|
+
./build/test/a.out [user] [pass]
|
105
|
+
```
|
106
|
+
|
107
|
+
### Ruby
|
108
|
+
|
109
|
+
```sh
|
110
|
+
ruby build.rb build dynamic
|
111
|
+
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$(pwd)/build" ruby example.rb [user] [pass]
|
112
|
+
# OR: copy the dynamic library to a path in LD_LIBRARY_PATH
|
113
|
+
```
|
114
|
+
|
115
|
+
## Questions
|
116
|
+
|
117
|
+
Feel free to ask any questions regarding the library as an issue.
|
118
|
+
|
119
|
+
## License
|
120
|
+
|
121
|
+
Licensed under the [MIT license](LICENSE).
|
122
|
+
email:
|
123
|
+
executables: []
|
124
|
+
extensions: []
|
125
|
+
extra_rdoc_files: []
|
126
|
+
files:
|
127
|
+
- keycloak.rb
|
128
|
+
homepage: https://github.com/Jomy10/libkeycloak
|
129
|
+
licenses:
|
130
|
+
- MIT
|
131
|
+
metadata: {}
|
132
|
+
post_install_message:
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '2.5'
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
requirements: []
|
147
|
+
rubygems_version: 3.5.10
|
148
|
+
signing_key:
|
149
|
+
specification_version: 4
|
150
|
+
summary: A library for interacting with Keycloak and validating jwt tokens returned
|
151
|
+
from it.
|
152
|
+
test_files: []
|