diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2009-09-09 09:09:59 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2009-09-09 09:09:59 -0700 |
commit | f0e5fd13d27bad797e7c6cb4dc5c50431aee8c26 (patch) | |
tree | 3e8860ca73a8cae1d02f01bc783530f7af3a6385 | |
parent | e65eb7cff86b438d863e24d551d585cda0759116 (diff) | |
parent | c2292b5a6306dc3a51c5065762958a0de06de047 (diff) | |
download | DotNetOpenAuth-f0e5fd13d27bad797e7c6cb4dc5c50431aee8c26.zip DotNetOpenAuth-f0e5fd13d27bad797e7c6cb4dc5c50431aee8c26.tar.gz DotNetOpenAuth-f0e5fd13d27bad797e7c6cb4dc5c50431aee8c26.tar.bz2 |
Merge branch 'v3.2'
Conflicts:
build.proj
11 files changed, 400 insertions, 4 deletions
diff --git a/samples/OpenIdProviderMvc/Global.asax.cs b/samples/OpenIdProviderMvc/Global.asax.cs index a2bcfb2..3ca6104 100644 --- a/samples/OpenIdProviderMvc/Global.asax.cs +++ b/samples/OpenIdProviderMvc/Global.asax.cs @@ -34,6 +34,7 @@ protected void Application_Start() { RegisterRoutes(RouteTable.Routes); DotNetOpenAuth.OpenId.Behaviors.PpidGeneration.PpidIdentifierProvider = new Code.AnonymousIdentifierProvider(); + DotNetOpenAuth.OpenId.Behaviors.GsaIcamProfile.PpidIdentifierProvider = new Code.AnonymousIdentifierProvider(); } } }
\ No newline at end of file diff --git a/samples/OpenIdProviderMvc/Web.config b/samples/OpenIdProviderMvc/Web.config index 6f0fdd1..f36bfcf 100644 --- a/samples/OpenIdProviderMvc/Web.config +++ b/samples/OpenIdProviderMvc/Web.config @@ -52,6 +52,7 @@ <!-- Behaviors activate themselves automatically for individual matching requests. The first one in this list to match an incoming request "owns" the request. If no profile matches, the default behavior is assumed. --> + <!--<add type="DotNetOpenAuth.OpenId.Behaviors.GsaIcamProfile, DotNetOpenAuth" />--> <add type="DotNetOpenAuth.OpenId.Behaviors.PpidGeneration, DotNetOpenAuth" /> </behaviors> <!-- Uncomment the following to activate the sample custom store. --> diff --git a/samples/OpenIdRelyingPartyWebForms/Web.config b/samples/OpenIdRelyingPartyWebForms/Web.config index 445c419..c48c198 100644 --- a/samples/OpenIdRelyingPartyWebForms/Web.config +++ b/samples/OpenIdRelyingPartyWebForms/Web.config @@ -23,7 +23,6 @@ <!--<servicePointManager checkCertificateRevocationList="true"/>--> </settings> </system.net> - <!-- this is an optional configuration section where aspects of dotnetopenauth can be customized --> <dotNetOpenAuth> <openid> @@ -33,6 +32,7 @@ <!-- The following OPTIONAL behavior allows RPs to use SREG only, but be compatible with OPs that use Attribute Exchange (in various formats). --> <add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth" /> + <!--<add type="DotNetOpenAuth.OpenId.Behaviors.GsaIcamProfile, DotNetOpenAuth" />--> </behaviors> <!-- Uncomment the following to activate the sample custom store. --> <!--<store type="OpenIdRelyingPartyWebForms.CustomStore, OpenIdRelyingPartyWebForms" />--> diff --git a/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs b/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs index 0c350cb..c8cf2aa 100644 --- a/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs +++ b/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs @@ -100,7 +100,7 @@ namespace DotNetOpenAuth.Configuration { source = HttpContext.Current.Server.MapPath(source); } using (Stream xamlFile = File.OpenRead(source)) { - return this.CreateInstanceFromXaml(xamlFile); + return CreateInstanceFromXaml(xamlFile); } } else { return defaultValue; @@ -119,7 +119,7 @@ namespace DotNetOpenAuth.Configuration { /// XamlSource attribute is never used, the PresentationFramework.dll never need /// be present. /// </remarks> - private T CreateInstanceFromXaml(Stream xaml) { + private static T CreateInstanceFromXaml(Stream xaml) { return (T)XamlReader.Load(xaml); } } diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index 3c576a7..314b8e5 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -511,6 +511,7 @@ <Compile Include="OpenId\OpenIdXrdsHelper.cs" /> <Compile Include="OpenId\RelyingParty\SimpleXrdsProviderEndpoint.cs" /> <Compile Include="OpenId\RelyingParty\StandardRelyingPartyApplicationStore.cs" /> + <Compile Include="OpenId\Behaviors\GsaIcamProfile.cs" /> <Compile Include="OpenId\RelyingParty\WellKnownProviders.cs" /> <Compile Include="OpenId\SecuritySettings.cs" /> <Compile Include="Messaging\UntrustedWebRequestHandler.cs" /> diff --git a/src/DotNetOpenAuth/GlobalSuppressions.cs b/src/DotNetOpenAuth/GlobalSuppressions.cs index 07fabc3..d0e0d05 100644 --- a/src/DotNetOpenAuth/GlobalSuppressions.cs +++ b/src/DotNetOpenAuth/GlobalSuppressions.cs @@ -44,3 +44,7 @@ [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions.UI")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.Messaging.Reflection")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "oauthverifier", Scope = "resource", Target = "DotNetOpenAuth.OAuth.OAuthStrings.resources")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "whitelist", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "icam", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "idmanagement", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "no-pii", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")] diff --git a/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs b/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs index a17f1e0..21911fa 100644 --- a/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs +++ b/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs @@ -194,6 +194,13 @@ namespace DotNetOpenAuth.InfoCard { private bool audienceSet; /// <summary> + /// Initializes a new instance of the <see cref="InfoCardSelector"/> class. + /// </summary> + public InfoCardSelector() { + this.ToolTip = InfoCardStrings.SelectorClickPrompt; + } + + /// <summary> /// Occurs when an InfoCard has been submitted but not decoded yet. /// </summary> [Category(InfoCardCategory)] @@ -257,6 +264,8 @@ namespace DotNetOpenAuth.InfoCard { /// </summary> [Description("The URL to this site's privacy policy.")] [Category(InfoCardCategory), DefaultValue(PrivacyUrlDefault)] + [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Uri", Justification = "We construct a Uri to validate the format of the string.")] + [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "That overload is NOT the same.")] public string PrivacyUrl { get { return (string)this.ViewState[PrivacyUrlViewStateKey] ?? PrivacyUrlDefault; @@ -553,7 +562,7 @@ namespace DotNetOpenAuth.InfoCard { Image image = new Image(); image.ImageUrl = this.Page.ClientScript.GetWebResourceUrl(typeof(InfoCardSelector), InfoCardImage.GetImageManifestResourceStreamName(this.ImageSize)); image.AlternateText = InfoCardStrings.SelectorClickPrompt; - image.ToolTip = InfoCardStrings.SelectorClickPrompt; + image.ToolTip = this.ToolTip; image.Style[HtmlTextWriterStyle.Cursor] = "hand"; image.Attributes["onclick"] = this.GetInfoCardSelectorActivationScript(false); diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs index 4166f19..937ecaf 100644 --- a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs +++ b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs @@ -61,6 +61,42 @@ namespace DotNetOpenAuth.OpenId.Behaviors { } /// <summary> + /// Looks up a localized string similar to The PAPE request has an incomplete set of authentication policies.. + /// </summary> + internal static string PapeRequestMissingRequiredPolicies { + get { + return ResourceManager.GetString("PapeRequestMissingRequiredPolicies", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to A PAPE response is missing or is missing required policies.. + /// </summary> + internal static string PapeResponseOrRequiredPoliciesMissing { + get { + return ResourceManager.GetString("PapeResponseOrRequiredPoliciesMissing", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to No personally identifiable information should be included in authentication responses when the PAPE authentication policy http://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdf is present.. + /// </summary> + internal static string PiiIncludedWithNoPiiPolicy { + get { + return ResourceManager.GetString("PiiIncludedWithNoPiiPolicy", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to No personally identifiable information should be requested when the http://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdf PAPE policy is present.. + /// </summary> + internal static string PiiRequestedWithNoPiiPolicy { + get { + return ResourceManager.GetString("PiiRequestedWithNoPiiPolicy", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to No PPID provider has been configured.. /// </summary> internal static string PpidProviderNotGiven { @@ -68,5 +104,23 @@ namespace DotNetOpenAuth.OpenId.Behaviors { return ResourceManager.GetString("PpidProviderNotGiven", resourceCulture); } } + + /// <summary> + /// Looks up a localized string similar to Discovery on the Realm URL MUST be performed before sending a positive assertion.. + /// </summary> + internal static string RealmDiscoveryNotPerformed { + get { + return ResourceManager.GetString("RealmDiscoveryNotPerformed", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The Realm in an authentication request must be an HTTPS URL.. + /// </summary> + internal static string RealmMustBeHttps { + get { + return ResourceManager.GetString("RealmMustBeHttps", resourceCulture); + } + } } } diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx index 23e3e73..a8bf2d6 100644 --- a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx +++ b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx @@ -117,7 +117,25 @@ <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> + <data name="PapeRequestMissingRequiredPolicies" xml:space="preserve"> + <value>The PAPE request has an incomplete set of authentication policies.</value> + </data> + <data name="PapeResponseOrRequiredPoliciesMissing" xml:space="preserve"> + <value>A PAPE response is missing or is missing required policies.</value> + </data> + <data name="PiiIncludedWithNoPiiPolicy" xml:space="preserve"> + <value>No personally identifiable information should be included in authentication responses when the PAPE authentication policy http://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdf is present.</value> + </data> + <data name="PiiRequestedWithNoPiiPolicy" xml:space="preserve"> + <value>No personally identifiable information should be requested when the http://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdf PAPE policy is present.</value> + </data> <data name="PpidProviderNotGiven" xml:space="preserve"> <value>No PPID provider has been configured.</value> </data> + <data name="RealmDiscoveryNotPerformed" xml:space="preserve"> + <value>Discovery on the Realm URL MUST be performed before sending a positive assertion.</value> + </data> + <data name="RealmMustBeHttps" xml:space="preserve"> + <value>The Realm in an authentication request must be an HTTPS URL.</value> + </data> </root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/GsaIcamProfile.cs b/src/DotNetOpenAuth/OpenId/Behaviors/GsaIcamProfile.cs new file mode 100644 index 0000000..8f3b78f --- /dev/null +++ b/src/DotNetOpenAuth/OpenId/Behaviors/GsaIcamProfile.cs @@ -0,0 +1,291 @@ +//----------------------------------------------------------------------- +// <copyright file="GsaIcamProfile.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId.Behaviors { + using System; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + using System.Linq; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; + using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy; + using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; + using DotNetOpenAuth.OpenId.Provider; + using DotNetOpenAuth.OpenId.RelyingParty; + + /// <summary> + /// Implements the Identity, Credential, & Access Management (ICAM) OpenID 2.0 Profile + /// for the General Services Administration (GSA). + /// </summary> + /// <remarks> + /// <para>Relying parties that include this profile are always held to the terms required by the profile, + /// but Providers are only affected by the special behaviors of the profile when the RP specifically + /// indicates that they want to use this profile. </para> + /// </remarks> + [Serializable] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Icam", Justification = "Acronym")] + public sealed class GsaIcamProfile : IRelyingPartyBehavior, IProviderBehavior { + /// <summary> + /// The maximum time a shared association can live. + /// </summary> + private static readonly TimeSpan MaximumAssociationLifetime = TimeSpan.FromSeconds(86400); + + /// <summary> + /// Initializes a new instance of the <see cref="GsaIcamProfile"/> class. + /// </summary> + public GsaIcamProfile() { + if (DisableSslRequirement) { + Logger.OpenId.Warn("GSA level 1 behavior has its RequireSsl requirement disabled."); + } + } + + /// <summary> + /// Gets or sets the provider for generating PPID identifiers. + /// </summary> + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Ppid", Justification = "Acronym")] + public static IDirectedIdentityIdentifierProvider PpidIdentifierProvider { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether PII is allowed to be requested or received via OpenID. + /// </summary> + /// <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 IRelyingPartyBehavior 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 IRelyingPartyBehavior.ApplySecuritySettings(RelyingPartySecuritySettings securitySettings) { + ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings"); + + if (securitySettings.MaximumHashBitLength < 256) { + securitySettings.MaximumHashBitLength = 256; + } + + securitySettings.RequireSsl = !DisableSslRequirement; + securitySettings.RequireDirectedIdentity = true; + securitySettings.RequireAssociation = true; + securitySettings.RejectDelegatingIdentifiers = true; + securitySettings.IgnoreUnsignedExtensions = true; + securitySettings.MinimumRequiredOpenIdVersion = ProtocolVersion.V20; + } + + /// <summary> + /// Called when an authentication request is about to be sent. + /// </summary> + /// <param name="request">The request.</param> + void IRelyingPartyBehavior.OnOutgoingAuthenticationRequest(RelyingParty.IAuthenticationRequest request) { + ErrorUtilities.VerifyArgumentNotNull(request, "request"); + + RelyingParty.AuthenticationRequest requestInternal = (RelyingParty.AuthenticationRequest)request; + ErrorUtilities.VerifyProtocol(string.Equals(request.Realm.Scheme, Uri.UriSchemeHttps, StringComparison.Ordinal) || DisableSslRequirement, BehaviorStrings.RealmMustBeHttps); + + var pape = requestInternal.AppliedExtensions.OfType<PolicyRequest>().SingleOrDefault(); + if (pape == null) { + request.AddExtension(pape = new PolicyRequest()); + } + + if (!pape.PreferredPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier)) { + pape.PreferredPolicies.Add(AuthenticationPolicies.PrivatePersonalIdentifier); + } + + if (!pape.PreferredPolicies.Contains(AuthenticationPolicies.USGovernmentTrustLevel1)) { + pape.PreferredPolicies.Add(AuthenticationPolicies.USGovernmentTrustLevel1); + } + + if (!AllowPersonallyIdentifiableInformation && !pape.PreferredPolicies.Contains(AuthenticationPolicies.NoPersonallyIdentifiableInformation)) { + pape.PreferredPolicies.Add(AuthenticationPolicies.NoPersonallyIdentifiableInformation); + } + + if (pape.PreferredPolicies.Contains(AuthenticationPolicies.NoPersonallyIdentifiableInformation)) { + ErrorUtilities.VerifyProtocol( + (!requestInternal.AppliedExtensions.OfType<ClaimsRequest>().Any() && + !requestInternal.AppliedExtensions.OfType<FetchRequest>().Any()), + BehaviorStrings.PiiIncludedWithNoPiiPolicy); + } + } + + /// <summary> + /// Called when an incoming positive assertion is received. + /// </summary> + /// <param name="assertion">The positive assertion.</param> + void IRelyingPartyBehavior.OnIncomingPositiveAssertion(IAuthenticationResponse assertion) { + ErrorUtilities.VerifyArgumentNotNull(assertion, "assertion"); + + PolicyResponse pape = assertion.GetExtension<PolicyResponse>(); + ErrorUtilities.VerifyProtocol( + pape != null && + pape.ActualPolicies.Contains(AuthenticationPolicies.USGovernmentTrustLevel1) && + pape.ActualPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier), + BehaviorStrings.PapeResponseOrRequiredPoliciesMissing); + + ErrorUtilities.VerifyProtocol(AllowPersonallyIdentifiableInformation || pape.ActualPolicies.Contains(AuthenticationPolicies.NoPersonallyIdentifiableInformation), BehaviorStrings.PapeResponseOrRequiredPoliciesMissing); + + if (pape.ActualPolicies.Contains(AuthenticationPolicies.NoPersonallyIdentifiableInformation)) { + ErrorUtilities.VerifyProtocol( + assertion.GetExtension<ClaimsResponse>() == null && + assertion.GetExtension<FetchResponse>() == null, + BehaviorStrings.PiiIncludedWithNoPiiPolicy); + } + } + + #endregion + + #region IProviderBehavior Members + + /// <summary> + /// Adapts the default security settings to the requirements of this behavior. + /// </summary> + /// <param name="securitySettings">The original security settings.</param> + void IProviderBehavior.ApplySecuritySettings(ProviderSecuritySettings securitySettings) { + if (securitySettings.MaximumHashBitLength < 256) { + securitySettings.MaximumHashBitLength = 256; + } + + SetMaximumAssociationLifetimeToNotExceed(Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA256, MaximumAssociationLifetime, securitySettings); + SetMaximumAssociationLifetimeToNotExceed(Protocol.Default.Args.SignatureAlgorithm.HMAC_SHA1, MaximumAssociationLifetime, securitySettings); + } + + /// <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) { + ErrorUtilities.VerifyArgumentNotNull(request, "request"); + + var hostProcessedRequest = request as IHostProcessedRequest; + if (hostProcessedRequest != null) { + // Only apply our special policies if the RP requested it. + var papeRequest = request.GetExtension<PolicyRequest>(); + if (papeRequest != null) { + if (papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.USGovernmentTrustLevel1)) { + // Whenever we see this GSA policy requested, we MUST also see the PPID policy requested. + ErrorUtilities.VerifyProtocol(papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier), BehaviorStrings.PapeRequestMissingRequiredPolicies); + ErrorUtilities.VerifyProtocol(string.Equals(hostProcessedRequest.Realm.Scheme, Uri.UriSchemeHttps, StringComparison.Ordinal) || DisableSslRequirement, BehaviorStrings.RealmMustBeHttps); + + // Apply GSA-specific security to this individual request. + request.SecuritySettings.RequireSsl = !DisableSslRequirement; + return true; + } + } + } + + 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(Provider.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.USGovernmentTrustLevel1)) { + result = true; + if (!papeResponse.ActualPolicies.Contains(AuthenticationPolicies.USGovernmentTrustLevel1)) { + papeResponse.ActualPolicies.Add(AuthenticationPolicies.USGovernmentTrustLevel1); + } + + // The spec requires that the OP perform discovery and if that fails, it must either sternly + // warn the user of a potential threat or just abort the authentication. + // We can't verify that the OP displayed anything to the user at this level, but we can + // at least verify that the OP performed the discovery on the realm and halt things if it didn't. + ErrorUtilities.VerifyHost(requestInternal.HasRealmDiscoveryBeenPerformed, BehaviorStrings.RealmDiscoveryNotPerformed); + } + + 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); + } + } + + if (papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.NoPersonallyIdentifiableInformation)) { + ErrorUtilities.VerifyProtocol( + !responseMessage.Extensions.OfType<ClaimsResponse>().Any() && + !responseMessage.Extensions.OfType<FetchResponse>().Any(), + BehaviorStrings.PiiIncludedWithNoPiiPolicy); + + // If no PII is given in extensions, and the claimed_id is a PPID, then we can state we issue no PII. + if (papeResponse.ActualPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier)) { + if (!papeResponse.ActualPolicies.Contains(AuthenticationPolicies.NoPersonallyIdentifiableInformation)) { + papeResponse.ActualPolicies.Add(AuthenticationPolicies.NoPersonallyIdentifiableInformation); + } + } + } + } + + return result; + } + + #endregion + + /// <summary> + /// Ensures the maximum association lifetime does not exceed a given limit. + /// </summary> + /// <param name="associationType">Type of the association.</param> + /// <param name="maximumLifetime">The maximum lifetime.</param> + /// <param name="securitySettings">The security settings to adjust.</param> + private static void SetMaximumAssociationLifetimeToNotExceed(string associationType, TimeSpan maximumLifetime, ProviderSecuritySettings securitySettings) { + Contract.Requires(!String.IsNullOrEmpty(associationType)); + Contract.Requires(maximumLifetime.TotalSeconds > 0); + if (!securitySettings.AssociationLifetimes.ContainsKey(associationType) || + securitySettings.AssociationLifetimes[associationType] > maximumLifetime) { + securitySettings.AssociationLifetimes[associationType] = maximumLifetime; + } + } + } +} diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs index 4392cd5..99c7a2e 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs @@ -43,6 +43,23 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy { public const string PrivatePersonalIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier"; /// <summary> + /// Indicates that the OP MUST only respond with a positive assertion if the requirements demonstrated + /// by the OP to obtain certification by a Federally adopted Trust Framework Provider have been met. + /// </summary> + /// <remarks> + /// Notwithstanding the RP may request this authentication policy, the RP MUST still + /// verify that this policy appears in the positive assertion response rather than assume the OP + /// recognized and complied with the request. + /// </remarks> + public const string USGovernmentTrustLevel1 = "http://www.idmanagement.gov/schema/2009/05/icam/openid-trust-level1.pdf"; + + /// <summary> + /// Indicates that the OP MUST not include any OpenID Attribute Exchange or Simple Registration + /// information regarding the user in the assertion. + /// </summary> + public const string NoPersonallyIdentifiableInformation = "http://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdf"; + + /// <summary> /// Used in a PAPE response to indicate that no PAPE authentication policies could be satisfied. /// </summary> /// <remarks> |