ruby-dbus 0.16.0 → 0.25.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 +4 -4
- data/NEWS.md +288 -3
- data/README.md +3 -5
- data/Rakefile +27 -12
- data/VERSION +1 -1
- data/doc/Reference.md +109 -10
- data/examples/doc/_extract_examples +7 -0
- data/examples/no-bus/pulseaudio.rb +50 -0
- data/examples/no-introspect/nm-test.rb +2 -0
- data/examples/no-introspect/tracker-test.rb +3 -1
- data/examples/rhythmbox/playpause.rb +2 -1
- data/examples/service/call_service.rb +2 -1
- data/examples/service/complex-property.rb +21 -0
- data/examples/service/service_newapi.rb +3 -3
- data/examples/simple/call_introspect.rb +1 -0
- data/examples/simple/get_id.rb +6 -3
- data/examples/simple/properties.rb +2 -0
- data/examples/utils/listnames.rb +10 -1
- data/examples/utils/notify.rb +1 -0
- data/lib/dbus/api_options.rb +9 -0
- data/lib/dbus/auth.rb +307 -217
- data/lib/dbus/bus.rb +155 -588
- data/lib/dbus/bus_name.rb +12 -8
- data/lib/dbus/connection.rb +363 -0
- data/lib/dbus/core_ext/class/attribute.rb +1 -1
- data/lib/dbus/data.rb +821 -0
- data/lib/dbus/emits_changed_signal.rb +83 -0
- data/lib/dbus/error.rb +4 -2
- data/lib/dbus/introspect.rb +133 -31
- data/lib/dbus/logger.rb +6 -3
- data/lib/dbus/main.rb +66 -0
- data/lib/dbus/marshall.rb +247 -296
- data/lib/dbus/matchrule.rb +16 -16
- data/lib/dbus/message.rb +66 -57
- data/lib/dbus/message_queue.rb +40 -25
- data/lib/dbus/node_tree.rb +105 -0
- data/lib/dbus/object.rb +442 -33
- data/lib/dbus/object_manager.rb +61 -0
- data/lib/dbus/object_path.rb +11 -6
- data/lib/dbus/object_server.rb +149 -0
- data/lib/dbus/org.freedesktop.DBus.xml +97 -0
- data/lib/dbus/platform.rb +26 -0
- data/lib/dbus/proxy_object.rb +30 -11
- data/lib/dbus/proxy_object_factory.rb +13 -7
- data/lib/dbus/proxy_object_interface.rb +63 -30
- data/lib/dbus/proxy_service.rb +107 -0
- data/lib/dbus/raw_message.rb +91 -0
- data/lib/dbus/type.rb +319 -86
- data/lib/dbus/xml.rb +40 -19
- data/lib/dbus.rb +28 -17
- data/ruby-dbus.gemspec +13 -6
- data/spec/async_spec.rb +2 -0
- data/spec/auth_spec.rb +225 -0
- data/spec/binding_spec.rb +2 -0
- data/spec/bus_and_xml_backend_spec.rb +5 -1
- data/spec/bus_connection_spec.rb +93 -0
- data/spec/bus_name_spec.rb +3 -1
- data/spec/bus_spec.rb +2 -0
- data/spec/byte_array_spec.rb +2 -0
- data/spec/client_robustness_spec.rb +4 -2
- data/spec/connection_spec.rb +37 -0
- data/spec/coverage_helper.rb +39 -0
- data/spec/data/marshall.yaml +1667 -0
- data/spec/data_spec.rb +673 -0
- data/spec/dbus_spec.rb +22 -0
- data/spec/emits_changed_signal_spec.rb +58 -0
- data/spec/err_msg_spec.rb +2 -0
- data/spec/introspect_xml_parser_spec.rb +2 -0
- data/spec/introspection_spec.rb +2 -0
- data/spec/main_loop_spec.rb +17 -1
- data/spec/message_spec.rb +21 -0
- data/spec/mock-service/cockpit-dbustests.rb +29 -0
- data/spec/mock-service/com.redhat.Cockpit.DBusTests.xml +180 -0
- data/spec/mock-service/org.ruby.service.service +4 -0
- data/spec/mock-service/org.rubygems.ruby_dbus.DBusTests.service +4 -0
- data/spec/{service_newapi.rb → mock-service/spaghetti-monster.rb} +122 -75
- data/spec/node_spec.rb +65 -0
- data/spec/object_manager_spec.rb +33 -0
- data/spec/object_path_spec.rb +3 -0
- data/spec/object_server_spec.rb +138 -0
- data/spec/object_spec.rb +194 -0
- data/spec/packet_marshaller_spec.rb +41 -0
- data/spec/packet_unmarshaller_spec.rb +248 -0
- data/spec/platform_spec.rb +14 -0
- data/spec/property_spec.rb +214 -12
- data/spec/proxy_object_interface_spec.rb +35 -0
- data/spec/proxy_object_spec.rb +2 -0
- data/spec/{bus_driver_spec.rb → proxy_service_spec.rb} +15 -8
- data/spec/raw_message_spec.rb +32 -0
- data/spec/server_robustness_spec.rb +20 -6
- data/spec/session_bus_spec.rb +36 -1
- data/spec/session_bus_spec_manual.rb +2 -0
- data/spec/signal_spec.rb +16 -3
- data/spec/spec_helper.rb +25 -33
- data/spec/thread_safety_spec.rb +55 -12
- data/spec/tools/dbus-launch-simple +9 -6
- data/spec/tools/dbus-limited-session.conf +32 -0
- data/spec/tools/test_env +26 -6
- data/spec/type_spec.rb +214 -6
- data/spec/value_spec.rb +16 -1
- data/spec/variant_spec.rb +4 -2
- data/spec/zzz_quit_spec.rb +16 -0
- metadata +92 -20
- data/examples/gdbus/gdbus +0 -257
- data/examples/gdbus/gdbus.glade +0 -98
- data/examples/gdbus/launch.sh +0 -4
- data/spec/server_spec.rb +0 -53
- data/spec/tools/test_server +0 -39
data/lib/dbus/xml.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# dbus/xml.rb - introspection parser, rexml/nokogiri abstraction
|
|
2
4
|
#
|
|
3
5
|
# This file is part of the ruby-dbus project
|
|
@@ -9,11 +11,17 @@
|
|
|
9
11
|
# License, version 2.1 as published by the Free Software Foundation.
|
|
10
12
|
# See the file "COPYING" for the exact licensing terms.
|
|
11
13
|
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
+
# Our gemspec says rexml is needed and nokogiri is optional
|
|
15
|
+
# but in fact either will do
|
|
16
|
+
|
|
14
17
|
begin
|
|
15
18
|
require "nokogiri"
|
|
16
19
|
rescue LoadError
|
|
20
|
+
begin
|
|
21
|
+
require "rexml/document"
|
|
22
|
+
rescue LoadError
|
|
23
|
+
raise LoadError, "cannot load nokogiri OR rexml/document"
|
|
24
|
+
end
|
|
17
25
|
end
|
|
18
26
|
|
|
19
27
|
module DBus
|
|
@@ -26,14 +34,23 @@ module DBus
|
|
|
26
34
|
attr_accessor :backend
|
|
27
35
|
end
|
|
28
36
|
# Creates a new parser for XML data in string _xml_.
|
|
37
|
+
# @param xml [String]
|
|
29
38
|
def initialize(xml)
|
|
30
39
|
@xml = xml
|
|
31
40
|
end
|
|
32
41
|
|
|
33
42
|
class AbstractXML
|
|
43
|
+
# @!method initialize(xml)
|
|
44
|
+
# @abstract
|
|
45
|
+
|
|
46
|
+
# @!method each(xpath)
|
|
47
|
+
# @abstract
|
|
48
|
+
# yields nodes which match xpath of type AbstractXML::Node
|
|
49
|
+
|
|
34
50
|
def self.have_nokogiri?
|
|
35
51
|
Object.const_defined?("Nokogiri")
|
|
36
52
|
end
|
|
53
|
+
|
|
37
54
|
class Node
|
|
38
55
|
def initialize(node)
|
|
39
56
|
@node = node
|
|
@@ -46,12 +63,6 @@ module DBus
|
|
|
46
63
|
# yields child nodes which match xpath of type AbstractXML::Node
|
|
47
64
|
def each(xpath); end
|
|
48
65
|
end
|
|
49
|
-
# required methods
|
|
50
|
-
# initialize parser with xml string
|
|
51
|
-
def initialize(xml); end
|
|
52
|
-
|
|
53
|
-
# yields nodes which match xpath of type AbstractXML::Node
|
|
54
|
-
def each(xpath); end
|
|
55
66
|
end
|
|
56
67
|
|
|
57
68
|
class NokogiriParser < AbstractXML
|
|
@@ -64,7 +75,9 @@ module DBus
|
|
|
64
75
|
@node.search(path).each { |node| block.call NokogiriNode.new(node) }
|
|
65
76
|
end
|
|
66
77
|
end
|
|
78
|
+
|
|
67
79
|
def initialize(xml)
|
|
80
|
+
super()
|
|
68
81
|
@doc = Nokogiri.XML(xml)
|
|
69
82
|
end
|
|
70
83
|
|
|
@@ -83,7 +96,9 @@ module DBus
|
|
|
83
96
|
@node.elements.each(path) { |node| block.call REXMLNode.new(node) }
|
|
84
97
|
end
|
|
85
98
|
end
|
|
99
|
+
|
|
86
100
|
def initialize(xml)
|
|
101
|
+
super()
|
|
87
102
|
@doc = REXML::Document.new(xml)
|
|
88
103
|
end
|
|
89
104
|
|
|
@@ -125,6 +140,10 @@ module DBus
|
|
|
125
140
|
parse_methsig(se, s)
|
|
126
141
|
i << s
|
|
127
142
|
end
|
|
143
|
+
e.each("property") do |pe|
|
|
144
|
+
p = Property.from_xml(pe)
|
|
145
|
+
i << p
|
|
146
|
+
end
|
|
128
147
|
end
|
|
129
148
|
d = Time.now - t
|
|
130
149
|
if d > 2
|
|
@@ -136,28 +155,30 @@ module DBus
|
|
|
136
155
|
######################################################################
|
|
137
156
|
private
|
|
138
157
|
|
|
139
|
-
# Parses a method signature XML element
|
|
140
|
-
# method/signal
|
|
141
|
-
|
|
142
|
-
|
|
158
|
+
# Parses a method signature XML element *elem* and initialises
|
|
159
|
+
# method/signal *methsig*.
|
|
160
|
+
# @param elem [AbstractXML::Node]
|
|
161
|
+
def parse_methsig(elem, methsig)
|
|
162
|
+
elem.each("arg") do |ae|
|
|
143
163
|
name = ae["name"]
|
|
144
164
|
dir = ae["direction"]
|
|
145
165
|
sig = ae["type"]
|
|
146
|
-
|
|
166
|
+
case methsig
|
|
167
|
+
when DBus::Signal
|
|
147
168
|
# Direction can only be "out", ignore it
|
|
148
|
-
|
|
149
|
-
|
|
169
|
+
methsig.add_fparam(name, sig)
|
|
170
|
+
when DBus::Method
|
|
150
171
|
case dir
|
|
151
172
|
# This is a method, so dir defaults to "in"
|
|
152
173
|
when "in", nil
|
|
153
|
-
|
|
174
|
+
methsig.add_fparam(name, sig)
|
|
154
175
|
when "out"
|
|
155
|
-
|
|
176
|
+
methsig.add_return(name, sig)
|
|
156
177
|
end
|
|
157
178
|
else
|
|
158
179
|
raise NotImplementedError, dir
|
|
159
180
|
end
|
|
160
181
|
end
|
|
161
182
|
end
|
|
162
|
-
end
|
|
163
|
-
end
|
|
183
|
+
end
|
|
184
|
+
end
|
data/lib/dbus.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# dbus.rb - Module containing the low-level D-Bus implementation
|
|
2
4
|
#
|
|
3
5
|
# This file is part of the ruby-dbus project
|
|
@@ -8,46 +10,55 @@
|
|
|
8
10
|
# License, version 2.1 as published by the Free Software Foundation.
|
|
9
11
|
# See the file "COPYING" for the exact licensing terms.
|
|
10
12
|
|
|
13
|
+
module DBus
|
|
14
|
+
# Protocol character signifying big endianness.
|
|
15
|
+
BIG_END = "B"
|
|
16
|
+
# Protocol character signifying little endianness.
|
|
17
|
+
LIL_END = "l"
|
|
18
|
+
|
|
19
|
+
# Protocol character signifying the host's endianness.
|
|
20
|
+
# "S": unpack as uint16, native endian
|
|
21
|
+
HOST_END = { 1 => BIG_END, 256 => LIL_END }.fetch("\x00\x01".unpack1("S"))
|
|
22
|
+
end
|
|
23
|
+
# ^ That's because dbus/message needs HOST_END early
|
|
24
|
+
|
|
11
25
|
require_relative "dbus/api_options"
|
|
12
26
|
require_relative "dbus/auth"
|
|
13
27
|
require_relative "dbus/bus"
|
|
14
28
|
require_relative "dbus/bus_name"
|
|
29
|
+
require_relative "dbus/connection"
|
|
30
|
+
require_relative "dbus/data"
|
|
31
|
+
require_relative "dbus/emits_changed_signal"
|
|
15
32
|
require_relative "dbus/error"
|
|
16
33
|
require_relative "dbus/introspect"
|
|
17
34
|
require_relative "dbus/logger"
|
|
35
|
+
require_relative "dbus/main"
|
|
18
36
|
require_relative "dbus/marshall"
|
|
19
37
|
require_relative "dbus/matchrule"
|
|
20
38
|
require_relative "dbus/message"
|
|
21
39
|
require_relative "dbus/message_queue"
|
|
40
|
+
require_relative "dbus/node_tree"
|
|
22
41
|
require_relative "dbus/object"
|
|
42
|
+
require_relative "dbus/object_manager"
|
|
23
43
|
require_relative "dbus/object_path"
|
|
44
|
+
require_relative "dbus/object_server"
|
|
45
|
+
require_relative "dbus/platform"
|
|
24
46
|
require_relative "dbus/proxy_object"
|
|
25
47
|
require_relative "dbus/proxy_object_factory"
|
|
26
48
|
require_relative "dbus/proxy_object_interface"
|
|
49
|
+
require_relative "dbus/proxy_service"
|
|
50
|
+
require_relative "dbus/raw_message"
|
|
27
51
|
require_relative "dbus/type"
|
|
28
52
|
require_relative "dbus/xml"
|
|
29
53
|
|
|
30
54
|
require "socket"
|
|
31
|
-
require "thread"
|
|
32
|
-
|
|
33
55
|
# = D-Bus main module
|
|
34
56
|
#
|
|
35
57
|
# Module containing all the D-Bus modules and classes.
|
|
36
58
|
module DBus
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# Byte signifying big endianness.
|
|
41
|
-
BIG_END = "B".freeze
|
|
42
|
-
# Byte signifying little endianness.
|
|
43
|
-
LIL_END = "l".freeze
|
|
44
|
-
|
|
45
|
-
# Byte signifying the host's endianness.
|
|
46
|
-
HOST_END = if [0x01020304].pack("L").unpack("V")[0] == 0x01020304
|
|
47
|
-
LIL_END
|
|
48
|
-
else
|
|
49
|
-
BIG_END
|
|
50
|
-
end
|
|
59
|
+
# Comparing symbols is faster than strings
|
|
60
|
+
# @return [:little,:big]
|
|
61
|
+
HOST_ENDIANNESS = RawMessage.endianness(HOST_END)
|
|
51
62
|
|
|
52
63
|
# General exceptions.
|
|
53
64
|
|
|
@@ -69,4 +80,4 @@ module DBus
|
|
|
69
80
|
# Exception raised when invalid introspection data is parsed/used.
|
|
70
81
|
class InvalidIntrospectionData < Exception
|
|
71
82
|
end
|
|
72
|
-
end
|
|
83
|
+
end
|
data/ruby-dbus.gemspec
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# -*- ruby -*-
|
|
2
|
-
require "rubygems"
|
|
3
4
|
|
|
4
5
|
GEMSPEC = Gem::Specification.new do |s|
|
|
5
6
|
s.name = "ruby-dbus"
|
|
@@ -7,7 +8,7 @@ GEMSPEC = Gem::Specification.new do |s|
|
|
|
7
8
|
s.summary = "Ruby module for interaction with D-Bus"
|
|
8
9
|
s.description = "Pure Ruby module for interaction with D-Bus IPC system"
|
|
9
10
|
s.version = File.read("VERSION").strip
|
|
10
|
-
s.license = "LGPL-2.1"
|
|
11
|
+
s.license = "LGPL-2.1-or-later"
|
|
11
12
|
s.author = "Ruby DBus Team"
|
|
12
13
|
s.email = "martin.github@vidner.net"
|
|
13
14
|
s.homepage = "https://github.com/mvidner/ruby-dbus"
|
|
@@ -18,15 +19,21 @@ GEMSPEC = Gem::Specification.new do |s|
|
|
|
18
19
|
]
|
|
19
20
|
s.require_path = "lib"
|
|
20
21
|
|
|
21
|
-
s.required_ruby_version = ">= 2.
|
|
22
|
+
s.required_ruby_version = ">= 2.4.0"
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
s.add_runtime_dependency "logger"
|
|
25
|
+
# Either of rexml and nokogiri is required
|
|
26
|
+
# but AFAIK gemspec cannot express that.
|
|
27
|
+
# Nokogiri is recommended as rexml is dead slow.
|
|
28
|
+
s.add_runtime_dependency "rexml"
|
|
24
29
|
# s.add_runtime_dependency "nokogiri"
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
# workaround: rubocop-1.0 needs base64 which is no longer in stdlib in newer rubies
|
|
32
|
+
s.add_development_dependency "base64"
|
|
33
|
+
s.add_development_dependency "ostruct"
|
|
27
34
|
s.add_development_dependency "packaging_rake_tasks"
|
|
28
35
|
s.add_development_dependency "rake"
|
|
29
36
|
s.add_development_dependency "rspec", "~> 3"
|
|
30
|
-
s.add_development_dependency "rubocop", "= 0.41.2"
|
|
31
37
|
s.add_development_dependency "simplecov"
|
|
38
|
+
s.add_development_dependency "simplecov-lcov"
|
|
32
39
|
end
|
data/spec/async_spec.rb
CHANGED
data/spec/auth_spec.rb
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env rspec
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "spec_helper"
|
|
5
|
+
require "dbus"
|
|
6
|
+
|
|
7
|
+
describe DBus::Authentication::Client do
|
|
8
|
+
let(:socket) { instance_double("Socket") }
|
|
9
|
+
let(:subject) { described_class.new(socket) }
|
|
10
|
+
|
|
11
|
+
before(:each) do
|
|
12
|
+
allow(Process).to receive(:uid).and_return(999)
|
|
13
|
+
allow(subject).to receive(:send_nul_byte)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe "#next_state" do
|
|
17
|
+
it "raises when I forget to handle a state" do
|
|
18
|
+
subject.instance_variable_set(:@state, :Denmark)
|
|
19
|
+
expect { subject.__send__(:next_state, []) }.to raise_error(RuntimeError, /unhandled state :Denmark/)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def expect_protocol(pairs)
|
|
24
|
+
pairs.each do |we_say, server_says|
|
|
25
|
+
expect(subject).to receive(:write_line).with(we_say)
|
|
26
|
+
next if server_says.nil?
|
|
27
|
+
|
|
28
|
+
expect(subject).to receive(:read_line).and_return(server_says)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
context "with ANONYMOUS" do
|
|
33
|
+
let(:subject) { described_class.new(socket, [DBus::Authentication::Anonymous]) }
|
|
34
|
+
|
|
35
|
+
it "authentication passes" do
|
|
36
|
+
expect_protocol [
|
|
37
|
+
["AUTH ANONYMOUS 527562792044427573\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
|
|
38
|
+
["NEGOTIATE_UNIX_FD\r\n", "ERROR not for anonymous\r\n"],
|
|
39
|
+
["BEGIN\r\n"]
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
expect { subject.authenticate }.to_not raise_error
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
context "with EXTERNAL" do
|
|
47
|
+
let(:subject) { described_class.new(socket, [DBus::Authentication::External]) }
|
|
48
|
+
|
|
49
|
+
it "authentication passes, and address_uuid is set" do
|
|
50
|
+
expect_protocol [
|
|
51
|
+
["AUTH EXTERNAL 393939\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
|
|
52
|
+
["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
|
|
53
|
+
["BEGIN\r\n"]
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
expect { subject.authenticate }.to_not raise_error
|
|
57
|
+
expect(subject.address_uuid).to eq "ffffffffffffffffffffffffffffffff"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
context "when the server says superfluous things before an OK" do
|
|
61
|
+
it "authentication passes" do
|
|
62
|
+
expect_protocol [
|
|
63
|
+
["AUTH EXTERNAL 393939\r\n", "WOULD_YOU_LIKE_SOME_TEA\r\n"],
|
|
64
|
+
["ERROR\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
|
|
65
|
+
["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
|
|
66
|
+
["BEGIN\r\n"]
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
expect { subject.authenticate }.to_not raise_error
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context "when the server messes up NEGOTIATE_UNIX_FD" do
|
|
74
|
+
it "authentication fails orderly" do
|
|
75
|
+
expect_protocol [
|
|
76
|
+
["AUTH EXTERNAL 393939\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
|
|
77
|
+
["NEGOTIATE_UNIX_FD\r\n", "I_DONT_NEGOTIATE_WITH_TENORISTS\r\n"]
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
allow(socket).to receive(:close) # want to get rid of this
|
|
81
|
+
# TODO: quote the server error message?
|
|
82
|
+
expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /Unknown server reply/)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context "when the server replies with ERROR" do
|
|
87
|
+
it "authentication fails orderly" do
|
|
88
|
+
expect_protocol [
|
|
89
|
+
["AUTH EXTERNAL 393939\r\n", "ERROR something failed\r\n"],
|
|
90
|
+
["CANCEL\r\n", "REJECTED DBUS_COOKIE_SHA1\r\n"]
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
allow(socket).to receive(:close) # want to get rid of this
|
|
94
|
+
# TODO: quote the server error message?
|
|
95
|
+
expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /exhausted/)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
context "with EXTERNAL without uid" do
|
|
101
|
+
let(:subject) do
|
|
102
|
+
described_class.new(socket, [DBus::Authentication::External, DBus::Authentication::ExternalWithoutUid])
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "authentication passes" do
|
|
106
|
+
expect_protocol [
|
|
107
|
+
["AUTH EXTERNAL 393939\r\n", "REJECTED EXTERNAL\r\n"],
|
|
108
|
+
# this succeeds when we connect to a privileged container,
|
|
109
|
+
# where outside-non-root becomes inside-root
|
|
110
|
+
["AUTH EXTERNAL\r\n", "DATA\r\n"],
|
|
111
|
+
["DATA\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
|
|
112
|
+
["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
|
|
113
|
+
["BEGIN\r\n"]
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
expect { subject.authenticate }.to_not raise_error
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
context "with a rejected mechanism and then EXTERNAL" do
|
|
121
|
+
let(:rejected_mechanism) do
|
|
122
|
+
double("Mechanism", name: "WIMP", call: [:MechContinue, "I expect to be rejected"])
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
let(:subject) { described_class.new(socket, [rejected_mechanism, DBus::Authentication::External]) }
|
|
126
|
+
|
|
127
|
+
it "authentication eventually passes" do
|
|
128
|
+
expect_protocol [
|
|
129
|
+
[/^AUTH WIMP .*\r\n/, "REJECTED EXTERNAL\r\n"],
|
|
130
|
+
["AUTH EXTERNAL 393939\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
|
|
131
|
+
["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
|
|
132
|
+
["BEGIN\r\n"]
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
expect { subject.authenticate }.to_not raise_error
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
context "with a DATA-using mechanism" do
|
|
140
|
+
let(:mechanism) do
|
|
141
|
+
double("Mechanism", name: "CHALLENGE_ME", call: [:MechContinue, "1"])
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# try it twice to test calling #use_next_mechanism
|
|
145
|
+
let(:subject) { described_class.new(socket, [mechanism, mechanism]) }
|
|
146
|
+
|
|
147
|
+
it "authentication fails orderly when the server says ERROR" do
|
|
148
|
+
expect_protocol [
|
|
149
|
+
["AUTH CHALLENGE_ME 31\r\n", "ERROR something failed\r\n"],
|
|
150
|
+
["CANCEL\r\n", "REJECTED DBUS_COOKIE_SHA1\r\n"],
|
|
151
|
+
["AUTH CHALLENGE_ME 31\r\n", "ERROR something failed\r\n"],
|
|
152
|
+
["CANCEL\r\n", "REJECTED DBUS_COOKIE_SHA1\r\n"]
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
allow(socket).to receive(:close) # want to get rid of this
|
|
156
|
+
# TODO: quote the server error message?
|
|
157
|
+
expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /exhausted/)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "authentication fails orderly when the server says ERROR and then changes its mind" do
|
|
161
|
+
expect_protocol [
|
|
162
|
+
["AUTH CHALLENGE_ME 31\r\n", "ERROR something failed\r\n"],
|
|
163
|
+
["CANCEL\r\n", "I_CHANGED_MY_MIND please come back\r\n"]
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
allow(socket).to receive(:close) # want to get rid of this
|
|
167
|
+
# TODO: quote the server error message?
|
|
168
|
+
expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /Unknown.*MIND.*REJECTED/)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "authentication passes when the server says superfluous things before DATA" do
|
|
172
|
+
expect_protocol [
|
|
173
|
+
["AUTH CHALLENGE_ME 31\r\n", "WOULD_YOU_LIKE_SOME_TEA\r\n"],
|
|
174
|
+
["ERROR\r\n", "DATA\r\n"],
|
|
175
|
+
["DATA 31\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
|
|
176
|
+
["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
|
|
177
|
+
["BEGIN\r\n"]
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
expect { subject.authenticate }.to_not raise_error
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it "authentication passes when the server decides not to need the DATA" do
|
|
184
|
+
expect_protocol [
|
|
185
|
+
["AUTH CHALLENGE_ME 31\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
|
|
186
|
+
["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
|
|
187
|
+
["BEGIN\r\n"]
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
expect { subject.authenticate }.to_not raise_error
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
context "with a mechanism returning :MechError" do
|
|
195
|
+
let(:fallible_mechanism) do
|
|
196
|
+
double(name: "FALLIBLE", call: [:MechError, "not my best day"])
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
let(:subject) { described_class.new(socket, [fallible_mechanism]) }
|
|
200
|
+
|
|
201
|
+
it "authentication fails orderly" do
|
|
202
|
+
expect_protocol [
|
|
203
|
+
["ERROR not my best day\r\n", "REJECTED DBUS_COOKIE_SHA1\r\n"]
|
|
204
|
+
]
|
|
205
|
+
|
|
206
|
+
allow(socket).to receive(:close) # want to get rid of thise
|
|
207
|
+
expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /exhausted/)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
context "with a badly implemented mechanism" do
|
|
212
|
+
let(:buggy_mechanism) do
|
|
213
|
+
double(name: "buggy", call: [:smurf, nil])
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
let(:subject) { described_class.new(socket, [buggy_mechanism]) }
|
|
217
|
+
|
|
218
|
+
it "authentication fails before protoxol is exchanged" do
|
|
219
|
+
expect(subject).to_not receive(:write_line)
|
|
220
|
+
expect(subject).to_not receive(:read_line)
|
|
221
|
+
|
|
222
|
+
expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /smurf/)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
data/spec/binding_spec.rb
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env rspec
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
2
4
|
# Test the bus class
|
|
3
5
|
require_relative "spec_helper"
|
|
4
6
|
|
|
5
7
|
require "rubygems"
|
|
6
|
-
|
|
8
|
+
# If we have nokogiri, rexml is normally omitted
|
|
9
|
+
# but here we include it for test coverage
|
|
10
|
+
require "rexml"
|
|
7
11
|
require "dbus"
|
|
8
12
|
|
|
9
13
|
describe "BusAndXmlBackendTest" do
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env rspec
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "spec_helper"
|
|
5
|
+
require "dbus"
|
|
6
|
+
|
|
7
|
+
describe DBus::BusConnection do
|
|
8
|
+
let(:bus) { DBus::ASessionBus.new }
|
|
9
|
+
|
|
10
|
+
# deprecated method
|
|
11
|
+
describe "#request_service", tag_bus: true, tag_deprecated: true do
|
|
12
|
+
context "when the name request succeeds" do
|
|
13
|
+
# Formerly it returned Service, now ObjectServer takes its role.
|
|
14
|
+
# Replacement: server = bus.object_server; bus.request_name(name)
|
|
15
|
+
it "returns something which can export objects" do
|
|
16
|
+
name = "org.rubygems.ruby_dbus.RequestServiceTest"
|
|
17
|
+
server = bus.request_service(name)
|
|
18
|
+
expect(server).to respond_to(:export)
|
|
19
|
+
bus.proxy.ReleaseName(name)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context "when the name is taken already", tag_service: true do
|
|
24
|
+
it "raises NameRequestError... too late" do
|
|
25
|
+
name = "org.ruby.service"
|
|
26
|
+
expect do
|
|
27
|
+
bus.request_service(name)
|
|
28
|
+
_unrelated_call = bus.proxy.GetId.first
|
|
29
|
+
end.to raise_error(DBus::Connection::NameRequestError)
|
|
30
|
+
# The call fails but it means we did not get the name RIGHT AWAY
|
|
31
|
+
# but we are still queued to get it as soon as the current owner
|
|
32
|
+
# gives it up.
|
|
33
|
+
# So even now we have to the bus to remove us from the queue
|
|
34
|
+
bus.proxy.ReleaseName(name)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# This only works with our special bus setup
|
|
39
|
+
context "when we're not allowed to own the name", tag_limited_bus: true do
|
|
40
|
+
it "raises an error... too late" do
|
|
41
|
+
name = "org.rubygems.ruby_dbus.NobodyCanOwnThisName"
|
|
42
|
+
expect do
|
|
43
|
+
bus.request_service(name)
|
|
44
|
+
_unrelated_call = bus.proxy.GetId.first
|
|
45
|
+
end.to raise_error(DBus::Error, /not allowed to own the service/)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe "#request_name", tag_bus: true do
|
|
51
|
+
context "when the name request succeeds" do
|
|
52
|
+
it "returns a success code" do
|
|
53
|
+
name = "org.rubygems.ruby_dbus.RequestNameTest"
|
|
54
|
+
expect(bus.request_name(name)).to eq DBus::Connection::REQUEST_NAME_REPLY_PRIMARY_OWNER
|
|
55
|
+
# second time, considered also a success
|
|
56
|
+
expect(bus.request_name(name)).to eq DBus::Connection::REQUEST_NAME_REPLY_ALREADY_OWNER
|
|
57
|
+
bus.release_name(name)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
context "when the name is taken already", tag_service: true do
|
|
62
|
+
it "raises NameRequestError" do
|
|
63
|
+
name = "org.ruby.service"
|
|
64
|
+
expect do
|
|
65
|
+
bus.request_name(name)
|
|
66
|
+
end.to raise_error(DBus::Connection::NameRequestError)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
context "when the name is taken already but we request queuing", tag_service: true do
|
|
71
|
+
it "raises NameRequestError but we are queued" do
|
|
72
|
+
name = "org.ruby.service"
|
|
73
|
+
owning = nil
|
|
74
|
+
# TODO: we do not expect the handlers to run
|
|
75
|
+
bus.on_name_acquired { owning = true }
|
|
76
|
+
bus.on_name_lost { owning = false }
|
|
77
|
+
expect do
|
|
78
|
+
bus.request_name(name, queue: true)
|
|
79
|
+
end.to raise_error(DBus::Connection::NameRequestError)
|
|
80
|
+
expect(bus.release_name(name)).to eq DBus::BusConnection::RELEASE_NAME_REPLY_RELEASED
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
context "when we're not allowed to own the name", tag_limited_bus: true do
|
|
85
|
+
it "raises an error" do
|
|
86
|
+
name = "org.rubygems.ruby_dbus.NobodyCanOwnThisName"
|
|
87
|
+
expect do
|
|
88
|
+
bus.request_name(name)
|
|
89
|
+
end.to raise_error(DBus::Error, /not allowed to own the service/)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
data/spec/bus_name_spec.rb
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env rspec
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
2
4
|
require_relative "spec_helper"
|
|
3
5
|
require "dbus"
|
|
4
6
|
|
|
@@ -17,7 +19,7 @@ describe DBus::BusName do
|
|
|
17
19
|
expect(described_class.valid?("Empty.Last.Component.")).to be_falsey
|
|
18
20
|
expect(described_class.valid?("Invalid.Ch@r@cter")).to be_falsey
|
|
19
21
|
expect(described_class.valid?("/Invalid-Character")).to be_falsey
|
|
20
|
-
long_name = "a
|
|
22
|
+
long_name = "a.#{"long." * 100}name"
|
|
21
23
|
expect(described_class.valid?(long_name)).to be_falsey
|
|
22
24
|
expect(described_class.valid?("org.7_zip.Archiver")).to be_falsey
|
|
23
25
|
end
|
data/spec/bus_spec.rb
CHANGED
data/spec/byte_array_spec.rb
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env rspec
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
2
4
|
# Test that a client survives various error cases
|
|
3
5
|
require_relative "spec_helper"
|
|
4
6
|
require "dbus"
|
|
@@ -12,14 +14,14 @@ describe "ClientRobustnessTest" do
|
|
|
12
14
|
context "when the bus name is invalid" do
|
|
13
15
|
it "tells the user the bus name is invalid" do
|
|
14
16
|
# user mistake, should be "org.ruby.service"
|
|
15
|
-
expect { @bus.service(".org.ruby.service") }.to raise_error(DBus::Error)
|
|
17
|
+
expect { @bus.service(".org.ruby.service") }.to raise_error(DBus::Error, /Invalid bus name/)
|
|
16
18
|
end
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
context "when the object path is invalid" do
|
|
20
22
|
it "tells the user the path is invalid" do
|
|
21
23
|
# user mistake, should be "/org/ruby/MyInstance"
|
|
22
|
-
expect { @svc.object("org.ruby.MyInstance") }.to raise_error(DBus::Error)
|
|
24
|
+
expect { @svc.object("org.ruby.MyInstance") }.to raise_error(DBus::Error, /Invalid object path/)
|
|
23
25
|
end
|
|
24
26
|
end
|
|
25
27
|
end
|