diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2009-06-02 00:21:35 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2009-06-02 16:59:52 -0700 |
commit | 66cb72fc2e8f41784f60526eb235186c8d830e6c (patch) | |
tree | 5bf11cfdcb553542b4e69253b557a2d2c6d28fac /src | |
parent | 12a5e3e0fc4d76b0d75dd740fb0c1c88031f7556 (diff) | |
download | DotNetOpenAuth-66cb72fc2e8f41784f60526eb235186c8d830e6c.zip DotNetOpenAuth-66cb72fc2e8f41784f60526eb235186c8d830e6c.tar.gz DotNetOpenAuth-66cb72fc2e8f41784f60526eb235186c8d830e6c.tar.bz2 |
Redesign of GSA security profile so that OPs can work with and without it on the same endpoint.
Diffstat (limited to 'src')
21 files changed, 253 insertions, 281 deletions
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdProviderElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdProviderElement.cs index d84766b..7674536 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 <securityProfiles> sub-element. + /// </summary> + private const string SecurityProfilesElementName = "securityProfiles"; + + /// <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 predefined security profiles to apply. + /// </summary> + [ConfigurationProperty(SecurityProfilesElementName, IsDefaultCollection = false)] + [ConfigurationCollection(typeof(TypeConfigurationCollection<IProviderSecurityProfile>))] + public TypeConfigurationCollection<IProviderSecurityProfile> SecurityProfiles { + get { return (TypeConfigurationCollection<IProviderSecurityProfile>)this[SecurityProfilesElementName] ?? new TypeConfigurationCollection<IProviderSecurityProfile>(); } + set { this[SecurityProfilesElementName] = 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 03f4786..457955c 100644 --- a/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs +++ b/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs @@ -41,11 +41,6 @@ namespace DotNetOpenAuth.Configuration { private const string RequireSslConfigName = "requireSsl"; /// <summary> - /// Gets the name of the <profiles> sub-element. - /// </summary> - private const string ProfilesElementName = "profiles"; - - /// <summary> /// Initializes a new instance of the <see cref="OpenIdProviderSecuritySettingsElement"/> class. /// </summary> public OpenIdProviderSecuritySettingsElement() { @@ -89,16 +84,6 @@ namespace DotNetOpenAuth.Configuration { } /// <summary> - /// Gets or sets the predefined security profiles to apply. - /// </summary> - [ConfigurationProperty(ProfilesElementName, IsDefaultCollection = false)] - [ConfigurationCollection(typeof(TypeConfigurationCollection<IProviderSecurityProfile>))] - public TypeConfigurationCollection<IProviderSecurityProfile> Profiles { - get { return (TypeConfigurationCollection<IProviderSecurityProfile>)this[ProfilesElementName] ?? new TypeConfigurationCollection<IProviderSecurityProfile>(); } - set { this[ProfilesElementName] = value; } - } - - /// <summary> /// Gets or sets the configured lifetimes of the various association types. /// </summary> [ConfigurationProperty(AssociationsConfigName, IsDefaultCollection = false)] @@ -129,10 +114,6 @@ namespace DotNetOpenAuth.Configuration { settings.AssociationLifetimes.Add(element.AssociationType, element.MaximumLifetime); } - foreach (var profile in this.Profiles.CreateInstances(false)) { - settings.SecurityProfiles.Add(profile); - } - return settings; } } diff --git a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs index e311969..7c1162c 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 <securityProfiles> sub-element. + /// </summary> + private const string SecurityProfilesElementName = "securityProfiles"; + + /// <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 predefined security profiles to apply. + /// </summary> + [ConfigurationProperty(SecurityProfilesElementName, IsDefaultCollection = false)] + [ConfigurationCollection(typeof(TypeConfigurationCollection<IRelyingPartySecurityProfile>))] + public TypeConfigurationCollection<IRelyingPartySecurityProfile> SecurityProfiles { + get { return (TypeConfigurationCollection<IRelyingPartySecurityProfile>)this[SecurityProfilesElementName] ?? new TypeConfigurationCollection<IRelyingPartySecurityProfile>(); } + set { this[SecurityProfilesElementName] = 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 ffc5616..d10d9bd 100644 --- a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs +++ b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs @@ -66,11 +66,6 @@ namespace DotNetOpenAuth.Configuration { private const string PrivateSecretMaximumAgeConfigName = "privateSecretMaximumAge"; /// <summary> - /// Gets the name of the <profiles> sub-element. - /// </summary> - private const string ProfilesElementName = "profiles"; - - /// <summary> /// Initializes a new instance of the <see cref="OpenIdRelyingPartySecuritySettingsElement"/> class. /// </summary> public OpenIdRelyingPartySecuritySettingsElement() { @@ -188,16 +183,6 @@ namespace DotNetOpenAuth.Configuration { } /// <summary> - /// Gets or sets the predefined security profiles to apply. - /// </summary> - [ConfigurationProperty(ProfilesElementName, IsDefaultCollection = false)] - [ConfigurationCollection(typeof(TypeConfigurationCollection<IRelyingPartySecurityProfile>))] - public TypeConfigurationCollection<IRelyingPartySecurityProfile> Profiles { - get { return (TypeConfigurationCollection<IRelyingPartySecurityProfile>)this[ProfilesElementName] ?? new TypeConfigurationCollection<IRelyingPartySecurityProfile>(); } - set { this[ProfilesElementName] = value; } - } - - /// <summary> /// Initializes a programmatically manipulatable bag of these security settings with the settings from the config file. /// </summary> /// <returns>The newly created security settings object.</returns> @@ -216,10 +201,6 @@ namespace DotNetOpenAuth.Configuration { settings.RejectDelegatingIdentifiers = this.RejectDelegatingIdentifiers; settings.IgnoreUnsignedExtensions = this.IgnoreUnsignedExtensions; - foreach (var profile in this.Profiles.CreateInstances(false)) { - settings.SecurityProfiles.Add(profile); - } - return settings; } } diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index 6c137a4..7af9539 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -368,7 +368,6 @@ <Compile Include="OpenId\Interop\AuthenticationResponseShim.cs" /> <Compile Include="OpenId\Interop\ClaimsResponseShim.cs" /> <Compile Include="OpenId\Interop\OpenIdRelyingPartyShim.cs" /> - <Compile Include="OpenId\ISecurityProfile.cs" /> <Compile Include="OpenId\Messages\CheckAuthenticationRequest.cs" /> <Compile Include="OpenId\Messages\CheckAuthenticationResponse.cs" /> <Compile Include="OpenId\Messages\CheckIdRequest.cs" /> diff --git a/src/DotNetOpenAuth/OpenId/ISecurityProfile.cs b/src/DotNetOpenAuth/OpenId/ISecurityProfile.cs deleted file mode 100644 index b9ec1fe..0000000 --- a/src/DotNetOpenAuth/OpenId/ISecurityProfile.cs +++ /dev/null @@ -1,80 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="ISecurityProfile.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.OpenId { - using System.Diagnostics.Contracts; - using DotNetOpenAuth.Messaging; - - /// <summary> - /// Applies a custom security policy to certain OpenID security settings and behaviors. - /// </summary> - [ContractClass(typeof(ISecurityProfileContract))] - internal interface ISecurityProfile { - /// <summary> - /// Applies a well known set of security requirements. - /// </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(SecuritySettings securitySettings); - - /// <summary> - /// Checks whether the given security settings comply with security requirements and throws otherwise. - /// </summary> - /// <param name="securitySettings">The security settings to check for compliance.</param> - /// <remarks> - /// Security settings should <em>not</em> be changed by this method. Any settings - /// that do not comply should cause an exception to be thrown. - /// </remarks> - /// <exception cref="ProtocolException">Thrown if the given security settings are not compliant with the requirements of this security profile.</exception> - void EnsureCompliance(SecuritySettings securitySettings); - } - - /// <summary> - /// Code contract for the <see cref="ISecurityProfile"/> type. - /// </summary> - [ContractClassFor(typeof(ISecurityProfile))] - internal abstract class ISecurityProfileContract : ISecurityProfile { - /// <summary> - /// Prevents a default instance of the <see cref="ISecurityProfileContract"/> class from being created. - /// </summary> - private ISecurityProfileContract() { - } - - #region ISecurityProfile Members - - /// <summary> - /// Applies a well known set of security requirements. - /// </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 ISecurityProfile.ApplySecuritySettings(SecuritySettings securitySettings) { - Contract.Requires(securitySettings != null); - } - - /// <summary> - /// Checks whether the given security settings comply with security requirements and throws otherwise. - /// </summary> - /// <param name="securitySettings">The security settings to check for compliance.</param> - /// <remarks> - /// Security settings should <em>not</em> be changed by this method. Any settings - /// that do not comply should cause an exception to be thrown. - /// </remarks> - /// <exception cref="ProtocolException">Thrown if the given security settings are not compliant with the requirements of this security profile.</exception> - void ISecurityProfile.EnsureCompliance(SecuritySettings securitySettings) { - Contract.Requires(securitySettings != null); - } - - #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 5de245c..e772b77 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs @@ -35,7 +35,7 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <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); @@ -126,7 +126,7 @@ namespace DotNetOpenAuth.OpenId.Provider { 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/IProviderSecurityProfile.cs b/src/DotNetOpenAuth/OpenId/Provider/IProviderSecurityProfile.cs index cf5e732..19217be 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/IProviderSecurityProfile.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/IProviderSecurityProfile.cs @@ -14,19 +14,30 @@ namespace DotNetOpenAuth.OpenId.Provider { /// 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 IProviderSecurityProfile : ISecurityProfile { + internal interface IProviderSecurityProfile { /// <summary> /// Called when a request is received by the Provider. /// </summary> - /// <param name="provider">The provider.</param> /// <param name="request">The incoming request.</param> - void OnIncomingRequest(OpenIdProvider provider, IRequest request); + /// <returns> + /// <c>true</c> if this security profile owns this request and wants to stop other security profiles + /// from handling it; <c>false</c> to allow other security profiles 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="provider">The provider.</param> /// <param name="request">The request that is configured to generate the outgoing response.</param> - void OnOutgoingResponse(OpenIdProvider provider, IAuthenticationRequest request); + /// <returns> + /// <c>true</c> if this security profile owns this request and wants to stop other security profiles + /// from handling it; <c>false</c> to allow other security profiles 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 0eb8d7e..2e769f3 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="SecurityProfiles"/> property. + /// </summary> + private readonly Collection<IProviderSecurityProfile> securityProfiles = new Collection<IProviderSecurityProfile>(); + + /// <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 securityProfile in DotNetOpenAuthSection.Configuration.OpenId.Provider.SecurityProfiles.CreateInstances(false)) { + this.securityProfiles.Add(securityProfile); + } + 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 security profiles to apply to OpenID actions. + /// </summary> + internal ICollection<IProviderSecurityProfile> SecurityProfiles { + get { return this.securityProfiles; } + } + + /// <summary> /// Gets the association store. /// </summary> internal IAssociationStore<AssociationRelyingPartyType> AssociationStore { get; private set; } @@ -216,20 +233,23 @@ namespace DotNetOpenAuth.OpenId.Provider { if (result == null) { var checkAuthMessage = incomingMessage as CheckAuthenticationRequest; if (checkAuthMessage != null) { - result = new AutoResponsiveRequest(incomingMessage, new CheckAuthenticationResponse(checkAuthMessage, this)); + result = new AutoResponsiveRequest(incomingMessage, new CheckAuthenticationResponse(checkAuthMessage, this), this.SecuritySettings); } } if (result == null) { var associateMessage = incomingMessage as AssociateRequest; if (associateMessage != null) { - result = new AutoResponsiveRequest(incomingMessage, associateMessage.CreateResponse(this.AssociationStore, this.SecuritySettings)); + result = new AutoResponsiveRequest(incomingMessage, associateMessage.CreateResponse(this.AssociationStore, this.SecuritySettings), this.SecuritySettings); } } if (result != null) { - foreach (var profile in this.SecuritySettings.SecurityProfiles) { - profile.OnIncomingRequest(this, result); + foreach (var profile in this.SecurityProfiles) { + if (profile.OnIncomingRequest(result)) { + // This security profile matched this request. + break; + } } return result; @@ -415,8 +435,11 @@ namespace DotNetOpenAuth.OpenId.Provider { private void ApplySecurityProfilesToResponse(IRequest request) { var authRequest = request as IAuthenticationRequest; if (authRequest != null) { - foreach (var profile in this.SecuritySettings.SecurityProfiles) { - profile.OnOutgoingResponse(this, authRequest); + foreach (var profile in this.SecurityProfiles) { + if (profile.OnOutgoingResponse(authRequest)) { + // This security profile matched this request. + break; + } } } } @@ -476,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/ProviderSecuritySettings.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs index bedc6d0..876e412 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs @@ -15,6 +15,7 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <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. @@ -27,11 +28,6 @@ namespace DotNetOpenAuth.OpenId.Provider { internal const bool SignOutgoingExtensionsDefault = true; /// <summary> - /// Backing store for the <see cref="SecurityProfiles"/> property. - /// </summary> - private readonly ObservableCollection<IProviderSecurityProfile> securityProfiles = new ObservableCollection<IProviderSecurityProfile>(); - - /// <summary> /// The subset of association types and their customized lifetimes. /// </summary> private IDictionary<string, TimeSpan> associationLifetimes = new Dictionary<string, TimeSpan>(); @@ -43,7 +39,6 @@ namespace DotNetOpenAuth.OpenId.Provider { : base(true) { this.SignOutgoingExtensions = SignOutgoingExtensionsDefault; this.ProtectDownlevelReplayAttacks = ProtectDownlevelReplayAttacksDefault; - this.securityProfiles.CollectionChanged += this.OnSecurityProfilesChanged; } /// <summary> @@ -62,13 +57,6 @@ namespace DotNetOpenAuth.OpenId.Provider { public bool RequireSsl { get; set; } /// <summary> - /// Gets a list of custom security profiles to apply to OpenID actions. - /// </summary> - internal ICollection<IProviderSecurityProfile> SecurityProfiles { - get { return this.securityProfiles; } - } - - /// <summary> /// Gets or sets a value indicating whether OpenID 1.x relying parties that may not be /// protecting their users from replay attacks are protected from /// replay attacks by this provider. @@ -99,10 +87,22 @@ namespace DotNetOpenAuth.OpenId.Provider { internal bool SignOutgoingExtensions { get; set; } /// <summary> - /// Gets the custom security profiles. + /// Creates a deep clone of this instance. /// </summary> - internal override IEnumerable<ISecurityProfile> CustomSecurityProfiles { - get { return this.securityProfiles.Cast<ISecurityProfile>(); } + /// <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 c2c07f4..83decb8 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs @@ -90,8 +90,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <value></value> public OutgoingWebResponse RedirectingResponse { get { - foreach (var profile in this.RelyingParty.SecuritySettings.SecurityProfiles) { - profile.OnOutgoingAuthenticationRequest(this.RelyingParty, this); + foreach (var profile in this.RelyingParty.SecurityProfiles) { + profile.OnOutgoingAuthenticationRequest(this); } return this.RelyingParty.Channel.PrepareResponse(this.CreateRequestMessage()); diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartySecurityProfile.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartySecurityProfile.cs index da7f06a..8d3848d 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartySecurityProfile.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartySecurityProfile.cs @@ -12,23 +12,32 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// 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 IRelyingPartySecurityProfile : ISecurityProfile { + internal interface IRelyingPartySecurityProfile { + /// <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="relyingParty">The relying party.</param> /// <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(OpenIdRelyingParty relyingParty, IAuthenticationRequest request); + void OnOutgoingAuthenticationRequest(IAuthenticationRequest request); /// <summary> /// Called when an incoming positive assertion is received. /// </summary> - /// <param name="relyingParty">The relying party.</param> /// <param name="assertion">The positive assertion.</param> - void OnIncomingPositiveAssertion(OpenIdRelyingParty relyingParty, IAuthenticationResponse assertion); + void OnIncomingPositiveAssertion(IAuthenticationResponse assertion); } } diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs index f2c8cc3..932e647 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="SecurityProfiles"/> property. + /// </summary> + private readonly ObservableCollection<IRelyingPartySecurityProfile> securityProfiles = new ObservableCollection<IRelyingPartySecurityProfile>(); + + /// <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.securityProfiles.CollectionChanged += this.OnSecurityProfilesChanged; + foreach (var securityProfile in DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.SecurityProfiles.CreateInstances(false)) { + this.securityProfiles.Add(securityProfile); + } // 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 security profiles to apply to OpenID actions. + /// </summary> + internal ICollection<IRelyingPartySecurityProfile> SecurityProfiles { + get { return this.securityProfiles; } + } + + /// <summary> /// Gets a value indicating whether this Relying Party can sign its return_to /// parameter in outgoing authentication requests. /// </summary> @@ -475,8 +492,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { IndirectSignedResponse positiveExtensionOnly; if ((positiveAssertion = message as PositiveAssertionResponse) != null) { var response = new PositiveAuthenticationResponse(positiveAssertion, this); - foreach (var profile in this.SecuritySettings.SecurityProfiles) { - profile.OnIncomingPositiveAssertion(this, response); + foreach (var profile in this.SecurityProfiles) { + profile.OnIncomingPositiveAssertion(response); } return response; @@ -560,5 +577,16 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } } } + + /// <summary> + /// Called by derived classes when security profiles 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 OnSecurityProfilesChanged(object sender, NotifyCollectionChangedEventArgs e) { + foreach (IRelyingPartySecurityProfile 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 7c6a59c..ff29498 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs @@ -16,17 +16,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> public sealed class RelyingPartySecuritySettings : SecuritySettings { /// <summary> - /// Backing store for the <see cref="SecurityProfiles"/> property. - /// </summary> - private readonly ObservableCollection<IRelyingPartySecurityProfile> securityProfiles = new ObservableCollection<IRelyingPartySecurityProfile>(); - - /// <summary> /// Initializes a new instance of the <see cref="RelyingPartySecuritySettings"/> class. /// </summary> internal RelyingPartySecuritySettings() : base(false) { this.PrivateSecretMaximumAge = TimeSpan.FromDays(7); - this.securityProfiles.CollectionChanged += this.OnSecurityProfilesChanged; } /// <summary> @@ -117,20 +111,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { public bool RequireAssociation { get; set; } /// <summary> - /// Gets a list of custom security profiles to apply to OpenID actions. - /// </summary> - internal ICollection<IRelyingPartySecurityProfile> SecurityProfiles { - get { return this.securityProfiles; } - } - - /// <summary> - /// Gets the custom security profiles. - /// </summary> - internal override IEnumerable<ISecurityProfile> CustomSecurityProfiles { - get { return this.securityProfiles.Cast<ISecurityProfile>(); } - } - - /// <summary> /// Filters out any disallowed endpoints. /// </summary> /// <param name="endpoints">The endpoints discovered on an Identifier.</param> diff --git a/src/DotNetOpenAuth/OpenId/SecurityProfiles/USGovernmentLevel1.cs b/src/DotNetOpenAuth/OpenId/SecurityProfiles/USGovernmentLevel1.cs index b0e92f9..2e5dd18 100644 --- a/src/DotNetOpenAuth/OpenId/SecurityProfiles/USGovernmentLevel1.cs +++ b/src/DotNetOpenAuth/OpenId/SecurityProfiles/USGovernmentLevel1.cs @@ -6,12 +6,9 @@ namespace DotNetOpenAuth.OpenId.SecurityProfiles { using System; - using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; - using System.Text; using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.OpenId.ChannelElements; using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy; using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; @@ -36,8 +33,6 @@ namespace DotNetOpenAuth.OpenId.SecurityProfiles { /// Initializes a new instance of the <see cref="USGovernmentLevel1"/> class. /// </summary> public USGovernmentLevel1() { - AllowPersonallyIdentifiableInformation = true; - DisableSslRequirement = true; } /// <summary> @@ -51,9 +46,12 @@ namespace DotNetOpenAuth.OpenId.SecurityProfiles { /// <value>The default value is <c>false</c>.</value> public static bool AllowPersonallyIdentifiableInformation { get; set; } + /// <summary> + /// Gets or sets a value indicating whether to ignore the SSL requirement (for testing purposes only). + /// </summary> public static bool DisableSslRequirement { get; set; } - #region ISecurityProfile Members + #region IRelyingPartySecurityProfile Members /// <summary> /// Applies a well known set of security requirements. @@ -64,71 +62,26 @@ namespace DotNetOpenAuth.OpenId.SecurityProfiles { /// Profiles should only enhance security requirements to avoid being /// incompatible with each other. /// </remarks> - void ISecurityProfile.ApplySecuritySettings(SecuritySettings securitySettings) { + void IRelyingPartySecurityProfile.ApplySecuritySettings(RelyingPartySecuritySettings securitySettings) { ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings"); if (securitySettings.MaximumHashBitLength < 256) { securitySettings.MaximumHashBitLength = 256; } - var rpSecuritySettings = securitySettings as RelyingPartySecuritySettings; - if (rpSecuritySettings != null) { - rpSecuritySettings.RequireSsl = !DisableSslRequirement; - rpSecuritySettings.RequireDirectedIdentity = true; - rpSecuritySettings.RequireAssociation = true; - rpSecuritySettings.RejectDelegatingIdentifiers = true; - rpSecuritySettings.IgnoreUnsignedExtensions = true; - rpSecuritySettings.MinimumRequiredOpenIdVersion = ProtocolVersion.V20; - } - - var opSecuritySettings = securitySettings as ProviderSecuritySettings; - if (opSecuritySettings != null) { - opSecuritySettings.RequireSsl = !DisableSslRequirement; - SetMaximumAssociationLifetimeToNotExceed(Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA256, MaximumAssociationLifetime, opSecuritySettings); - SetMaximumAssociationLifetimeToNotExceed(Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA1, MaximumAssociationLifetime, opSecuritySettings); - } - } - - /// <summary> - /// Checks whether the given security settings comply with security requirements and throws otherwise. - /// </summary> - /// <param name="securitySettings">The security settings to check for compliance.</param> - /// <remarks> - /// Security settings should <em>not</em> be changed by this method. Any settings - /// that do not comply should cause an exception to be thrown. - /// </remarks> - /// <exception cref="ProtocolException">Thrown if the given security settings are not compliant with the requirements of this security profile.</exception> - void ISecurityProfile.EnsureCompliance(SecuritySettings securitySettings) { - ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings"); - ErrorUtilities.VerifyProtocol(securitySettings.MaximumHashBitLength >= 256, SecurityProfileStrings.SecuritySettingsNotCompliantWithProfile, this.GetType().Name); - - var rpSecuritySettings = securitySettings as RelyingPartySecuritySettings; - if (rpSecuritySettings != null) { - ErrorUtilities.VerifyProtocol(rpSecuritySettings.RequireSsl || DisableSslRequirement, SecurityProfileStrings.SecuritySettingsNotCompliantWithProfile, this.GetType().Name); - ErrorUtilities.VerifyProtocol(rpSecuritySettings.RequireDirectedIdentity, SecurityProfileStrings.SecuritySettingsNotCompliantWithProfile, this.GetType().Name); - ErrorUtilities.VerifyProtocol(rpSecuritySettings.RequireAssociation, SecurityProfileStrings.SecuritySettingsNotCompliantWithProfile, this.GetType().Name); - ErrorUtilities.VerifyProtocol(rpSecuritySettings.IgnoreUnsignedExtensions, SecurityProfileStrings.SecuritySettingsNotCompliantWithProfile, this.GetType().Name); - } - - var opSecuritySettings = securitySettings as ProviderSecuritySettings; - if (opSecuritySettings != null) { - ErrorUtilities.VerifyProtocol(opSecuritySettings.RequireSsl || DisableSslRequirement, SecurityProfileStrings.SecuritySettingsNotCompliantWithProfile, this.GetType().Name); - ErrorUtilities.VerifyProtocol(opSecuritySettings.AssociationLifetimes.ContainsKey(Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA256) && opSecuritySettings.AssociationLifetimes[Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA256] <= MaximumAssociationLifetime, SecurityProfileStrings.SecuritySettingsNotCompliantWithProfile, this.GetType().Name); - ErrorUtilities.VerifyProtocol(opSecuritySettings.AssociationLifetimes.ContainsKey(Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA1) && opSecuritySettings.AssociationLifetimes[Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA1] <= MaximumAssociationLifetime, SecurityProfileStrings.SecuritySettingsNotCompliantWithProfile, this.GetType().Name); - } + securitySettings.RequireSsl = !DisableSslRequirement; + securitySettings.RequireDirectedIdentity = true; + securitySettings.RequireAssociation = true; + securitySettings.RejectDelegatingIdentifiers = true; + securitySettings.IgnoreUnsignedExtensions = true; + securitySettings.MinimumRequiredOpenIdVersion = ProtocolVersion.V20; } - #endregion - - #region IRelyingPartySecurityProfile Members - /// <summary> /// Called when an authentication request is about to be sent. /// </summary> - /// <param name="relyingParty">The relying party.</param> /// <param name="request">The request.</param> - void IRelyingPartySecurityProfile.OnOutgoingAuthenticationRequest(OpenIdRelyingParty relyingParty, RelyingParty.IAuthenticationRequest request) { - ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty"); + void IRelyingPartySecurityProfile.OnOutgoingAuthenticationRequest(RelyingParty.IAuthenticationRequest request) { ErrorUtilities.VerifyArgumentNotNull(request, "request"); RelyingParty.AuthenticationRequest requestInternal = (RelyingParty.AuthenticationRequest)request; @@ -162,10 +115,8 @@ namespace DotNetOpenAuth.OpenId.SecurityProfiles { /// <summary> /// Called when an incoming positive assertion is received. /// </summary> - /// <param name="relyingParty">The relying party.</param> /// <param name="assertion">The positive assertion.</param> - void IRelyingPartySecurityProfile.OnIncomingPositiveAssertion(OpenIdRelyingParty relyingParty, IAuthenticationResponse assertion) { - ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty"); + void IRelyingPartySecurityProfile.OnIncomingPositiveAssertion(IAuthenticationResponse assertion) { ErrorUtilities.VerifyArgumentNotNull(assertion, "assertion"); PolicyResponse pape = assertion.GetExtension<PolicyResponse>(); @@ -192,10 +143,17 @@ namespace DotNetOpenAuth.OpenId.SecurityProfiles { /// <summary> /// Called when a request is received by the Provider. /// </summary> - /// <param name="provider">The provider.</param> /// <param name="request">The incoming request.</param> - void IProviderSecurityProfile.OnIncomingRequest(OpenIdProvider provider, IRequest request) { - ErrorUtilities.VerifyArgumentNotNull(provider, "provider"); + /// <returns> + /// <c>true</c> if this security profile owns this request and wants to stop other security profiles + /// from handling it; <c>false</c> to allow other security profiles 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 IProviderSecurityProfile.OnIncomingRequest(IRequest request) { ErrorUtilities.VerifyArgumentNotNull(request, "request"); var hostProcessedRequest = request as IHostProcessedRequest; @@ -207,23 +165,32 @@ namespace DotNetOpenAuth.OpenId.SecurityProfiles { // Whenever we see this GSA policy requested, we MUST also see the PPID policy requested. ErrorUtilities.VerifyProtocol(papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier), SecurityProfileStrings.PapeRequestMissingRequiredPolicies); ErrorUtilities.VerifyProtocol(string.Equals(hostProcessedRequest.Realm.Scheme, Uri.UriSchemeHttps, StringComparison.Ordinal) || DisableSslRequirement, SecurityProfileStrings.RealmMustBeHttps); + + request.SecuritySettings = GetProviderSecuritySettings(request.SecuritySettings); + return true; } } } + + return false; } /// <summary> /// Called when the Provider is preparing to send a response to an authentication request. /// </summary> - /// <param name="provider">The provider.</param> /// <param name="request">The request that is configured to generate the outgoing response.</param> - void IProviderSecurityProfile.OnOutgoingResponse(OpenIdProvider provider, Provider.IAuthenticationRequest request) { - ErrorUtilities.VerifyArgumentNotNull(provider, "provider"); + /// <returns> + /// <c>true</c> if this security profile owns this request and wants to stop other security profiles + /// from handling it; <c>false</c> to allow other security profiles to process this request. + /// </returns> + bool IProviderSecurityProfile.OnOutgoingResponse(Provider.IAuthenticationRequest request) { ErrorUtilities.VerifyArgumentNotNull(request, "request"); + bool result = false; + // Nothing to do for negative assertions. if (!request.IsAuthenticated.Value) { - return; + return result; } var requestInternal = (Provider.AuthenticationRequest)request; @@ -238,6 +205,7 @@ namespace DotNetOpenAuth.OpenId.SecurityProfiles { } if (papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.USGovernmentTrustLevel1)) { + result = true; if (!papeResponse.ActualPolicies.Contains(AuthenticationPolicies.USGovernmentTrustLevel1)) { papeResponse.ActualPolicies.Add(AuthenticationPolicies.USGovernmentTrustLevel1); } @@ -277,11 +245,32 @@ namespace DotNetOpenAuth.OpenId.SecurityProfiles { } } } + + return result; } #endregion /// <summary> + /// Adapts the default security settings to the requirements of this security profile. + /// </summary> + /// <param name="originalSecuritySettings">The original security settings.</param> + /// <returns>A new security settings instance that should be used for all qualifying incoming requests.</returns> + private static ProviderSecuritySettings GetProviderSecuritySettings(ProviderSecuritySettings originalSecuritySettings) { + var securitySettings = originalSecuritySettings.Clone(); + + if (securitySettings.MaximumHashBitLength < 256) { + securitySettings.MaximumHashBitLength = 256; + } + + securitySettings.RequireSsl = !DisableSslRequirement; + SetMaximumAssociationLifetimeToNotExceed(Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA256, MaximumAssociationLifetime, securitySettings); + SetMaximumAssociationLifetimeToNotExceed(Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA1, MaximumAssociationLifetime, securitySettings); + + return securitySettings; + } + + /// <summary> /// Ensures the maximum association lifetime does not exceed a given limit. /// </summary> /// <param name="associationType">Type of the association.</param> diff --git a/src/DotNetOpenAuth/OpenId/SecuritySettings.cs b/src/DotNetOpenAuth/OpenId/SecuritySettings.cs index 43533c7..d4df697 100644 --- a/src/DotNetOpenAuth/OpenId/SecuritySettings.cs +++ b/src/DotNetOpenAuth/OpenId/SecuritySettings.cs @@ -5,6 +5,7 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OpenId { + using System; using System.Collections.Generic; using System.Collections.Specialized; using DotNetOpenAuth.Messaging; @@ -12,6 +13,7 @@ namespace DotNetOpenAuth.OpenId { /// <summary> /// Security settings that may be applicable to both relying parties and providers. /// </summary> + [Serializable] public abstract class SecuritySettings { /// <summary> /// Gets the default minimum hash bit length. @@ -65,11 +67,6 @@ namespace DotNetOpenAuth.OpenId { public int MaximumHashBitLength { get; set; } /// <summary> - /// Gets the custom security profiles that apply to these security settings. - /// </summary> - internal abstract IEnumerable<ISecurityProfile> CustomSecurityProfiles { get; } - - /// <summary> /// Determines whether a named association fits the security requirements. /// </summary> /// <param name="protocol">The protocol carrying the association.</param> @@ -93,25 +90,5 @@ namespace DotNetOpenAuth.OpenId { ErrorUtilities.VerifyArgumentNotNull(association, "association"); return association.HashBitLength >= this.MinimumHashBitLength && association.HashBitLength <= this.MaximumHashBitLength; } - - /// <summary> - /// Ensures that all security profiles are satisfied. - /// </summary> - internal void EnsureSecurityProfilesSatisfied() { - foreach (ISecurityProfile profile in this.CustomSecurityProfiles) { - profile.EnsureCompliance(this); - } - } - - /// <summary> - /// Called by derived classes when security profiles 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> - protected void OnSecurityProfilesChanged(object sender, NotifyCollectionChangedEventArgs e) { - foreach (ISecurityProfile profile in e.NewItems) { - profile.ApplySecuritySettings(this); - } - } } } |