nats 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8caff8d7b6a0c87a312c9942174fe3c455e2cfc1c3abdb7c6baf7eb5db7445ef
4
- data.tar.gz: 2dd5eb84f34dea3c6509c732b8da35d095af409fb955de312bc75c0b5306c7b0
3
+ metadata.gz: da544e9e03c583ba122aebfae0813c138de976f84da6255255aa414b8d637f82
4
+ data.tar.gz: a98249f31459f3a6d917d3bc6660c9ac8a727c0af5b77cac5f445b6969de670b
5
5
  SHA512:
6
- metadata.gz: 2d8e2aa2738f152cc9f15bb1a5759714062535d55a6c449fc0a55e61b2cd59f45d37e39129fade9acf9cb0a82c2f5bf555f00fd82302e97ec9ec603ee2d2e493
7
- data.tar.gz: '07828fd12c5c5a89eebfabaf8dae0315fa0bb0eb5dd013fee13282a63a5c037e04d2a072424fd97c266cabeb9d465ad3f79b66c4ace6c1280af85a4acf5da203'
6
+ metadata.gz: 0230ee1118324856fc933c80292623e858115feeb8423c7ab66e5f0d4fb75e23cf804183117423279aa4a7b684af40bfa2a5ae52f225eb7601a7a292291d6443
7
+ data.tar.gz: b9b7de57143d9898310ce65f591ca217c55becb5085287c2f0ad2d9982862af17aa2834786a34596aa0bde6c4e4198f4a9e9cda4d9e3438061fd028bb67a2809
data/HISTORY.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # HISTORY
2
2
 
3
+ ## v0.11.0 (June 10, 2019)
4
+ - NATS v2.0 support! (#162)
5
+
3
6
  ## v0.8.4 (Feb 23, 2018)
4
7
  - Support to include connection `name` as part of CONNECT options (#145)
5
8
  - Fixed support for Ruby 2.5 due to missing OpenSSL `require` (#144)
data/README.md CHANGED
@@ -3,16 +3,7 @@
3
3
  A [Ruby](http://ruby-lang.org) client for the [NATS messaging system](https://nats.io).
4
4
 
5
5
  [![License Apache 2.0](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
6
- [![Build Status](https://travis-ci.org/nats-io/ruby-nats.svg)](http://travis-ci.org/nats-io/ruby-nats) [![Gem Version](https://d25lcipzij17d.cloudfront.net/badge.svg?id=rb&type=5&v=0.10.0)](https://rubygems.org/gems/nats/versions/0.10.0) [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/gems/nats)
7
-
8
- ## Supported Platforms
9
-
10
- This gem and the client are known to work on the following Ruby platforms:
11
-
12
- - MRI 2.3.0, 2.4.0, 2.5.0
13
- - JRuby 9.1.2.0, 9.1.15.0, 9.2.0.0
14
-
15
- If you're looking for a non-EventMachine alternative, check out the [nats-pure](https://github.com/nats-io/pure-ruby-nats) gem.
6
+ [![Build Status](https://travis-ci.org/nats-io/nats.rb.svg)](http://travis-ci.org/nats-io/nats.rb) [![Gem Version](https://d25lcipzij17d.cloudfront.net/badge.svg?id=rb&type=5&v=0.11.0)](https://rubygems.org/gems/nats/versions/0.11.0) [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/gems/nats)
16
7
 
17
8
  ## Getting Started
18
9
 
@@ -23,6 +14,16 @@ nats-sub foo &
23
14
  nats-pub foo 'Hello World!'
24
15
  ```
25
16
 
17
+ Starting from [v0.11.0](https://github.com/nats-io/nats.py/releases/tag/v0.11.0) release,
18
+ you can also optionally install [NKEYS](https://github.com/nats-io/nkeys.rb) in order to use
19
+ the new NATS v2.0 auth features:
20
+
21
+ ```bash
22
+ gem install nkeys
23
+ ```
24
+
25
+ If you're looking for a non-EventMachine alternative, check out the [nats-pure](https://github.com/nats-io/nats-pure.rb) gem.
26
+
26
27
  ## Basic Usage
27
28
 
28
29
  ```ruby
@@ -285,6 +286,47 @@ NATS.start {
285
286
  }
286
287
  ```
287
288
 
289
+ ### New Authentication (Nkeys and User Credentials)
290
+
291
+ This requires server with version >= 2.0.0
292
+
293
+ NATS servers have a new security and authentication mechanism to authenticate with user credentials and NKEYS. A single file containing the JWT and NKEYS to authenticate against a NATS v2 server can be set with the `user_credentials` option:
294
+
295
+ ```ruby
296
+ require 'nats/client'
297
+
298
+ NATS.start("tls://connect.ngs.global", user_credentials: "/path/to/creds") do |nc|
299
+ nc.subscribe("hello") do |msg|
300
+ puts "[Received] #{msg}"
301
+ end
302
+ nc.publish('hello', 'world')
303
+ end
304
+ ```
305
+
306
+ This will create two callback handlers to present the user JWT and sign the nonce challenge from the server. The core client library never has direct access to your private key and simply performs the callback for signing the server challenge. The library will load and wipe and clear the objects it uses for each connect or reconnect.
307
+
308
+ Bare NKEYS are also supported. The nkey seed should be in a read only file, e.g. `seed.txt`.
309
+
310
+ ```bash
311
+ > cat seed.txt
312
+ # This is my seed nkey!
313
+ SUAGMJH5XLGZKQQWAWKRZJIGMOU4HPFUYLXJMXOO5NLFEO2OOQJ5LPRDPM
314
+ ```
315
+
316
+ Then in the client specify the path to the seed using the `nkeys_seed` option:
317
+
318
+ ```ruby
319
+ require 'nats/client'
320
+
321
+ NATS.start("tls://connect.ngs.global", nkeys_seed: "path/to/seed.txt") do |nc|
322
+ nc.subscribe("hello") do |msg|
323
+ puts "[Received] #{msg}"
324
+ end
325
+ nc.publish('hello', 'world')
326
+ end
327
+
328
+ ```
329
+
288
330
  ## License
289
331
 
290
332
  Unless otherwise noted, the NATS source files are distributed under
@@ -18,12 +18,13 @@ require 'rubygems'
18
18
  require 'nats/client'
19
19
 
20
20
  def usage
21
- puts "Usage: nats-pub <subject> <msg> [-s server]"; exit
21
+ puts "Usage: nats-pub <subject> <msg> [-s server] [--creds CREDS]"; exit
22
22
  end
23
23
 
24
24
  args = ARGV.dup
25
25
  opts_parser = OptionParser.new do |opts|
26
26
  opts.on('-s SERVER') { |server| $nats_server = server }
27
+ opts.on('--creds CREDS') { |creds| $creds = creds }
27
28
  end
28
29
  args = opts_parser.parse!(args)
29
30
 
@@ -33,7 +34,7 @@ msg ||= 'Hello World'
33
34
 
34
35
  NATS.on_error { |err| puts "Server Error: #{err}"; exit! }
35
36
 
36
- NATS.start(:uri => $nats_server) do
37
+ NATS.start($nats_server, user_credentials: $creds) do
37
38
  NATS.publish(subject, msg) { NATS.stop }
38
39
  end
39
40
 
@@ -28,6 +28,7 @@ opts_parser = OptionParser.new do |opts|
28
28
  opts.on('-s SERVER') { |server| $nats_server = server }
29
29
  opts.on('-t','--time') { $show_time = true }
30
30
  opts.on('-r','--raw') { $show_raw = true }
31
+ opts.on('--creds CREDS') { |creds| $creds = creds }
31
32
  end
32
33
  args = opts_parser.parse!(args)
33
34
 
@@ -54,7 +55,7 @@ end
54
55
 
55
56
  NATS.on_error { |err| puts "Server Error: #{err}"; exit! }
56
57
 
57
- NATS.start(:uri => $nats_server) do
58
+ NATS.start($nats_server, user_credentials: $creds) do
58
59
  puts "Listening on [#{subject}], queue group [#{queue_group}]" unless $show_raw
59
60
  NATS.subscribe(subject, :queue => queue_group) { |msg, _, sub|
60
61
  puts decorate(sub, msg)
@@ -29,6 +29,7 @@ opts_parser = OptionParser.new do |opts|
29
29
  opts.on('-t','--time') { $show_time = true }
30
30
  opts.on('-r','--raw') { $show_raw = true }
31
31
  opts.on('-n RESPONSES') { |responses| $responses = Integer(responses) if Integer(responses) > 0 }
32
+ opts.on('--creds CREDS') { |creds| $creds = creds }
32
33
  end
33
34
  args = opts_parser.parse!(args)
34
35
 
@@ -55,7 +56,7 @@ end
55
56
 
56
57
  NATS.on_error { |err| puts "Server Error: #{err}"; exit! }
57
58
 
58
- NATS.start(:uri => $nats_server) do
59
+ NATS.start($nats_server, user_credentials: $creds) do
59
60
  NATS.request(subject, msg) { |(msg, reply)|
60
61
  puts decorate(msg)
61
62
  exit! if $responses && ($responses-=1) < 1
@@ -20,7 +20,7 @@ require 'nats/client'
20
20
  ['TERM', 'INT'].each { |s| trap(s) { puts; exit! } }
21
21
 
22
22
  def usage
23
- puts "Usage: nats-sub <subject> [-s server] [-t] [-r]"; exit
23
+ puts "Usage: nats-sub <subject> [-s server] [--creds CREDS] [-t] [-r]"; exit
24
24
  end
25
25
 
26
26
  args = ARGV.dup
@@ -28,6 +28,7 @@ opts_parser = OptionParser.new do |opts|
28
28
  opts.on('-s SERVER') { |server| $nats_server = server }
29
29
  opts.on('-t','--time') { $show_time = true }
30
30
  opts.on('-r','--raw') { $show_raw = true }
31
+ opts.on('--creds CREDS') { |creds| $creds = creds }
31
32
  end
32
33
  args = opts_parser.parse!(args)
33
34
 
@@ -53,7 +54,7 @@ end
53
54
 
54
55
  NATS.on_error { |err| puts "Server Error: #{err}"; exit! }
55
56
 
56
- NATS.start(:uri => $nats_server) do
57
+ NATS.start($nats_server, user_credentials: $creds) do
57
58
  puts "Listening on [#{subject}]" unless $show_raw
58
59
  NATS.subscribe(subject) { |msg, _, sub| puts decorate(sub, msg) }
59
60
  end
@@ -72,6 +72,7 @@ module NATS
72
72
  # Parser
73
73
  AWAITING_CONTROL_LINE = 1 #:nodoc:
74
74
  AWAITING_MSG_PAYLOAD = 2 #:nodoc:
75
+ AWAITING_INFO_LINE = 3 # :nodoc:
75
76
 
76
77
  class Error < StandardError; end #:nodoc:
77
78
 
@@ -150,7 +151,9 @@ module NATS
150
151
  def connect(uri=nil, opts={}, &blk)
151
152
  case uri
152
153
  when String
153
- opts[:uri] = process_uri(uri)
154
+ # Initialize TLS defaults in case any url is using it.
155
+ uris = opts[:uri] = process_uri(uri)
156
+ opts[:tls] ||= {} if uris.any? {|u| u.scheme == 'tls'}
154
157
  when Hash
155
158
  opts = uri
156
159
  end
@@ -482,7 +485,16 @@ module NATS
482
485
  # Drain mode
483
486
  @draining = false
484
487
  @drained_subs = false
485
- send_connect_command
488
+
489
+ # NKEYS
490
+ @user_credentials = options[:user_credentials] if options[:user_credentials]
491
+ @nkeys_seed = options[:nkeys_seed] if options[:nkeys_seed]
492
+ @user_nkey_cb = nil
493
+ @user_jwt_cb = nil
494
+ @signature_cb = nil
495
+
496
+ # NKEYS
497
+ setup_nkeys_connect if @user_credentials or @nkeys_seed
486
498
  end
487
499
 
488
500
  # Publish a message to a given subject, with optional reply subject and completion block
@@ -770,7 +782,7 @@ module NATS
770
782
  end
771
783
 
772
784
  def auth_connection?
773
- !@uri.user.nil? || @options[:token]
785
+ !@uri.user.nil? || @options[:token] || @server_info[:auth_required]
774
786
  end
775
787
 
776
788
  def connect_command #:nodoc:
@@ -782,7 +794,16 @@ module NATS
782
794
  :protocol => ::NATS::PROTOCOL_VERSION,
783
795
  :echo => !@options[:no_echo]
784
796
  }
797
+
785
798
  case
799
+ when @options[:user_credentials]
800
+ nonce = @server_info[:nonce]
801
+ cs[:jwt] = @user_jwt_cb.call
802
+ cs[:sig] = @signature_cb.call(nonce)
803
+ when @options[:nkeys_seed]
804
+ nonce = @server_info[:nonce]
805
+ cs[:nkey] = @user_nkey_cb.call
806
+ cs[:sig] = @signature_cb.call(nonce)
786
807
  when @options[:token]
787
808
  cs[:auth_token] = @options[:token]
788
809
  when @uri.password.nil?
@@ -853,6 +874,15 @@ module NATS
853
874
 
854
875
  while (@buf)
855
876
  case @parse_state
877
+ when AWAITING_INFO_LINE
878
+ case @buf
879
+ when INFO
880
+ @buf = $'
881
+ process_connect_init($1)
882
+ else
883
+ # If we are here we do not have a complete line yet that we understand.
884
+ return
885
+ end
856
886
  when AWAITING_CONTROL_LINE
857
887
  case @buf
858
888
  when MSG
@@ -901,7 +931,7 @@ module NATS
901
931
  end
902
932
  end
903
933
 
904
- def process_info(info) #:nodoc:
934
+ def process_connect_init(info) # :nodoc:
905
935
  # Each JSON parser uses a different key/value pair to use symbol keys
906
936
  # instead of strings when parsing. Passing all three pairs assures each
907
937
  # parser gets what it needs. For the json gem :symbolize_name, for yajl
@@ -931,9 +961,29 @@ module NATS
931
961
  close_connection_after_writing
932
962
  end
933
963
  end
964
+ send_connect_command
965
+
966
+ # Only initial INFO command is treated specially for auth reasons,
967
+ # the rest are processed asynchronously to discover servers.
968
+ @parse_state = AWAITING_CONTROL_LINE
969
+ process_info(info)
970
+ process_connect
971
+
972
+ if @server_info[:auth_required]
973
+ current = server_pool.first
974
+ current[:auth_required] = true
975
+
976
+ # Send pending connect followed by ping/pong to ensure we're authorized.
977
+ queue_server_rt { current[:auth_ok] = true }
978
+ end
979
+ flush_pending
980
+ end
981
+
982
+ def process_info(info_line) #:nodoc:
983
+ info = JSON.parse(info_line, :symbolize_keys => true, :symbolize_names => true, :symbol_keys => true)
934
984
 
935
985
  # Detect any announced server that we might not be aware of...
936
- connect_urls = @server_info[:connect_urls]
986
+ connect_urls = info[:connect_urls]
937
987
  if connect_urls
938
988
  srvs = []
939
989
 
@@ -963,15 +1013,7 @@ module NATS
963
1013
  server_pool.push(*srvs)
964
1014
  end
965
1015
 
966
- if @server_info[:auth_required]
967
- current = server_pool.first
968
- current[:auth_required] = true
969
- # Send pending connect followed by ping/pong to ensure we're authorized.
970
- queue_server_rt { current[:auth_ok] = true }
971
- flush_pending
972
- end
973
-
974
- @server_info
1016
+ info
975
1017
  end
976
1018
 
977
1019
  def client_using_secure_connection?
@@ -1002,22 +1044,19 @@ module NATS
1002
1044
  end
1003
1045
 
1004
1046
  def connection_completed #:nodoc:
1005
- @parse_state = AWAITING_CONTROL_LINE
1047
+ @parse_state = AWAITING_INFO_LINE
1006
1048
 
1007
1049
  # Delay sending CONNECT or any other command here until we are sure
1008
1050
  # that we have a valid established secure connection.
1009
1051
  return if (@ssl or @tls)
1010
1052
 
1011
- # Mark that we established already TCP connection to the server. In case of TLS,
1012
- # prepare commands which will be dispatched to server and delay flushing until
1013
- # we have processed the INFO line sent by the server and done the handshake.
1053
+ # Mark that we established already TCP connection to the server,
1054
+ # when using TLS we only do so after handshake has been completed.
1014
1055
  @connected = true
1015
- process_connect
1016
1056
  end
1017
1057
 
1018
1058
  def ssl_handshake_completed
1019
1059
  @connected = true
1020
- process_connect
1021
1060
  end
1022
1061
 
1023
1062
  def process_connect #:nodoc:
@@ -1174,7 +1213,6 @@ module NATS
1174
1213
  current[:reconnect_attempts] ||= 0
1175
1214
  current[:reconnect_attempts] += 1
1176
1215
 
1177
- send_connect_command
1178
1216
  begin
1179
1217
  EM.reconnect(@uri.host, @uri.port, self)
1180
1218
  rescue
@@ -1201,6 +1239,84 @@ module NATS
1201
1239
  true
1202
1240
  end
1203
1241
 
1242
+ def setup_nkeys_connect
1243
+ begin
1244
+ require 'nkeys'
1245
+ require 'base64'
1246
+ rescue LoadError
1247
+ raise(Error, "nkeys is not installed")
1248
+ end
1249
+
1250
+ case
1251
+ when @nkeys_seed
1252
+ @user_nkey_cb = proc {
1253
+ seed = File.read(@nkeys_seed).chomp
1254
+ kp = NKEYS::from_seed(seed)
1255
+
1256
+ # Take a copy since original will be gone with the wipe.
1257
+ pub_key = kp.public_key.dup
1258
+ kp.wipe!
1259
+
1260
+ pub_key
1261
+ }
1262
+
1263
+ @signature_cb = proc { |nonce|
1264
+ seed = File.read(@nkeys_seed).chomp
1265
+ kp = NKEYS::from_seed(seed)
1266
+ raw_signed = kp.sign(nonce)
1267
+ kp.wipe!
1268
+ encoded = Base64.urlsafe_encode64(raw_signed)
1269
+ encoded.gsub('=', '')
1270
+ }
1271
+ when @user_credentials
1272
+ # When the credentials are within a single decorated file.
1273
+ @user_jwt_cb = proc {
1274
+ jwt_start = "BEGIN NATS USER JWT".freeze
1275
+ found = false
1276
+ jwt = nil
1277
+ File.readlines(@user_credentials).each do |line|
1278
+ case
1279
+ when found
1280
+ jwt = line.chomp
1281
+ break
1282
+ when line.include?(jwt_start)
1283
+ found = true
1284
+ end
1285
+ end
1286
+ raise(Error, "No JWT found in #{@user_credentials}") if not found
1287
+
1288
+ jwt
1289
+ }
1290
+
1291
+ @signature_cb = proc { |nonce|
1292
+ seed_start = "BEGIN USER NKEY SEED".freeze
1293
+ found = false
1294
+ seed = nil
1295
+ File.readlines(@user_credentials).each do |line|
1296
+ case
1297
+ when found
1298
+ seed = line.chomp
1299
+ break
1300
+ when line.include?(seed_start)
1301
+ found = true
1302
+ end
1303
+ end
1304
+ raise(Error, "No nkey user seed found in #{@user_credentials}") if not found
1305
+
1306
+ kp = NKEYS::from_seed(seed)
1307
+ raw_signed = kp.sign(nonce)
1308
+
1309
+ # seed is a reference so also cleared when doing wipe,
1310
+ # which can be done since Ruby strings are mutable.
1311
+ kp.wipe
1312
+ encoded = Base64.urlsafe_encode64(raw_signed)
1313
+
1314
+ # Remove padding
1315
+ encoded.gsub('=', '')
1316
+ }
1317
+ end
1318
+ end
1319
+
1204
1320
  # Parse out URIs which can now be an array of server choices
1205
1321
  # The server pool will contain both explicit and implicit members.
1206
1322
  def process_uri_options #:nodoc
@@ -14,7 +14,7 @@
14
14
 
15
15
  module NATS
16
16
  # NOTE: These are all announced to the server on CONNECT
17
- VERSION = "0.10.0".freeze
17
+ VERSION = "0.11.0".freeze
18
18
  LANG = RUBY_ENGINE
19
19
  PROTOCOL_VERSION = 1
20
20
  end
metadata CHANGED
@@ -1,33 +1,33 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derek Collison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-31 00:00:00.000000000 Z
11
+ date: 2019-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: eventmachine
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.2'
20
- - - ">="
20
+ - - "~>"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '1.2'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - "~>"
27
+ - - ">="
28
28
  - !ruby/object:Gem::Version
29
29
  version: '1.2'
30
- - - ">="
30
+ - - "~>"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '1.2'
33
33
  description: NATS is an open-source, high-performance, lightweight cloud messaging
@@ -88,8 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  requirements: []
91
- rubyforge_project:
92
- rubygems_version: 2.7.3
91
+ rubygems_version: 3.0.3
93
92
  signing_key:
94
93
  specification_version: 4
95
94
  summary: NATS is an open-source, high-performance, lightweight cloud messaging system.