zyre 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,14 +10,23 @@
10
10
  #ifndef ZYRE_EXT_H_90322ABD
11
11
  #define ZYRE_EXT_H_90322ABD
12
12
 
13
+ #include "extconf.h"
14
+
13
15
  #include <ruby.h>
14
16
  #include <ruby/intern.h>
15
17
  #include <ruby/thread.h>
16
18
  #include <ruby/encoding.h>
19
+ #include <ruby/version.h>
20
+
21
+ #ifdef HAVE_ZCERT_UNSET_META
22
+ # define CZMQ_BUILD_DRAFT_API 1
23
+ #endif
24
+ #ifdef HAVE_ZYRE_SET_BEACON_PEER_PORT
25
+ # define ZYRE_BUILD_DRAFT_API 1
26
+ #endif
17
27
 
18
28
  #include "zyre.h"
19
29
  #include "czmq.h"
20
- #include "extconf.h"
21
30
 
22
31
  #ifndef TRUE
23
32
  # define TRUE 1
@@ -28,7 +37,8 @@
28
37
  #endif
29
38
 
30
39
 
31
- // For synthesized events
40
+ // For synthesized events, sizeof calculations, copied from the czmq and zyre
41
+ // source
32
42
  struct _zyre_event_t {
33
43
  char *type; // Event type as string
34
44
  char *peer_uuid; // Sender UUID as string
@@ -40,6 +50,7 @@ struct _zyre_event_t {
40
50
  };
41
51
 
42
52
 
53
+
43
54
  /* --------------------------------------------------------------
44
55
  * Declarations
45
56
  * -------------------------------------------------------------- */
@@ -74,6 +85,9 @@ extern VALUE rzyre_mZyre;
74
85
  extern VALUE rzyre_cZyreNode;
75
86
  extern VALUE rzyre_cZyreEvent;
76
87
  extern VALUE rzyre_cZyrePoller;
88
+ extern VALUE rzyre_cZyreCert;
89
+
90
+ extern zactor_t *auth_actor;
77
91
 
78
92
 
79
93
  /* --------------------------------------------------------------
@@ -83,6 +97,7 @@ extern VALUE rzyre_cZyrePoller;
83
97
  #define IsZyreNode( obj ) rb_obj_is_kind_of( (obj), rzyre_cZyreNode )
84
98
  #define IsZyreEvent( obj ) rb_obj_is_kind_of( (obj), rzyre_cZyreEvent )
85
99
  #define IsZyrePoller( obj ) rb_obj_is_kind_of( (obj), rzyre_cZyrePoller )
100
+ #define IsZyreCert( obj ) rb_obj_is_kind_of( (obj), rzyre_cZyreCert )
86
101
 
87
102
  /* --------------------------------------------------------------
88
103
  * Utility functions
@@ -98,8 +113,10 @@ extern void Init_zyre_ext _(( void ));
98
113
  extern void rzyre_init_node _(( void ));
99
114
  extern void rzyre_init_event _(( void ));
100
115
  extern void rzyre_init_poller _(( void ));
116
+ extern void rzyre_init_cert _(( void ));
101
117
 
102
118
  extern zyre_t * rzyre_get_node _(( VALUE ));
119
+ extern zcert_t * rzyre_get_cert _(( VALUE ));
103
120
 
104
121
  #endif /* end of include guard: ZYRE_EXT_H_90322ABD */
105
122
 
data/lib/zyre.rb CHANGED
@@ -1,35 +1,28 @@
1
1
  # -*- ruby -*-
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'set'
4
5
  require 'loggability'
5
6
 
6
7
  require_relative 'zyre_ext'
7
8
 
8
9
 
9
- # A binding to libzyre
10
+ #--
11
+ # See also: ext/zyre_ext/zyre_ext.c
10
12
  module Zyre
11
13
  extend Loggability
12
14
 
13
15
 
14
16
  # Gem version (semver)
15
- VERSION = '0.3.1'
17
+ VERSION = '0.4.0'
16
18
 
17
19
 
18
20
  # Set up a logger for Zyre classes
19
21
  log_as :zyre
20
22
 
21
23
 
22
- ### Wait on one or more +nodes+ to become readable, returning the first one that does
23
- ### or +nil+ if the +timeout+ is zero or greater and at least that many seconds elapse.
24
- ### Specify a +timeout+ of -1 to wait indefinitely. The timeout is in floating-point
25
- ### seconds.
26
- ###
27
- ### Raises an Interrupt if the call is interrupted or the ZMQ context is destroyed.
28
- def self::wait( *nodes, timeout: -1 )
29
- nodes = nodes.flatten
30
- return nil if nodes.empty?
31
- return self.wait2( nodes, timeout )
32
- end
24
+ @whitelisted_ips = Set.new
25
+ @blacklisted_ips = Set.new
33
26
 
34
27
 
35
28
  ### If the given +key+ is a Symbol, transform it into an RFC822-style header key. If
@@ -50,4 +43,36 @@ module Zyre
50
43
  transform_values {|v| v.to_s.encode('us-ascii') }
51
44
  end
52
45
 
46
+
47
+ ### Allow (whitelist) a list of IP +addresses+. For NULL, all clients from
48
+ ### these addresses will be accepted. For PLAIN and CURVE, they will be
49
+ ### allowed to continue with authentication. You can call this method
50
+ ### multiple times to whitelist more IP addresses. If you whitelist one
51
+ ### or more addresses, any non-whitelisted addresses are treated as
52
+ ### blacklisted:
53
+ def self::allow( *addresses )
54
+ @whitelisted_ips.merge( addresses )
55
+ end
56
+
57
+
58
+ ### Deny (blacklist) a list of IP +addresses+. For all security mechanisms,
59
+ ### this rejects the connection without any further authentication. Use
60
+ ### either a whitelist, or a blacklist, not not both. If you define both
61
+ ### a whitelist and a blacklist, only the whitelist takes effect:
62
+ def self::deny( *addresses )
63
+ @blacklisted_ips.merge( addresses )
64
+ end
65
+
66
+
67
+ ### Returns +true+ if the underlying Czmq library was built with draft APIs.
68
+ def self::has_draft_czmq_apis?
69
+ return Zyre::BUILT_WITH_DRAFT_CZMQ_API ? true : false
70
+ end
71
+
72
+
73
+ ### Returns +true+ if the underlying Zyre library was built with draft APIs.
74
+ def self::has_draft_apis?
75
+ return Zyre::BUILT_WITH_DRAFT_API ? true : false
76
+ end
77
+
53
78
  end # module Zyre
data/lib/zyre/cert.rb ADDED
@@ -0,0 +1,75 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'loggability'
5
+
6
+ require 'zyre' unless defined?( Zyre )
7
+
8
+
9
+ #--
10
+ # See also: ext/zyre_ext/cert.c
11
+ class Zyre::Cert
12
+ extend Loggability
13
+
14
+
15
+ # The placeholder key that is set as the secret key for a public certificate.
16
+ EMPTY_KEY = "\x00" * 32
17
+
18
+
19
+ # Use the Zyre module's logger
20
+ log_to :zyre
21
+
22
+
23
+ # Set up some more Rubyish aliases
24
+ alias_method :private_key, :secret_key
25
+ alias_method :==, :eql?
26
+
27
+
28
+ ### Fetch the value for the cert metadata with the given +name+.
29
+ def []( name )
30
+ return self.meta( name.to_s )
31
+ end
32
+
33
+
34
+ ### Set the value for the cert metadata with the given +name+ to +value+.
35
+ def []=( name, value )
36
+ return self.set_meta( name.to_s, value.to_s )
37
+ end
38
+
39
+
40
+ ### Return the metadata from the cert as a Hash.
41
+ def meta_hash
42
+ hash = self.meta_keys.each_with_object( {} ) do |key, h|
43
+ h[ key ] = self[ key ]
44
+ end
45
+ hash.freeze
46
+ return hash
47
+ end
48
+
49
+
50
+ ### Delete the value for the cert metadata with the given +name+. Requires
51
+ ### CZMQ to have been built with Draft APIs.
52
+ def delete( name )
53
+ name = name.to_s
54
+
55
+ deleted_val = self[ name ]
56
+ self.unset_meta( name )
57
+
58
+ return deleted_val
59
+ end
60
+
61
+
62
+ ### Returns +true+ if the certificate has a secret key.
63
+ def have_secret_key?
64
+ return self.secret_key != EMPTY_KEY
65
+ end
66
+
67
+
68
+ ### Apply the certificate to the specified +zyre_node+, i.e. use the
69
+ ### cert for CURVE security. If the receiving certificate doesn't have a
70
+ ### private key, an exception will be raised.
71
+ def apply( zyre_node )
72
+ return zyre_node.zcert = self
73
+ end
74
+
75
+ end # class Zyre::Cert
data/lib/zyre/event.rb CHANGED
@@ -25,6 +25,7 @@ class Zyre::Event
25
25
  autoload :Exit, 'zyre/event/exit'
26
26
  autoload :Join, 'zyre/event/join'
27
27
  autoload :Leave, 'zyre/event/leave'
28
+ autoload :Leader, 'zyre/event/leader'
28
29
  autoload :Shout, 'zyre/event/shout'
29
30
  autoload :Silent, 'zyre/event/silent'
30
31
  autoload :Stop, 'zyre/event/stop'
@@ -6,4 +6,9 @@ require 'zyre/event' unless defined?( Zyre::Event )
6
6
 
7
7
  class Zyre::Event::Stop < Zyre::Event
8
8
 
9
+ ### Provide the details of the inspect message.
10
+ def inspect_details
11
+ return " node is stopping"
12
+ end
13
+
9
14
  end # class Zyre::Event::Stop
data/lib/zyre/testing.rb CHANGED
@@ -221,6 +221,18 @@ module Zyre::Testing
221
221
  end
222
222
 
223
223
 
224
+ ### Generate a LEADER event.
225
+ def leader( **overrides )
226
+ uuid = overrides.delete( :peer_uuid ) || self.peer_uuid
227
+ config = {
228
+ peer_name: self.peer_name,
229
+ group: self.group
230
+ }.merge( overrides )
231
+
232
+ return Zyre::Event.synthesize( :leader, uuid, **config )
233
+ end
234
+
235
+
224
236
  ### Generate an EXIT event.
225
237
  def exit( **overrides )
226
238
  uuid = overrides.delete( :peer_uuid ) || self.peer_uuid
@@ -231,6 +243,17 @@ module Zyre::Testing
231
243
  return Zyre::Event.synthesize( :exit, uuid, **config )
232
244
  end
233
245
 
246
+
247
+ ### Generate a STOP event.
248
+ def stop( **overrides )
249
+ uuid = overrides.delete( :peer_uuid ) || self.peer_uuid
250
+ config = {
251
+ peer_name: self.peer_name
252
+ }.merge( overrides )
253
+
254
+ return Zyre::Event.synthesize( :stop, uuid, **config )
255
+ end
256
+
234
257
  end # class EventFactory
235
258
 
236
259
 
@@ -260,6 +283,7 @@ module Zyre::Testing
260
283
  def started_node( name=nil )
261
284
  node = Zyre::Node.new( name )
262
285
  node.endpoint = 'inproc://node-test-%s' % [ SecureRandom.hex(16) ]
286
+
263
287
  yield( node ) if block_given?
264
288
 
265
289
  if @gossip_endpoint
data/spec/spec_helper.rb CHANGED
@@ -55,6 +55,10 @@ RSpec.configure do |config|
55
55
  Zyre::Testing.check_fdmax
56
56
  end
57
57
 
58
+ config.filter_run_excluding( :draft_api ) unless Zyre.has_draft_apis?
59
+ config.filter_run_excluding( :czmq_draft_api ) unless Zyre.has_draft_czmq_apis?
60
+ config.filter_run_excluding( :no_czmq_draft_api ) if Zyre.has_draft_czmq_apis?
61
+
58
62
  config.include( Zyre::Testing )
59
63
  config.include( Loggability::SpecHelpers )
60
64
  end
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env rspec -cfd
2
+
3
+ require_relative '../spec_helper'
4
+
5
+ require 'tempfile'
6
+
7
+ require 'zyre/cert'
8
+
9
+
10
+ RSpec.describe( Zyre::Cert ) do
11
+
12
+ let( :cert_file ) { Tempfile.new('zyre_cert_test') }
13
+
14
+
15
+ it "can be created in-memory" do
16
+ expect( described_class.new ).to be_a( described_class )
17
+ end
18
+
19
+
20
+ it "can be created from a keypair" do
21
+ cert = described_class.new
22
+
23
+ result = described_class.from( cert.public_key, cert.private_key )
24
+
25
+ expect( result ).to be_a( described_class )
26
+ expect( result ).to eq( cert )
27
+ end
28
+
29
+
30
+ it "can be saved to and loaded from a file" do
31
+ cert = described_class.new
32
+ cert.save( cert_file.path )
33
+
34
+ result = described_class.load( cert_file.path )
35
+
36
+ expect( result ).to be_a( described_class )
37
+ expect( result ).to eq( cert )
38
+ end
39
+
40
+
41
+ it "can be saved as a public cert to and loaded from a file" do
42
+ cert = described_class.new
43
+ cert.save_public( cert_file.path )
44
+
45
+ result = described_class.load( cert_file.path )
46
+
47
+ expect( result ).to be_a( described_class )
48
+ expect( result.public_key ).to eq( cert.public_key )
49
+ expect( result.secret_key ).to eq( Zyre::Cert::EMPTY_KEY )
50
+ end
51
+
52
+
53
+ it "can be saved as a secret cert to and loaded from a file" do
54
+ cert = described_class.new
55
+ cert.save_secret( cert_file.path )
56
+
57
+ result = described_class.load( cert_file.path )
58
+
59
+ expect( result ).to be_a( described_class )
60
+ expect( result ).to eq( cert )
61
+ end
62
+
63
+
64
+ it "knows what its public key is" do
65
+ cert = described_class.new
66
+
67
+ key = cert.public_key
68
+
69
+ expect( key.encoding ).to eq( Encoding::ASCII_8BIT )
70
+ expect( key.bytesize ).to eq( 32 )
71
+ end
72
+
73
+
74
+ it "knows what its secret key is" do
75
+ cert = described_class.new
76
+
77
+ key = cert.secret_key
78
+
79
+ expect( key.encoding ).to eq( Encoding::ASCII_8BIT )
80
+ expect( key.bytesize ).to eq( 32 )
81
+ end
82
+
83
+
84
+ it "knows what its Z85-armored public key is" do
85
+ cert = described_class.new
86
+
87
+ key = cert.public_txt
88
+
89
+ expect( key.encoding ).to eq( Encoding::US_ASCII )
90
+ expect( key.length ).to eq( 40 )
91
+ end
92
+
93
+
94
+ it "knows what its Z85-armored secret key is" do
95
+ cert = described_class.new
96
+
97
+ key = cert.secret_txt
98
+
99
+ expect( key.encoding ).to eq( Encoding::US_ASCII )
100
+ expect( key.length ).to eq( 40 )
101
+ end
102
+
103
+
104
+ it "can set metadata on the cert" do
105
+ cert = described_class.new
106
+
107
+ cert[ :username ] = 'timmy'
108
+
109
+ expect( cert['username'] ).to eq( 'timmy' )
110
+ end
111
+
112
+
113
+ it "stringifies non-string metadata on the cert" do
114
+ cert = described_class.new
115
+
116
+ cert[ :euid ] = 12
117
+
118
+ expect( cert[:euid] ).to eq( '12' )
119
+ end
120
+
121
+
122
+ it "fetches non-existant values as nil" do
123
+ cert = described_class.new
124
+
125
+ expect( cert[:nonexistant_key] ).to be_nil
126
+ end
127
+
128
+
129
+ it "silently ignores overwrites of a metadata value if not built with Drafts", :no_czmq_draft_api do
130
+ cert = described_class.new
131
+
132
+ cert.set_meta( 'foo', 'bar' )
133
+ cert.set_meta( 'foo', 'baz' )
134
+
135
+ expect( cert.meta('foo') ).to eq( 'bar' )
136
+ end
137
+
138
+
139
+ it "can overwrite a metadata value if built with Drafts", :czmq_draft_api do
140
+ cert = described_class.new
141
+
142
+ cert.set_meta( 'foo', 'bar' )
143
+ cert.set_meta( 'foo', 'baz' )
144
+
145
+ expect( cert.meta('foo') ).to eq( 'baz' )
146
+ end
147
+
148
+
149
+ it "knows what metadata has been set on the cert" do
150
+ cert = described_class.new
151
+
152
+ cert[ :euid ] = 0
153
+ cert[ :username ] = 'jrandom'
154
+ cert[ 'firstname' ] = 'James'
155
+ cert[ :lastname ] = 'Random'
156
+ cert[ 'key 2' ] = 'cf67c750-c704-4ef7-ab83-ecb2cd2e326c'
157
+
158
+ expect( cert.meta_keys ).
159
+ to contain_exactly( 'euid', 'username', 'firstname', 'lastname', 'key 2' )
160
+ end
161
+
162
+
163
+ it "can delete one of its metadata key-value pairs", :czmq_draft_api do
164
+ cert = described_class.new
165
+
166
+ cert[ :euid ] = 0
167
+ cert[ :username ] = 'jrandom'
168
+ cert[ 'firstname' ] = 'James'
169
+ cert[ :lastname ] = 'Random'
170
+ cert[ 'key 2' ] = 'cf67c750-c704-4ef7-ab83-ecb2cd2e326c'
171
+
172
+ cert.delete( 'lastname' )
173
+
174
+ expect( cert.meta_keys ).
175
+ to contain_exactly( 'euid', 'username', 'firstname', 'key 2' )
176
+ end
177
+
178
+
179
+ it "can return all of its metadata as a Hash" do
180
+ cert = described_class.new
181
+
182
+ cert[ :euid ] = 0
183
+ cert[ :username ] = 'jrandom'
184
+ cert[ 'firstname' ] = 'James'
185
+ cert[ :lastname ] = 'Random'
186
+ cert[ 'key 2' ] = 'cf67c750-c704-4ef7-ab83-ecb2cd2e326c'
187
+
188
+ expect( cert.meta_hash ).to eq({
189
+ 'euid' => '0',
190
+ 'username' => 'jrandom',
191
+ 'firstname' => 'James',
192
+ 'lastname' => 'Random',
193
+ 'key 2' => 'cf67c750-c704-4ef7-ab83-ecb2cd2e326c',
194
+ })
195
+ end
196
+
197
+
198
+ it "can be applied to a Zyre node", :draft_apis do
199
+ node = instance_double( Zyre::Node )
200
+ cert = Zyre::Cert.new
201
+
202
+ expect( node ).to receive( :zcert= ).with( cert )
203
+ cert.apply( node )
204
+ end
205
+
206
+
207
+ it "can be duplicated" do
208
+ cert = described_class.new
209
+ cert[ :node_id ] = '05343500-f908-4903-9441-e648eb1754ec'
210
+ cert[ :node_order ] = 1
211
+
212
+ other = cert.dup
213
+
214
+ expect( other.object_id ).not_to eq( cert.object_id )
215
+ end
216
+
217
+
218
+ end