summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2009-06-17 07:40:19 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2009-06-17 07:40:19 -0700
commit12f899e678cd5fdafcd9fac84fadcf91029db88c (patch)
treedf7aa91df22832757958eaf6e80de32699ddf904 /src
parent0a8227a503dc39c1d14790dd31d9aaf1b00e81cb (diff)
downloadDotNetOpenAuth-12f899e678cd5fdafcd9fac84fadcf91029db88c.zip
DotNetOpenAuth-12f899e678cd5fdafcd9fac84fadcf91029db88c.tar.gz
DotNetOpenAuth-12f899e678cd5fdafcd9fac84fadcf91029db88c.tar.bz2
Added OpenID pluggable behaviors support, including a sample PPID generator.
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdProviderElement.cs15
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs1
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs15
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs1
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj15
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs72
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx123
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/PpidGeneration.cs99
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs12
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs10
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs32
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs12
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs43
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IRequest.cs17
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs78
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs5
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs23
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/Request.cs19
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs15
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs43
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs35
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs1
-rw-r--r--src/DotNetOpenAuth/OpenId/SecuritySettings.cs6
25 files changed, 675 insertions, 25 deletions
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdProviderElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdProviderElement.cs
index d84766b..b51ccfb 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdProviderElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdProviderElement.cs
@@ -20,6 +20,11 @@ namespace DotNetOpenAuth.Configuration {
private const string SecuritySettingsConfigName = "security";
/// <summary>
+ /// Gets the name of the &lt;behaviors&gt; sub-element.
+ /// </summary>
+ private const string BehaviorsElementName = "behaviors";
+
+ /// <summary>
/// The name of the custom store sub-element.
/// </summary>
private const string StoreConfigName = "store";
@@ -40,6 +45,16 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets or sets the special behaviors to apply.
+ /// </summary>
+ [ConfigurationProperty(BehaviorsElementName, IsDefaultCollection = false)]
+ [ConfigurationCollection(typeof(TypeConfigurationCollection<IProviderBehavior>))]
+ public TypeConfigurationCollection<IProviderBehavior> Behaviors {
+ get { return (TypeConfigurationCollection<IProviderBehavior>)this[BehaviorsElementName] ?? new TypeConfigurationCollection<IProviderBehavior>(); }
+ set { this[BehaviorsElementName] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the type to use for storing application state.
/// </summary>
[ConfigurationProperty(StoreConfigName)]
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs
index 72a6481..457955c 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs
@@ -113,6 +113,7 @@ namespace DotNetOpenAuth.Configuration {
Contract.Assume(element != null);
settings.AssociationLifetimes.Add(element.AssociationType, element.MaximumLifetime);
}
+
return settings;
}
}
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs
index e311969..cdf4fd3 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs
@@ -25,6 +25,11 @@ namespace DotNetOpenAuth.Configuration {
private const string SecuritySettingsConfigName = "security";
/// <summary>
+ /// Gets the name of the &lt;behaviors&gt; sub-element.
+ /// </summary>
+ private const string BehaviorsElementName = "behaviors";
+
+ /// <summary>
/// Initializes a new instance of the <see cref="OpenIdRelyingPartyElement"/> class.
/// </summary>
public OpenIdRelyingPartyElement() {
@@ -40,6 +45,16 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets or sets the special behaviors to apply.
+ /// </summary>
+ [ConfigurationProperty(BehaviorsElementName, IsDefaultCollection = false)]
+ [ConfigurationCollection(typeof(TypeConfigurationCollection<IRelyingPartyBehavior>))]
+ public TypeConfigurationCollection<IRelyingPartyBehavior> Behaviors {
+ get { return (TypeConfigurationCollection<IRelyingPartyBehavior>)this[BehaviorsElementName] ?? new TypeConfigurationCollection<IRelyingPartyBehavior>(); }
+ set { this[BehaviorsElementName] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the type to use for storing application state.
/// </summary>
[ConfigurationProperty(StoreConfigName)]
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs
index 437a0da..d10d9bd 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs
@@ -200,6 +200,7 @@ namespace DotNetOpenAuth.Configuration {
settings.RejectUnsolicitedAssertions = this.RejectUnsolicitedAssertions;
settings.RejectDelegatingIdentifiers = this.RejectDelegatingIdentifiers;
settings.IgnoreUnsignedExtensions = this.IgnoreUnsignedExtensions;
+
return settings;
}
}
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index 80e1a72..7da3a47 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -169,6 +169,9 @@
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
+ <Reference Include="WindowsBase">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="ComponentModel\ClaimTypeSuggestions.cs" />
@@ -314,6 +317,12 @@
<Compile Include="OpenId\Association.cs" />
<Compile Include="OpenId\AssociationMemoryStore.cs" />
<Compile Include="OpenId\Associations.cs" />
+ <Compile Include="OpenId\Behaviors\BehaviorStrings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>BehaviorStrings.resx</DependentUpon>
+ </Compile>
+ <Compile Include="OpenId\Behaviors\PpidGeneration.cs" />
<Compile Include="OpenId\ChannelElements\BackwardCompatibilityBindingElement.cs" />
<Compile Include="OpenId\ChannelElements\ExtensionsBindingElement.cs" />
<Compile Include="OpenId\ChannelElements\IOpenIdExtensionFactory.cs" />
@@ -394,6 +403,7 @@
<Compile Include="OpenId\Provider\IdentityEndpointNormalizationEventArgs.cs" />
<Compile Include="OpenId\Provider\IErrorReporting.cs" />
<Compile Include="OpenId\Provider\IProviderApplicationStore.cs" />
+ <Compile Include="OpenId\Provider\IProviderBehavior.cs" />
<Compile Include="OpenId\Provider\IRequest.cs" />
<Compile Include="OpenId\Provider\ProviderEndpoint.cs" />
<Compile Include="OpenId\Provider\RelyingPartyDiscoveryResult.cs" />
@@ -431,6 +441,7 @@
<Compile Include="OpenId\RelyingParty\AssociationPreference.cs" />
<Compile Include="OpenId\RelyingParty\AuthenticationRequest.cs" />
<Compile Include="OpenId\RelyingParty\AuthenticationRequestMode.cs" />
+ <Compile Include="OpenId\RelyingParty\IRelyingPartyBehavior.cs" />
<Compile Include="OpenId\RelyingParty\OpenIdRelyingPartyAjaxControlBase.cs" />
<Compile Include="OpenId\RelyingParty\NegativeAuthenticationResponse.cs" />
<Compile Include="OpenId\RelyingParty\OpenIdAjaxTextBox.cs" />
@@ -566,6 +577,10 @@
<EmbeddedResource Include="OpenId\RelyingParty\OpenIdRelyingPartyControlBase.js" />
</ItemGroup>
<ItemGroup>
+ <EmbeddedResource Include="OpenId\Behaviors\BehaviorStrings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>BehaviorStrings.Designer.cs</LastGenOutput>
+ </EmbeddedResource>
<EmbeddedResource Include="OpenId\RelyingParty\OpenIdRelyingPartyAjaxControlBase.js" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs
new file mode 100644
index 0000000..4166f19
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs
@@ -0,0 +1,72 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.4918
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Behaviors {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class BehaviorStrings {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal BehaviorStrings() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings", typeof(BehaviorStrings).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to No PPID provider has been configured..
+ /// </summary>
+ internal static string PpidProviderNotGiven {
+ get {
+ return ResourceManager.GetString("PpidProviderNotGiven", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx
new file mode 100644
index 0000000..23e3e73
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="PpidProviderNotGiven" xml:space="preserve">
+ <value>No PPID provider has been configured.</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/PpidGeneration.cs b/src/DotNetOpenAuth/OpenId/Behaviors/PpidGeneration.cs
new file mode 100644
index 0000000..3d2836e
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/PpidGeneration.cs
@@ -0,0 +1,99 @@
+//-----------------------------------------------------------------------
+// <copyright file="PpidGeneration.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Behaviors {
+ using System;
+ using System.Linq;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy;
+ using DotNetOpenAuth.OpenId.Provider;
+
+ /// <summary>
+ /// Offers OpenID Providers automatic PPID Claimed Identifier generation when requested
+ /// by a PAPE request.
+ /// </summary>
+ /// <remarks>
+ /// <para>PPIDs are set on positive authentication responses when the PAPE request includes
+ /// the <see cref="AuthenticationPolicies.PrivatePersonalIdentifier"/> authentication policy.</para>
+ /// <para>The static member <see cref="PpidGeneration.PpidIdentifierProvider"/> MUST
+ /// be set prior to any PPID requests come in. Typically this should be set in the
+ /// <see cref="HttpApplication.Start"/> event handler in the global.asax.cs file.</para>
+ /// </remarks>
+ [Serializable]
+ public sealed class PpidGeneration : IProviderBehavior {
+ /// <summary>
+ /// Gets or sets the provider for generating PPID identifiers.
+ /// </summary>
+ public static IDirectedIdentityIdentifierProvider PpidIdentifierProvider { get; set; }
+
+ #region IProviderBehavior Members
+
+ /// <summary>
+ /// Called when a request is received by the Provider.
+ /// </summary>
+ /// <param name="request">The incoming request.</param>
+ /// <returns>
+ /// <c>true</c> if this behavior owns this request and wants to stop other behaviors
+ /// from handling it; <c>false</c> to allow other behaviors to process this request.
+ /// </returns>
+ /// <remarks>
+ /// Implementations may set a new value to <see cref="IRequest.SecuritySettings"/> but
+ /// should not change the properties on the instance of <see cref="ProviderSecuritySettings"/>
+ /// itself as that instance may be shared across many requests.
+ /// </remarks>
+ bool IProviderBehavior.OnIncomingRequest(IRequest request) {
+ return false;
+ }
+
+ /// <summary>
+ /// Called when the Provider is preparing to send a response to an authentication request.
+ /// </summary>
+ /// <param name="request">The request that is configured to generate the outgoing response.</param>
+ /// <returns>
+ /// <c>true</c> if this behavior owns this request and wants to stop other behaviors
+ /// from handling it; <c>false</c> to allow other behaviors to process this request.
+ /// </returns>
+ bool IProviderBehavior.OnOutgoingResponse(IAuthenticationRequest request) {
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
+ bool result = false;
+
+ // Nothing to do for negative assertions.
+ if (!request.IsAuthenticated.Value) {
+ return result;
+ }
+
+ var requestInternal = (Provider.AuthenticationRequest)request;
+ var responseMessage = (IProtocolMessageWithExtensions)requestInternal.Response;
+
+ // Only apply our special policies if the RP requested it.
+ var papeRequest = request.GetExtension<PolicyRequest>();
+ if (papeRequest != null) {
+ var papeResponse = responseMessage.Extensions.OfType<PolicyResponse>().SingleOrDefault();
+ if (papeResponse == null) {
+ request.AddResponseExtension(papeResponse = new PolicyResponse());
+ }
+
+ if (papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier)) {
+ ErrorUtilities.VerifyProtocol(request.ClaimedIdentifier == request.LocalIdentifier, OpenIdStrings.DelegatingIdentifiersNotAllowed);
+
+ // Mask the user's identity with a PPID.
+ ErrorUtilities.VerifyHost(PpidIdentifierProvider != null, BehaviorStrings.PpidProviderNotGiven);
+ Identifier ppidIdentifier = PpidIdentifierProvider.GetIdentifier(request.LocalIdentifier, request.Realm);
+ requestInternal.ResetClaimedAndLocalIdentifiers(ppidIdentifier);
+
+ // Indicate that the RP is receiving a PPID claimed_id
+ if (!papeResponse.ActualPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier)) {
+ papeResponse.ActualPolicies.Add(AuthenticationPolicies.PrivatePersonalIdentifier);
+ }
+ }
+ }
+ return result;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs
index d39692c..4392cd5 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs
@@ -37,6 +37,12 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy {
public const string PhysicalMultiFactor = "http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical";
/// <summary>
+ /// Indicates that the Provider MUST use a pair-wise pseudonym for the user that is persistent
+ /// and unique across the requesting realm as the openid.claimed_id and openid.identity (see Section 4.2).
+ /// </summary>
+ public const string PrivatePersonalIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier";
+
+ /// <summary>
/// Used in a PAPE response to indicate that no PAPE authentication policies could be satisfied.
/// </summary>
/// <remarks>
diff --git a/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs
index 7a547dd..56e73da 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs
@@ -200,6 +200,18 @@ namespace DotNetOpenAuth.OpenId.Provider {
this.positiveResponse.ClaimedIdentifier = builder.Uri;
}
+ /// <summary>
+ /// Sets the Claimed and Local identifiers even after they have been initially set.
+ /// </summary>
+ /// <param name="identifier">The value to set to the <see cref="ClaimedIdentifier"/> and <see cref="LocalIdentifier"/> properties.</param>
+ internal void ResetClaimedAndLocalIdentifiers(Identifier identifier) {
+ Contract.Requires(identifier != null);
+ ErrorUtilities.VerifyArgumentNotNull(identifier, "identifier");
+
+ this.positiveResponse.ClaimedIdentifier = identifier;
+ this.positiveResponse.LocalIdentifier = identifier;
+ }
+
#endregion
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs
index 344c72f..d1d310e 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs
@@ -28,8 +28,9 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// </summary>
/// <param name="request">The request message.</param>
/// <param name="response">The response that is ready for transmittal.</param>
- internal AutoResponsiveRequest(IDirectedProtocolMessage request, IProtocolMessage response)
- : base(request) {
+ /// <param name="securitySettings">The security settings.</param>
+ internal AutoResponsiveRequest(IDirectedProtocolMessage request, IProtocolMessage response, ProviderSecuritySettings securitySettings)
+ : base(request, securitySettings) {
ErrorUtilities.VerifyArgumentNotNull(response, "response");
this.response = response;
@@ -40,8 +41,9 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// for a response to an unrecognizable request.
/// </summary>
/// <param name="response">The response that is ready for transmittal.</param>
- internal AutoResponsiveRequest(IProtocolMessage response)
- : base(IndirectResponseBase.GetVersion(response)) {
+ /// <param name="securitySettings">The security settings.</param>
+ internal AutoResponsiveRequest(IProtocolMessage response, ProviderSecuritySettings securitySettings)
+ : base(IndirectResponseBase.GetVersion(response), securitySettings) {
ErrorUtilities.VerifyArgumentNotNull(response, "response");
this.response = response;
diff --git a/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs
index b368022..4bb7d28 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs
@@ -25,12 +25,17 @@ namespace DotNetOpenAuth.OpenId.Provider {
private readonly NegativeAssertionResponse negativeResponse;
/// <summary>
+ /// A cache of the result from discovery of the Realm URL.
+ /// </summary>
+ private RelyingPartyDiscoveryResult? realmDiscoveryResult;
+
+ /// <summary>
/// Initializes a new instance of the <see cref="HostProcessedRequest"/> class.
/// </summary>
/// <param name="provider">The provider that received the request.</param>
/// <param name="request">The incoming request message.</param>
protected HostProcessedRequest(OpenIdProvider provider, SignedResponseRequest request)
- : base(request) {
+ : base(request, provider.SecuritySettings) {
Contract.Requires(provider != null);
this.negativeResponse = new NegativeAssertionResponse(request, provider.Channel);
@@ -64,6 +69,13 @@ namespace DotNetOpenAuth.OpenId.Provider {
#endregion
/// <summary>
+ /// Gets a value indicating whether realm discovery been performed.
+ /// </summary>
+ internal bool HasRealmDiscoveryBeenPerformed {
+ get { return this.realmDiscoveryResult.HasValue; }
+ }
+
+ /// <summary>
/// Gets the negative response.
/// </summary>
protected NegativeAssertionResponse NegativeResponse {
@@ -96,9 +108,25 @@ namespace DotNetOpenAuth.OpenId.Provider {
Contract.Requires(provider != null);
ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
+ if (!this.realmDiscoveryResult.HasValue) {
+ this.realmDiscoveryResult = this.IsReturnUrlDiscoverableCore(provider);
+ }
+
+ return this.realmDiscoveryResult.Value;
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether verification of the return URL claimed by the Relying Party
+ /// succeeded.
+ /// </summary>
+ /// <param name="provider">The OpenIdProvider that is performing the RP discovery.</param>
+ /// <returns>Result of realm discovery.</returns>
+ private RelyingPartyDiscoveryResult IsReturnUrlDiscoverableCore(OpenIdProvider provider) {
+ Contract.Requires(provider != null);
+
ErrorUtilities.VerifyInternal(this.Realm != null, "Realm should have been read or derived by now.");
try {
- if (provider.SecuritySettings.RequireSsl && this.Realm.Scheme != Uri.UriSchemeHttps) {
+ if (this.SecuritySettings.RequireSsl && this.Realm.Scheme != Uri.UriSchemeHttps) {
Logger.OpenId.WarnFormat("RP discovery failed because RequireSsl is true and RP discovery would begin at insecure URL {0}.", this.Realm);
return RelyingPartyDiscoveryResult.NoServiceDocument;
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs
index eee99f1..5256fdd 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs
@@ -5,6 +5,7 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId.Provider {
+ using System;
using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
@@ -80,6 +81,17 @@ namespace DotNetOpenAuth.OpenId.Provider {
#region IRequest Members
/// <summary>
+ /// Gets or sets the security settings that apply to this request.
+ /// </summary>
+ /// <value>
+ /// Defaults to the <see cref="OpenIdProvider.SecuritySettings"/> on the <see cref="OpenIdProvider"/>.
+ /// </value>
+ ProviderSecuritySettings IRequest.SecuritySettings {
+ get { throw new NotImplementedException(); }
+ set { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
/// Gets a value indicating whether the response is ready to be sent to the user agent.
/// </summary>
/// <remarks>
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs b/src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs
new file mode 100644
index 0000000..7159c02
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs
@@ -0,0 +1,43 @@
+//-----------------------------------------------------------------------
+// <copyright file="IProviderBehavior.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Provider {
+ using DotNetOpenAuth.OpenId.ChannelElements;
+
+ /// <summary>
+ /// Applies a custom security policy to certain OpenID security settings and behaviors.
+ /// </summary>
+ /// <remarks>
+ /// BEFORE MARKING THIS INTERFACE PUBLIC: it's very important that we shift the methods to be channel-level
+ /// rather than facade class level and for the OpenIdChannel to be the one to invoke these methods.
+ /// </remarks>
+ internal interface IProviderBehavior {
+ /// <summary>
+ /// Called when a request is received by the Provider.
+ /// </summary>
+ /// <param name="request">The incoming request.</param>
+ /// <returns>
+ /// <c>true</c> if this behavior owns this request and wants to stop other behaviors
+ /// from handling it; <c>false</c> to allow other behaviors to process this request.
+ /// </returns>
+ /// <remarks>
+ /// Implementations may set a new value to <see cref="IRequest.SecuritySettings"/> but
+ /// should not change the properties on the instance of <see cref="ProviderSecuritySettings"/>
+ /// itself as that instance may be shared across many requests.
+ /// </remarks>
+ bool OnIncomingRequest(IRequest request);
+
+ /// <summary>
+ /// Called when the Provider is preparing to send a response to an authentication request.
+ /// </summary>
+ /// <param name="request">The request that is configured to generate the outgoing response.</param>
+ /// <returns>
+ /// <c>true</c> if this behavior owns this request and wants to stop other behaviors
+ /// from handling it; <c>false</c> to allow other behaviors to process this request.
+ /// </returns>
+ bool OnOutgoingResponse(IAuthenticationRequest request);
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/IRequest.cs
index 9f0f633..2ca96ca 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/IRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/IRequest.cs
@@ -33,6 +33,12 @@ namespace DotNetOpenAuth.OpenId.Provider {
bool IsResponseReady { get; }
/// <summary>
+ /// Gets or sets the security settings that apply to this request.
+ /// </summary>
+ /// <value>Defaults to the <see cref="OpenIdProvider.SecuritySettings"/> on the <see cref="OpenIdProvider"/>.</value>
+ ProviderSecuritySettings SecuritySettings { get; set; }
+
+ /// <summary>
/// Adds an extension to the response to send to the relying party.
/// </summary>
/// <param name="extension">The extension to add to the response message.</param>
@@ -68,6 +74,17 @@ namespace DotNetOpenAuth.OpenId.Provider {
#region IRequest Members
/// <summary>
+ /// Gets or sets the security settings that apply to this request.
+ /// </summary>
+ /// <value>
+ /// Defaults to the <see cref="OpenIdProvider.SecuritySettings"/> on the <see cref="OpenIdProvider"/>.
+ /// </value>
+ ProviderSecuritySettings IRequest.SecuritySettings {
+ get { throw new NotImplementedException(); }
+ set { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
/// Gets a value indicating whether the response is ready to be sent to the user agent.
/// </summary>
/// <value></value>
diff --git a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
index 0ec97ff..58b6887 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.Provider {
using System;
using System.Collections.Generic;
+ using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
@@ -31,6 +32,11 @@ namespace DotNetOpenAuth.OpenId.Provider {
private const string ApplicationStoreKey = "DotNetOpenAuth.OpenId.Provider.OpenIdProvider.ApplicationStore";
/// <summary>
+ /// Backing store for the <see cref="Behaviors"/> property.
+ /// </summary>
+ private readonly Collection<IProviderBehavior> behaviors = new Collection<IProviderBehavior>();
+
+ /// <summary>
/// Backing field for the <see cref="SecuritySettings"/> property.
/// </summary>
private ProviderSecuritySettings securitySettings;
@@ -73,6 +79,10 @@ namespace DotNetOpenAuth.OpenId.Provider {
this.AssociationStore = associationStore;
this.SecuritySettings = DotNetOpenAuthSection.Configuration.OpenId.Provider.SecuritySettings.CreateSecuritySettings();
+ foreach (var behavior in DotNetOpenAuthSection.Configuration.OpenId.Provider.Behaviors.CreateInstances(false)) {
+ this.behaviors.Add(behavior);
+ }
+
this.Channel = new OpenIdChannel(this.AssociationStore, nonceStore, this.SecuritySettings);
}
@@ -138,6 +148,13 @@ namespace DotNetOpenAuth.OpenId.Provider {
public IErrorReporting ErrorReporting { get; set; }
/// <summary>
+ /// Gets a list of custom behaviors to apply to OpenID actions.
+ /// </summary>
+ internal ICollection<IProviderBehavior> Behaviors {
+ get { return this.behaviors; }
+ }
+
+ /// <summary>
/// Gets the association store.
/// </summary>
internal IAssociationStore<AssociationRelyingPartyType> AssociationStore { get; private set; }
@@ -199,24 +216,43 @@ namespace DotNetOpenAuth.OpenId.Provider {
ErrorUtilities.ThrowProtocol(MessagingStrings.UnexpectedMessageReceivedOfMany);
}
+ IRequest result = null;
+
var checkIdMessage = incomingMessage as CheckIdRequest;
if (checkIdMessage != null) {
- return new AuthenticationRequest(this, checkIdMessage);
+ result = new AuthenticationRequest(this, checkIdMessage);
+ }
+
+ if (result == null) {
+ var extensionOnlyRequest = incomingMessage as SignedResponseRequest;
+ if (extensionOnlyRequest != null) {
+ result = new AnonymousRequest(this, extensionOnlyRequest);
+ }
}
- var extensionOnlyRequest = incomingMessage as SignedResponseRequest;
- if (extensionOnlyRequest != null) {
- return new AnonymousRequest(this, extensionOnlyRequest);
+ if (result == null) {
+ var checkAuthMessage = incomingMessage as CheckAuthenticationRequest;
+ if (checkAuthMessage != null) {
+ result = new AutoResponsiveRequest(incomingMessage, new CheckAuthenticationResponse(checkAuthMessage, this), this.SecuritySettings);
+ }
}
- var checkAuthMessage = incomingMessage as CheckAuthenticationRequest;
- if (checkAuthMessage != null) {
- return new AutoResponsiveRequest(incomingMessage, new CheckAuthenticationResponse(checkAuthMessage, this));
+ if (result == null) {
+ var associateMessage = incomingMessage as AssociateRequest;
+ if (associateMessage != null) {
+ result = new AutoResponsiveRequest(incomingMessage, associateMessage.CreateResponse(this.AssociationStore, this.SecuritySettings), this.SecuritySettings);
+ }
}
- var associateMessage = incomingMessage as AssociateRequest;
- if (associateMessage != null) {
- return new AutoResponsiveRequest(incomingMessage, associateMessage.CreateResponse(this.AssociationStore, this.SecuritySettings));
+ if (result != null) {
+ foreach (var behavior in this.Behaviors) {
+ if (behavior.OnIncomingRequest(result)) {
+ // This behavior matched this request.
+ break;
+ }
+ }
+
+ return result;
}
throw ErrorUtilities.ThrowProtocol(MessagingStrings.UnexpectedMessageReceivedOfMany);
@@ -248,6 +284,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
Contract.Requires(((Request)request).IsResponseReady);
ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ this.ApplyBehaviorsToResponse(request);
Request requestInternal = (Request)request;
this.Channel.Send(requestInternal.Response);
}
@@ -264,6 +301,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
Contract.Requires(((Request)request).IsResponseReady);
ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ this.ApplyBehaviorsToResponse(request);
Request requestInternal = (Request)request;
return this.Channel.PrepareResponse(requestInternal.Response);
}
@@ -391,6 +429,22 @@ namespace DotNetOpenAuth.OpenId.Provider {
#endregion
/// <summary>
+ /// Applies all behaviors to the response message.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ private void ApplyBehaviorsToResponse(IRequest request) {
+ var authRequest = request as IAuthenticationRequest;
+ if (authRequest != null) {
+ foreach (var behavior in this.Behaviors) {
+ if (behavior.OnOutgoingResponse(authRequest)) {
+ // This behavior matched this request.
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
/// Prepares the return value for the GetRequest method in the event of an exception.
/// </summary>
/// <param name="ex">The exception that forms the basis of the error response. Must not be null.</param>
@@ -445,9 +499,9 @@ namespace DotNetOpenAuth.OpenId.Provider {
}
if (incomingMessage != null) {
- return new AutoResponsiveRequest(incomingMessage, errorMessage);
+ return new AutoResponsiveRequest(incomingMessage, errorMessage, this.SecuritySettings);
} else {
- return new AutoResponsiveRequest(errorMessage);
+ return new AutoResponsiveRequest(errorMessage, this.SecuritySettings);
}
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs b/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs
index 8372b8f..43b258c 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs
@@ -123,6 +123,11 @@ namespace DotNetOpenAuth.OpenId.Provider {
ErrorUtilities.VerifyArgumentNotNull(localIdentifier, "localIdentifier");
ErrorUtilities.VerifyArgumentNotNull(relyingPartyRealm, "relyingPartyRealm");
+ if (localIdentifier.ToString().StartsWith(this.BaseIdentifier.AbsoluteUri, StringComparison.Ordinal)) {
+ Logger.OpenId.Warn("Trying to generate a PPID from a PPID. Returning original PPID.");
+ return new Uri(localIdentifier);
+ }
+
byte[] salt = this.GetHashSaltForLocalIdentifier(localIdentifier);
string valueToHash = localIdentifier + "#";
switch (this.PairwiseUnique) {
diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs
index 4568dc2..876e412 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs
@@ -7,11 +7,15 @@
namespace DotNetOpenAuth.OpenId.Provider {
using System;
using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Collections.Specialized;
+ using System.Linq;
using DotNetOpenAuth.Messaging;
/// <summary>
/// Security settings that are applicable to providers.
/// </summary>
+ [Serializable]
public sealed class ProviderSecuritySettings : SecuritySettings {
/// <summary>
/// The default value for the <see cref="ProtectDownlevelReplayAttacks"/> property.
@@ -81,5 +85,24 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// needed for testing the RP's rejection of unsigned extensions.
/// </remarks>
internal bool SignOutgoingExtensions { get; set; }
+
+ /// <summary>
+ /// Creates a deep clone of this instance.
+ /// </summary>
+ /// <returns>A new instance that is a deep clone of this instance.</returns>
+ internal ProviderSecuritySettings Clone() {
+ var securitySettings = new ProviderSecuritySettings();
+ foreach (var pair in this.AssociationLifetimes) {
+ securitySettings.AssociationLifetimes.Add(pair);
+ }
+
+ securitySettings.MaximumHashBitLength = this.MaximumHashBitLength;
+ securitySettings.MinimumHashBitLength = this.MinimumHashBitLength;
+ securitySettings.ProtectDownlevelReplayAttacks = this.ProtectDownlevelReplayAttacks;
+ securitySettings.RequireSsl = this.RequireSsl;
+ securitySettings.SignOutgoingExtensions = this.SignOutgoingExtensions;
+
+ return securitySettings;
+ }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/Request.cs b/src/DotNetOpenAuth/OpenId/Provider/Request.cs
index 1c5ad02..4c2ee98 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/Request.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/Request.cs
@@ -52,11 +52,15 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// Initializes a new instance of the <see cref="Request"/> class.
/// </summary>
/// <param name="request">The incoming request message.</param>
- protected Request(IDirectedProtocolMessage request) {
+ /// <param name="securitySettings">The security settings from the channel.</param>
+ protected Request(IDirectedProtocolMessage request, ProviderSecuritySettings securitySettings) {
Contract.Requires(request != null);
+ Contract.Requires(securitySettings != null);
ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
this.request = request;
+ this.SecuritySettings = securitySettings;
this.protocolVersion = this.request.Version;
this.extensibleMessage = request as IProtocolMessageWithExtensions;
}
@@ -65,11 +69,15 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// Initializes a new instance of the <see cref="Request"/> class.
/// </summary>
/// <param name="version">The version.</param>
- protected Request(Version version) {
+ /// <param name="securitySettings">The security settings.</param>
+ protected Request(Version version, ProviderSecuritySettings securitySettings) {
Contract.Requires(version != null);
+ Contract.Requires(securitySettings != null);
ErrorUtilities.VerifyArgumentNotNull(version, "version");
+ ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
this.protocolVersion = version;
+ this.SecuritySettings = securitySettings;
}
#region IRequest Members
@@ -77,7 +85,6 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <summary>
/// Gets a value indicating whether the response is ready to be sent to the user agent.
/// </summary>
- /// <value></value>
/// <remarks>
/// This property returns false if there are properties that must be set on this
/// request instance before the response can be sent.
@@ -85,6 +92,12 @@ namespace DotNetOpenAuth.OpenId.Provider {
public abstract bool IsResponseReady { get; }
/// <summary>
+ /// Gets or sets the security settings that apply to this request.
+ /// </summary>
+ /// <value>Defaults to the <see cref="OpenIdProvider.SecuritySettings"/> on the <see cref="OpenIdProvider"/>.</value>
+ public ProviderSecuritySettings SecuritySettings { get; set; }
+
+ /// <summary>
/// Gets the response to send to the user agent.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if <see cref="IsResponseReady"/> is <c>false</c>.</exception>
diff --git a/src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs b/src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs
index ab84289..b94b37d 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs
@@ -20,7 +20,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <summary>
/// Prevents a default instance of the <see cref="RequestContract"/> class from being created.
/// </summary>
- private RequestContract() : base((Version)null) {
+ private RequestContract() : base((Version)null, null) {
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
index 25c4a15..19db0fa 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
@@ -89,7 +89,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <value></value>
public OutgoingWebResponse RedirectingResponse {
- get { return this.RelyingParty.Channel.PrepareResponse(this.CreateRequestMessage()); }
+ get {
+ foreach (var behavior in this.RelyingParty.Behaviors) {
+ behavior.OnOutgoingAuthenticationRequest(this);
+ }
+
+ return this.RelyingParty.Channel.PrepareResponse(this.CreateRequestMessage());
+ }
}
/// <summary>
@@ -162,6 +168,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
set { this.associationPreference = value; }
}
+ /// <summary>
+ /// Gets the extensions that have been added to th request.
+ /// </summary>
+ internal IEnumerable<IOpenIdMessageExtension> AppliedExtensions {
+ get { return this.extensions; }
+ }
+
#region IAuthenticationRequest methods
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs
new file mode 100644
index 0000000..e7c38db
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs
@@ -0,0 +1,43 @@
+//-----------------------------------------------------------------------
+// <copyright file="IRelyingPartyBehavior.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ /// <summary>
+ /// Applies a custom security policy to certain OpenID security settings and behaviors.
+ /// </summary>
+ /// <remarks>
+ /// BEFORE MARKING THIS INTERFACE PUBLIC: it's very important that we shift the methods to be channel-level
+ /// rather than facade class level and for the OpenIdChannel to be the one to invoke these methods.
+ /// </remarks>
+ internal interface IRelyingPartyBehavior {
+ /// <summary>
+ /// Applies a well known set of security requirements to a default set of security settings.
+ /// </summary>
+ /// <param name="securitySettings">The security settings to enhance with the requirements of this profile.</param>
+ /// <remarks>
+ /// Care should be taken to never decrease security when applying a profile.
+ /// Profiles should only enhance security requirements to avoid being
+ /// incompatible with each other.
+ /// </remarks>
+ void ApplySecuritySettings(RelyingPartySecuritySettings securitySettings);
+
+ /// <summary>
+ /// Called when an authentication request is about to be sent.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <remarks>
+ /// Implementations should be prepared to be called multiple times on the same outgoing message
+ /// without malfunctioning.
+ /// </remarks>
+ void OnOutgoingAuthenticationRequest(IAuthenticationRequest request);
+
+ /// <summary>
+ /// Called when an incoming positive assertion is received.
+ /// </summary>
+ /// <param name="assertion">The positive assertion.</param>
+ void OnIncomingPositiveAssertion(IAuthenticationResponse assertion);
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
index 7533000..532f033 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
using System.Collections.Generic;
+ using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
@@ -43,6 +44,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const string ApplicationStoreKey = "DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingParty.ApplicationStore";
/// <summary>
+ /// Backing store for the <see cref="Behaviors"/> property.
+ /// </summary>
+ private readonly ObservableCollection<IRelyingPartyBehavior> behaviors = new ObservableCollection<IRelyingPartyBehavior>();
+
+ /// <summary>
/// Backing field for the <see cref="SecuritySettings"/> property.
/// </summary>
private RelyingPartySecuritySettings securitySettings;
@@ -85,6 +91,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
ErrorUtilities.VerifyArgument(associationStore == null || nonceStore != null, OpenIdStrings.AssociationStoreRequiresNonceStore);
this.securitySettings = DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.SecuritySettings.CreateSecuritySettings();
+ this.behaviors.CollectionChanged += this.OnBehaviorsChanged;
+ foreach (var behavior in DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.Behaviors.CreateInstances(false)) {
+ this.behaviors.Add(behavior);
+ }
// Without a nonce store, we must rely on the Provider to protect against
// replay attacks. But only 2.0+ Providers can be expected to provide
@@ -210,6 +220,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Gets a list of custom behaviors to apply to OpenID actions.
+ /// </summary>
+ internal ICollection<IRelyingPartyBehavior> Behaviors {
+ get { return this.behaviors; }
+ }
+
+ /// <summary>
/// Gets a value indicating whether this Relying Party can sign its return_to
/// parameter in outgoing authentication requests.
/// </summary>
@@ -474,7 +491,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
NegativeAssertionResponse negativeAssertion;
IndirectSignedResponse positiveExtensionOnly;
if ((positiveAssertion = message as PositiveAssertionResponse) != null) {
- return new PositiveAuthenticationResponse(positiveAssertion, this);
+ var response = new PositiveAuthenticationResponse(positiveAssertion, this);
+ foreach (var behavior in this.Behaviors) {
+ behavior.OnIncomingPositiveAssertion(response);
+ }
+
+ return response;
} else if ((positiveExtensionOnly = message as IndirectSignedResponse) != null) {
return new PositiveAnonymousResponse(positiveExtensionOnly);
} else if ((negativeAssertion = message as NegativeAssertionResponse) != null) {
@@ -560,5 +582,16 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
}
}
+
+ /// <summary>
+ /// Called by derived classes when behaviors are added or removed.
+ /// </summary>
+ /// <param name="sender">The collection being modified.</param>
+ /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
+ private void OnBehaviorsChanged(object sender, NotifyCollectionChangedEventArgs e) {
+ foreach (IRelyingPartyBehavior profile in e.NewItems) {
+ profile.ApplySecuritySettings(this.SecuritySettings);
+ }
+ }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs
index 7d910d2..ff29498 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
using System.Collections.Generic;
+ using System.Collections.ObjectModel;
using System.Linq;
using DotNetOpenAuth.Messaging;
diff --git a/src/DotNetOpenAuth/OpenId/SecuritySettings.cs b/src/DotNetOpenAuth/OpenId/SecuritySettings.cs
index a44b5fe..d4df697 100644
--- a/src/DotNetOpenAuth/OpenId/SecuritySettings.cs
+++ b/src/DotNetOpenAuth/OpenId/SecuritySettings.cs
@@ -5,12 +5,16 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
using DotNetOpenAuth.Messaging;
/// <summary>
/// Security settings that may be applicable to both relying parties and providers.
/// </summary>
- public class SecuritySettings {
+ [Serializable]
+ public abstract class SecuritySettings {
/// <summary>
/// Gets the default minimum hash bit length.
/// </summary>