diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2011-04-23 17:05:07 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2011-04-23 17:05:07 -0700 |
commit | 11e5a2fa89cff0f3d54cca09d96fb0e082fdb64c (patch) | |
tree | c9f4f1af7e077ef25c0d5d7f306ec88110e681fb /src | |
parent | 9f33f5d7a65c678299d63081e8e2021ca5da1c90 (diff) | |
parent | d01b8ce1bdc289dcee5206d88b3a3dc88dc358a8 (diff) | |
download | DotNetOpenAuth-11e5a2fa89cff0f3d54cca09d96fb0e082fdb64c.zip DotNetOpenAuth-11e5a2fa89cff0f3d54cca09d96fb0e082fdb64c.tar.gz DotNetOpenAuth-11e5a2fa89cff0f3d54cca09d96fb0e082fdb64c.tar.bz2 |
Merge branch 'TrustedProviders' into v3.4
Diffstat (limited to 'src')
10 files changed, 232 insertions, 24 deletions
diff --git a/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd b/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd index 9c0ab77..9786068 100644 --- a/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd +++ b/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd @@ -243,6 +243,44 @@ </xs:documentation> </xs:annotation> <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="trustedProviders"> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="add"> + <xs:complexType> + <xs:attribute name="endpoint" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The OpenID Provider Endpoint (aka "OP Endpoint") that this relying party trusts. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element name="remove"> + <xs:complexType> + <xs:attribute name="endpoint" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + <xs:element name="clear"> + <xs:complexType> + <!--tag is empty--> + </xs:complexType> + </xs:element> + </xs:choice> + <xs:attribute name="rejectAssertionsFromUntrustedProviders" type="xs:boolean" default="false"> + <xs:annotation> + <xs:documentation> + A value indicating whether any login attempt coming from an OpenID Provider Endpoint that is not on this + whitelist of trusted OP Endpoints will be rejected. If the trusted providers list is empty and this value + is true, all assertions are rejected. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:choice> <xs:attribute name="requireSsl" type="xs:boolean" default="false"> <xs:annotation> <xs:documentation> @@ -269,7 +307,7 @@ <xs:attribute name="minimumHashBitLength" type="xs:int"> <xs:annotation> <xs:documentation> - Shared associations with OpenID Providers will only be formed or used if they + Shared associations with OpenID Providers will only be formed or used if they are willing to form associations equal to or greater than a given level of protection. </xs:documentation> </xs:annotation> @@ -524,7 +562,7 @@ so security is comparable to OpenID 2.0 relying parties. </xs:documentation> </xs:annotation> - </xs:attribute> + </xs:attribute> <xs:attribute name="unsolicitedAssertionVerification"> <xs:annotation> <xs:documentation> @@ -735,7 +773,7 @@ </xs:documentation> </xs:annotation> <xs:complexType> - + </xs:complexType> </xs:element> </xs:choice> @@ -856,7 +894,7 @@ <xs:attribute name="includeEventStatistics" type="xs:boolean" default="true"> <xs:annotation> <xs:documentation> - Whether a set of counters that track how often certain events (such as an + Whether a set of counters that track how often certain events (such as an successful or failed authentication) is included in the report. </xs:documentation> </xs:annotation> diff --git a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs index 1bf2ebc..4347e2c 100644 --- a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs +++ b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs @@ -81,6 +81,11 @@ namespace DotNetOpenAuth.Configuration { private const string ProtectDownlevelReplayAttacksConfigName = "protectDownlevelReplayAttacks"; /// <summary> + /// The name of the <trustedProviders> sub-element. + /// </summary> + private const string TrustedProvidersElementName = "trustedProviders"; + + /// <summary> /// Initializes a new instance of the <see cref="OpenIdRelyingPartySecuritySettingsElement"/> class. /// </summary> public OpenIdRelyingPartySecuritySettingsElement() { @@ -235,6 +240,16 @@ namespace DotNetOpenAuth.Configuration { } /// <summary> + /// Gets or sets the set of trusted OpenID Provider Endpoints. + /// </summary> + [ConfigurationProperty(TrustedProvidersElementName, IsDefaultCollection = false)] + [ConfigurationCollection(typeof(TrustedProviderConfigurationCollection))] + public TrustedProviderConfigurationCollection TrustedProviders { + get { return (TrustedProviderConfigurationCollection)this[TrustedProvidersElementName] ?? new TrustedProviderConfigurationCollection(); } + set { this[TrustedProvidersElementName] = 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> @@ -256,6 +271,11 @@ namespace DotNetOpenAuth.Configuration { settings.AllowApproximateIdentifierDiscovery = this.AllowApproximateIdentifierDiscovery; settings.ProtectDownlevelReplayAttacks = this.ProtectDownlevelReplayAttacks; + settings.RejectAssertionsFromUntrustedProviders = this.TrustedProviders.RejectAssertionsFromUntrustedProviders; + foreach (TrustedProviderEndpointConfigurationElement opEndpoint in this.TrustedProviders) { + settings.TrustedProviderEndpoints.Add(opEndpoint.ProviderEndpoint); + } + return settings; } } diff --git a/src/DotNetOpenAuth/Configuration/TrustedProviderConfigurationCollection.cs b/src/DotNetOpenAuth/Configuration/TrustedProviderConfigurationCollection.cs new file mode 100644 index 0000000..78c74e9 --- /dev/null +++ b/src/DotNetOpenAuth/Configuration/TrustedProviderConfigurationCollection.cs @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------- +// <copyright file="TrustedProviderConfigurationCollection.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Configuration { + using System; + using System.Collections.Generic; + using System.Configuration; + using System.Diagnostics.Contracts; + + /// <summary> + /// A configuration collection of trusted OP Endpoints. + /// </summary> + internal class TrustedProviderConfigurationCollection : ConfigurationElementCollection { + /// <summary> + /// The name of the "rejectAssertionsFromUntrustedProviders" element. + /// </summary> + private const string RejectAssertionsFromUntrustedProvidersConfigName = "rejectAssertionsFromUntrustedProviders"; + + /// <summary> + /// Initializes a new instance of the <see cref="TrustedProviderConfigurationCollection"/> class. + /// </summary> + internal TrustedProviderConfigurationCollection() { + } + + /// <summary> + /// Initializes a new instance of the <see cref="TrustedProviderConfigurationCollection"/> class. + /// </summary> + internal TrustedProviderConfigurationCollection(IEnumerable<TrustedProviderEndpointConfigurationElement> elements) { + Contract.Requires<ArgumentNullException>(elements != null); + + foreach (TrustedProviderEndpointConfigurationElement element in elements) { + this.BaseAdd(element); + } + } + + /// <summary> + /// Gets or sets a value indicating whether any login attempt coming from an OpenID Provider Endpoint that is not on this + /// whitelist of trusted OP Endpoints will be rejected. If the trusted providers list is empty and this value + /// is true, all assertions are rejected. + /// </summary> + [ConfigurationProperty(RejectAssertionsFromUntrustedProvidersConfigName, DefaultValue = false)] + internal bool RejectAssertionsFromUntrustedProviders { + get { return (bool)this[RejectAssertionsFromUntrustedProvidersConfigName]; } + set { this[RejectAssertionsFromUntrustedProvidersConfigName] = value; } + } + + /// <summary> + /// When overridden in a derived class, creates a new <see cref="T:System.Configuration.ConfigurationElement"/>. + /// </summary> + /// <returns> + /// A new <see cref="T:System.Configuration.ConfigurationElement"/>. + /// </returns> + protected override ConfigurationElement CreateNewElement() { + return new TrustedProviderEndpointConfigurationElement(); + } + + /// <summary> + /// Gets the element key for a specified configuration element when overridden in a derived class. + /// </summary> + /// <param name="element">The <see cref="T:System.Configuration.ConfigurationElement"/> to return the key for.</param> + /// <returns> + /// An <see cref="T:System.Object"/> that acts as the key for the specified <see cref="T:System.Configuration.ConfigurationElement"/>. + /// </returns> + protected override object GetElementKey(ConfigurationElement element) { + return ((TrustedProviderEndpointConfigurationElement)element).ProviderEndpoint; + } + } +} diff --git a/src/DotNetOpenAuth/Configuration/TrustedProviderEndpointConfigurationElement.cs b/src/DotNetOpenAuth/Configuration/TrustedProviderEndpointConfigurationElement.cs new file mode 100644 index 0000000..dc49d8c --- /dev/null +++ b/src/DotNetOpenAuth/Configuration/TrustedProviderEndpointConfigurationElement.cs @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------- +// <copyright file="TrustedProviderEndpoint.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Configuration { + using System; + using System.Configuration; + + /// <summary> + /// A configuration element that records a trusted Provider Endpoint. + /// </summary> + internal class TrustedProviderEndpointConfigurationElement : ConfigurationElement { + /// <summary> + /// The name of the attribute that stores the <see cref="ProviderEndpoint"/> value. + /// </summary> + private const string ProviderEndpointConfigName = "endpoint"; + + /// <summary> + /// Initializes a new instance of the <see cref="TrustedProviderEndpointConfigurationElement"/> class. + /// </summary> + public TrustedProviderEndpointConfigurationElement() { + } + + /// <summary> + /// Gets or sets the OpenID Provider Endpoint (aka "OP Endpoint") that this relying party trusts. + /// </summary> + [ConfigurationProperty(ProviderEndpointConfigName, IsRequired = true, IsKey = true)] + public Uri ProviderEndpoint { + get { return (Uri)this[ProviderEndpointConfigName]; } + set { this[ProviderEndpointConfigName] = value; } + } + } +} diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index 4073bde..6517a87 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -295,6 +295,8 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="Configuration\OpenIdRelyingPartyElement.cs" /> <Compile Include="Configuration\OpenIdRelyingPartySecuritySettingsElement.cs" /> <Compile Include="Configuration\ReportingElement.cs" /> + <Compile Include="Configuration\TrustedProviderConfigurationCollection.cs" /> + <Compile Include="Configuration\TrustedProviderEndpointConfigurationElement.cs" /> <Compile Include="Configuration\TypeConfigurationCollection.cs" /> <Compile Include="Configuration\TypeConfigurationElement.cs" /> <Compile Include="Configuration\UntrustedWebRequestElement.cs" /> diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs index 43283ac..ff3fea6 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs +++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:4.0.30319.1 +// Runtime Version:4.0.30319.225 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -524,11 +524,11 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Looks up a localized string similar to An positive OpenID assertion was received from OP endpoint {0} that is not on this relying party's whitelist.. + /// Looks up a localized string similar to An positive OpenID assertion was received from OP endpoint {0} and was rejected based on this site's security settings.. /// </summary> - internal static string PositiveAssertionFromNonWhitelistedProvider { + internal static string PositiveAssertionFromNonQualifiedProvider { get { - return ResourceManager.GetString("PositiveAssertionFromNonWhitelistedProvider", resourceCulture); + return ResourceManager.GetString("PositiveAssertionFromNonQualifiedProvider", resourceCulture); } } diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx index fab03a9..13764bb 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx +++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx @@ -337,8 +337,8 @@ Discovered endpoint info: <data name="BadExtension" xml:space="preserve"> <value>The {0} extension failed to deserialize and will be skipped. {1}</value> </data> - <data name="PositiveAssertionFromNonWhitelistedProvider" xml:space="preserve"> - <value>An positive OpenID assertion was received from OP endpoint {0} that is not on this relying party's whitelist.</value> + <data name="PositiveAssertionFromNonQualifiedProvider" xml:space="preserve"> + <value>An positive OpenID assertion was received from OP endpoint {0} and was rejected based on this site's security settings.</value> </data> <data name="HeadTagMustIncludeRunatServer" xml:space="preserve"> <value>The HTML head tag must include runat="server".</value> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs index 967ef60..3a17263 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs @@ -501,14 +501,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { Contract.Requires<ArgumentNullException>(endpoints != null); Contract.Requires<ArgumentNullException>(relyingParty != null); - // Construct the endpoints filters based on criteria given by the host web site. - EndpointSelector versionFilter = ep => ep.Version >= Protocol.Lookup(relyingParty.SecuritySettings.MinimumRequiredOpenIdVersion).Version; - EndpointSelector hostingSiteFilter = relyingParty.EndpointFilter ?? (ep => true); - bool anyFilteredOut = false; var filteredEndpoints = new List<IdentifierDiscoveryResult>(); foreach (var endpoint in endpoints) { - if (versionFilter(endpoint) && hostingSiteFilter(endpoint)) { + if (relyingParty.FilterEndpoint(endpoint)) { filteredEndpoints.Add(endpoint); } else { anyFilteredOut = true; diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs index a5fdf9b..5cffe03 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs @@ -542,15 +542,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { NegativeAssertionResponse negativeAssertion; IndirectSignedResponse positiveExtensionOnly; if ((positiveAssertion = message as PositiveAssertionResponse) != null) { - if (this.EndpointFilter != null) { - // We need to make sure that this assertion is coming from an endpoint - // that the host deems acceptable. - var providerEndpoint = new SimpleXrdsProviderEndpoint(positiveAssertion); - ErrorUtilities.VerifyProtocol( - this.EndpointFilter(providerEndpoint), - OpenIdStrings.PositiveAssertionFromNonWhitelistedProvider, - providerEndpoint.Uri); - } + // We need to make sure that this assertion is coming from an endpoint + // that the host deems acceptable. + var providerEndpoint = new SimpleXrdsProviderEndpoint(positiveAssertion); + ErrorUtilities.VerifyProtocol( + this.FilterEndpoint(providerEndpoint), + OpenIdStrings.PositiveAssertionFromNonQualifiedProvider, + providerEndpoint.Uri); var response = new PositiveAuthenticationResponse(positiveAssertion, this); foreach (var behavior in this.Behaviors) { @@ -761,6 +759,38 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Checks whether a given OP Endpoint is permitted by the host relying party. + /// </summary> + /// <param name="endpoint">The OP endpoint.</param> + /// <returns><c>true</c> if the OP Endpoint is allowed; <c>false</c> otherwise.</returns> + protected internal bool FilterEndpoint(IProviderEndpoint endpoint) { + if (this.SecuritySettings.RejectAssertionsFromUntrustedProviders) { + if (!this.SecuritySettings.TrustedProviderEndpoints.Contains(endpoint.Uri)) { + Logger.OpenId.InfoFormat("Filtering out OP endpoint {0} because it is not on the exclusive trusted provider whitelist.", endpoint.Uri.AbsoluteUri); + return false; + } + } + + if (endpoint.Version < Protocol.Lookup(this.SecuritySettings.MinimumRequiredOpenIdVersion).Version) { + Logger.OpenId.InfoFormat( + "Filtering out OP endpoint {0} because it implements OpenID {1} but this relying party requires OpenID {2} or later.", + endpoint.Uri.AbsoluteUri, + endpoint.Version, + Protocol.Lookup(this.SecuritySettings.MinimumRequiredOpenIdVersion).Version); + return false; + } + + if (this.EndpointFilter != null) { + if (!this.EndpointFilter(endpoint)) { + Logger.OpenId.InfoFormat("Filtering out OP endpoint {0} because the host rejected it.", endpoint.Uri.AbsoluteUri); + return false; + } + } + + return true; + } + + /// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs index a7686c5..fc6d4c7 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs @@ -8,6 +8,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System; using System.Collections.Generic; using System.Collections.ObjectModel; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; using System.Linq; using DotNetOpenAuth.Messaging; @@ -28,6 +30,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { this.PrivateSecretMaximumAge = TimeSpan.FromDays(7); this.ProtectDownlevelReplayAttacks = ProtectDownlevelReplayAttacksDefault; this.AllowApproximateIdentifierDiscovery = true; + this.TrustedProviderEndpoints = new HashSet<Uri>(); } /// <summary> @@ -143,6 +146,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { public bool AllowApproximateIdentifierDiscovery { get; set; } /// <summary> + /// Gets the set of trusted OpenID Provider Endpoint URIs. + /// </summary> + public HashSet<Uri> TrustedProviderEndpoints { get; private set; } + + /// <summary> + /// Gets or sets a value indicating whether any login attempt coming from an OpenID Provider Endpoint that is not on this + /// whitelist of trusted OP Endpoints will be rejected. If the trusted providers list is empty and this value + /// is true, all assertions are rejected. + /// </summary> + /// <value>Default is <c>false</c>.</value> + public bool RejectAssertionsFromUntrustedProviders { get; set; } + + /// <summary> /// Gets or sets a value indicating whether special measures are taken to /// protect users from replay attacks when those users' identities are hosted /// by OpenID 1.x Providers. |