aerospike 2.19.0 → 2.21.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.
@@ -18,24 +18,40 @@ module Aerospike
18
18
 
19
19
  private
20
20
  # Commands
21
- AUTHENTICATE = 0
22
- CREATE_USER = 1
23
- DROP_USER = 2
24
- SET_PASSWORD = 3
25
- CHANGE_PASSWORD = 4
26
- GRANT_ROLES = 5
27
- REVOKE_ROLES = 6
28
- #CREATE_ROLE = 8
29
- QUERY_USERS = 9
30
- #QUERY_ROLES = 10
21
+ AUTHENTICATE = 0
22
+ CREATE_USER = 1
23
+ DROP_USER = 2
24
+ SET_PASSWORD = 3
25
+ CHANGE_PASSWORD = 4
26
+ GRANT_ROLES = 5
27
+ REVOKE_ROLES = 6
28
+ QUERY_USERS = 9
29
+ CREATE_ROLE = 10
30
+ DROP_ROLE = 11
31
+ GRANT_PRIVILEGES = 12
32
+ REVOKE_PRIVILEGES = 13
33
+ SET_WHITELIST = 14
34
+ SET_QUOTAS = 15
35
+ QUERY_ROLES = 16
36
+ LOGIN = 20
31
37
 
32
38
  # Field IDs
33
- USER = 0
34
- PASSWORD = 1
35
- OLD_PASSWORD = 2
36
- CREDENTIAL = 3
37
- ROLES = 10
38
- #PRIVILEGES = 11
39
+ USER = 0
40
+ PASSWORD = 1
41
+ OLD_PASSWORD = 2
42
+ CREDENTIAL = 3
43
+ CLEAR_PASSWORD = 4
44
+ SESSION_TOKEN = 5
45
+ SESSION_TTL = 6
46
+ ROLES = 10
47
+ ROLE = 11
48
+ PRIVILEGES = 12
49
+ ALLOWLIST = 13
50
+ READ_QUOTA = 14
51
+ WRITE_QUOTA = 15
52
+ READ_INFO = 16
53
+ WRITE_INFO = 17
54
+ CONNECTIONS = 18
39
55
 
40
56
  # Misc
41
57
  MSG_VERSION = 2
@@ -53,28 +69,6 @@ module Aerospike
53
69
  @data_offset = 8
54
70
  end
55
71
 
56
- def authenticate(conn, user, password)
57
- begin
58
- set_authenticate(user, password)
59
- conn.write(@data_buffer, @data_offset)
60
- conn.read(@data_buffer, HEADER_SIZE)
61
-
62
- result = @data_buffer.read(RESULT_CODE)
63
- raise Exceptions::Aerospike.new(result, "Authentication failed") if result != 0
64
- ensure
65
- Buffer.put(@data_buffer)
66
- end
67
- end
68
-
69
- def set_authenticate(user, password)
70
- write_header(AUTHENTICATE, 2)
71
- write_field_str(USER, user)
72
- write_field_bytes(CREDENTIAL, password)
73
- write_size
74
-
75
- return @data_offset
76
- end
77
-
78
72
  def create_user(cluster, policy, user, password, roles)
79
73
  write_header(CREATE_USER, 3)
80
74
  write_field_str(USER, user)
@@ -118,6 +112,61 @@ module Aerospike
118
112
  execute_command(cluster, policy)
119
113
  end
120
114
 
115
+ def create_role(cluster, policy, role_name, privileges = [], allowlist = [], read_quota = 0, write_quota = 0)
116
+ field_count = 1
117
+ field_count += 1 if privileges.size > 0
118
+ field_count += 1 if allowlist.size > 0
119
+ field_count += 1 if read_quota > 0
120
+ field_count += 1 if write_quota > 0
121
+
122
+ write_header(CREATE_ROLE, field_count)
123
+ write_field_str(ROLE, role_name)
124
+
125
+ write_privileges(privileges) if privileges.size > 0
126
+ write_allowlist(allowlist) if allowlist.size > 0
127
+
128
+ write_field_uint32(READ_QUOTA, read_quota) if read_quota > 0
129
+ write_field_uint32(WRITE_QUOTA, write_quota) if write_quota > 0
130
+
131
+ execute_command(cluster, policy)
132
+ end
133
+
134
+ def drop_role(cluster, policy, role)
135
+ write_header(DROP_ROLE, 1)
136
+ write_field_str(ROLE, role)
137
+ execute_command(cluster, policy)
138
+ end
139
+
140
+ def grant_privileges(cluster, policy, role, privileges)
141
+ write_header(GRANT_PRIVILEGES, 2)
142
+ write_field_str(ROLE, role)
143
+ write_privileges(privileges)
144
+ execute_command(cluster, policy)
145
+ end
146
+
147
+ def revoke_privileges(cluster, policy, role, privileges)
148
+ write_header(REVOKE_PRIVILEGES, 2)
149
+ write_field_str(ROLE, role)
150
+ write_privileges(privileges)
151
+ execute_command(cluster, policy)
152
+ end
153
+
154
+ def set_allowlist(cluster, policy, role, allowlist = [])
155
+ field_count = 1
156
+ field_count += 1 if allowlist.size > 0
157
+ write_header(SET_WHITELIST, field_count)
158
+ write_allowlist(allowlist) if allowlist.size > 0
159
+ execute_command(cluster, policy)
160
+ end
161
+
162
+ def set_quotas(cluster, policy, role, read_quota, write_quota)
163
+ write_header(SET_QUOTAS, 3)
164
+ write_field_str(ROLE, role)
165
+ write_field_uint32(READ_QUOTA, read_quota)
166
+ write_field_uint32(WRITE_QUOTA, write_quota)
167
+ execute_command(cluster, policy)
168
+ end
169
+
121
170
  def query_user(cluster, policy, user)
122
171
  # TODO: Remove the workaround in the future
123
172
  sleep(0.010)
@@ -144,6 +193,32 @@ module Aerospike
144
193
  end
145
194
  end
146
195
 
196
+ def query_role(cluster, policy, role)
197
+ # TODO: Remove the workaround in the future
198
+ sleep(0.010)
199
+
200
+ list = []
201
+ begin
202
+ write_header(QUERY_ROLES, 1)
203
+ write_field_str(ROLE, role)
204
+ list = read_roles(cluster, policy)
205
+ return (list.is_a?(Array) && list.length > 0 ? list.first : nil)
206
+ ensure
207
+ Buffer.put(@data_buffer)
208
+ end
209
+ end
210
+
211
+ def query_roles(cluster, policy)
212
+ # TODO: Remove the workaround in the future
213
+ sleep(0.010)
214
+ begin
215
+ write_header(QUERY_ROLES, 0)
216
+ return read_roles(cluster, policy)
217
+ ensure
218
+ Buffer.put(@data_buffer)
219
+ end
220
+ end
221
+
147
222
  def write_roles(roles)
148
223
  offset = @data_offset + FIELD_HEADER_SIZE
149
224
  @data_buffer.write_byte(roles.length.ord, offset)
@@ -166,6 +241,54 @@ module Aerospike
166
241
  @data_buffer.write_int64(size, 0)
167
242
  end
168
243
 
244
+ def write_privileges(privileges)
245
+ offset = @data_offset
246
+ @data_offset += FIELD_HEADER_SIZE
247
+ write_byte(privileges.size)
248
+
249
+ for privilege in privileges
250
+ write_byte(privilege.to_code)
251
+ if privilege.can_scope?
252
+ if privilege.set_name.to_s.size > 0 && privilege.namespace.to_s.size == 0
253
+ raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::INVALID_PRIVILEGE, "Admin privilege #{privilege.namespace} has a set scope with an empty namespace")
254
+ end
255
+
256
+ write_str(privilege.namespace.to_s)
257
+ write_str(privilege.set_name.to_s)
258
+ else
259
+ if privilege.set_name.to_s.bytesize > 0 || privilege.namespace.to_s.bytesize > 0
260
+ raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::INVALID_PRIVILEGE, "Admin global privilege #{privilege} can't have a namespace or set")
261
+ end
262
+ end
263
+ end
264
+
265
+ size = @data_offset - offset - FIELD_HEADER_SIZE
266
+ @data_offset = offset
267
+ write_field_header(PRIVILEGES, size)
268
+ @data_offset += size
269
+ end
270
+
271
+ def write_allowlist(allowlist)
272
+ offset = @data_offset
273
+ @data_offset += FIELD_HEADER_SIZE
274
+
275
+ comma = false
276
+ for addr in allowlist
277
+ if comma
278
+ write_byte(",")
279
+ else
280
+ comma = true
281
+ end
282
+
283
+ @data_offset += @data_buffer.write_binary(addr, @data_offset)
284
+ end
285
+
286
+ size = @data_offset - offset - FIELD_HEADER_SIZE
287
+ @data_offset = offset
288
+ write_field_header(ALLOWLIST, size)
289
+ @data_offset += size
290
+ end
291
+
169
292
  def write_header(command, field_count)
170
293
  # Authenticate header is almost all zeros
171
294
  i = @data_offset
@@ -178,12 +301,27 @@ module Aerospike
178
301
  @data_offset += 16
179
302
  end
180
303
 
304
+ def write_byte(b)
305
+ @data_offset += @data_buffer.write_byte(b, @data_offset)
306
+ end
307
+
308
+ def write_str(str)
309
+ @data_offset += @data_buffer.write_byte(str.bytesize, @data_offset)
310
+ @data_offset += @data_buffer.write_binary(str, @data_offset)
311
+ end
312
+
181
313
  def write_field_str(id, str)
182
314
  len = @data_buffer.write_binary(str, @data_offset+FIELD_HEADER_SIZE)
183
315
  write_field_header(id, len)
184
316
  @data_offset += len
185
317
  end
186
318
 
319
+ def write_field_uint32(id, val)
320
+ len = @data_buffer.write_uint32(val, @data_offset+FIELD_HEADER_SIZE)
321
+ write_field_header(id, len)
322
+ @data_offset += len
323
+ end
324
+
187
325
  def write_field_bytes(id, bytes)
188
326
  @data_buffer.write_binary(bytes, @data_offset+FIELD_HEADER_SIZE)
189
327
  write_field_header(id, bytes.bytesize)
@@ -284,7 +422,7 @@ module Aerospike
284
422
  return (result_code == QUERY_END ? -1 : result_code)
285
423
  end
286
424
 
287
- userRoles = UserRoles.new
425
+ user_roles = UserRoles.new
288
426
  field_count = @data_buffer.read(@data_offset+3)
289
427
  @data_offset += HEADER_REMAINING
290
428
 
@@ -298,10 +436,17 @@ module Aerospike
298
436
 
299
437
  case id
300
438
  when USER
301
- userRoles.user = @data_buffer.read(@data_offset, len)
439
+ user_roles.user = @data_buffer.read(@data_offset, len)
302
440
  @data_offset += len
303
441
  when ROLES
304
- parse_roles(userRoles)
442
+ parse_roles(user_roles)
443
+ when READ_INFO
444
+ user_roles.read_info = parse_info
445
+ when WRITE_INFO
446
+ user_roles.write_info = parse_info
447
+ when CONNECTIONS
448
+ user_roles.conns_in_use = @data_buffer.read_int32(@data_offset)
449
+ @data_offset += len
305
450
  else
306
451
  @data_offset += len
307
452
  end
@@ -309,19 +454,19 @@ module Aerospike
309
454
  i = i.succ
310
455
  end
311
456
 
312
- next if userRoles.user == "" && userRoles.roles == nil
457
+ next if user_roles.user == "" && user_roles.roles == nil
313
458
 
314
- userRoles.roles = [] if userRoles.roles == nil
315
- list << userRoles
459
+ user_roles.roles = [] if user_roles.roles == nil
460
+ list << user_roles
316
461
  end
317
462
 
318
463
  return 0, list
319
464
  end
320
465
 
321
- def parse_roles(userRoles)
466
+ def parse_roles(user_roles)
322
467
  size = @data_buffer.read(@data_offset)
323
468
  @data_offset += 1
324
- userRoles.roles = []
469
+ user_roles.roles = []
325
470
 
326
471
  i = 0
327
472
  while i < size
@@ -329,17 +474,188 @@ module Aerospike
329
474
  @data_offset += 1
330
475
  role = @data_buffer.read(@data_offset, len)
331
476
  @data_offset += len
332
- userRoles.roles << role
477
+ user_roles.roles << role
333
478
 
334
479
  i = i.succ
335
480
  end
336
481
  end
337
482
 
338
- SALT = '$2a$10$7EqJtq98hPqEX7fNZaFWoO'
339
- def self.hash_password(password)
340
- # Hashing the password with the cost of 10, with a static salt
341
- return BCrypt::Engine.hash_secret(password, SALT, :cost => 10)
483
+ def parse_info
484
+ size = @data_buffer.read(@data_offset)
485
+ @data_offset += 1
486
+ list = []
487
+
488
+ i = 0
489
+ while i < size
490
+ val = @data_buffer.read_int32(@data_offset)
491
+ @data_offset += 4
492
+ list << val
493
+
494
+ i = i.succ
495
+ end
496
+
497
+ list
342
498
  end
499
+
500
+ def read_roles(cluster, policy)
501
+ write_size
502
+ node = cluster.random_node
503
+
504
+ timeout = 1
505
+ timeout = policy.timeout if policy != nil && policy.timeout > 0
506
+
507
+ status = -1
508
+ list = []
509
+ begin
510
+ conn = node.get_connection(timeout)
511
+ conn.write(@data_buffer, @data_offset)
512
+ status, list = read_role_blocks(conn)
513
+ node.put_connection(conn)
514
+ rescue => e
515
+ conn.close if conn
516
+ raise e
517
+ end
518
+
519
+ raise Exceptions::Aerospike.new(status) if status > 0
520
+
521
+ return list
522
+ end
523
+
524
+ def read_role_blocks(conn)
525
+ rlist = []
526
+ status = 0
527
+ begin
528
+ while status == 0
529
+ conn.read(@data_buffer, 8)
530
+ size = @data_buffer.read_int64(0)
531
+ receive_size = (size & 0xFFFFFFFFFFFF)
532
+
533
+ if receive_size > 0
534
+ @data_buffer.resize(receive_size) if receive_size > @data_buffer.size
535
+
536
+ conn.read(@data_buffer, receive_size)
537
+ status, list = parse_roles_full(receive_size)
538
+ rlist.concat(list.to_a)
539
+ else
540
+ break
541
+ end
542
+ end
543
+ return status, rlist
544
+ rescue => e
545
+ return -1, []
546
+ end
547
+ end
548
+
549
+ def parse_roles_full(receive_size)
550
+ @data_offset = 0
551
+ list = []
552
+
553
+ while @data_offset < receive_size
554
+ result_code = @data_buffer.read(@data_offset+1)
555
+
556
+ if result_code != 0
557
+ return (result_code == QUERY_END ? -1 : result_code)
558
+ end
559
+
560
+ role = Role.new
561
+ field_count = @data_buffer.read(@data_offset+3)
562
+ @data_offset += HEADER_REMAINING
563
+
564
+ i = 0
565
+ while i < field_count
566
+ len = @data_buffer.read_int32(@data_offset)
567
+ @data_offset += 4
568
+ id = @data_buffer.read(@data_offset)
569
+ @data_offset += 1
570
+ len -= 1
571
+
572
+ case id
573
+ when ROLE
574
+ role.name = @data_buffer.read(@data_offset, len).to_s
575
+ @data_offset += len
576
+ when PRIVILEGES
577
+ parse_privileges(role)
578
+ when ALLOWLIST
579
+ role.allowlist = parse_allowlist(len)
580
+ when READ_QUOTA
581
+ role.read_quota = @data_buffer.read_uint32(@data_offset)
582
+ @data_offset += len
583
+ when WRITE_QUOTA
584
+ role.write_quota = @data_buffer.read_uint32(@data_offset)
585
+ @data_offset += len
586
+ else
587
+ @data_offset += len
588
+ end
589
+
590
+ i = i.succ
591
+ end
592
+
593
+ next if role.name == "" && role.privileges == nil
594
+
595
+ role.privileges ||= []
596
+ list << role
597
+ end
598
+
599
+ return 0, list
600
+ end
601
+
602
+ def parse_privileges(role)
603
+ size = @data_buffer.read(@data_offset)
604
+ @data_offset += 1
605
+ role.privileges = []
606
+
607
+ i = 0
608
+ while i < size
609
+ priv = Privilege.new
610
+ priv.code = Privilege.from(@data_buffer.read(@data_offset))
611
+ @data_offset += 1
612
+
613
+ if priv.can_scope?
614
+ len = @data_buffer.read(@data_offset)
615
+ @data_offset += 1
616
+ priv.namespace = @data_buffer.read(@data_offset, len)
617
+ @data_offset += len
618
+
619
+ len = @data_buffer.read(@data_offset)
620
+ @data_offset += 1
621
+ priv.set_name = @data_buffer.read(@data_offset, len)
622
+ @data_offset += len
623
+ end
624
+
625
+ role.privileges << priv
626
+
627
+ i = i.succ
628
+ end
629
+ end
630
+
631
+ def parse_allowlist(len)
632
+ list = []
633
+ begn = @data_offset
634
+ max = begn + len
635
+
636
+ while @data_offset < max
637
+ if @data_buffer.read(@data_offset) == ','
638
+ l = @data_offset - begn
639
+ if l > 0
640
+ s = @data_buffer.read(begn, l)
641
+ list << s
642
+ end
643
+ @data_offset += 1
644
+ begn = @data_offset
645
+ else
646
+ @data_offset += 1
647
+ end
648
+ end
649
+
650
+ l = @data_offset - begn
651
+ if l > 0
652
+ s = @data_buffer.read(begn, l)
653
+ list << s
654
+ end
655
+
656
+ list
657
+ end
658
+
343
659
  end
344
660
  end
345
661
 
@@ -110,16 +110,12 @@ module Aerospike
110
110
  field_count = @data_buffer.read_int16(18)
111
111
  op_count = @data_buffer.read_int16(20)
112
112
 
113
- key = parse_key(field_count)
113
+ skip_key(field_count)
114
114
  req_key = batch.key_for_index(batch_index)
115
115
 
116
- if key.digest == req_key.digest
117
- if result_code == 0
118
- record = parse_record(req_key, op_count, generation, expiration)
119
- results[batch_index] = record
120
- end
121
- else
122
- Aerospike.logger.warn("Unexpected batch key returned: #{key}")
116
+ if result_code == 0
117
+ record = parse_record(req_key, op_count, generation, expiration)
118
+ results[batch_index] = record
123
119
  end
124
120
  end
125
121
 
@@ -36,7 +36,7 @@ module Aerospike
36
36
  raise Aerospike::Exceptions::Parse.new('Received bins that were not requested!')
37
37
  end
38
38
 
39
- parse_key(field_count)
39
+ skip_key(field_count)
40
40
  results[batch_index] = (result_code == 0)
41
41
  end
42
42
 
@@ -66,6 +66,8 @@ module Aerospike
66
66
  INFO3_LAST = Integer(1 << 0)
67
67
  # Commit to master only before declaring success.
68
68
  INFO3_COMMIT_MASTER = Integer(1 << 1)
69
+ # Partition is complete response in scan.
70
+ INFO3_PARTITION_DONE = Integer(1 << 2)
69
71
  # Update only. Merge bins.
70
72
  INFO3_UPDATE_ONLY = Integer(1 << 3)
71
73
 
@@ -333,7 +335,7 @@ module Aerospike
333
335
  mark_compressed(policy)
334
336
  end
335
337
 
336
- def set_scan(policy, namespace, set_name, bin_names)
338
+ def set_scan(policy, namespace, set_name, bin_names, partitions)
337
339
  # Estimate buffer size
338
340
  begin_cmd
339
341
  field_count = 0
@@ -357,7 +359,10 @@ module Aerospike
357
359
  field_count += 1 if predexp_size > 0
358
360
 
359
361
  # Estimate scan options size.
360
- @data_offset += 2 + FIELD_HEADER_SIZE
362
+ # @data_offset += 2 + FIELD_HEADER_SIZE
363
+ # field_count += 1
364
+
365
+ @data_offset += partitions.length * 2 + FIELD_HEADER_SIZE
361
366
  field_count += 1
362
367
 
363
368
  # Estimate scan timeout size.
@@ -392,24 +397,30 @@ module Aerospike
392
397
  write_field_string(set_name, Aerospike::FieldType::TABLE)
393
398
  end
394
399
 
400
+ write_field_header(partitions.length * 2, Aerospike::FieldType::PID_ARRAY)
401
+ for pid in partitions
402
+ @data_buffer.write_uint16_little_endian(pid, @data_offset)
403
+ @data_offset += 2
404
+ end
405
+
395
406
  if policy.records_per_second > 0
396
407
  write_field_int(policy.records_per_second, Aerospike::FieldType::RECORDS_PER_SECOND)
397
408
  end
398
409
 
399
410
  write_predexp(policy.predexp, predexp_size)
400
411
 
401
- write_field_header(2, Aerospike::FieldType::SCAN_OPTIONS)
412
+ # write_field_header(2, Aerospike::FieldType::SCAN_OPTIONS)
402
413
 
403
- priority = policy.priority & 0xFF
404
- priority <<= 4
405
- if policy.fail_on_cluster_change
406
- priority |= 0x08
407
- end
414
+ # priority = policy.priority & 0xFF
415
+ # priority <<= 4
416
+ # if policy.fail_on_cluster_change
417
+ # priority |= 0x08
418
+ # end
408
419
 
409
- @data_buffer.write_byte(priority, @data_offset)
410
- @data_offset += 1
411
- @data_buffer.write_byte(policy.scan_percent.to_i.ord, @data_offset)
412
- @data_offset += 1
420
+ # @data_buffer.write_byte(priority, @data_offset)
421
+ # @data_offset += 1
422
+ # @data_buffer.write_byte(policy.scan_percent.to_i.ord, @data_offset)
423
+ # @data_offset += 1
413
424
 
414
425
  write_field_header(4, Aerospike::FieldType::SCAN_TIMEOUT)
415
426
  @data_buffer.write_uint32(policy.socket_timeout.to_i, @data_offset)
@@ -786,6 +797,12 @@ module Aerospike
786
797
  @data_offset += len
787
798
  end
788
799
 
800
+ def write_u16_little_endian(i, ftype)
801
+ @data_buffer.write_uint16_little_endian(i, @data_offset+FIELD_HEADER_SIZE)
802
+ write_field_header(2, ftype)
803
+ @data_offset += 2
804
+ end
805
+
789
806
  def write_field_int(i, ftype)
790
807
  @data_buffer.write_int32(i, @data_offset+FIELD_HEADER_SIZE)
791
808
  write_field_header(4, ftype)
@@ -32,6 +32,7 @@ module Aerospike
32
32
  SCAN_OPTIONS = 8
33
33
  SCAN_TIMEOUT = 9
34
34
  RECORDS_PER_SECOND = 10
35
+ PID_ARRAY = 11
35
36
  INDEX_NAME = 21
36
37
  INDEX_RANGE = 22
37
38
  INDEX_FILTER = 23