rene-adhearsion 0.8.6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +73 -0
- data/EVENTS +11 -0
- data/LICENSE +456 -0
- data/Rakefile +130 -0
- data/adhearsion.gemspec +173 -0
- data/app_generators/ahn/USAGE +5 -0
- data/app_generators/ahn/ahn_generator.rb +96 -0
- data/app_generators/ahn/templates/.ahnrc +34 -0
- data/app_generators/ahn/templates/README +8 -0
- data/app_generators/ahn/templates/Rakefile +25 -0
- data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +15 -0
- data/app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE +7 -0
- data/app_generators/ahn/templates/components/disabled/restful_rpc/README.markdown +11 -0
- data/app_generators/ahn/templates/components/disabled/restful_rpc/example-client.rb +48 -0
- data/app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.rb +91 -0
- data/app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.yml +34 -0
- data/app_generators/ahn/templates/components/disabled/restful_rpc/spec/restful_rpc_spec.rb +263 -0
- data/app_generators/ahn/templates/components/disabled/sandbox/sandbox.rb +104 -0
- data/app_generators/ahn/templates/components/disabled/sandbox/sandbox.yml +2 -0
- data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +47 -0
- data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +34 -0
- data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.yml +12 -0
- data/app_generators/ahn/templates/components/disabled/xmpp_gateway/README.markdown +3 -0
- data/app_generators/ahn/templates/components/disabled/xmpp_gateway/xmpp_gateway.rb +11 -0
- data/app_generators/ahn/templates/components/disabled/xmpp_gateway/xmpp_gateway.yml +0 -0
- data/app_generators/ahn/templates/components/simon_game/simon_game.rb +56 -0
- data/app_generators/ahn/templates/config/startup.rb +83 -0
- data/app_generators/ahn/templates/dialplan.rb +3 -0
- data/app_generators/ahn/templates/events.rb +32 -0
- data/bin/ahn +28 -0
- data/bin/ahnctl +68 -0
- data/bin/jahn +42 -0
- data/examples/asterisk_manager_interface/standalone.rb +51 -0
- data/lib/adhearsion.rb +45 -0
- data/lib/adhearsion/cli.rb +228 -0
- data/lib/adhearsion/component_manager.rb +272 -0
- data/lib/adhearsion/component_manager/component_tester.rb +55 -0
- data/lib/adhearsion/component_manager/spec_framework.rb +24 -0
- data/lib/adhearsion/events_support.rb +84 -0
- data/lib/adhearsion/foundation/all.rb +15 -0
- data/lib/adhearsion/foundation/blank_slate.rb +3 -0
- data/lib/adhearsion/foundation/custom_daemonizer.rb +45 -0
- data/lib/adhearsion/foundation/event_socket.rb +204 -0
- data/lib/adhearsion/foundation/future_resource.rb +36 -0
- data/lib/adhearsion/foundation/metaprogramming.rb +17 -0
- data/lib/adhearsion/foundation/numeric.rb +13 -0
- data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
- data/lib/adhearsion/foundation/relationship_properties.rb +42 -0
- data/lib/adhearsion/foundation/string.rb +26 -0
- data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
- data/lib/adhearsion/foundation/thread_safety.rb +7 -0
- data/lib/adhearsion/host_definitions.rb +67 -0
- data/lib/adhearsion/initializer.rb +395 -0
- data/lib/adhearsion/initializer/asterisk.rb +87 -0
- data/lib/adhearsion/initializer/configuration.rb +321 -0
- data/lib/adhearsion/initializer/database.rb +60 -0
- data/lib/adhearsion/initializer/drb.rb +31 -0
- data/lib/adhearsion/initializer/freeswitch.rb +22 -0
- data/lib/adhearsion/initializer/ldap.rb +57 -0
- data/lib/adhearsion/initializer/rails.rb +41 -0
- data/lib/adhearsion/initializer/xmpp.rb +42 -0
- data/lib/adhearsion/logging.rb +92 -0
- data/lib/adhearsion/tasks.rb +16 -0
- data/lib/adhearsion/tasks/database.rb +5 -0
- data/lib/adhearsion/tasks/deprecations.rb +59 -0
- data/lib/adhearsion/tasks/generating.rb +20 -0
- data/lib/adhearsion/tasks/lint.rb +4 -0
- data/lib/adhearsion/tasks/testing.rb +37 -0
- data/lib/adhearsion/version.rb +33 -0
- data/lib/adhearsion/voip/asterisk.rb +4 -0
- data/lib/adhearsion/voip/asterisk/agi_server.rb +115 -0
- data/lib/adhearsion/voip/asterisk/commands.rb +1510 -0
- data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +140 -0
- data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +101 -0
- data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +250 -0
- data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +240 -0
- data/lib/adhearsion/voip/asterisk/config_manager.rb +71 -0
- data/lib/adhearsion/voip/asterisk/manager_interface.rb +705 -0
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +1680 -0
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +340 -0
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
- data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +80 -0
- data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
- data/lib/adhearsion/voip/call.rb +497 -0
- data/lib/adhearsion/voip/call_routing.rb +64 -0
- data/lib/adhearsion/voip/commands.rb +9 -0
- data/lib/adhearsion/voip/constants.rb +39 -0
- data/lib/adhearsion/voip/conveniences.rb +18 -0
- data/lib/adhearsion/voip/dial_plan.rb +246 -0
- data/lib/adhearsion/voip/dsl/dialing_dsl.rb +151 -0
- data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +37 -0
- data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +27 -0
- data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +124 -0
- data/lib/adhearsion/voip/dsl/dialplan/parser.rb +69 -0
- data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +16 -0
- data/lib/adhearsion/voip/dsl/numerical_string.rb +115 -0
- data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +48 -0
- data/lib/adhearsion/voip/freeswitch/event_handler.rb +58 -0
- data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +129 -0
- data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +38 -0
- data/lib/adhearsion/voip/freeswitch/oes_server.rb +195 -0
- data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +80 -0
- data/lib/adhearsion/voip/menu_state_machine/matchers.rb +123 -0
- data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +58 -0
- data/lib/adhearsion/voip/menu_state_machine/menu_class.rb +149 -0
- data/lib/adhearsion/xmpp/connection.rb +61 -0
- data/lib/theatre.rb +151 -0
- data/lib/theatre/README.markdown +64 -0
- data/lib/theatre/callback_definition_loader.rb +84 -0
- data/lib/theatre/guid.rb +23 -0
- data/lib/theatre/invocation.rb +121 -0
- data/lib/theatre/namespace_manager.rb +153 -0
- data/lib/theatre/version.rb +2 -0
- metadata +241 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
# This file is for the "rake" tool which automates project-related tasks. If you need to automate things, you can create
|
2
|
+
# a new Rake task here. See http://rake.rubyforge.org for more info.
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'adhearsion'
|
7
|
+
require 'adhearsion/tasks'
|
8
|
+
rescue LoadError
|
9
|
+
STDERR.puts "\nCannot load Adhearsion! Not all Rake tasks will be loaded!\n\n"
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Writes a .gitignore file that ignores certain SCM annoyances such as log files"
|
13
|
+
task :gitignore do
|
14
|
+
ignore_file = "#{Dir.pwd}/.gitignore"
|
15
|
+
if File.exists? ignore_file
|
16
|
+
STDERR.puts "File #{ignore_file} already exists!"
|
17
|
+
else
|
18
|
+
File.open ignore_file, 'w' do |file|
|
19
|
+
# Add other files to the Array below
|
20
|
+
%w[ log ].each do |pattern|
|
21
|
+
file.puts pattern
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
methods_for :rpc do
|
2
|
+
|
3
|
+
# Simply create proxy methods for the high-level AMI methods
|
4
|
+
|
5
|
+
[:send_action, :introduce, :originate, :call_into_context, :call_and_exec, :ping].each do |method_name|
|
6
|
+
define_method(method_name) do |*args|
|
7
|
+
if VoIP::Asterisk.manager_interface
|
8
|
+
VoIP::Asterisk.manager_interface.send(method_name, *args)
|
9
|
+
else
|
10
|
+
ahn_log.ami_remote.error "AMI has not been enabled in startup.rb!"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Adhearsion RESTful RPC Component
|
2
|
+
================================
|
3
|
+
|
4
|
+
This is a component for people want to integrate their telephony systems with non-Ruby systems. When enabled, this component
|
5
|
+
will start up a HTTP server within the Adhearsion process and accept POST requests to invoke Ruby methods shared in the
|
6
|
+
`methods_for(:rpc)` context.
|
7
|
+
|
8
|
+
Protocol Notes
|
9
|
+
--------------
|
10
|
+
|
11
|
+
When POSTing your data to.
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rest_client'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
# You must have the "rest-client" and "json" gems installed for this file to work.
|
6
|
+
|
7
|
+
class RESTfulAdhearsion
|
8
|
+
|
9
|
+
DEFAULT_OPTIONS = {
|
10
|
+
# Note: :user and :password are non-existent by default
|
11
|
+
:host => "localhost",
|
12
|
+
:port => "5000",
|
13
|
+
:path_nesting => "/"
|
14
|
+
}
|
15
|
+
|
16
|
+
def initialize(options={})
|
17
|
+
@options = DEFAULT_OPTIONS.merge options
|
18
|
+
|
19
|
+
@path_nesting = @options.delete :path_nesting
|
20
|
+
@host = @options.delete :host
|
21
|
+
@port = @options.delete :port
|
22
|
+
|
23
|
+
@url_beginning = "http://#{@host}:#{@port}#{@path_nesting}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(method_name, *args)
|
27
|
+
JSON.parse RestClient::Resource.new(@url_beginning + method_name.to_s, @options).post(args.to_json)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
Adhearsion = RESTfulAdhearsion.new :host => "localhost", :port => 5000, :user => "jicksta", :password => "roflcopterz"
|
33
|
+
|
34
|
+
# ### Sample component code. Try doing "ahn create component testing123" and pasting this code in.
|
35
|
+
#
|
36
|
+
# methods_for :rpc do
|
37
|
+
# def i_like_hashes(options={})
|
38
|
+
# options.has_key?(:foo)
|
39
|
+
# end
|
40
|
+
# def i_like_arrays(*args)
|
41
|
+
# args.reverse
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
|
45
|
+
# Note: everything returned will be wrapped in an Array
|
46
|
+
|
47
|
+
p Adhearsion.i_like_hashes(:foo => "bar")
|
48
|
+
p Adhearsion.i_like_arrays(1,2,3,4,5)
|
@@ -0,0 +1,91 @@
|
|
1
|
+
begin
|
2
|
+
require 'rack'
|
3
|
+
require 'json'
|
4
|
+
rescue LoadError
|
5
|
+
abort "ERROR: restful_rpc requires the 'rack' and 'json' gems"
|
6
|
+
end
|
7
|
+
|
8
|
+
# Don't you love regular expressions? Matches only 0-255 octets. Recognizes "*" as an octet wildcard.
|
9
|
+
VALID_IP_ADDRESS = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|\*)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|\*)$/
|
10
|
+
|
11
|
+
def ip_allowed?(ip)
|
12
|
+
raise ArgumentError, "#{ip.inspect} is not a valid IP address!" unless ip.kind_of?(String) && ip =~ VALID_IP_ADDRESS
|
13
|
+
|
14
|
+
octets = ip.split "."
|
15
|
+
|
16
|
+
case COMPONENTS.restful_rpc["access"]
|
17
|
+
when "everyone"
|
18
|
+
true
|
19
|
+
when "whitelist"
|
20
|
+
whitelist = COMPONENTS.restful_rpc["whitelist"]
|
21
|
+
!! whitelist.find do |pattern|
|
22
|
+
pattern_octets = pattern.split "."
|
23
|
+
# Traverse both arrays in parallel
|
24
|
+
octets.zip(pattern_octets).map do |octet, octet_pattern|
|
25
|
+
octet_pattern == "*" ? true : (octet == octet_pattern)
|
26
|
+
end == [true, true, true, true]
|
27
|
+
end
|
28
|
+
when "blacklist"
|
29
|
+
blacklist = COMPONENTS.restful_rpc["blacklist"]
|
30
|
+
! blacklist.find do |pattern|
|
31
|
+
pattern_octets = pattern.split "."
|
32
|
+
# Traverse both arrays in parallel
|
33
|
+
octets.zip(pattern_octets).map do |octet, octet_pattern|
|
34
|
+
octet_pattern == "*" ? true : (octet == octet_pattern)
|
35
|
+
end == [true, true, true, true]
|
36
|
+
end
|
37
|
+
else
|
38
|
+
raise Adhearsion::Components::ConfigurationError, 'Unrecognized "access" configuration value!'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
RESTFUL_API_HANDLER = lambda do |env|
|
43
|
+
json = env["rack.input"].read
|
44
|
+
|
45
|
+
# Return "Bad Request" HTTP error if the client forgot
|
46
|
+
return [400, {}, "You must POST a valid JSON object!"] if json.blank?
|
47
|
+
|
48
|
+
json = JSON.parse json
|
49
|
+
|
50
|
+
nesting = COMPONENTS.restful_rpc["path_nesting"]
|
51
|
+
path = env["PATH_INFO"]
|
52
|
+
|
53
|
+
return [404, {}, "This resource does not respond to #{path.inspect}"] unless path[0...nesting.size] == nesting
|
54
|
+
|
55
|
+
path = path[nesting.size..-1]
|
56
|
+
|
57
|
+
return [404, {"Content-Type" => "application/json"}, "You cannot nest method names!"] if path.include?("/")
|
58
|
+
|
59
|
+
rpc_object = Adhearsion::Components.component_manager.extend_object_with(Object.new, :rpc)
|
60
|
+
|
61
|
+
# TODO: set the content-type and other HTTP headers
|
62
|
+
response_object = rpc_object.send(path, *json)
|
63
|
+
[200, {"Content-Type" => "application/json"}, response_object.to_json]
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
initialization do
|
68
|
+
config = COMPONENTS.restful_rpc
|
69
|
+
|
70
|
+
api = RESTFUL_API_HANDLER
|
71
|
+
|
72
|
+
port = config["port"] || 5000
|
73
|
+
authentication = config["authentication"]
|
74
|
+
show_exceptions = config["show_exceptions"]
|
75
|
+
handler = Rack::Handler.const_get(config["handler"] || "Mongrel")
|
76
|
+
|
77
|
+
if authentication
|
78
|
+
api = Rack::Auth::Basic.new(api) do |username, password|
|
79
|
+
authentication[username] == password
|
80
|
+
end
|
81
|
+
api.realm = "Adhearsion API"
|
82
|
+
end
|
83
|
+
|
84
|
+
if show_exceptions
|
85
|
+
api = Rack::ShowStatus.new(Rack::ShowExceptions.new(api))
|
86
|
+
end
|
87
|
+
|
88
|
+
Thread.new do
|
89
|
+
handler.run api, :Port => port
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Use path_nesting to specify an arbitrarily nested. Could be used for additional security or in HTTP reverse proxy server.
|
2
|
+
path_nesting: /
|
3
|
+
|
4
|
+
port: 5000
|
5
|
+
|
6
|
+
# The "handler" option here can be any valid Rack::Handler constant name.
|
7
|
+
# Other options: WEBrick, EventedMongrel.
|
8
|
+
# If you don't know the differences between these, "Mongrel" is definitely a good choice.
|
9
|
+
handler: Mongrel
|
10
|
+
|
11
|
+
# In a production system, you should make this "false" since
|
12
|
+
show_exceptions: true
|
13
|
+
|
14
|
+
# The "authentication" config option can either be "false" or key/value pairs representing allowed usernames and passwords.
|
15
|
+
|
16
|
+
#authentication: false
|
17
|
+
authentication:
|
18
|
+
jicksta: roflcopterz
|
19
|
+
foo: bar6213671682
|
20
|
+
|
21
|
+
access: everyone # When allowing "everyone" access, no IPs are blocked.
|
22
|
+
#access: whitelist # When using a whitelist, the "whitelist" data below will be used.
|
23
|
+
#access: blacklist # When using a blacklist, the "blacklist" data below will be used.
|
24
|
+
|
25
|
+
# This is a list of IPs which are exclusively allowed to call this web service.
|
26
|
+
# Note: whitelists are far more secure than blacklists.
|
27
|
+
whitelist:
|
28
|
+
- 127.0.0.1
|
29
|
+
- 192.168.*.*
|
30
|
+
|
31
|
+
# This is a list of the IPs which are explicitly NOT allowed to call this web service. This will only be used if "access" is
|
32
|
+
# set to "blacklist" above.
|
33
|
+
blacklist:
|
34
|
+
- 100.200.100.200
|
@@ -0,0 +1,263 @@
|
|
1
|
+
unless defined? Adhearsion
|
2
|
+
if File.exists? File.dirname(__FILE__) + "/../../../adhearsion/lib/adhearsion.rb"
|
3
|
+
# If you wish to freeze a copy of Adhearsion to this app, simply place a copy of Adhearsion
|
4
|
+
# into a folder named "adhearsion" within this app's main directory.
|
5
|
+
require File.dirname(__FILE__) + "/../../../adhearsion/lib/adhearsion.rb"
|
6
|
+
elsif File.exists? File.dirname(__FILE__) + "/../../../../../../lib/adhearsion.rb"
|
7
|
+
# This file may be ran from the within the Adhearsion framework code (before a project has been generated)
|
8
|
+
require File.dirname(__FILE__) + "/../../../../../../lib/adhearsion.rb"
|
9
|
+
else
|
10
|
+
require 'rubygems'
|
11
|
+
gem 'adhearsion', '>= 0.7.999'
|
12
|
+
require 'adhearsion'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'adhearsion/component_manager/spec_framework'
|
17
|
+
|
18
|
+
RESTFUL_RPC = ComponentTester.new("restful_rpc", File.dirname(__FILE__) + "/../..")
|
19
|
+
|
20
|
+
##### This is here for a reference
|
21
|
+
#{"CONTENT_LENGTH" => "12",
|
22
|
+
# "CONTENT_TYPE" => "application/x-www-form-urlencoded",
|
23
|
+
# "GATEWAY_INTERFACE" => "CGI/1.1",
|
24
|
+
# "HTTP_ACCEPT" => "application/xml",
|
25
|
+
# "HTTP_ACCEPT_ENCODING" => "gzip, deflate",
|
26
|
+
# "HTTP_AUTHORIZATION" => "Basic amlja3N0YTpyb2ZsY29wdGVyeg==",
|
27
|
+
# "HTTP_HOST" => "localhost:5000",
|
28
|
+
# "HTTP_VERSION" => "HTTP/1.1",
|
29
|
+
# "PATH_INFO" => "/rofl",
|
30
|
+
# "QUERY_STRING" => "",
|
31
|
+
# "rack.errors" => StringIO.new(""),
|
32
|
+
# "rack.input" => StringIO.new('["o","hai!"]'),
|
33
|
+
# "rack.multiprocess" => false,
|
34
|
+
# "rack.multithread" => true,
|
35
|
+
# "rack.run_once" => false,
|
36
|
+
# "rack.url_scheme" => "http",
|
37
|
+
# "rack.version" => [0, 1],
|
38
|
+
# "REMOTE_ADDR" => "::1",
|
39
|
+
# "REMOTE_HOST" => "localhost",
|
40
|
+
# "REMOTE_USER" => "jicksta",
|
41
|
+
# "REQUEST_METHOD" => "POST"
|
42
|
+
# "REQUEST_PATH" => "/",
|
43
|
+
# "REQUEST_URI" => "http://localhost:5000/rofl",
|
44
|
+
# "SCRIPT_NAME" => "",
|
45
|
+
# "SERVER_NAME" => "localhost",
|
46
|
+
# "SERVER_PORT" => "5000",
|
47
|
+
# "SERVER_PROTOCOL" => "HTTP/1.1",
|
48
|
+
# "SERVER_SOFTWARE" => "WEBrick/1.3.1 (Ruby/1.8.6/2008-03-03)"}
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
describe "The VALID_IP_ADDRESS regular expression" do
|
53
|
+
|
54
|
+
it "should match only valid IP addresses" do
|
55
|
+
valid_ip_addresses = ["192.168.1.98", "10.0.1.200", "255.255.255.0", "123.*.4.*"]
|
56
|
+
invalid_ip_addresses = ["10.0.1.1 foo", "bar 255.255.255.0", "0*0*0*0", "1234"]
|
57
|
+
|
58
|
+
valid_ip_addresses. each { |ip| RESTFUL_RPC::VALID_IP_ADDRESS.should =~ ip }
|
59
|
+
invalid_ip_addresses.each { |ip| RESTFUL_RPC::VALID_IP_ADDRESS.should_not =~ ip }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "The initialization block" do
|
64
|
+
|
65
|
+
it "should create a new Thread" do
|
66
|
+
mock_component_config_with :restful_rpc => {}
|
67
|
+
mock(Thread).new { nil }
|
68
|
+
RESTFUL_RPC.initialize!
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should run the Rack adapter specified in the configuration" do
|
72
|
+
mock(Thread).new.yields
|
73
|
+
mock_component_config_with :restful_rpc => {"adapter" => "Mongrel"}
|
74
|
+
mock(Rack::Handler::Mongrel).run is_a(Proc), :Port => 5000
|
75
|
+
RESTFUL_RPC.initialize!
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should wrap the RESTFUL_API_HANDLER in an Rack::Auth::Basic object if authentication is enabled" do
|
79
|
+
mock(Thread).new.yields
|
80
|
+
mock_component_config_with :restful_rpc => {"authentication" => {"foo" => "bar"}}
|
81
|
+
|
82
|
+
proper_authenticator = lambda do |obj|
|
83
|
+
request = OpenStruct.new :credentials => ["foo", "bar"]
|
84
|
+
obj.is_a?(Rack::Auth::Basic) && obj.send(:valid?, request)
|
85
|
+
end
|
86
|
+
|
87
|
+
mock(Rack::Handler::Mongrel).run(satisfy(&proper_authenticator), :Port => 5000)
|
88
|
+
RESTFUL_RPC.initialize!
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should wrap the RESTFUL_API_HANDLER in ShowStatus and ShowExceptions objects when show_exceptions is enabled' do
|
92
|
+
mock(Thread).new.yields
|
93
|
+
mock_component_config_with :restful_rpc => {"show_exceptions" => true}
|
94
|
+
|
95
|
+
mock.proxy(Rack::ShowExceptions).new(is_a(Proc))
|
96
|
+
mock.proxy(Rack::ShowStatus).new is_a(Rack::ShowExceptions)
|
97
|
+
|
98
|
+
mock(Rack::Handler::Mongrel).run is_a(Rack::ShowStatus), :Port => 5000
|
99
|
+
RESTFUL_RPC.initialize!
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'Private helper methods' do
|
105
|
+
|
106
|
+
describe "the RESTFUL_API_HANDLER lambda" do
|
107
|
+
|
108
|
+
it "should return a 200 for requests which execute a method that has been defined in the methods_for(:rpc) context" do
|
109
|
+
component_manager = Adhearsion::Components::ComponentManager.new('/path/shouldnt/matter')
|
110
|
+
|
111
|
+
mock(Adhearsion::Components).component_manager { component_manager }
|
112
|
+
component_manager.load_code <<-RUBY
|
113
|
+
methods_for(:rpc) do
|
114
|
+
def testing_123456(one,two)
|
115
|
+
[two.reverse, one.reverse]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
RUBY
|
119
|
+
|
120
|
+
input = StringIO.new %w[jay phillips].to_json
|
121
|
+
|
122
|
+
mock_component_config_with :restful_rpc => {"path_nesting" => "/"}
|
123
|
+
|
124
|
+
env = {"PATH_INFO" => "/testing_123456", "rack.input" => input}
|
125
|
+
|
126
|
+
response = RESTFUL_RPC::RESTFUL_API_HANDLER.call(env)
|
127
|
+
response.should be_kind_of(Array)
|
128
|
+
response.should have(3).items
|
129
|
+
response.first.should equal(200)
|
130
|
+
JSON.parse(response.last).should eql(%w[jay phillips].map(&:reverse).reverse)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should return a 400 when no data is POSTed" do
|
134
|
+
env = {"rack.input" => StringIO.new(""), "REQUEST_URI" => "/foobar"}
|
135
|
+
RESTFUL_RPC::RESTFUL_API_HANDLER.call(env).first.should equal(400)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should work with a high level test of a successful method invocation" do
|
139
|
+
|
140
|
+
component_manager = Adhearsion::Components::ComponentManager.new('/path/shouldnt/matter')
|
141
|
+
|
142
|
+
mock(Adhearsion::Components).component_manager { component_manager }
|
143
|
+
|
144
|
+
component_manager.load_code '
|
145
|
+
methods_for(:rpc) do
|
146
|
+
def rofl(one,two)
|
147
|
+
"Hai! #{one} #{two}"
|
148
|
+
end
|
149
|
+
end'
|
150
|
+
|
151
|
+
env = {
|
152
|
+
"CONTENT_LENGTH" => "12",
|
153
|
+
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
|
154
|
+
"GATEWAY_INTERFACE" => "CGI/1.1",
|
155
|
+
"HTTP_ACCEPT" => "application/xml",
|
156
|
+
"HTTP_ACCEPT_ENCODING" => "gzip, deflate",
|
157
|
+
"HTTP_AUTHORIZATION" => "Basic amlja3N0YTpyb2ZsY29wdGVyeg==",
|
158
|
+
"HTTP_HOST" => "localhost:5000",
|
159
|
+
"HTTP_VERSION" => "HTTP/1.1",
|
160
|
+
"PATH_INFO" => "/rofl",
|
161
|
+
"QUERY_STRING" => "",
|
162
|
+
"rack.errors" => StringIO.new(""),
|
163
|
+
"rack.input" => StringIO.new('["o","hai!"]'),
|
164
|
+
"rack.multiprocess" => false,
|
165
|
+
"rack.multithread" => true,
|
166
|
+
"rack.run_once" => false,
|
167
|
+
"rack.url_scheme" => "http",
|
168
|
+
"rack.version" => [0, 1],
|
169
|
+
"REMOTE_ADDR" => "::1",
|
170
|
+
"REMOTE_HOST" => "localhost",
|
171
|
+
"REMOTE_USER" => "jicksta",
|
172
|
+
"REQUEST_METHOD" => "POST",
|
173
|
+
"REQUEST_PATH" => "/",
|
174
|
+
"REQUEST_URI" => "http://localhost:5000/rofl",
|
175
|
+
"SCRIPT_NAME" => "",
|
176
|
+
"SERVER_NAME" => "localhost",
|
177
|
+
"SERVER_PORT" => "5000",
|
178
|
+
"SERVER_PROTOCOL" => "HTTP/1.1",
|
179
|
+
"SERVER_SOFTWARE" => "WEBrick/1.3.1 (Ruby/1.8.6/2008-03-03)" }
|
180
|
+
|
181
|
+
response = RESTFUL_RPC::RESTFUL_API_HANDLER.call(env)
|
182
|
+
JSON.parse(response.last).should == ["Hai! o hai!"]
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should contain backtrace information when show_errors is enabled and an exception occurs" do
|
187
|
+
mock_component_config_with :restful_api => {"show_errors" => true}
|
188
|
+
pending
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
describe 'the ip_allowed?() method' do
|
194
|
+
|
195
|
+
before :each do
|
196
|
+
@method = RESTFUL_RPC.helper_method :ip_allowed?
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'should raise a ConfigurationError if "access" is not one of "everyone", "whitelist" or "blacklist"' do
|
200
|
+
good_access_values = %w[everyone whitelist blacklist]
|
201
|
+
bad_access_values = %w[foo bar qaz qwerty everone blaclist whitlist]
|
202
|
+
|
203
|
+
good_access_values.each do |access_value|
|
204
|
+
mock_component_config_with :restful_rpc => {"access" => access_value, "whitelist" => [], "blacklist" => []}
|
205
|
+
lambda { @method.call("10.0.0.1") }.should_not raise_error
|
206
|
+
end
|
207
|
+
|
208
|
+
bad_access_values.each do |access_value|
|
209
|
+
mock_component_config_with :restful_rpc => {"access" => access_value, "authentication" => false}
|
210
|
+
lambda { @method.call("10.0.0.1") }.should raise_error(Adhearsion::Components::ConfigurationError)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe 'whitelists' do
|
215
|
+
|
216
|
+
it "should parse *'s as wildcards" do
|
217
|
+
mock_component_config_with :restful_rpc => {"access" => "whitelist", "whitelist" => ["10.*.*.*"]}
|
218
|
+
@method.call("10.1.2.3").should be_true
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should allow IPs which are explictly specified" do
|
222
|
+
mock_component_config_with :restful_rpc => {"access" => "whitelist", "whitelist" => ["4.3.2.1"]}
|
223
|
+
@method.call("4.3.2.1").should be_true
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should not allow IPs which are not explicitly specified" do
|
227
|
+
mock_component_config_with :restful_rpc => {"access" => "whitelist", "whitelist" => %w[ 1.2.3.4 4.3.2.1]}
|
228
|
+
@method.call("2.2.2.2").should be_false
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
describe 'blacklists' do
|
234
|
+
|
235
|
+
it "should parse *'s as wildcards" do
|
236
|
+
mock_component_config_with :restful_rpc => {"access" => "blacklist", "blacklist" => ["10.*.*.*"]}
|
237
|
+
@method.call("10.1.2.3").should be_false
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should not allow IPs which are explicitly specified" do
|
241
|
+
mock_component_config_with :restful_rpc => {"access" => "blacklist", "blacklist" => ["9.8.7.6", "9.8.7.5"]}
|
242
|
+
@method.call("9.8.7.5").should be_false
|
243
|
+
end
|
244
|
+
|
245
|
+
it "should allow IPs which are not explicitly specified" do
|
246
|
+
mock_component_config_with :restful_rpc => {"access" => "blacklist", "blacklist" => ["10.20.30.40"]}
|
247
|
+
@method.call("1.1.1.1").should be_true
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
251
|
+
|
252
|
+
describe '"everyone" access' do
|
253
|
+
it "should return true for any IP given, irrespective of the configuration" do
|
254
|
+
ip_addresses = %w[100.200.100.200 0.0.0.0 *.0.0.*]
|
255
|
+
ip_addresses.each do |address|
|
256
|
+
RESTFUL_RPC.helper_method(:ip_allowed?).call(address).should equal(true)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|