//-----------------------------------------------------------------------
//
// 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);
}
}
}