//----------------------------------------------------------------------- // // Copyright (c) Outercurve Foundation. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth.Test.OpenId.ChannelElements { using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Text.RegularExpressions; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.ChannelElements; using DotNetOpenAuth.OpenId.Extensions; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; using DotNetOpenAuth.Test.Mocks; using DotNetOpenAuth.Test.OpenId.Extensions; using NUnit.Framework; [TestFixture] public class ExtensionsBindingElementTests : OpenIdTestBase { private StandardOpenIdExtensionFactory factory; private ExtensionsBindingElement rpElement; private IProtocolMessageWithExtensions request; [SetUp] public override void SetUp() { base.SetUp(); this.factory = new StandardOpenIdExtensionFactory(); this.factory.RegisterExtension(MockOpenIdExtension.Factory); this.rpElement = new ExtensionsBindingElementRelyingParty(this.factory, new RelyingPartySecuritySettings()); this.rpElement.Channel = new TestChannel(this.MessageDescriptions); this.request = new SignedResponseRequest(Protocol.Default.Version, OpenIdTestBase.OPUri, AuthenticationRequestMode.Immediate); } [Test] public void RoundTripFullStackTest() { IOpenIdMessageExtension request = new MockOpenIdExtension("requestPart", "requestData"); IOpenIdMessageExtension response = new MockOpenIdExtension("responsePart", "responseData"); ExtensionTestUtilities.Roundtrip( Protocol.Default, new IOpenIdMessageExtension[] { request }, new IOpenIdMessageExtension[] { response }); } [Test] public void ExtensionFactory() { Assert.AreSame(this.factory, this.rpElement.ExtensionFactory); } [Test] public void PrepareMessageForSendingNull() { Assert.IsNull(this.rpElement.ProcessOutgoingMessage(null)); } /// /// Verifies that false is returned when a non-extendable message is sent. /// [Test] public void PrepareMessageForSendingNonExtendableMessage() { IProtocolMessage request = new AssociateDiffieHellmanRequest(Protocol.Default.Version, OpenIdTestBase.OPUri); Assert.IsNull(this.rpElement.ProcessOutgoingMessage(request)); } [Test] public void PrepareMessageForSending() { this.request.Extensions.Add(new MockOpenIdExtension("part", "extra")); Assert.IsNotNull(this.rpElement.ProcessOutgoingMessage(this.request)); string alias = GetAliases(this.request.ExtraData).Single(); Assert.AreEqual(MockOpenIdExtension.MockTypeUri, this.request.ExtraData["openid.ns." + alias]); Assert.AreEqual("part", this.request.ExtraData["openid." + alias + ".Part"]); Assert.AreEqual("extra", this.request.ExtraData["openid." + alias + ".data"]); } [Test] public void PrepareMessageForReceiving() { this.request.ExtraData["openid.ns.mock"] = MockOpenIdExtension.MockTypeUri; this.request.ExtraData["openid.mock.Part"] = "part"; this.request.ExtraData["openid.mock.data"] = "extra"; Assert.IsNotNull(this.rpElement.ProcessIncomingMessage(this.request)); MockOpenIdExtension ext = this.request.Extensions.OfType().Single(); Assert.AreEqual("part", ext.Part); Assert.AreEqual("extra", ext.Data); } /// /// Verifies that extension responses are included in the OP's signature. /// [Test] public void ExtensionResponsesAreSigned() { Protocol protocol = Protocol.Default; var op = this.CreateProvider(); IndirectSignedResponse response = this.CreateResponseWithExtensions(protocol); op.Channel.PrepareResponse(response); ITamperResistantOpenIdMessage signedResponse = (ITamperResistantOpenIdMessage)response; string extensionAliasKey = signedResponse.ExtraData.Single(kv => kv.Value == MockOpenIdExtension.MockTypeUri).Key; Assert.IsTrue(extensionAliasKey.StartsWith("openid.ns.")); string extensionAlias = extensionAliasKey.Substring("openid.ns.".Length); // Make sure that the extension members and the alias=namespace declaration are all signed. Assert.IsNotNull(signedResponse.SignedParameterOrder); string[] signedParameters = signedResponse.SignedParameterOrder.Split(','); Assert.IsTrue(signedParameters.Contains(extensionAlias + ".Part")); Assert.IsTrue(signedParameters.Contains(extensionAlias + ".data")); Assert.IsTrue(signedParameters.Contains("ns." + extensionAlias)); } /// /// Verifies that unsigned extension responses (where any or all fields are unsigned) are ignored. /// [Test] public void ExtensionsAreIdentifiedAsSignedOrUnsigned() { Protocol protocol = Protocol.Default; OpenIdCoordinator coordinator = new OpenIdCoordinator( rp => { RegisterMockExtension(rp.Channel); var response = rp.Channel.ReadFromRequest(); Assert.AreEqual(1, response.SignedExtensions.Count(), "Signed extension should have been received."); Assert.AreEqual(0, response.UnsignedExtensions.Count(), "No unsigned extension should be present."); response = rp.Channel.ReadFromRequest(); Assert.AreEqual(0, response.SignedExtensions.Count(), "No signed extension should have been received."); Assert.AreEqual(1, response.UnsignedExtensions.Count(), "Unsigned extension should have been received."); }, op => { RegisterMockExtension(op.Channel); op.Channel.Respond(CreateResponseWithExtensions(protocol)); op.Respond(op.GetRequest()); // check_auth op.SecuritySettings.SignOutgoingExtensions = false; op.Channel.Respond(CreateResponseWithExtensions(protocol)); op.Respond(op.GetRequest()); // check_auth }); coordinator.Run(); } /// /// Verifies that two extensions with the same TypeURI cannot be applied to the same message. /// /// /// OpenID Authentication 2.0 section 12 states that /// "A namespace MUST NOT be assigned more than one alias in the same message". /// [Test] public void TwoExtensionsSameTypeUri() { IOpenIdMessageExtension request1 = new MockOpenIdExtension("requestPart1", "requestData1"); IOpenIdMessageExtension request2 = new MockOpenIdExtension("requestPart2", "requestData2"); try { ExtensionTestUtilities.Roundtrip( Protocol.Default, new IOpenIdMessageExtension[] { request1, request2 }, new IOpenIdMessageExtension[0]); Assert.Fail("Expected ProtocolException not thrown."); } catch (AssertionException ex) { Assert.IsInstanceOf(ex.InnerException); } } private static IEnumerable GetAliases(IDictionary extraData) { Regex regex = new Regex(@"^openid\.ns\.(\w+)"); return from key in extraData.Keys let m = regex.Match(key) where m.Success select m.Groups[1].Value; } private static void RegisterMockExtension(Channel channel) { Requires.NotNull(channel, "channel"); ExtensionTestUtilities.RegisterExtension(channel, MockOpenIdExtension.Factory); } /// /// Creates a response message with one extensions. /// /// The protocol to construct the message with. /// The message ready to send from OP to RP. private IndirectSignedResponse CreateResponseWithExtensions(Protocol protocol) { Requires.NotNull(protocol, "protocol"); IndirectSignedResponse response = new IndirectSignedResponse(protocol.Version, RPUri); response.ProviderEndpoint = OPUri; response.Extensions.Add(new MockOpenIdExtension("pv", "ev")); return response; } } }