dot_net_services 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+