omf_common 6.0.7.1 → 6.0.8.pre.1

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.
@@ -11,6 +11,7 @@ require 'omf_common/auth'
11
11
  module OmfCommon::Auth
12
12
 
13
13
  class MissingPrivateKeyException < AuthException; end
14
+ class MissingCertificateException < AuthException; end
14
15
 
15
16
  class CertificateStore
16
17
  include MonitorMixin
@@ -29,44 +30,56 @@ module OmfCommon::Auth
29
30
  @@instance
30
31
  end
31
32
 
32
- def register(certificate, address = nil)
33
+ def register_trusted(certificate)
33
34
  @@instance.synchronize do
34
35
  begin
35
- @x509_store.add_cert(certificate.to_x509) if @certs[address].nil? && @certs[certificate.subject].nil?
36
+ @x509_store.add_cert(certificate.to_x509)
36
37
  rescue OpenSSL::X509::StoreError => e
37
38
  if e.message == "cert already in hash table"
38
- raise "X509 cert '#{address}' already registered in X509 store"
39
+ warn "X509 cert '#{certificate.subject}' already registered in X509 store"
39
40
  else
40
41
  raise e
41
42
  end
42
43
  end
44
+ @certs[certificate.subject] ||= certificate
45
+ end
46
+ end
43
47
 
44
- address ||= certificate.address
48
+ def register(certificate)
49
+ raise "Expected Certificate, but got '#{certificate.class}'" unless certificate.is_a? Certificate
45
50
 
46
- if address
47
- @certs[address] ||= certificate
48
- else
49
- debug "Register certificate without address - #{certificate}, is it a CA cert?"
51
+ debug "Registering certificate for '#{certificate.addresses}' - #{certificate.subject}"
52
+ @@instance.synchronize do
53
+ _set(certificate.subject, certificate)
54
+ if rid = certificate.resource_id
55
+ _set(rid, certificate)
56
+ end
57
+ certificate.addresses.each do |type, name|
58
+ _set(name, certificate)
50
59
  end
51
-
52
- @certs[certificate.subject] ||= certificate
53
60
  end
54
61
  end
55
62
 
56
- def register_x509(cert_pem, address = nil)
57
- if (cert = Certificate.create_from_x509(cert_pem))
63
+ def register_x509(cert_pem)
64
+ if (cert = Certificate.create_from_pem(cert_pem))
58
65
  debug "REGISTERED #{cert}"
59
- register(cert, address)
66
+ register(cert)
60
67
  end
61
68
  end
62
69
 
63
70
  def cert_for(url)
64
- @certs[url]
71
+ # The key of @certs could be a OpenSSL::X509::Name instance
72
+ unless (cert = @certs.find { |k, v| k.to_s == url.to_s })
73
+ warn "Unknown cert '#{url}'"
74
+ raise MissingCertificateException.new(url)
75
+ end
76
+ cert[1]
65
77
  end
66
78
 
67
79
  # @param [OpenSSL::X509::Certificate] cert
68
80
  #
69
81
  def verify(cert)
82
+ #puts "VERIFY: #{cert}::#{cert.class}}"
70
83
  cert = cert.to_x509 if cert.kind_of? OmfCommon::Auth::Certificate
71
84
  v_result = @x509_store.verify(cert)
72
85
  warn "Cert verification failed: '#{@x509_store.error_string}'" unless v_result
@@ -79,7 +92,7 @@ module OmfCommon::Auth
79
92
  #
80
93
  def register_default_certs(folder)
81
94
  Dir["#{folder}/*"].each do |cert|
82
- register_x509(File.read(cert))
95
+ register_trusted(Certificate.create_from_pem(File.read(cert)))
83
96
  end
84
97
  end
85
98
 
@@ -97,6 +110,17 @@ module OmfCommon::Auth
97
110
 
98
111
  super()
99
112
  end
113
+
114
+ def _set(name, certificate)
115
+ if old = @certs[name]
116
+ return if old.to_pem == certificate.to_pem
117
+ warn "Overriding certificate '#{name}' - new: #{certificate.subject} old: #{old.subject}"
118
+ end
119
+ @certs[name] = certificate
120
+ unless name.is_a? String
121
+ _set(name.to_s, certificate)
122
+ end
123
+ end
100
124
  end # class
101
125
 
102
126
  end # module
@@ -0,0 +1,69 @@
1
+
2
+ require 'json'
3
+ require 'json/jwt'
4
+
5
+ module OmfCommon::Auth
6
+ class JWTAuthenticator
7
+
8
+ def self.sign(content, signer, signer_name = signer.subject)
9
+ msg = {cnt: content, iss: signer_name.to_s}
10
+ JSON::JWT.new(msg).sign(signer.key , :RS256).to_s
11
+ end
12
+
13
+ def self.parse(jwt_string)
14
+ jwt_string = jwt_string.split.join
15
+ # Code lifted from 'json-jwt-0.4.3/lib/json/jwt.rb'
16
+ case jwt_string.count('.')
17
+ when 2 # JWT / JWS
18
+ header, claims, signature = jwt_string.split('.', 3).collect do |segment|
19
+ UrlSafeBase64.decode64 segment.to_s
20
+ end
21
+ header, claims = [header, claims].collect do |json|
22
+ #MultiJson.load(json).with_indifferent_access
23
+ puts "JSON>>> #{json}"
24
+ JSON.parse(json, :symbolize_names => true)
25
+ end
26
+ signature_base_string = jwt_string.split('.')[0, 2].join('.')
27
+ jwt = JSON::JWT.new claims
28
+ jwt.header = header
29
+ jwt.signature = signature
30
+
31
+ # NOTE:
32
+ # Some JSON libraries generates wrong format of JSON (spaces between keys and values etc.)
33
+ # So we need to use raw base64 strings for signature verification.
34
+ unless issuer = claims[:iss]
35
+ warn "JWT: Message is missing :iss element"
36
+ return nil
37
+ end
38
+ # if cert_pem = claims[:crt]
39
+ # # let's the credential store take care of it
40
+ # OmfCommon::Auth::CertificateStore.instance.register_x509(cert_pem, src)
41
+ # end
42
+ cert = nil
43
+ issuer.split(',').compact.select do |addr|
44
+ begin
45
+ cert = OmfCommon::Auth::CertificateStore.instance.cert_for(addr)
46
+ rescue OmfCommon::Auth::MissingCertificateException
47
+ nil
48
+ end
49
+ end
50
+ unless cert
51
+ warn "JWT: Can't find cert for issuer '#{issuer}'"
52
+ return nil
53
+ end
54
+
55
+ unless OmfCommon::Auth::CertificateStore.instance.verify(cert)
56
+ warn "JWT: Invalid certificate '#{cert.to_s}', NOT signed by CA certs, or its CA cert NOT loaded into cert store."
57
+ end
58
+
59
+ jwt.verify signature_base_string, cert.to_x509.public_key
60
+ #JSON.parse(claims[:cnt], :symbolize_names => true)
61
+ claims[:cnt]
62
+ else
63
+ warn('JWT: Invalid Format. JWT should include 2 or 3 dots.')
64
+ return nil
65
+ end
66
+ end
67
+
68
+ end # class
69
+ end # module
@@ -0,0 +1,21 @@
1
+ # Copyright (c) 2012 National ICT Australia Limited (NICTA).
2
+ # This software may be used and distributed solely under the terms of the MIT license (License).
3
+ # You should find a copy of the License in LICENSE.TXT or at http://opensource.org/licenses/MIT.
4
+ # By downloading or using this software you accept the terms and the liability disclaimer in the License.
5
+
6
+ require 'omf_common/auth'
7
+
8
+ module OmfCommon::Auth::PDP
9
+ class TestPDP
10
+
11
+ def initialize(opts = {})
12
+ puts "AUTH INIT>>> #{opts}"
13
+ end
14
+
15
+ def authorize(msg, &block)
16
+ puts "AUTH(#{msg.issuer})>>> #{msg}"
17
+ sender = msg.src.address
18
+ msg
19
+ end
20
+ end
21
+ end
@@ -67,7 +67,7 @@ module OmfCommon
67
67
  end
68
68
  @@instance = inst
69
69
  mopts = provider[:message_provider]
70
- mopts[:authenticate] = (opts[:auth] != nil)
70
+ mopts[:authenticate] = opts[:auth]
71
71
  Message.init(mopts)
72
72
 
73
73
  if aopts = opts[:auth]
@@ -132,6 +132,13 @@ module OmfCommon
132
132
  { proto: nil, user: nil, domain: nil }
133
133
  end
134
134
 
135
+ # Return a valid address for this type of communicator
136
+ # Must be implemented by subclasses
137
+ #
138
+ def string_to_address(a_string)
139
+ raise NotImplementedError
140
+ end
141
+
135
142
  # Subscribe to a pubsub topic
136
143
  #
137
144
  # @param [String, Array] topic_name Pubsub topic name
@@ -41,8 +41,14 @@ module OmfCommon
41
41
  { proto: :amqp, user: ::AMQP.settings[:user], domain: ::AMQP.settings[:host] }
42
42
  end
43
43
 
44
+ def string_to_address(a_string)
45
+ @address_prefix+a_string
46
+ end
47
+
44
48
  # Shut down comms layer
45
49
  def disconnect(opts = {})
50
+ info "Disconnecting..."
51
+ OmfCommon.eventloop.stop
46
52
  end
47
53
 
48
54
  # TODO: Should be thread safe and check if already connected
@@ -66,7 +72,7 @@ module OmfCommon
66
72
  #
67
73
  # @param [String] topic Pubsub topic name
68
74
  def create_topic(topic, opts = {})
69
- raise "Topic can't be nil or empty" if topic.nil? || topic.empty?
75
+ raise "Topic can't be nil or empty" if topic.nil? || topic.to_s.empty?
70
76
  opts = opts.dup
71
77
  opts[:communicator] = self
72
78
  topic = topic.to_s
@@ -0,0 +1,29 @@
1
+ # Copyright (c) 2013 National ICT Australia Limited (NICTA).
2
+ # This software may be used and distributed solely under the terms of the MIT license (License).
3
+ # You should find a copy of the License in LICENSE.TXT or at http://opensource.org/licenses/MIT.
4
+ # By downloading or using this software you accept the terms and the liability disclaimer in the License.
5
+
6
+ require 'oml4r'
7
+
8
+ module OmfCommon
9
+ class Comm
10
+ class AMQP
11
+
12
+ class MPPublished < OML4R::MPBase
13
+ name :amqp_published
14
+ param :time, :type => :double
15
+ param :topic, :type => :string
16
+ param :mid, :type => :string
17
+ end
18
+
19
+ class MPReceived < OML4R::MPBase
20
+ name :amqp_received
21
+ param :time, :type => :double
22
+ param :topic, :type => :string
23
+ param :mid, :type => :string
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+
@@ -3,7 +3,7 @@
3
3
  # You should find a copy of the License in LICENSE.TXT or at http://opensource.org/licenses/MIT.
4
4
  # By downloading or using this software you accept the terms and the liability disclaimer in the License.
5
5
 
6
-
6
+ require 'omf_common/comm/amqp/amqp_mp'
7
7
 
8
8
  module OmfCommon
9
9
  class Comm
@@ -11,7 +11,7 @@ module OmfCommon
11
11
  class Topic < OmfCommon::Comm::Topic
12
12
 
13
13
  def to_s
14
- "AMQP::Topic<#{id}>"
14
+ @address
15
15
  end
16
16
 
17
17
  def address
@@ -70,6 +70,7 @@ module OmfCommon
70
70
  queue.subscribe do |headers, payload|
71
71
  #puts "===(#{id}) Incoming message '#{headers.content_type}'"
72
72
  debug "Received message on #{@address}"
73
+ MPReceived.inject(Time.now.to_f, @address, payload.to_s[/mid\":\"(.{36})/, 1]) if OmfCommon::Measure.enabled?
73
74
  Message.parse(payload, headers.content_type) do |msg|
74
75
  #puts "---(#{id}) Parsed message '#{msg}'"
75
76
  on_incoming_message(msg)
@@ -93,6 +94,7 @@ module OmfCommon
93
94
  debug "(#{id}) Send message (#{content_type}) #{msg.inspect}"
94
95
  if @exchange
95
96
  @exchange.publish(content, content_type: content_type, message_id: msg.mid)
97
+ MPPublished.inject(Time.now.to_f, @address, msg.mid) if OmfCommon::Measure.enabled?
96
98
  else
97
99
  warn "Unavailable AMQP channel. Dropping message '#{msg}'"
98
100
  end
@@ -10,35 +10,35 @@ module OmfCommon
10
10
  class Local
11
11
  class Topic < OmfCommon::Comm::Topic
12
12
  @@marshall_messages = true
13
-
13
+
14
14
  # If set to 'true' marshall and immediately unmarshall before handing it on
15
15
  # messages
16
16
  def self.marshall_messages=(flag)
17
17
  @@marshall_messages = (flag == true)
18
18
  end
19
-
19
+
20
20
  # def self.address_for(name)
21
21
  # "#{name}@local"
22
22
  # end
23
-
23
+
24
24
  def to_s
25
- "Mock::Topic<#{id}>"
25
+ "Local::Topic<#{id}>"
26
26
  end
27
-
27
+
28
28
  def address
29
- "local:/#{id}"
29
+ @id
30
30
  end
31
-
31
+
32
32
  def on_subscribed(&block)
33
33
  return unless block
34
-
34
+
35
35
  OmfCommon.eventloop.after(0) do
36
36
  block.arity == 1 ? block.call(self) : block.call
37
37
  end
38
- end
39
-
38
+ end
39
+
40
40
  private
41
-
41
+
42
42
  def _send_message(msg, block = nil)
43
43
  super
44
44
  debug "(#{id}) Send message #{msg.inspect}"
@@ -47,7 +47,7 @@ module OmfCommon
47
47
  Message.parse(payload, content_type) do
48
48
  OmfCommon.eventloop.after(0) do
49
49
  on_incoming_message(msg)
50
- end
50
+ end
51
51
  end
52
52
  else
53
53
  OmfCommon.eventloop.after(0) do
@@ -55,9 +55,9 @@ module OmfCommon
55
55
  end
56
56
  end
57
57
  end
58
-
58
+
59
59
 
60
60
  end # class
61
- end # module
61
+ end # module
62
62
  end # module
63
63
  end # module
@@ -57,6 +57,10 @@ class Comm
57
57
  { proto: :xmpp, user: jid.node, domain: jid.domain }
58
58
  end
59
59
 
60
+ def string_to_address(a_string)
61
+ "xmpp://#{a_string}@#{jid.domain}"
62
+ end
63
+
60
64
  # Capture system :INT & :TERM signal
61
65
  def on_interrupted(&block)
62
66
  @cbks[:interpreted] << block
@@ -100,6 +104,7 @@ class Comm
100
104
  info "Reconnected"
101
105
  else
102
106
  info "Connected"
107
+ OmfCommon::DSL::Xmpp::MPConnection.inject(Time.now.to_f, jid, 'connected') if OmfCommon::Measure.enabled?
103
108
  @cbks[:connected].each { |cbk| cbk.call(self) }
104
109
  # It will be reconnection after this
105
110
  @lock.synchronize do
@@ -196,7 +201,7 @@ class Comm
196
201
  def subscribe(topic, opts = {}, &block)
197
202
  topic = topic.first if topic.is_a? Array
198
203
  OmfCommon::Comm::XMPP::Topic.create(topic, &block)
199
- MPSubscription.inject(Time.now.to_f, jid, 'join', topic) if OmfCommon::Measure.enabled?
204
+ OmfCommon::DSL::Xmpp::MPSubscription.inject(Time.now.to_f, jid, 'join', topic) if OmfCommon::Measure.enabled?
200
205
  end
201
206
 
202
207
  def _subscribe(topic, pubsub_host = default_host, &block)
@@ -212,7 +217,7 @@ class Comm
212
217
  pubsub.subscriptions(pubsub_host) do |m|
213
218
  m[:subscribed] && m[:subscribed].each do |s|
214
219
  pubsub.unsubscribe(s[:node], nil, s[:subid], pubsub_host, &callback_logging(__method__, s[:node], s[:subid]))
215
- MPSubscription.inject(Time.now.to_f, jid, 'leave', s[:node]) if OmfCommon::Measure.enabled?
220
+ OmfCommon::DSL::Xmpp::MPSubscription.inject(Time.now.to_f, jid, 'leave', s[:node]) if OmfCommon::Measure.enabled?
216
221
  end
217
222
  end
218
223
  end
@@ -240,7 +245,7 @@ class Comm
240
245
  end
241
246
 
242
247
  pubsub.publish(topic, message, pubsub_host, &callback_logging(__method__, topic, &new_block))
243
- MPPublished.inject(Time.now.to_f, jid, topic, message.to_s.gsub("\n",'')) if OmfCommon::Measure.enabled?
248
+ OmfCommon::DSL::Xmpp::MPPublished.inject(Time.now.to_f, jid, topic, message.to_s[/mid="(.{36})/, 1]) if OmfCommon::Measure.enabled?
244
249
  end
245
250
 
246
251
  # Event callback for pubsub topic event(item published)
@@ -250,15 +255,18 @@ class Comm
250
255
  passed = !event.delayed? && event.items? && !event.items.first.payload.nil? #&&
251
256
  #!published_messages.include?(OpenSSL::Digest::SHA1.new(event.items.first.payload))
252
257
 
253
- MPReceived.inject(Time.now.to_f, jid, event.node, event.items.first.payload.to_s.gsub("\n",'')) if OmfCommon::Measure.enabled? && passed
254
-
255
258
  if additional_guard
256
259
  passed && additional_guard.call(event)
257
260
  else
258
261
  passed
259
262
  end
260
263
  end
261
- pubsub_event(guard_block, &callback_logging(__method__, &block))
264
+
265
+ mblock = proc do |stanza|
266
+ OmfCommon::DSL::Xmpp::MPReceived.inject(Time.now.to_f, jid, stanza.node, stanza.to_s[/mid="(.{36})/, 1]) if OmfCommon::Measure.enabled?
267
+ block.call(stanza) if block
268
+ end
269
+ pubsub_event(guard_block, &callback_logging(__method__, &mblock))
262
270
  end
263
271
 
264
272
  private
@@ -20,7 +20,7 @@ module OmfCommon
20
20
  param :time, :type => :double
21
21
  param :jid, :type => :string
22
22
  param :topic, :type => :string
23
- param :xml_stanza, :type => :string
23
+ param :mid, :type => :string
24
24
  end
25
25
 
26
26
  class MPReceived < OML4R::MPBase
@@ -28,7 +28,7 @@ module OmfCommon
28
28
  param :time, :type => :double
29
29
  param :jid, :type => :string
30
30
  param :topic, :type => :string
31
- param :xml_stanza, :type => :string
31
+ param :mid, :type => :string
32
32
  end
33
33
 
34
34
  class MPSubscription < OML4R::MPBase