Added more tests for RP.AuthenticationRequest.
[dotnetoauth.git] / src / DotNetOpenAuth.Test / OpenId / TestSupport.cs
blobe82841d6db323537fcaaf3510eae51b2c89fc194
1 //-----------------------------------------------------------------------
2 // <copyright file="TestSupport.cs" company="Andrew Arnott">
3 // Copyright (c) Andrew Arnott. All rights reserved.
4 // </copyright>
5 //-----------------------------------------------------------------------
7 namespace DotNetOpenAuth.Test.OpenId {
8 using System;
9 using System.Collections.Generic;
10 using System.IO;
11 using System.Reflection;
12 using DotNetOAuth.Test.OpenId.UI;
13 using DotNetOpenAuth.Messaging;
14 using DotNetOpenAuth.OpenId;
15 using DotNetOpenAuth.OpenId.Provider;
16 using DotNetOpenAuth.OpenId.RelyingParty;
17 using DotNetOpenAuth.Test.Mocks;
18 ////using DotNetOpenAuth.Test.UI;
19 using log4net;
21 public class TestSupport {
22 public const string HostTestPage = "HostTest.aspx";
24 public const string ProviderPage = "ProviderEndpoint.aspx";
26 public const string DirectedProviderEndpoint = "DirectedProviderEndpoint.aspx";
28 public const string MobileConsumerPage = "RelyingPartyMobile.aspx";
30 public const string ConsumerPage = "RelyingParty.aspx";
32 public const string OPDefaultPage = "OPDefault.aspx";
34 public static readonly ILog Logger = LogManager.GetLogger("DotNetOpenId.Test");
36 public static readonly string TestWebDirectory = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), @"..\..\src\DotNetOpenId.TestWeb"));
38 private const string IdentityPage = "IdentityEndpoint.aspx";
40 private const string DirectedIdentityPage = "DirectedIdentityEndpoint.aspx";
42 public enum Scenarios {
43 // Authentication test scenarios
44 AutoApproval,
46 AutoApprovalAddFragment,
48 ApproveOnSetup,
50 AlwaysDeny,
52 /* Extension test scenarios */
54 /// <summary>
55 /// Provides all required and requested fields.
56 /// </summary>
57 ExtensionFullCooperation,
59 /// <summary>
60 /// Provides only those fields marked as required.
61 /// </summary>
62 ExtensionPartialCooperation,
65 public static Uri ReturnTo {
66 get { return TestSupport.GetFullUrl(TestSupport.ConsumerPage); }
69 public static Realm Realm {
70 get { return new Realm(TestSupport.GetFullUrl(TestSupport.ConsumerPage).AbsoluteUri); }
73 public static Identifier GetDelegateUrl(Scenarios scenario) {
74 return GetDelegateUrl(scenario, false);
77 public static Identifier GetDelegateUrl(Scenarios scenario, bool useSsl) {
78 return new UriIdentifier(GetFullUrl("/" + scenario, null, useSsl));
81 public static Uri GetFullUrl(string url) {
82 return GetFullUrl(url, null, false);
85 public static Uri GetFullUrl(string url, string key, object value) {
86 var dictionary = new Dictionary<string, string> {
87 { key, value.ToString() },
89 return GetFullUrl(url, dictionary, false);
92 public static Uri GetFullUrl(string url, IDictionary<string, string> args, bool useSsl) {
93 Uri defaultUriBase = new Uri(useSsl ? "https://localhost/" : "http://localhost/");
94 Uri baseUri = UITestSupport.Host != null ? UITestSupport.Host.BaseUri : defaultUriBase;
95 UriBuilder builder = new UriBuilder(new Uri(baseUri, url));
96 MessagingUtilities.AppendQueryArgs(builder, args);
97 return builder.Uri;
100 /// <summary>
101 /// Returns the content of a given embedded resource.
102 /// </summary>
103 /// <param name="path">The path of the file as it appears within the project,
104 /// where the leading / marks the root directory of the project.</param>
105 /// <returns>The content of the requested resource.</returns>
106 internal static string LoadEmbeddedFile(string path) {
107 if (!path.StartsWith("/")) {
108 path = "/" + path;
110 path = "DotNetOpenAuth.Test.OpenId" + path.Replace('/', '.');
111 Stream resource = Assembly.GetExecutingAssembly().GetManifestResourceStream(path);
112 if (resource == null) {
113 throw new ArgumentException();
115 using (StreamReader sr = new StreamReader(resource)) {
116 return sr.ReadToEnd();
120 ////internal static UriIdentifier GetOPIdentityUrl(Scenarios scenario, bool useSsl) {
121 //// var args = new Dictionary<string, string> {
122 //// { "user", scenario.ToString() },
123 ////};
124 //// return new UriIdentifier(GetFullUrl("/" + OPDefaultPage, args, useSsl));
125 ////}
126 internal static UriIdentifier GetIdentityUrl(Scenarios scenario, ProtocolVersion providerVersion) {
127 return GetIdentityUrl(scenario, providerVersion, false);
130 internal static UriIdentifier GetIdentityUrl(Scenarios scenario, ProtocolVersion providerVersion, bool useSsl) {
131 var dictionary = new Dictionary<string, string> {
132 { "user", scenario.ToString() },
133 { "version", providerVersion.ToString() },
135 return new UriIdentifier(GetFullUrl("/" + IdentityPage, dictionary, useSsl));
138 internal static MockIdentifier GetMockIdentifier(Scenarios scenario, MockHttpRequest mockRequest, ProtocolVersion providerVersion) {
139 return GetMockIdentifier(scenario, mockRequest, providerVersion, false);
142 internal static MockIdentifier GetMockIdentifier(Scenarios scenario, MockHttpRequest mockRequest, ProtocolVersion providerVersion, bool useSsl) {
143 ServiceEndpoint se = GetServiceEndpoint(scenario, providerVersion, 10, useSsl);
144 return new MockIdentifier(GetIdentityUrl(scenario, providerVersion, useSsl), mockRequest, new ServiceEndpoint[] { se });
147 internal static ServiceEndpoint GetServiceEndpoint(Scenarios scenario, ProtocolVersion providerVersion, int servicePriority, bool useSsl) {
148 var providerEndpoint = new ProviderEndpointDescription(GetFullUrl("/" + ProviderPage, null, useSsl), new string[] { Protocol.Lookup(providerVersion).ClaimedIdentifierServiceTypeURI });
149 return ServiceEndpoint.CreateForClaimedIdentifier(
150 GetIdentityUrl(scenario, providerVersion, useSsl),
151 GetIdentityUrl(scenario, providerVersion, useSsl),
152 GetDelegateUrl(scenario, useSsl),
153 providerEndpoint,
154 servicePriority,
155 10);
158 /// <summary>
159 /// A default implementation of a simple provider that responds to authentication requests
160 /// per the scenario that is being simulated.
161 /// </summary>
162 /// <param name="provider">The OpenIdProvider on which the process messages.</param>
163 /// <remarks>
164 /// This is a very useful method to pass to the OpenIdCoordinator constructor for the Provider argument.
165 /// </remarks>
166 internal static void AutoProvider(OpenIdProvider provider) {
167 IRequest request;
168 while ((request = provider.GetRequest()) != null) {
169 if (!request.IsResponseReady) {
170 var authRequest = (DotNetOpenAuth.OpenId.Provider.IAuthenticationRequest)request;
171 var scenario = (Scenarios)Enum.Parse(typeof(Scenarios), new Uri(authRequest.LocalIdentifier).AbsolutePath.TrimStart('/'));
172 switch (scenario) {
173 case TestSupport.Scenarios.AutoApproval:
174 authRequest.IsAuthenticated = true;
175 break;
176 case Scenarios.AutoApprovalAddFragment:
177 authRequest.SetClaimedIdentifierFragment("frag");
178 authRequest.IsAuthenticated = true;
179 break;
180 case TestSupport.Scenarios.ApproveOnSetup:
181 authRequest.IsAuthenticated = !authRequest.Immediate;
182 break;
183 case Scenarios.AlwaysDeny:
184 authRequest.IsAuthenticated = false;
185 break;
186 default:
187 // All other scenarios are done programmatically only.
188 throw new InvalidOperationException("Unrecognized scenario");
192 request.Response.Send();
196 ////internal static UriIdentifier GetDirectedIdentityUrl(Scenarios scenario, ProtocolVersion providerVersion) {
197 //// return GetDirectedIdentityUrl(scenario, providerVersion, false);
198 ////}
200 ////internal static UriIdentifier GetDirectedIdentityUrl(Scenarios scenario, ProtocolVersion providerVersion, bool useSsl) {
201 //// return new UriIdentifier(GetFullUrl("/" + DirectedIdentityPage, new Dictionary<string, string> {
202 //// { "user", scenario.ToString() },
203 //// { "version", providerVersion.ToString() },
204 //// }, useSsl));
205 ////}
207 ////internal static IRelyingPartyApplicationStore RelyingPartyStore;
208 ////internal static IProviderAssociationStore ProviderStore;
209 /////// <summary>
210 /////// Generates a new, stateful <see cref="OpenIdRelyingParty"/> whose direct messages
211 /////// will be automatically handled by an internal <see cref="OpenIdProvider"/>
212 /////// that uses the shared <see cref="ProviderStore"/>.
213 /////// </summary>
214 ////internal static OpenIdRelyingParty CreateRelyingParty(NameValueCollection fields) {
215 //// return CreateRelyingParty(RelyingPartyStore, null, fields);
216 ////}
217 ////internal static OpenIdRelyingParty CreateRelyingParty(IRelyingPartyApplicationStore store, NameValueCollection fields) {
218 //// return CreateRelyingParty(store, null, fields);
219 ////}
220 /////// <summary>
221 /////// Generates a new <see cref="OpenIdRelyingParty"/> whose direct messages
222 /////// will be automatically handled by an internal <see cref="OpenIdProvider"/>
223 /////// that uses the shared <see cref="ProviderStore"/>.
224 /////// </summary>
225 ////internal static OpenIdRelyingParty CreateRelyingParty(IRelyingPartyApplicationStore store, Uri requestUrl, NameValueCollection fields) {
226 //// var rp = new OpenIdRelyingParty(store, requestUrl ?? GetFullUrl(ConsumerPage), fields ?? new NameValueCollection());
227 //// if (fields == null || fields.Count == 0) {
228 //// Assert.IsNull(rp.Response);
229 //// }
230 //// rp.DirectMessageChannel = new DirectMessageTestRedirector(ProviderStore);
231 //// return rp;
232 ////}
233 ////internal static DotNetOpenId.RelyingParty.IAuthenticationRequest CreateRelyingPartyRequest(bool stateless, Scenarios scenario, ProtocolVersion version, bool useSsl) {
234 //// // Publish RP discovery information
235 //// MockHttpRequest.RegisterMockRPDiscovery();
237 //// var rp = TestSupport.CreateRelyingParty(stateless ? null : RelyingPartyStore, null);
238 //// var rpReq = rp.CreateRequest(TestSupport.GetMockIdentifier(scenario, version, useSsl), Realm, ReturnTo);
240 //// // Sidetrack: verify URLs and other default properties
241 //// {
242 //// Assert.AreEqual(AuthenticationRequestMode.Setup, rpReq.Mode);
243 //// Assert.AreEqual(Realm, rpReq.Realm);
244 //// Assert.AreEqual(ReturnTo, rpReq.ReturnToUrl);
245 //// }
247 //// return rpReq;
248 ////}
249 /////// <summary>
250 /////// Generates a new <see cref="OpenIdRelyingParty"/> ready to process a
251 /////// response from an <see cref="OpenIdProvider"/>.
252 /////// </summary>
253 ////internal static IAuthenticationResponse CreateRelyingPartyResponse(IRelyingPartyApplicationStore store, IResponse providerResponse) {
254 //// return CreateRelyingPartyResponse(store, providerResponse, false);
255 ////}
256 ////internal static IAuthenticationResponse CreateRelyingPartyResponse(IRelyingPartyApplicationStore store, IResponse providerResponse, bool requireSsl) {
257 //// if (providerResponse == null) throw new ArgumentNullException("providerResponse");
259 //// var opAuthWebResponse = (Response)providerResponse;
260 //// var opAuthResponse = (EncodableResponse)opAuthWebResponse.EncodableMessage;
261 //// var rp = CreateRelyingParty(store, opAuthResponse.RedirectUrl,
262 //// opAuthResponse.EncodedFields.ToNameValueCollection());
263 //// rp.Settings.RequireSsl = requireSsl;
264 //// // Get the response now, before trying the replay attack. The Response
265 //// // property is lazily-evaluated, so the replay attack can be evaluated first
266 //// // and pass, while this one that SUPPOSED to pass fails, if we don't force it now.
267 //// var response = rp.Response;
269 //// // Side-track to test for replay attack while we're at it.
270 //// // This simulates a network sniffing user who caught the
271 //// // authenticating query en route to either the user agent or
272 //// // the consumer, and tries the same query to the consumer in an
273 //// // attempt to spoof the identity of the authenticating user.
274 //// try {
275 //// Logger.Info("Attempting replay attack...");
276 //// var replayRP = CreateRelyingParty(store, opAuthResponse.RedirectUrl,
277 //// opAuthResponse.EncodedFields.ToNameValueCollection());
278 //// replayRP.Settings.RequireSsl = requireSsl;
279 //// Assert.AreNotEqual(AuthenticationStatus.Authenticated, replayRP.Response.Status, "Replay attack succeeded!");
280 //// } catch (OpenIdException) { // nonce already used
281 //// // another way to pass
282 //// }
284 //// // Return the result of the initial response (not the replay attack one).
285 //// return response;
286 ////}
287 /////// <summary>
288 /////// Generates a new <see cref="OpenIdProvider"/> that uses the shared
289 /////// store in <see cref="ProviderStore"/>.
290 /////// </summary>
291 ////internal static OpenIdProvider CreateProvider(NameValueCollection fields) {
292 //// return CreateProvider(fields, false);
293 ////}
294 ////internal static OpenIdProvider CreateProvider(NameValueCollection fields, bool useSsl) {
295 //// Protocol protocol = fields != null ? Protocol.Detect(fields.ToDictionary()) : Protocol.V20;
296 //// Uri opEndpoint = GetFullUrl(ProviderPage, null, useSsl);
297 //// var provider = new OpenIdProvider(ProviderStore, opEndpoint, opEndpoint, fields ?? new NameValueCollection());
298 //// return provider;
299 ////}
300 ////internal static OpenIdProvider CreateProviderForRequest(DotNetOpenId.RelyingParty.IAuthenticationRequest request) {
301 //// IResponse relyingPartyAuthenticationRequest = request.RedirectingResponse;
302 //// var rpWebMessageToOP = (Response)relyingPartyAuthenticationRequest;
303 //// var rpMessageToOP = (IndirectMessageRequest)rpWebMessageToOP.EncodableMessage;
304 //// var opEndpoint = (ServiceEndpoint)request.Provider;
305 //// var provider = new OpenIdProvider(ProviderStore, opEndpoint.ProviderEndpoint,
306 //// opEndpoint.ProviderEndpoint, rpMessageToOP.EncodedFields.ToNameValueCollection());
307 //// return provider;
308 ////}
309 ////internal static IResponse CreateProviderResponseToRequest(
310 //// DotNetOpenId.RelyingParty.IAuthenticationRequest request,
311 //// Action<DotNetOpenId.Provider.IAuthenticationRequest> prepareProviderResponse) {
312 //// {
313 //// // Sidetrack: Verify the return_to and realm URLs
314 //// var consumerToProviderQuery = HttpUtility.ParseQueryString(request.RedirectingResponse.ExtractUrl().Query);
315 //// Protocol protocol = Protocol.Detect(consumerToProviderQuery.ToDictionary());
316 //// Assert.IsTrue(consumerToProviderQuery[protocol.openid.return_to].StartsWith(request.ReturnToUrl.AbsoluteUri, StringComparison.Ordinal));
317 //// Assert.AreEqual(request.Realm.ToString(), consumerToProviderQuery[protocol.openid.Realm]);
318 //// }
320 //// var op = TestSupport.CreateProviderForRequest(request);
321 //// var opReq = (DotNetOpenId.Provider.IAuthenticationRequest)op.Request;
322 //// prepareProviderResponse(opReq);
323 //// Assert.IsTrue(opReq.IsResponseReady);
324 //// return opReq.Response;
325 ////}
326 ////internal static IAuthenticationResponse CreateRelyingPartyResponseThroughProvider(
327 //// DotNetOpenId.RelyingParty.IAuthenticationRequest request,
328 //// Action<DotNetOpenId.Provider.IAuthenticationRequest> providerAction) {
329 //// var rpReq = (AuthenticationRequest)request;
330 //// var opResponse = CreateProviderResponseToRequest(rpReq, providerAction);
331 //// // Be careful to use whatever store the original RP was using.
332 //// var rp = CreateRelyingPartyResponse(rpReq.RelyingParty.Store, opResponse,
333 //// ((AuthenticationRequest)request).RelyingParty.Settings.RequireSsl);
334 //// Assert.IsNotNull(rp);
335 //// return rp;
336 ////}
338 ////[SetUp]
339 ////public void SetUp() {
340 //// log4net.Config.XmlConfigurator.Configure(Assembly.GetExecutingAssembly().GetManifestResourceStream("DotNetOpenId.Test.Logging.config"));
342 //// ResetStores();
343 ////}
345 ////[TearDown]
346 ////public void TearDown() {
347 //// log4net.LogManager.Shutdown();
348 ////}
350 ////internal static void ResetStores() {
351 //// RelyingPartyStore = new ApplicationMemoryStore();
352 //// ProviderStore = new ProviderMemoryStore();
353 ////}
355 ////internal static void SetAuthenticationFromScenario(Scenarios scenario, DotNetOpenId.Provider.IAuthenticationRequest request) {
356 //// Assert.IsTrue(request.IsReturnUrlDiscoverable);
357 //// switch (scenario) {
358 //// case TestSupport.Scenarios.ExtensionFullCooperation:
359 //// case TestSupport.Scenarios.ExtensionPartialCooperation:
360 //// case TestSupport.Scenarios.AutoApproval:
361 //// // immediately approve
362 //// request.IsAuthenticated = true;
363 //// break;
364 //// case TestSupport.Scenarios.AutoApprovalAddFragment:
365 //// request.SetClaimedIdentifierFragment("frag");
366 //// request.IsAuthenticated = true;
367 //// break;
368 //// case TestSupport.Scenarios.ApproveOnSetup:
369 //// request.IsAuthenticated = !request.Immediate;
370 //// break;
371 //// case TestSupport.Scenarios.AlwaysDeny:
372 //// request.IsAuthenticated = false;
373 //// break;
374 //// default:
375 //// throw new InvalidOperationException("Unrecognized scenario");
376 //// }
377 ////}
379 /////// <summary>
380 /////// Uses an RPs stored association to resign an altered message from a Provider,
381 /////// to simulate a Provider that deliberately sent a bad message in an attempt
382 /////// to thwart RP security.
383 /////// </summary>
384 ////internal static void Resign(NameValueCollection nvc, IRelyingPartyApplicationStore store) {
385 //// Debug.Assert(nvc != null);
386 //// Debug.Assert(store != null);
387 //// var dict = Util.NameValueCollectionToDictionary(nvc);
388 //// Protocol protocol = Protocol.Detect(dict);
389 //// Uri providerEndpoint = new Uri(nvc[protocol.openid.op_endpoint]);
390 //// string assoc_handle = nvc[protocol.openid.assoc_handle];
391 //// Association assoc = store.GetAssociation(providerEndpoint, assoc_handle);
392 //// Debug.Assert(assoc != null, "Association not found in RP's store. Maybe you're communicating with a hosted OP instead of the TestSupport one?");
393 //// IList<string> signed = nvc[protocol.openid.signed].Split(',');
394 //// var subsetDictionary = new Dictionary<string, string>();
395 //// foreach (string signedKey in signed) {
396 //// string keyName = protocol.openid.Prefix + signedKey;
397 //// subsetDictionary.Add(signedKey, dict[keyName]);
398 //// }
399 //// nvc[protocol.openid.sig] = Convert.ToBase64String(assoc.Sign(subsetDictionary, signed));
400 ////}
402 ////public static IAssociationStore<AssociationRelyingPartyType> ProviderStoreContext {
403 //// get {
404 //// return DotNetOpenId.Provider.OpenIdProvider.HttpApplicationStore;
405 //// }
406 ////}
407 ////internal static MockIdentifier GetMockOPIdentifier(Scenarios scenario, UriIdentifier expectedClaimedId) {
408 //// return GetMockOPIdentifier(scenario, expectedClaimedId, false, false);
409 ////}
410 ////internal static MockIdentifier GetMockOPIdentifier(Scenarios scenario, UriIdentifier expectedClaimedId, bool useSslOpIdentifier, bool useSslProviderEndpoint) {
411 //// var fields = new Dictionary<string, string> {
412 //// { "user", scenario.ToString() },
413 ////};
414 //// Uri opEndpoint = GetFullUrl(DirectedProviderEndpoint, fields, useSslProviderEndpoint);
415 //// Uri opIdentifier = GetOPIdentityUrl(scenario, useSslOpIdentifier);
416 //// ServiceEndpoint se = ServiceEndpoint.CreateForProviderIdentifier(
417 //// opIdentifier,
418 //// opEndpoint,
419 //// new string[] { Protocol.V20.OPIdentifierServiceTypeURI },
420 //// 10,
421 //// 10);
423 //// // Register the Claimed Identifier that directed identity will choose so that RP
424 //// // discovery on that identifier can be mocked up.
425 //// MockHttpRequest.RegisterMockXrdsResponse(expectedClaimedId, se);
427 //// return new MockIdentifier(opIdentifier, new ServiceEndpoint[] { se });
428 ////}