dot_net_services 0.0.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.
@@ -0,0 +1,58 @@
1
+ namespace DotNetServicesRuby
2
+ {
3
+ using System;
4
+ using System.Collections.Generic;
5
+ using System.ServiceModel;
6
+ using System.ServiceModel.Channels;
7
+
8
+ [ServiceBehavior(Name = "SyndicationService", Namespace = "http://samples.microsoft.com/ServiceModel/Relay/")]
9
+ class ResourceService : ResourceContract
10
+ {
11
+ class HimomBodyWriter : BodyWriter
12
+ {
13
+ string query;
14
+ string verb;
15
+ string body;
16
+
17
+ public HimomBodyWriter(string theQuery, string theVerb, string theBody) : base(true)
18
+ {
19
+ query = theQuery;
20
+ verb = theVerb;
21
+ body = theBody;
22
+ }
23
+
24
+ protected override void OnWriteBodyContents(System.Xml.XmlDictionaryWriter writer)
25
+ {
26
+ writer.WriteStartElement("body");
27
+ writer.WriteString("\n");
28
+ writer.WriteString("Hi, mom\n");
29
+ writer.WriteString("verb: " + verb + "\n");
30
+ writer.WriteString("query: " + query + "\n");
31
+ writer.WriteString("body: " + body + "\n");
32
+ writer.WriteEndElement();
33
+ }
34
+ }
35
+
36
+ public Message Process(Message message)
37
+ {
38
+ var httpRequest = (HttpRequestMessageProperty)(message.Properties["httpRequest"]);
39
+
40
+ var query = httpRequest.QueryString;
41
+ var verb = httpRequest.Method;
42
+ var body = "";
43
+
44
+ Message response = Message.CreateMessage(message.Version, "GETRESPONSE", new HimomBodyWriter(query, verb, body));
45
+
46
+ HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty();
47
+ responseProperty.Headers.Add("Content-Type", "application/xml; charset=utf-8");
48
+ response.Properties.Add(HttpResponseMessageProperty.Name, responseProperty);
49
+ return response;
50
+ }
51
+
52
+ public Message ProcessPost(Message message)
53
+ {
54
+ throw new NotImplementedException();
55
+ }
56
+
57
+ }
58
+ }
@@ -0,0 +1,71 @@
1
+ <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
2
+ <PropertyGroup>
3
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
4
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
5
+ <ProductVersion>9.0.21022</ProductVersion>
6
+ <SchemaVersion>2.0</SchemaVersion>
7
+ <ProjectGuid>{27CF1BE6-0280-46F8-BB19-BC7147E02B6A}</ProjectGuid>
8
+ <OutputType>Exe</OutputType>
9
+ <AppDesignerFolder>Properties</AppDesignerFolder>
10
+ <RootNamespace>System.ServiceBus.Samples</RootNamespace>
11
+ <AssemblyName>Service</AssemblyName>
12
+ <FileUpgradeFlags>
13
+ </FileUpgradeFlags>
14
+ <OldToolsVersion>2.0</OldToolsVersion>
15
+ <UpgradeBackupLocation>
16
+ </UpgradeBackupLocation>
17
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
18
+ </PropertyGroup>
19
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
20
+ <DebugSymbols>true</DebugSymbols>
21
+ <DebugType>full</DebugType>
22
+ <Optimize>false</Optimize>
23
+ <OutputPath>bin\Debug\</OutputPath>
24
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
25
+ <ErrorReport>prompt</ErrorReport>
26
+ <WarningLevel>4</WarningLevel>
27
+ </PropertyGroup>
28
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
29
+ <DebugType>pdbonly</DebugType>
30
+ <Optimize>true</Optimize>
31
+ <OutputPath>bin\Release\</OutputPath>
32
+ <DefineConstants>TRACE</DefineConstants>
33
+ <ErrorReport>prompt</ErrorReport>
34
+ <WarningLevel>4</WarningLevel>
35
+ </PropertyGroup>
36
+ <ItemGroup>
37
+ <Reference Include="Microsoft.ServiceBus, Version=0.12.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
38
+ <Reference Include="System" />
39
+ <Reference Include="System.Core">
40
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
41
+ </Reference>
42
+ <Reference Include="System.Data" />
43
+ <Reference Include="System.Runtime.Serialization, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
44
+ <SpecificVersion>False</SpecificVersion>
45
+ </Reference>
46
+ <Reference Include="System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL" />
47
+ <Reference Include="System.ServiceModel.Web">
48
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
49
+ </Reference>
50
+ <Reference Include="System.Xml" />
51
+ </ItemGroup>
52
+ <ItemGroup>
53
+ <Compile Include="AnonymousResourceService.cs" />
54
+ <Compile Include="PlainTextService.cs" />
55
+ <Compile Include="Program.cs" />
56
+ <Compile Include="Properties\AssemblyInfo.cs" />
57
+ <Compile Include="ResourceContract.cs" />
58
+ <Compile Include="ResourceService.cs" />
59
+ </ItemGroup>
60
+ <ItemGroup>
61
+ <None Include="App.config" />
62
+ </ItemGroup>
63
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
64
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
65
+ Other similar extension points exist, see Microsoft.Common.targets.
66
+ <Target Name="BeforeBuild">
67
+ </Target>
68
+ <Target Name="AfterBuild">
69
+ </Target>
70
+ -->
71
+ </Project>
@@ -0,0 +1,33 @@
1
+ 
2
+ Microsoft Visual Studio Solution File, Format Version 10.00
3
+ # Visual Studio 2008
4
+ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F20C1D91-6F22-4047-85D9-423AB58D1C5C}"
5
+ ProjectSection(SolutionItems) = preProject
6
+ Readme.htm = Readme.htm
7
+ EndProjectSection
8
+ ProjectSection(WebsiteProperties) = preProject
9
+ Debug.AspNetCompiler.Debug = "True"
10
+ Release.AspNetCompiler.Debug = "False"
11
+ EndProjectSection
12
+ EndProject
13
+ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.csproj", "{27CF1BE6-0280-46F8-BB19-BC7147E02B6A}"
14
+ ProjectSection(WebsiteProperties) = preProject
15
+ Debug.AspNetCompiler.Debug = "True"
16
+ Release.AspNetCompiler.Debug = "False"
17
+ EndProjectSection
18
+ EndProject
19
+ Global
20
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
21
+ Debug|Any CPU = Debug|Any CPU
22
+ Release|Any CPU = Release|Any CPU
23
+ EndGlobalSection
24
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
25
+ {27CF1BE6-0280-46F8-BB19-BC7147E02B6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26
+ {27CF1BE6-0280-46F8-BB19-BC7147E02B6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
27
+ {27CF1BE6-0280-46F8-BB19-BC7147E02B6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
28
+ {27CF1BE6-0280-46F8-BB19-BC7147E02B6A}.Release|Any CPU.Build.0 = Release|Any CPU
29
+ EndGlobalSection
30
+ GlobalSection(SolutionProperties) = preSolution
31
+ HideSolutionNode = FALSE
32
+ EndGlobalSection
33
+ EndGlobal
@@ -0,0 +1,84 @@
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
+
3
+ describe "end to end usage scenarios" do
4
+
5
+ it "should be able to perform a simple GET from the endpoint" do
6
+ result = DotNetServices::Session.open(anonymous_test_endpoint) { |s| s.get }
7
+ result.class.to_s.should == "Net::HTTPOK"
8
+ result.content_type.should == 'application/xml'
9
+ result.body.should =~ /^Hi, mom$/
10
+ end
11
+
12
+ it "should pass query string through a GET correctly" do
13
+ result = DotNetServices::Session.open(anonymous_test_endpoint) { |s| s.get :foo => 'bar' }
14
+ result.class.to_s.should == "Net::HTTPOK"
15
+ result.content_type.should == 'application/xml'
16
+ result.body.should =~ /^query: foo=bar/
17
+ end
18
+
19
+ it "should pass query string with URL escaped characters through a GET correctly" do
20
+ result = DotNetServices::Session.open(anonymous_test_endpoint) { |s| s.get :foo => 'b+a~r+' }
21
+ result.class.to_s.should == "Net::HTTPOK"
22
+ result.content_type.should == 'application/xml'
23
+ result.body.should =~ /^query: foo=b\+a~r/
24
+ end
25
+
26
+ # This scenario doesn't work, known bug in CTP 12
27
+ # it "should pass a form data through a POST" do
28
+ # result = DotNetServices::Session.open(test_endpoint) { |s| s.post :foo => 'bar' }
29
+ # result.class.to_s.should == "Net::HTTPOK"
30
+ # result.content_type.should == 'application/xml'
31
+ # result.body.should =~ /^verb: POST$/
32
+ # result.body.should =~ /^body: foo=bar$/
33
+ # end
34
+
35
+ it "should obtain a security token" do
36
+ DotNetServices::Session.open(test_endpoint, :username => 'alexeyv', :password => '12345678') do
37
+ |session|
38
+ session.authenticator.token.should_not be_nil
39
+ end
40
+ end
41
+
42
+ it "should be able to do an authenticated GET" do
43
+ result = DotNetServices::Session.open(test_endpoint, :username => 'alexeyv', :password => '12345678') { |s| s.get }
44
+ result.class.to_s.should == "Net::HTTPOK"
45
+ result.body.should =~ /^Hi, mom$/
46
+ # TODO: once we know how, make sure that service has some idea of who is talking to it
47
+ end
48
+
49
+ it "should pass a form data through an authenticated POST" do
50
+ result = DotNetServices::Session.open(test_endpoint, :username => 'alexeyv', :password => '12345678') { |s| s.post :foo => 'bar' }
51
+ result.class.to_s.should == "Net::HTTPOK"
52
+ result.content_type.should == 'application/xml'
53
+ result.body.should =~ /^verb: POST$/
54
+ result.body.should =~ /^body: foo=bar$/
55
+ end
56
+
57
+ it "plain text GET" do
58
+ result = DotNetServices::Session.open(plain_text_test_endpoint, :username => 'alexeyv', :password => '12345678') { |s| s.get :foo => 'bar' }
59
+ result.class.to_s.should == "Net::HTTPOK"
60
+ result.body.should == "hi mom"
61
+ end
62
+
63
+ it "plain text POST" do
64
+ result = DotNetServices::Session.open(plain_text_test_endpoint, :username => 'alexeyv', :password => '12345678') { |s| s.post :foo => 'bar' }
65
+ result.class.to_s.should == "Net::HTTPOK"
66
+ result.content_type.should == 'text/plain'
67
+ result.body.should =~ /^verb: POST$/
68
+ result.body.should =~ /^body: foo=bar$/
69
+ end
70
+
71
+ it "should detect an invalid user/password situation and blow up correctly"
72
+
73
+
74
+ # it "should not be slow as a turtle with laryngitis walking through molasses in January" do
75
+ # start = Time.now
76
+ # 10.times { DotNetServices::Session.open(anonymous_test_endpoint) { |s| s.get :foo => 'bar' } }
77
+ # duration = Time.now - start
78
+ #
79
+ # if duration > 10
80
+ # raise "10 simple get requests took #{duration} seconds to perform. This is too long."
81
+ # end
82
+ # end
83
+
84
+ end
@@ -0,0 +1,30 @@
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
+
3
+ describe "end to end usage scenarios" do
4
+
5
+ it "should do end-to-end VMB interaction with itself" do
6
+ authenticator = DotNetServices::Authentication.setup(:username => 'RubyInterop', :password => '12345678')
7
+ authenticator.authenticate
8
+ authenticator.token.should_not be_nil
9
+
10
+ create_request = Net::HTTP::CreateMB.new("/services/RubyInterop/MessageBuffer")
11
+
12
+ authenticator.enhance(create_request)
13
+
14
+ puts
15
+ p create_request
16
+ create_request.each { |k, v| puts "#{k}: #{v}"}
17
+ puts
18
+
19
+ result = Net::HTTP.start(DotNetServices.relay_host) { |http| http.request(create_request) }
20
+
21
+ p result
22
+ puts
23
+ result.each { |k, v| puts "#{k}: #{v}"}
24
+ puts
25
+ puts result.body
26
+ puts
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
3
+
4
+ require 'spec'
5
+ require 'dot_net_services'
6
+
7
+ Spec::Runner.configure do |config|
8
+ config.before(:each) do
9
+ DotNetServices::Authentication.clear_cache!
10
+ end
11
+ end
12
+
13
+ def test_endpoint
14
+ DotNetServices.root_url + "/BillBoard/TestService/"
15
+ end
16
+
17
+ def anonymous_test_endpoint
18
+ DotNetServices.root_url + "/BillBoard/AnonymousTestService/"
19
+ end
20
+
21
+ def plain_text_test_endpoint
22
+ DotNetServices.root_url + "/BillBoard/PlainTextTestService/"
23
+ end
@@ -0,0 +1,289 @@
1
+ require "#{File.dirname(__FILE__)}/../../spec_helper"
2
+
3
+ module DotNetServices
4
+
5
+ describe Authentication::UsernamePassword do
6
+
7
+ describe "acting as a hash key" do
8
+ before :each do
9
+ @auth = Authentication::UsernamePassword.new('frodo', 'passw0rd')
10
+ end
11
+
12
+ it "should not be equal to an instance of another class" do
13
+ different_class = stub('not the same class', :username => 'frodo', :password => 'passw0rd')
14
+ different_class.should_receive(:is_a?).with(Authentication::UsernamePassword).and_return(false)
15
+ @auth.should_not == different_class
16
+ end
17
+
18
+ it "should not be equal to an instance with different username or password" do
19
+ @auth.should_not == Authentication::UsernamePassword.new('gandalf', 'passw0rd')
20
+ @auth.should_not == Authentication::UsernamePassword.new('frodo', 'different passw0rd')
21
+ end
22
+
23
+ it "should equal to another instance with same username and password" do
24
+ @auth.should == @auth
25
+ @auth.should == Authentication::UsernamePassword.new('frodo', 'passw0rd')
26
+ end
27
+
28
+ it "should adequately identify an instance in a hash" do
29
+ hash = {}
30
+ hash[@auth] = :found
31
+ hash[@auth].should == :found
32
+
33
+ equivalent_auth = Authentication::UsernamePassword.new('frodo', 'passw0rd')
34
+ hash[equivalent_auth].should == :found
35
+
36
+ different_auth = Authentication::UsernamePassword.new('frodo', 'different passw0rd')
37
+ hash[different_auth].should be_nil
38
+ end
39
+
40
+ end
41
+
42
+ describe "authenticate()" do
43
+
44
+ it "should obtain a token from DotNetServices STS" do
45
+ http = mock_http
46
+ response = mock('response')
47
+ Time.stub!(:now).and_return(Time.at(0))
48
+
49
+ http.should_receive(:get).with("/issuetoken.aspx?u=frodo&p=password").and_return(response)
50
+ response.should_receive(:is_a?).with(Net::HTTPOK).and_return(true)
51
+ response.stub!(:body).and_return('bWyFxQgby0gHyPWZ2LcIXnHgA1J5kALK4iM+QA==')
52
+
53
+ auth = Authentication::UsernamePassword.new('frodo', 'password')
54
+ auth.authenticate
55
+ auth.token.value.should == 'bWyFxQgby0gHyPWZ2LcIXnHgA1J5kALK4iM+QA=='
56
+ auth.token.expiry.should == Time.at(24 * 60 * 60)
57
+ end
58
+
59
+ it "should rewrite exception messages to avoid accidentally exposing username/password" do
60
+ http = mock_http
61
+ http.should_receive(:get).with("/issuetoken.aspx?u=frodo&p=password").and_raise("p=password")
62
+ auth = Authentication::UsernamePassword.new('frodo', 'password')
63
+
64
+ begin
65
+ auth.authenticate
66
+ rescue => e
67
+ e.should be_instance_of(AuthenticationError)
68
+ e.message.should_not =~ /password/
69
+ end
70
+ end
71
+
72
+ it "should detect when the HTTP response code is wrong and blow up correctly" do
73
+ http = mock_http
74
+ not_found = stub("HTTPNotFound", :class => Net::HTTPNotFound)
75
+ not_found.should_receive(:is_a?).with(Net::HTTPOK).and_return(false)
76
+
77
+ http.should_receive(:get).with("/issuetoken.aspx?u=frodo&p=password").and_return(not_found)
78
+ auth = Authentication::UsernamePassword.new('frodo', 'password')
79
+
80
+ lambda { auth.authenticate }.should raise_error(AuthenticationError,
81
+ "Failed to obtain a security token from the identity service. HTTP response was Net::HTTPNotFound")
82
+ end
83
+
84
+ # TODO Identity service response for this situation is not correct now. Implement test when they fix it
85
+ it "should detect an invalid user/password situation and blow up correctly"
86
+
87
+ # TODO asked MS what future-proof assertions can be made to see if it's a token
88
+ it "should detect when response is not a token and blow up correctly"
89
+ # do
90
+ # http = mock_http
91
+ # response = mock('response')
92
+ # http.should_receive(:get).with("/issuetoken.aspx?u=frodo&p=password").and_return(response)
93
+ # response.should_receive(:is_a?).with(Net::HTTPOK).and_return(true)
94
+ # response.stub!(:body).and_return('NOT A TOKEN')
95
+ #
96
+ # auth = Authentication::UsernamePassword.new('frodo', 'password')
97
+ # proc { auth.authenticate }.should raise_error(
98
+ # AuthenticationError, "Response from identity service is not a valid token. " +
99
+ # # TODO remove the line below when we are able to detect this situation explicitly
100
+ # "This can indicate username/password mismatch.")
101
+ # end
102
+
103
+ it "should not acquire a new token if a token is already defined" do
104
+ token = Authentication::Token.new("nh8hUpTSAeMW0RVkMRRaZtxJqfM==")
105
+ auth = Authentication::UsernamePassword.new('frodo', 'password', token)
106
+ Net::HTTP.should_not_receive(:new)
107
+ auth.authenticate
108
+ end
109
+
110
+ it "should acquire new token if token is expired" do
111
+ expired_token = Authentication::Token.new('expired/token==', Time.now - 1)
112
+ new_token = Authentication::Token.new('new/token==', Time.now)
113
+ auth = Authentication::UsernamePassword.new('frodo', 'password', expired_token)
114
+
115
+ auth.should_receive(:acquire_token).and_return(new_token)
116
+ request = {}
117
+ auth.enhance(request)
118
+ auth.token.should equal(new_token)
119
+ request['X-MS-Identity-Token'].should == 'new/token=='
120
+ end
121
+
122
+ it "should acquire new token if token is not set" do
123
+ auth = Authentication::UsernamePassword.new('frodo', 'password')
124
+ auth.token.should be_nil
125
+ new_token = Authentication::Token.new('new/token==', Time.now)
126
+
127
+ auth.should_receive(:acquire_token).and_return(new_token)
128
+
129
+ request = {}
130
+ auth.enhance(request)
131
+ auth.token.should equal(new_token)
132
+ request['X-MS-Identity-Token'].should == 'new/token=='
133
+ end
134
+
135
+ def mock_http
136
+ http = mock('http')
137
+ Net::HTTP.should_receive(:new).with(DotNetServices.identity_host, 443).and_return(http)
138
+ http.should_receive(:use_ssl=).with(true)
139
+ http.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
140
+ http
141
+ end
142
+
143
+ end
144
+
145
+ describe "enhance(request)" do
146
+
147
+ it "should put a token into X-MS-Identity-Token header" do
148
+ Time.stub!(:now).and_return(Time.at(0))
149
+ request = {}
150
+ token = Authentication::Token.new("a/token==", Time.at(1))
151
+ auth = Authentication::UsernamePassword.new('frodo', 'password', token)
152
+ auth.enhance(request)
153
+ request['X-MS-Identity-Token'].should == "a/token=="
154
+ end
155
+
156
+ it "should acquire the token if necessary" do
157
+ token = Authentication::Token.new("a/token==", Time.at(1))
158
+ auth = Authentication::UsernamePassword.new('frodo', 'password')
159
+ auth.should_receive(:acquire_token).and_return(token)
160
+ request = {}
161
+ auth.enhance(request)
162
+ request['X-MS-Identity-Token'].should == "a/token=="
163
+ end
164
+
165
+ end
166
+
167
+ end
168
+
169
+ describe Authentication::Anonymous do
170
+ describe "acting as a hash key" do
171
+ it "should equal to any instance of the same class" do
172
+ auth = Authentication::Anonymous.new
173
+ auth.should == auth
174
+ auth.should == Authentication::Anonymous.new
175
+ auth.should_not == Object.new
176
+ end
177
+
178
+ it "should have a constant hash" do
179
+ auth = Authentication::Anonymous.new
180
+ auth.hash.should == -1
181
+ end
182
+ end
183
+ end
184
+
185
+ describe "setup(auth_data)" do
186
+ it "should construct a user/password authentication handler given user and password" do
187
+ authenticator = Authentication.setup(:username => 'me', :password => 'himom')
188
+ authenticator.should be_instance_of(Authentication::UsernamePassword)
189
+ end
190
+
191
+ it "should construct an anonymous authentication handler given nothing" do
192
+ authenticator = Authentication.setup(nil)
193
+ authenticator.should be_instance_of(Authentication::Anonymous)
194
+ end
195
+
196
+ it "should construct an anonymous authentication handler given an empty hash" do
197
+ authenticator = Authentication.setup({})
198
+ authenticator.should be_instance_of(Authentication::Anonymous)
199
+ end
200
+
201
+ it "should construct a certificate authentication handler given certificate" do
202
+ authenticator = Authentication.setup(:certificate => 'foo')
203
+ authenticator.should be_instance_of(Authentication::Certificate)
204
+ end
205
+
206
+ it "should treat auth_data as auth handler if it's not a hash" do
207
+ authenticator = Authentication.setup(:foo)
208
+ authenticator.should == :foo
209
+ end
210
+
211
+ it "should blow up if username is given but password isn't, or vice versa" do
212
+ proc {
213
+ Authentication.setup(:username => 'foo')
214
+ }.should raise_error(ArgumentError, "Auth data specifies username, but no password.")
215
+
216
+ proc {
217
+ authenticator = Authentication.setup(:password => 'foo')
218
+ }.should raise_error(ArgumentError, "Auth data specifies password, but no username.")
219
+ end
220
+
221
+ it "should blow up if both username/password and certificate are in auth_data hash" do
222
+ proc {
223
+ authenticator = Authentication.setup(:username => 'foo', :password => 'bar', :certificate => 'baz')
224
+ }.should raise_error(ArgumentError, "Cannot determine authentication type from auth data.")
225
+ end
226
+
227
+ it "should blow up if given unknown options in auth data" do
228
+ proc {
229
+ authenticator = Authentication.setup(:username => 'foo', :password => 'bar', :something => true, :something_else => true)
230
+ }.should raise_error(ArgumentError, /^Auth data contains unknown options:/)
231
+ end
232
+
233
+ it "should cache authenticators" do
234
+ Authentication.instance_variable_get(:@cache).size.should == 0
235
+
236
+ anon_auth = Authentication.setup(nil)
237
+ Authentication.instance_variable_get(:@cache).length.should == 1
238
+
239
+ another_anon_auth = Authentication.setup(nil)
240
+ Authentication.instance_variable_get(:@cache).length.should == 1
241
+ another_anon_auth.should equal(anon_auth)
242
+
243
+ user_auth = Authentication.setup(:username => 'frodo', :password => 'passw0rd')
244
+ Authentication.instance_variable_get(:@cache).length.should == 2
245
+ another_user_auth = Authentication.setup(:username => 'frodo', :password => 'passw0rd')
246
+ Authentication.instance_variable_get(:@cache).length.should == 2
247
+ another_user_auth.should equal(user_auth)
248
+ end
249
+
250
+ it "should reacquire expired tokens" do
251
+ old_token = Authentication::Token.new('old/token==', Time.at(1))
252
+ new_token = Authentication::Token.new('new/token==', Time.at(4))
253
+
254
+ Time.stub!(:now).and_return(Time.at(0))
255
+ old_auth = Authentication.setup(:username => 'frodo', :password => 'passw0rd')
256
+
257
+ # old_auth will have to reacquire the token because the one it had initially will be expired
258
+ old_auth.should_receive(:acquire_token).twice.and_return(old_token, new_token)
259
+
260
+ old_auth.authenticate
261
+ old_auth.token.should equal(old_token)
262
+
263
+ Time.stub!(:now).and_return(Time.at(2))
264
+
265
+ new_auth = Authentication.setup(:username => 'frodo', :password => 'passw0rd')
266
+ new_auth.authenticate
267
+
268
+ new_auth.should equal(old_auth)
269
+ new_auth.token.should equal(new_token)
270
+ end
271
+
272
+ end
273
+
274
+ describe Authentication::Token do
275
+ it "should check that value is a well-formed token" do
276
+ lambda { Authentication::Token.new("foo") }.
277
+ should raise_error(AuthenticationError,
278
+ /^Response from access control service doesn't seem to contain a valid authentication token/)
279
+ end
280
+
281
+ it "should ignore the rest of the body, if there is anything after the token (bug workaround)" do
282
+ token = Authentication::Token.new('bWyFxQgby0gHyPWZ2LcIXnHgA1J5kALK4iM+QA==<html>....</html>')
283
+ token.value.should == 'bWyFxQgby0gHyPWZ2LcIXnHgA1J5kALK4iM+QA=='
284
+ end
285
+
286
+ end
287
+
288
+ end
289
+