//----------------------------------------------------------------------- // // Copyright (c) Outercurve Foundation. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy { using System; using System.Collections.Generic; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.Messages; /// /// The PAPE request part of an OpenID Authentication request message. /// [Serializable] public sealed class PolicyRequest : ExtensionBase, IMessageWithEvents { /// /// The factory method that may be used in deserialization of this message. /// internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => { if (typeUri == Constants.TypeUri && isProviderRole) { return new PolicyRequest(); } return null; }; /// /// The transport field for the RP's preferred authentication policies. /// /// /// This field is written to/read from during custom serialization. /// [MessagePart("preferred_auth_policies", IsRequired = true)] private string preferredPoliciesString; /// /// Initializes a new instance of the class. /// public PolicyRequest() : base(new Version(1, 0), Constants.TypeUri, null) { this.PreferredPolicies = new List(1); this.PreferredAuthLevelTypes = new List(1); } /// /// Gets or sets the maximum acceptable time since the End User has /// actively authenticated to the OP in a manner fitting the requested /// policies, beyond which the Provider SHOULD authenticate the /// End User for this request. /// /// /// The OP should realize that not adhering to the request for re-authentication /// most likely means that the End User will not be allowed access to the /// services provided by the RP. If this parameter is absent in the request, /// the OP should authenticate the user at its own discretion. /// [MessagePart("max_auth_age", IsRequired = false, Encoder = typeof(TimespanSecondsEncoder))] public TimeSpan? MaximumAuthenticationAge { get; set; } /// /// Gets the list of authentication policy URIs that the OP SHOULD /// conform to when authenticating the user. If multiple policies are /// requested, the OP SHOULD satisfy as many as it can. /// /// List of authentication policy URIs obtainable from /// the class or from a custom /// list. /// /// If no policies are requested, the RP may be interested in other /// information such as the authentication age. /// public IList PreferredPolicies { get; private set; } /// /// Gets the namespaces of the custom Assurance Level the /// Relying Party requests, in the order of its preference. /// public IList PreferredAuthLevelTypes { get; private set; } #region IMessageWithEvents Members /// /// Called when the message is about to be transmitted, /// before it passes through the channel binding elements. /// void IMessageWithEvents.OnSending() { var extraData = ((IMessage)this).ExtraData; extraData.Clear(); this.preferredPoliciesString = SerializePolicies(this.PreferredPolicies); if (this.PreferredAuthLevelTypes.Count > 0) { AliasManager authLevelAliases = new AliasManager(); authLevelAliases.AssignAliases(this.PreferredAuthLevelTypes, Constants.AssuranceLevels.PreferredTypeUriToAliasMap); // Add a definition for each Auth Level Type alias. foreach (string alias in authLevelAliases.Aliases) { extraData.Add(Constants.AuthLevelNamespaceDeclarationPrefix + alias, authLevelAliases.ResolveAlias(alias)); } // Now use the aliases for those type URIs to list a preferred order. extraData.Add(Constants.RequestParameters.PreferredAuthLevelTypes, SerializeAuthLevels(this.PreferredAuthLevelTypes, authLevelAliases)); } } /// /// Called when the message has been received, /// after it passes through the channel binding elements. /// void IMessageWithEvents.OnReceiving() { var extraData = ((IMessage)this).ExtraData; this.PreferredPolicies.Clear(); string[] preferredPolicies = this.preferredPoliciesString.Split(' '); foreach (string policy in preferredPolicies) { if (policy.Length > 0) { this.PreferredPolicies.Add(policy); } } this.PreferredAuthLevelTypes.Clear(); AliasManager authLevelAliases = PapeUtilities.FindIncomingAliases(extraData); string preferredAuthLevelAliases; if (extraData.TryGetValue(Constants.RequestParameters.PreferredAuthLevelTypes, out preferredAuthLevelAliases)) { foreach (string authLevelAlias in preferredAuthLevelAliases.Split(' ')) { if (authLevelAlias.Length == 0) { continue; } this.PreferredAuthLevelTypes.Add(authLevelAliases.ResolveAlias(authLevelAlias)); } } } #endregion /// /// Determines whether the specified is equal to the current . /// /// The to compare with the current . /// /// true if the specified is equal to the current ; otherwise, false. /// /// /// The parameter is null. /// public override bool Equals(object obj) { PolicyRequest other = obj as PolicyRequest; if (other == null) { return false; } if (this.MaximumAuthenticationAge != other.MaximumAuthenticationAge) { return false; } if (this.PreferredPolicies.Count != other.PreferredPolicies.Count) { return false; } foreach (string policy in this.PreferredPolicies) { if (!other.PreferredPolicies.Contains(policy)) { return false; } } if (this.PreferredAuthLevelTypes.Count != other.PreferredAuthLevelTypes.Count) { return false; } foreach (string authLevel in this.PreferredAuthLevelTypes) { if (!other.PreferredAuthLevelTypes.Contains(authLevel)) { return false; } } return true; } /// /// Serves as a hash function for a particular type. /// /// /// A hash code for the current . /// public override int GetHashCode() { // This is a poor hash function, but an site that cares will likely have a bunch // of look-alike instances anyway, so a good hash function would still bunch // all the instances into the same hash code. if (this.MaximumAuthenticationAge.HasValue) { return this.MaximumAuthenticationAge.Value.GetHashCode(); } else { return 1; } } /// /// Serializes the policies as a single string per the PAPE spec.. /// /// The policies to include in the list. /// The concatenated string of the given policies. private static string SerializePolicies(IEnumerable policies) { return PapeUtilities.ConcatenateListOfElements(policies); } /// /// Serializes the auth levels to a list of aliases. /// /// The preferred auth level types. /// The alias manager. /// A space-delimited list of aliases. private static string SerializeAuthLevels(IList preferredAuthLevelTypes, AliasManager aliases) { var aliasList = new List(); foreach (string typeUri in preferredAuthLevelTypes) { aliasList.Add(aliases.GetAlias(typeUri)); } return PapeUtilities.ConcatenateListOfElements(aliasList); } } }