diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2009-04-15 17:07:03 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2009-04-15 17:07:03 -0700 |
commit | ef1c63fc17bd5ae7eda4444ded592574d437d309 (patch) | |
tree | f9101be9ffc1bffe79221097e3e1e4600128b9c2 /src | |
parent | 217ac4c345ff523715c638f2869ac5cb1b4eb195 (diff) | |
parent | c925ab3d2c5697c9f5f6e57aa58a042f49cf8ecd (diff) | |
download | DotNetOpenAuth-ef1c63fc17bd5ae7eda4444ded592574d437d309.zip DotNetOpenAuth-ef1c63fc17bd5ae7eda4444ded592574d437d309.tar.gz DotNetOpenAuth-ef1c63fc17bd5ae7eda4444ded592574d437d309.tar.bz2 |
Merge branch 'v3.0'
Diffstat (limited to 'src')
33 files changed, 363 insertions, 54 deletions
diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs index f611552..2e5a9ce 100644 --- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs +++ b/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs @@ -40,6 +40,9 @@ namespace DotNetOpenAuth.Test.Mocks { /// <param name="recipient">The recipient.</param> internal CoordinatingHttpRequestInfo(MessageReceivingEndpoint recipient) { this.recipient = recipient; + if (recipient != null) { + this.Url = recipient.Location; + } if (recipient == null || (recipient.AllowedMethods & HttpDeliveryMethods.GetRequest) != 0) { this.HttpMethod = "GET"; diff --git a/src/DotNetOpenAuth.Test/Mocks/MockOpenIdExtension.cs b/src/DotNetOpenAuth.Test/Mocks/MockOpenIdExtension.cs index 86b77bc..f9d418f 100644 --- a/src/DotNetOpenAuth.Test/Mocks/MockOpenIdExtension.cs +++ b/src/DotNetOpenAuth.Test/Mocks/MockOpenIdExtension.cs @@ -15,7 +15,7 @@ namespace DotNetOpenAuth.Test.Mocks { internal class MockOpenIdExtension : IOpenIdMessageExtension { internal const string MockTypeUri = "http://mockextension"; - internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => { + internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => { if (typeUri == MockTypeUri) { return new MockOpenIdExtension(); } diff --git a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs index 24c62e1..5af1caf 100644 --- a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs @@ -21,7 +21,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { [TestClass] public class ExtensionsBindingElementTests : OpenIdTestBase { - private OpenIdExtensionFactory factory; + private StandardOpenIdExtensionFactory factory; private ExtensionsBindingElement rpElement; private IProtocolMessageWithExtensions request; @@ -29,7 +29,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { public override void SetUp() { base.SetUp(); - this.factory = new OpenIdExtensionFactory(); + this.factory = new StandardOpenIdExtensionFactory(); this.factory.RegisterExtension(MockOpenIdExtension.Factory); this.rpElement = new ExtensionsBindingElement(this.factory, new RelyingPartySecuritySettings()); this.rpElement.Channel = new TestChannel(this.MessageDescriptions); @@ -169,7 +169,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements { private static void RegisterMockExtension(Channel channel) { ErrorUtilities.VerifyArgumentNotNull(channel, "channel"); - ((OpenIdExtensionFactory)channel.BindingElements.OfType<ExtensionsBindingElement>().Single().ExtensionFactory).RegisterExtension(MockOpenIdExtension.Factory); + ExtensionTestUtilities.RegisterExtension(channel, MockOpenIdExtension.Factory); } /// <summary> diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionTestUtilities.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionTestUtilities.cs index 7792ad9..47c8ec4 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionTestUtilities.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionTestUtilities.cs @@ -70,11 +70,11 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions { coordinator.Run(); } - internal static void RegisterExtension(Channel channel, OpenIdExtensionFactory.CreateDelegate extensionFactory) { + internal static void RegisterExtension(Channel channel, StandardOpenIdExtensionFactory.CreateDelegate extensionFactory) { ErrorUtilities.VerifyArgumentNotNull(channel, "channel"); - OpenIdExtensionFactory factory = (OpenIdExtensionFactory)channel.BindingElements.OfType<ExtensionsBindingElement>().Single().ExtensionFactory; - factory.RegisterExtension(extensionFactory); + var factory = (OpenIdExtensionFactoryAggregator)channel.BindingElements.OfType<ExtensionsBindingElement>().Single().ExtensionFactory; + factory.Factories.OfType<StandardOpenIdExtensionFactory>().Single().RegisterExtension(extensionFactory); } } } diff --git a/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs b/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs index f1994d1..76a46d0 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs @@ -6,11 +6,9 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Extensions; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.Provider; using DotNetOpenAuth.OpenId.RelyingParty; @@ -54,6 +52,14 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { Assert.AreSame(newSettings, this.provider.SecuritySettings); } + [TestMethod] + public void ExtensionFactories() { + var factories = this.provider.ExtensionFactories; + Assert.IsNotNull(factories); + Assert.AreEqual(1, factories.Count); + Assert.IsInstanceOfType(factories[0], typeof(StandardOpenIdExtensionFactory)); + } + /// <summary> /// Verifies the Channel property. /// </summary> diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs index d2a75de..68bbff3 100644 --- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs @@ -8,9 +8,8 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { using System; using System.Linq; using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OpenId; - using DotNetOpenAuth.OpenId.ChannelElements; + using DotNetOpenAuth.OpenId.Extensions; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -38,6 +37,15 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty { } [TestMethod] + public void ExtensionFactories() { + var rp = new OpenIdRelyingParty(null); + var factories = rp.ExtensionFactories; + Assert.IsNotNull(factories); + Assert.AreEqual(1, factories.Count); + Assert.IsInstanceOfType(factories[0], typeof(StandardOpenIdExtensionFactory)); + } + + [TestMethod] public void CreateRequest() { var rp = this.CreateRelyingParty(); StoreAssociation(rp, OPUri, HmacShaAssociation.Create("somehandle", new byte[20], TimeSpan.FromDays(1))); diff --git a/src/DotNetOpenAuth.sln b/src/DotNetOpenAuth.sln index cf830c6..f49cd4d 100644 --- a/src/DotNetOpenAuth.sln +++ b/src/DotNetOpenAuth.sln @@ -9,7 +9,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject DotNetOpenAuth.vsmdi = DotNetOpenAuth.vsmdi LocalTestRun.testrunconfig = LocalTestRun.testrunconfig + ..\doc\README.Bin.html = ..\doc\README.Bin.html ..\doc\README.html = ..\doc\README.html + ..\samples\README.html = ..\samples\README.html EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specs", "Specs", "{CD57219F-24F4-4136-8741-6063D0D7A031}" diff --git a/src/DotNetOpenAuth/Configuration/OpenIdElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdElement.cs index d5986c6..25e067e 100644 --- a/src/DotNetOpenAuth/Configuration/OpenIdElement.cs +++ b/src/DotNetOpenAuth/Configuration/OpenIdElement.cs @@ -8,6 +8,8 @@ namespace DotNetOpenAuth.Configuration { using System; using System.Configuration; using System.Diagnostics.Contracts; + using DotNetOpenAuth.OpenId.ChannelElements; + using DotNetOpenAuth.OpenId.Messages; /// <summary> /// Represents the <openid> element in the host's .config file. @@ -15,21 +17,32 @@ namespace DotNetOpenAuth.Configuration { [ContractVerification(true)] internal class OpenIdElement : ConfigurationElement { /// <summary> - /// Gets the name of the <relyingParty> sub-element. + /// The name of the <relyingParty> sub-element. /// </summary> private const string RelyingPartyElementName = "relyingParty"; /// <summary> - /// Gets the name of the <provider> sub-element. + /// The name of the <provider> sub-element. /// </summary> private const string ProviderElementName = "provider"; /// <summary> + /// The name of the <extensions> sub-element. + /// </summary> + private const string ExtensionFactoriesElementName = "extensionFactories"; + + /// <summary> /// Gets the name of the @maxAuthenticationTime attribute. /// </summary> private const string MaxAuthenticationTimePropertyName = "maxAuthenticationTime"; /// <summary> + /// Initializes a new instance of the <see cref="OpenIdElement"/> class. + /// </summary> + internal OpenIdElement() { + } + + /// <summary> /// Gets or sets the maximum time a user can take to complete authentication. /// </summary> /// <remarks> @@ -61,5 +74,15 @@ namespace DotNetOpenAuth.Configuration { get { return (OpenIdProviderElement)this[ProviderElementName] ?? new OpenIdProviderElement(); } set { this[ProviderElementName] = value; } } + + /// <summary> + /// Gets or sets the registered OpenID extensions. + /// </summary> + [ConfigurationProperty(ExtensionFactoriesElementName, IsDefaultCollection = false)] + [ConfigurationCollection(typeof(TypeConfigurationCollection<IOpenIdMessageExtension>))] + internal TypeConfigurationCollection<IOpenIdExtensionFactory> ExtensionFactories { + get { return (TypeConfigurationCollection<IOpenIdExtensionFactory>)this[ExtensionFactoriesElementName] ?? new TypeConfigurationCollection<IOpenIdExtensionFactory>(); } + set { this[ExtensionFactoriesElementName] = value; } + } } } diff --git a/src/DotNetOpenAuth/Configuration/TypeConfigurationCollection.cs b/src/DotNetOpenAuth/Configuration/TypeConfigurationCollection.cs new file mode 100644 index 0000000..00eeb15 --- /dev/null +++ b/src/DotNetOpenAuth/Configuration/TypeConfigurationCollection.cs @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------- +// <copyright file="TypeConfigurationCollection.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Configuration { + using System; + using System.Collections.Generic; + using System.Configuration; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Text; + using DotNetOpenAuth.Messaging; + + /// <summary> + /// A collection of <see cref="TypeConfigurationElement"/>. + /// </summary> + /// <typeparam name="T">The type that all types specified in the elements must derive from.</typeparam> + [ContractVerification(true)] + internal class TypeConfigurationCollection<T> : ConfigurationElementCollection { + /// <summary> + /// Initializes a new instance of the TypeConfigurationCollection class. + /// </summary> + internal TypeConfigurationCollection() { + } + + /// <summary> + /// Initializes a new instance of the TypeConfigurationCollection class. + /// </summary> + /// <param name="elements">The elements that should be added to the collection initially.</param> + internal TypeConfigurationCollection(IEnumerable<Type> elements) { + Contract.Requires(elements != null); + ErrorUtilities.VerifyArgumentNotNull(elements, "elements"); + + foreach (Type element in elements) { + this.BaseAdd(new TypeConfigurationElement<T> { TypeName = element.FullName }); + } + } + + /// <summary> + /// Creates instances of all the types listed in the collection. + /// </summary> + /// <param name="allowInternals">if set to <c>true</c> then internal types may be instantiated.</param> + /// <returns>A sequence of instances generated from types in this collection. May be empty, but never null.</returns> + internal IEnumerable<T> CreateInstances(bool allowInternals) { + Contract.Ensures(Contract.Result<IEnumerable<T>>() != null); + return from element in this.Cast<TypeConfigurationElement<T>>() + where element.CustomType != null + select element.CreateInstance(default(T), allowInternals); + } + + /// <summary> + /// When overridden in a derived class, creates a new <see cref="T:System.Configuration.ConfigurationElement"/>. + /// </summary> + /// <returns> + /// A new <see cref="T:System.Configuration.ConfigurationElement"/>. + /// </returns> + protected override ConfigurationElement CreateNewElement() { + return new TypeConfigurationElement<T>(); + } + + /// <summary> + /// Gets the element key for a specified configuration element when overridden in a derived class. + /// </summary> + /// <param name="element">The <see cref="T:System.Configuration.ConfigurationElement"/> to return the key for.</param> + /// <returns> + /// An <see cref="T:System.Object"/> that acts as the key for the specified <see cref="T:System.Configuration.ConfigurationElement"/>. + /// </returns> + protected override object GetElementKey(ConfigurationElement element) { + Contract.Assume(element != null); // this should be Contract.Requires in base class. + return ((TypeConfigurationElement<T>)element).TypeName; + } + } +} diff --git a/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs b/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs index 5375046..022ef40 100644 --- a/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs +++ b/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs @@ -51,11 +51,23 @@ namespace DotNetOpenAuth.Configuration { /// <param name="defaultValue">The value to return if no type is given in the .config file.</param> /// <returns>The newly instantiated type.</returns> public T CreateInstance(T defaultValue) { + return this.CreateInstance(defaultValue, false); + } + + /// <summary> + /// Creates an instance of the type described in the .config file. + /// </summary> + /// <param name="defaultValue">The value to return if no type is given in the .config file.</param> + /// <param name="allowInternals">if set to <c>true</c> then internal types may be instantiated.</param> + /// <returns>The newly instantiated type.</returns> + public T CreateInstance(T defaultValue, bool allowInternals) { if (this.CustomType != null) { - // Although .NET will usually prevent our instantiating non-public types, - // it will allow our instantiation of internal types within this same assembly. - // But we don't want the host site to be able to do this, so we check ourselves. - ErrorUtilities.VerifyArgument((this.CustomType.Attributes & TypeAttributes.Public) != 0, Strings.ConfigurationTypeMustBePublic, this.CustomType.FullName); + if (!allowInternals) { + // Although .NET will usually prevent our instantiating non-public types, + // it will allow our instantiation of internal types within this same assembly. + // But we don't want the host site to be able to do this, so we check ourselves. + ErrorUtilities.VerifyArgument((this.CustomType.Attributes & TypeAttributes.Public) != 0, Strings.ConfigurationTypeMustBePublic, this.CustomType.FullName); + } return (T)Activator.CreateInstance(this.CustomType); } else { return defaultValue; diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index a15b3a8..5c13092 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -181,6 +181,7 @@ <Compile Include="Configuration\OpenIdProviderSecuritySettingsElement.cs" /> <Compile Include="Configuration\OpenIdRelyingPartyElement.cs" /> <Compile Include="Configuration\OpenIdRelyingPartySecuritySettingsElement.cs" /> + <Compile Include="Configuration\TypeConfigurationCollection.cs" /> <Compile Include="Configuration\TypeConfigurationElement.cs" /> <Compile Include="Configuration\UntrustedWebRequestElement.cs" /> <Compile Include="Configuration\HostNameOrRegexCollection.cs" /> @@ -328,7 +329,8 @@ <Compile Include="OpenId\Extensions\ExtensionBase.cs" /> <Compile Include="OpenId\Extensions\ExtensionArgumentsManager.cs" /> <Compile Include="OpenId\Extensions\IClientScriptExtensionResponse.cs" /> - <Compile Include="OpenId\Extensions\OpenIdExtensionFactory.cs" /> + <Compile Include="OpenId\Extensions\OpenIdExtensionFactoryAggregator.cs" /> + <Compile Include="OpenId\Extensions\StandardOpenIdExtensionFactory.cs" /> <Compile Include="OpenId\Extensions\ProviderAuthenticationPolicy\AuthenticationPolicies.cs" /> <Compile Include="OpenId\Extensions\ProviderAuthenticationPolicy\Constants.cs" /> <Compile Include="OpenId\Extensions\ProviderAuthenticationPolicy\DateTimeEncoder.cs" /> diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs index de6b5ec..c0a20ab 100644 --- a/src/DotNetOpenAuth/Messaging/Channel.cs +++ b/src/DotNetOpenAuth/Messaging/Channel.cs @@ -358,6 +358,11 @@ namespace DotNetOpenAuth.Messaging { /// <returns>The deserialized message, if one is found. Null otherwise.</returns> public IDirectedProtocolMessage ReadFromRequest(HttpRequestInfo httpRequest) { Contract.Requires(httpRequest != null); + ErrorUtilities.VerifyArgumentNotNull(httpRequest, "httpRequest"); + + if (Logger.Channel.IsInfoEnabled && httpRequest.UrlBeforeRewriting != null) { + Logger.Channel.InfoFormat("Scanning incoming request for messages: {0}", httpRequest.UrlBeforeRewriting.AbsoluteUri); + } IDirectedProtocolMessage requestMessage = this.ReadFromRequestCore(httpRequest); if (requestMessage != null) { Logger.Channel.DebugFormat("Incoming request received: {0}", requestMessage.GetType().Name); diff --git a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs index 418f6b2..367c14f 100644 --- a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs +++ b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs @@ -194,8 +194,9 @@ namespace DotNetOpenAuth.Messaging { /// </summary> internal Uri UrlBeforeRewriting { get { - Contract.Requires(this.Url != null); - Contract.Requires(this.RawUrl != null); + if (this.Url == null || this.RawUrl == null) { + return null; + } // We use Request.Url for the full path to the server, and modify it // with Request.RawUrl to capture both the cookieless session "directory" if it exists diff --git a/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs b/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs index 5f4936d..dee81dc 100644 --- a/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs +++ b/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs @@ -42,7 +42,11 @@ namespace DotNetOpenAuth.Messaging { this.RequestUri = requestUri; if (!string.IsNullOrEmpty(response.ContentType)) { - this.ContentType = new ContentType(response.ContentType); + try { + this.ContentType = new ContentType(response.ContentType); + } catch (FormatException) { + Logger.Messaging.ErrorFormat("HTTP response to {0} included an invalid Content-Type header value: {1}", response.ResponseUri.AbsoluteUri, response.ContentType); + } } this.ContentEncoding = string.IsNullOrEmpty(response.ContentEncoding) ? DefaultContentEncoding : response.ContentEncoding; this.FinalUri = response.ResponseUri; @@ -64,7 +68,11 @@ namespace DotNetOpenAuth.Messaging { this.RequestUri = requestUri; this.Status = statusCode; if (!string.IsNullOrEmpty(contentType)) { - this.ContentType = new ContentType(contentType); + try { + this.ContentType = new ContentType(contentType); + } catch (FormatException) { + Logger.Messaging.ErrorFormat("HTTP response to {0} included an invalid Content-Type header value: {1}", responseUri.AbsoluteUri, contentType); + } } this.ContentEncoding = string.IsNullOrEmpty(contentEncoding) ? DefaultContentEncoding : contentEncoding; this.Headers = headers; diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs index 3f72c48..cdb571b 100644 --- a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs +++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs @@ -191,6 +191,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// returned in the sequence. May be null.</param> /// <returns>A sequence of extensions in the message.</returns> private IEnumerable<IOpenIdMessageExtension> GetExtensions(IProtocolMessageWithExtensions message, bool ignoreUnsigned, Func<string, bool> extensionFilter) { + bool isAtProvider = message is SignedResponseRequest; + // We have a helper class that will do all the heavy-lifting of organizing // all the extensions, their aliases, and their parameters. var extensionManager = ExtensionArgumentsManager.CreateIncomingExtensions(this.GetExtensionsDictionary(message, ignoreUnsigned)); @@ -204,7 +206,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { var extensionData = extensionManager.GetExtensionArguments(typeUri); // Initialize this particular extension. - IOpenIdMessageExtension extension = this.ExtensionFactory.Create(typeUri, extensionData, message); + IOpenIdMessageExtension extension = this.ExtensionFactory.Create(typeUri, extensionData, message, isAtProvider); if (extension != null) { MessageDictionary extensionDictionary = this.Channel.MessageDescriptions.GetAccessor(extension); foreach (var pair in extensionData) { diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/IOpenIdExtensionFactory.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/IOpenIdExtensionFactory.cs index 4bae08f..31223a0 100644 --- a/src/DotNetOpenAuth/OpenId/ChannelElements/IOpenIdExtensionFactory.cs +++ b/src/DotNetOpenAuth/OpenId/ChannelElements/IOpenIdExtensionFactory.cs @@ -12,13 +12,29 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// <summary> /// OpenID extension factory class for creating extensions based on received Type URIs. /// </summary> - internal interface IOpenIdExtensionFactory { + /// <remarks> + /// OpenID extension factories must be registered with the library. This can be + /// done by adding a factory to <see cref="OpenIdRelyingParty.ExtensionFactories"/> + /// or <see cref="OpenIdProvider.ExtensionFactories"/>, or by adding a snippet + /// such as the following to your web.config file: + /// <example> + /// <dotNetOpenAuth> + /// <openid> + /// <extensionFactories> + /// <add type="DotNetOpenAuth.ApplicationBlock.CustomExtensions.Acme, DotNetOpenAuth.ApplicationBlock" /> + /// </extensionFactories> + /// </openid> + /// </dotNetOpenAuth> + /// </example> + /// </remarks> + public interface IOpenIdExtensionFactory { /// <summary> /// Creates a new instance of some extension based on the received extension parameters. /// </summary> /// <param name="typeUri">The type URI of the extension.</param> /// <param name="data">The parameters associated specifically with this extension.</param> /// <param name="baseMessage">The OpenID message carrying this extension.</param> + /// <param name="isProviderRole">A value indicating whether this extension is being received at the OpenID Provider.</param> /// <returns> /// An instance of <see cref="IOpenIdMessageExtension"/> if the factory recognizes /// the extension described in the input parameters; <c>null</c> otherwise. @@ -27,6 +43,6 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// This factory method need only initialize properties in the instantiated extension object /// that are not bound using <see cref="MessagePartAttribute"/>. /// </remarks> - IOpenIdMessageExtension Create(string typeUri, IDictionary<string, string> data, IProtocolMessageWithExtensions baseMessage); + IOpenIdMessageExtension Create(string typeUri, IDictionary<string, string> data, IProtocolMessageWithExtensions baseMessage, bool isProviderRole); } } diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs index d1c5db4..67fbcd4 100644 --- a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs +++ b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs @@ -134,14 +134,17 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// A value indicating whether the channel is set up /// with no functional security binding elements. /// </summary> + /// <returns>A new <see cref="OpenIdChannel"/> instance that will not perform verification on incoming messages or apply any security to outgoing messages.</returns> /// <remarks> - /// <para>A value of <c>true</c> allows the relying party to preview incoming + /// <para>A value of <c>true</c> allows the relying party to preview incoming /// messages without invalidating nonces or checking signatures.</para> - /// <para>Setting this to <c>true</c> poses a great security risk and is only + /// <para>Setting this to <c>true</c> poses a great security risk and is only /// present to support the <see cref="OpenIdAjaxTextBox"/> which needs to preview /// messages, and will validate them later.</para> /// </remarks> internal static OpenIdChannel CreateNonVerifyingChannel() { + Contract.Ensures(Contract.Result<OpenIdChannel>() != null); + return new OpenIdChannel(null, null, new OpenIdMessageFactory(), new RelyingPartySecuritySettings(), true); } @@ -333,9 +336,11 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { signingElement = new SigningBindingElement(opAssociationStore, opSecuritySettings); } + var extensionFactory = OpenIdExtensionFactoryAggregator.LoadFromConfiguration(); + List<IChannelBindingElement> elements = new List<IChannelBindingElement>(7); if (isRelyingPartyRole) { - elements.Add(new ExtensionsBindingElement(new OpenIdExtensionFactory(), rpSecuritySettings)); + elements.Add(new ExtensionsBindingElement(extensionFactory, rpSecuritySettings)); elements.Add(new BackwardCompatibilityBindingElement()); if (associationStore != null) { @@ -351,7 +356,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { elements.Add(new ReturnToSignatureBindingElement(rpAssociationStore, rpSecuritySettings)); } } else { - elements.Add(new ExtensionsBindingElement(new OpenIdExtensionFactory(), opSecuritySettings)); + elements.Add(new ExtensionsBindingElement(extensionFactory, opSecuritySettings)); // Providers must always have a nonce store. ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore"); diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchRequest.cs index 5ea04aa..a69e226 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchRequest.cs @@ -21,8 +21,8 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange { /// <summary> /// The factory method that may be used in deserialization of this message. /// </summary> - internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => { - if (typeUri == Constants.TypeUri && baseMessage is SignedResponseRequest) { + internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => { + if (typeUri == Constants.TypeUri && isProviderRole) { string mode; if (data.TryGetValue("mode", out mode) && mode == Mode) { return new FetchRequest(); diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchResponse.cs index 7f4bd77..9413e2f 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/FetchResponse.cs @@ -19,8 +19,8 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange { /// <summary> /// The factory method that may be used in deserialization of this message. /// </summary> - internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => { - if (typeUri == Constants.TypeUri && baseMessage is IndirectSignedResponse) { + internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => { + if (typeUri == Constants.TypeUri && !isProviderRole) { string mode; if (data.TryGetValue("mode", out mode) && mode == Mode) { return new FetchResponse(); diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/StoreRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/StoreRequest.cs index e3743e4..641b17a 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/StoreRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/StoreRequest.cs @@ -19,8 +19,8 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange { /// <summary> /// The factory method that may be used in deserialization of this message. /// </summary> - internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => { - if (typeUri == Constants.TypeUri && baseMessage is SignedResponseRequest) { + internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => { + if (typeUri == Constants.TypeUri && isProviderRole) { string mode; if (data.TryGetValue("mode", out mode) && mode == Mode) { return new StoreRequest(); diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/StoreResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/StoreResponse.cs index b6761b5..ba7f091 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/StoreResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/StoreResponse.cs @@ -17,8 +17,8 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange { /// <summary> /// The factory method that may be used in deserialization of this message. /// </summary> - internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => { - if (typeUri == Constants.TypeUri && baseMessage is IndirectSignedResponse) { + internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => { + if (typeUri == Constants.TypeUri && !isProviderRole) { string mode; if (data.TryGetValue("mode", out mode) && (mode == SuccessMode || mode == FailureMode)) { return new StoreResponse(); diff --git a/src/DotNetOpenAuth/OpenId/Extensions/OpenIdExtensionFactoryAggregator.cs b/src/DotNetOpenAuth/OpenId/Extensions/OpenIdExtensionFactoryAggregator.cs new file mode 100644 index 0000000..05e6687 --- /dev/null +++ b/src/DotNetOpenAuth/OpenId/Extensions/OpenIdExtensionFactoryAggregator.cs @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------- +// <copyright file="OpenIdExtensionFactoryAggregator.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId.Extensions { + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId.ChannelElements; + using DotNetOpenAuth.OpenId.Messages; + + /// <summary> + /// An OpenID extension factory that only delegates extension + /// instantiation requests to other factories. + /// </summary> + internal class OpenIdExtensionFactoryAggregator : IOpenIdExtensionFactory { + /// <summary> + /// The list of factories this factory delegates to. + /// </summary> + private List<IOpenIdExtensionFactory> factories = new List<IOpenIdExtensionFactory>(2); + + /// <summary> + /// Initializes a new instance of the <see cref="OpenIdExtensionFactoryAggregator"/> class. + /// </summary> + internal OpenIdExtensionFactoryAggregator() { + } + + /// <summary> + /// Gets the extension factories that this aggregating factory delegates to. + /// </summary> + /// <value>A list of factories. May be empty, but never null.</value> + internal IList<IOpenIdExtensionFactory> Factories { + get { return this.factories; } + } + + #region IOpenIdExtensionFactory Members + + /// <summary> + /// Creates a new instance of some extension based on the received extension parameters. + /// </summary> + /// <param name="typeUri">The type URI of the extension.</param> + /// <param name="data">The parameters associated specifically with this extension.</param> + /// <param name="baseMessage">The OpenID message carrying this extension.</param> + /// <param name="isProviderRole">A value indicating whether this extension is being received at the OpenID Provider.</param> + /// <returns> + /// An instance of <see cref="IOpenIdMessageExtension"/> if the factory recognizes + /// the extension described in the input parameters; <c>null</c> otherwise. + /// </returns> + /// <remarks> + /// This factory method need only initialize properties in the instantiated extension object + /// that are not bound using <see cref="MessagePartAttribute"/>. + /// </remarks> + public IOpenIdMessageExtension Create(string typeUri, IDictionary<string, string> data, IProtocolMessageWithExtensions baseMessage, bool isProviderRole) { + foreach (var factory in this.factories) { + IOpenIdMessageExtension result = factory.Create(typeUri, data, baseMessage, isProviderRole); + if (result != null) { + return result; + } + } + + return null; + } + + #endregion + + /// <summary> + /// Loads the default factory and additional ones given by the configuration. + /// </summary> + /// <returns>A new instance of <see cref="OpenIdExtensionFactoryAggregator"/>.</returns> + internal static OpenIdExtensionFactoryAggregator LoadFromConfiguration() { + Contract.Ensures(Contract.Result<OpenIdExtensionFactoryAggregator>() != null); + var factoriesElement = DotNetOpenAuth.Configuration.DotNetOpenAuthSection.Configuration.OpenId.ExtensionFactories; + var aggregator = new OpenIdExtensionFactoryAggregator(); + aggregator.Factories.Add(new StandardOpenIdExtensionFactory()); + aggregator.factories.AddRange(factoriesElement.CreateInstances(false)); + return aggregator; + } + } +} diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequest.cs index 5b319be..84589dc 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequest.cs @@ -18,8 +18,8 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy { /// <summary> /// The factory method that may be used in deserialization of this message. /// </summary> - internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => { - if (typeUri == Constants.TypeUri && baseMessage is SignedResponseRequest) { + internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => { + if (typeUri == Constants.TypeUri && isProviderRole) { return new PolicyRequest(); } diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs index 75965e3..4b2bcc9 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs @@ -20,8 +20,8 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy { /// <summary> /// The factory method that may be used in deserialization of this message. /// </summary> - internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => { - if (typeUri == Constants.TypeUri && baseMessage is IndirectSignedResponse) { + internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => { + if (typeUri == Constants.TypeUri && !isProviderRole) { return new PolicyResponse(); } diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs index 300827c..800a2ff 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs @@ -22,8 +22,8 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration { /// <summary> /// The factory method that may be used in deserialization of this message. /// </summary> - internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => { - if (typeUri == Constants.sreg_ns && baseMessage is SignedResponseRequest) { + internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => { + if (typeUri == Constants.sreg_ns && isProviderRole) { return new ClaimsRequest(typeUri); } diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs index 5696d43..a58c754 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs @@ -25,8 +25,8 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration { /// <summary> /// The factory method that may be used in deserialization of this message. /// </summary> - internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => { - if (typeUri == Constants.sreg_ns && baseMessage is IndirectSignedResponse) { + internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => { + if (typeUri == Constants.sreg_ns && !isProviderRole) { return new ClaimsResponse(typeUri); } diff --git a/src/DotNetOpenAuth/OpenId/Extensions/OpenIdExtensionFactory.cs b/src/DotNetOpenAuth/OpenId/Extensions/StandardOpenIdExtensionFactory.cs index 1c44282..9dda6ad 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/OpenIdExtensionFactory.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/StandardOpenIdExtensionFactory.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------- -// <copyright file="OpenIdExtensionFactory.cs" company="Andrew Arnott"> +// <copyright file="StandardOpenIdExtensionFactory.cs" company="Andrew Arnott"> // Copyright (c) Andrew Arnott. All rights reserved. // </copyright> //----------------------------------------------------------------------- @@ -17,16 +17,16 @@ namespace DotNetOpenAuth.OpenId.Extensions { /// An OpenID extension factory that supports registration so that third-party /// extensions can add themselves to this library's supported extension list. /// </summary> - internal class OpenIdExtensionFactory : IOpenIdExtensionFactory { + internal class StandardOpenIdExtensionFactory : IOpenIdExtensionFactory { /// <summary> /// A collection of the registered OpenID extensions. /// </summary> private List<CreateDelegate> registeredExtensions = new List<CreateDelegate>(); /// <summary> - /// Initializes a new instance of the <see cref="OpenIdExtensionFactory"/> class. + /// Initializes a new instance of the <see cref="StandardOpenIdExtensionFactory"/> class. /// </summary> - internal OpenIdExtensionFactory() { + internal StandardOpenIdExtensionFactory() { this.RegisterExtension(ClaimsRequest.Factory); this.RegisterExtension(ClaimsResponse.Factory); this.RegisterExtension(FetchRequest.Factory); @@ -43,11 +43,12 @@ namespace DotNetOpenAuth.OpenId.Extensions { /// <param name="typeUri">The type URI of the extension.</param> /// <param name="data">The parameters associated specifically with this extension.</param> /// <param name="baseMessage">The OpenID message carrying this extension.</param> + /// <param name="isProviderRole">A value indicating whether this extension is being received at the OpenID Provider.</param> /// <returns> /// An instance of <see cref="IOpenIdMessageExtension"/> if the factory recognizes /// the extension described in the input parameters; <c>null</c> otherwise. /// </returns> - internal delegate IOpenIdMessageExtension CreateDelegate(string typeUri, IDictionary<string, string> data, IProtocolMessageWithExtensions baseMessage); + internal delegate IOpenIdMessageExtension CreateDelegate(string typeUri, IDictionary<string, string> data, IProtocolMessageWithExtensions baseMessage, bool isProviderRole); #region IOpenIdExtensionFactory Members @@ -57,6 +58,7 @@ namespace DotNetOpenAuth.OpenId.Extensions { /// <param name="typeUri">The type URI of the extension.</param> /// <param name="data">The parameters associated specifically with this extension.</param> /// <param name="baseMessage">The OpenID message carrying this extension.</param> + /// <param name="isProviderRole">A value indicating whether this extension is being received at the OpenID Provider.</param> /// <returns> /// An instance of <see cref="IOpenIdMessageExtension"/> if the factory recognizes /// the extension described in the input parameters; <c>null</c> otherwise. @@ -65,9 +67,9 @@ namespace DotNetOpenAuth.OpenId.Extensions { /// This factory method need only initialize properties in the instantiated extension object /// that are not bound using <see cref="MessagePartAttribute"/>. /// </remarks> - public IOpenIdMessageExtension Create(string typeUri, IDictionary<string, string> data, IProtocolMessageWithExtensions baseMessage) { + public IOpenIdMessageExtension Create(string typeUri, IDictionary<string, string> data, IProtocolMessageWithExtensions baseMessage, bool isProviderRole) { foreach (var factoryMethod in this.registeredExtensions) { - IOpenIdMessageExtension result = factoryMethod(typeUri, data, baseMessage); + IOpenIdMessageExtension result = factoryMethod(typeUri, data, baseMessage, isProviderRole); if (result != null) { return result; } diff --git a/src/DotNetOpenAuth/OpenId/Messages/IOpenIdMessageExtension.cs b/src/DotNetOpenAuth/OpenId/Messages/IOpenIdMessageExtension.cs index 8a1a8e5..131b4ea 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/IOpenIdMessageExtension.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/IOpenIdMessageExtension.cs @@ -14,6 +14,11 @@ namespace DotNetOpenAuth.OpenId.Messages { /// <summary> /// The contract any OpenID extension for DotNetOpenAuth must implement. /// </summary> + /// <remarks> + /// Classes that implement this interface should be marked as + /// [<see cref="Serializable"/>] to allow serializing state servers + /// to cache messages, particularly responses. + /// </remarks> public interface IOpenIdMessageExtension : IExtensionMessage { /// <summary> /// Gets the TypeURI the extension uses in the OpenID protocol and in XRDS advertisements. diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs index d948073..3c4116a 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs +++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.3521 +// Runtime Version:2.0.50727.4912 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -578,6 +578,15 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> + /// Looks up a localized string similar to This feature is unavailable due to an unrecognized channel configuration.. + /// </summary> + internal static string UnsupportedChannelConfiguration { + get { + return ResourceManager.GetString("UnsupportedChannelConfiguration", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to The openid.user_setup_url parameter is required when sending negative assertion messages in response to immediate mode requests.. /// </summary> internal static string UserSetupUrlRequiredInImmediateNegativeResponse { diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx index 16b1780..7356c10 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx +++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx @@ -298,4 +298,7 @@ Discovered endpoint info: <data name="ResponseNotReady" xml:space="preserve"> <value>The response is not ready. Use IsResponseReady to check whether a response is ready first.</value> </data> + <data name="UnsupportedChannelConfiguration" xml:space="preserve"> + <value>This feature is unavailable due to an unrecognized channel configuration.</value> + </data> </root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs b/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs index 4271eab..3c5f00d 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs +++ b/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs @@ -15,6 +15,7 @@ namespace DotNetOpenAuth.OpenId { using System.Web.UI; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.ChannelElements; + using DotNetOpenAuth.OpenId.Extensions; /// <summary> /// A set of utilities especially useful to OpenID. @@ -126,5 +127,29 @@ namespace DotNetOpenAuth.OpenId { return fullyQualifiedRealm; } + + /// <summary> + /// Gets the extension factories from the extension aggregator on an OpenID channel. + /// </summary> + /// <param name="channel">The channel.</param> + /// <returns>The list of factories that will be used to generate extension instances.</returns> + /// <remarks> + /// This is an extension method on <see cref="Channel"/> rather than an instance + /// method on <see cref="OpenIdChannel"/> because the <see cref="OpenIdRelyingParty"/> + /// and <see cref="OpenIdProvider"/> classes don't strong-type to <see cref="OpenIdChannel"/> + /// to allow flexibility in the specific type of channel the user (or tests) + /// can plug in. + /// </remarks> + internal static IList<IOpenIdExtensionFactory> GetExtensionFactories(this Channel channel) { + Contract.Requires(channel != null); + ErrorUtilities.VerifyArgumentNotNull(channel, "channel"); + + var extensionsBindingElement = channel.BindingElements.OfType<ExtensionsBindingElement>().SingleOrDefault(); + ErrorUtilities.VerifyOperation(extensionsBindingElement != null, OpenIdStrings.UnsupportedChannelConfiguration); + IOpenIdExtensionFactory factory = extensionsBindingElement.ExtensionFactory; + var aggregator = factory as OpenIdExtensionFactoryAggregator; + ErrorUtilities.VerifyOperation(aggregator != null, OpenIdStrings.UnsupportedChannelConfiguration); + return aggregator.Factories; + } } } diff --git a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs index ee4d6cb..c3780a9 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs @@ -6,6 +6,7 @@ namespace DotNetOpenAuth.OpenId.Provider { using System; + using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; @@ -124,6 +125,13 @@ namespace DotNetOpenAuth.OpenId.Provider { } /// <summary> + /// Gets the extension factories. + /// </summary> + public IList<IOpenIdExtensionFactory> ExtensionFactories { + get { return this.Channel.GetExtensionFactories(); } + } + + /// <summary> /// Gets or sets the mechanism a host site can use to receive /// notifications of errors when communicating with remote parties. /// </summary> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs index a78c2e9..4bcaf10 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs @@ -16,6 +16,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OpenId.ChannelElements; + using DotNetOpenAuth.OpenId.Extensions; using DotNetOpenAuth.OpenId.Messages; /// <summary> @@ -201,6 +202,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Gets the extension factories. + /// </summary> + public IList<IOpenIdExtensionFactory> ExtensionFactories { + get { return this.Channel.GetExtensionFactories(); } + } + + /// <summary> /// Gets a value indicating whether this Relying Party can sign its return_to /// parameter in outgoing authentication requests. /// </summary> |