diff options
Diffstat (limited to 'src')
11 files changed, 379 insertions, 34 deletions
diff --git a/src/DotNetOpenId.Test/Extensions/ExtensionTestBase.cs b/src/DotNetOpenId.Test/Extensions/ExtensionTestBase.cs index 6e0a05d..cfa9cca 100644 --- a/src/DotNetOpenId.Test/Extensions/ExtensionTestBase.cs +++ b/src/DotNetOpenId.Test/Extensions/ExtensionTestBase.cs @@ -8,6 +8,7 @@ using DotNetOpenId.RelyingParty; using NUnit.Framework;
using OPRequest = DotNetOpenId.Provider.IAuthenticationRequest;
using SregDemandLevel = DotNetOpenId.Extensions.SimpleRegistration.DemandLevel;
+using PapeConstants = DotNetOpenId.Extensions.ProviderAuthenticationPolicy.Constants;
namespace DotNetOpenId.Test.Extensions {
public class ExtensionTestBase {
@@ -81,6 +82,9 @@ namespace DotNetOpenId.Test.Extensions { if (papeRequest.MaximumAuthenticationAge.HasValue) {
papeResponse.AuthenticationTimeUtc = DateTime.UtcNow - (papeRequest.MaximumAuthenticationAge.Value - TimeSpan.FromSeconds(30));
}
+ if (papeRequest.PreferredAuthLevelTypes.Contains(PapeConstants.AuthenticationLevels.NistTypeUri)) {
+ papeResponse.NistAssuranceLevel = NistAssuranceLevel.Level1;
+ }
}
break;
case TestSupport.Scenarios.ExtensionPartialCooperation:
diff --git a/src/DotNetOpenId.Test/Extensions/PapeTests.cs b/src/DotNetOpenId.Test/Extensions/PapeTests.cs index 5c100ff..6fb693d 100644 --- a/src/DotNetOpenId.Test/Extensions/PapeTests.cs +++ b/src/DotNetOpenId.Test/Extensions/PapeTests.cs @@ -19,11 +19,15 @@ namespace DotNetOpenId.Test.Extensions { public void Full() {
var request = new PolicyRequest();
request.MaximumAuthenticationAge = TimeSpan.FromMinutes(10);
+ request.PreferredAuthLevelTypes.Add(Constants.AuthenticationLevels.NistTypeUri);
var response = ParameterizedTest<PolicyResponse>(
TestSupport.Scenarios.ExtensionFullCooperation, Version, request);
Assert.IsNotNull(response);
Assert.IsNotNull(response.AuthenticationTimeUtc);
Assert.IsTrue(response.AuthenticationTimeUtc.Value > DateTime.UtcNow - request.MaximumAuthenticationAge);
+ Assert.IsTrue(response.AssuranceLevels.ContainsKey(Constants.AuthenticationLevels.NistTypeUri));
+ Assert.AreEqual("1", response.AssuranceLevels[Constants.AuthenticationLevels.NistTypeUri]);
+ Assert.AreEqual(NistAssuranceLevel.Level1, response.NistAssuranceLevel);
}
}
}
diff --git a/src/DotNetOpenId.Test/Extensions/PolicyRequestTests.cs b/src/DotNetOpenId.Test/Extensions/PolicyRequestTests.cs index 508dace..40155db 100644 --- a/src/DotNetOpenId.Test/Extensions/PolicyRequestTests.cs +++ b/src/DotNetOpenId.Test/Extensions/PolicyRequestTests.cs @@ -5,6 +5,7 @@ using System.Text; using NUnit.Framework;
using DotNetOpenId.Extensions.ProviderAuthenticationPolicy;
using DotNetOpenId.Extensions;
+using System.Globalization;
namespace DotNetOpenId.Test.Extensions {
[TestFixture]
@@ -50,6 +51,14 @@ namespace DotNetOpenId.Test.Extensions { }
[Test]
+ public void AddAuthLevelTypes() {
+ PolicyRequest req = new PolicyRequest();
+ req.PreferredAuthLevelTypes.Add(Constants.AuthenticationLevels.NistTypeUri);
+ Assert.AreEqual(1, req.PreferredAuthLevelTypes.Count);
+ Assert.IsTrue(req.PreferredAuthLevelTypes.Contains(Constants.AuthenticationLevels.NistTypeUri));
+ }
+
+ [Test]
public void EqualsTest() {
PolicyRequest req = new PolicyRequest();
PolicyRequest req2 = new PolicyRequest();
@@ -77,6 +86,15 @@ namespace DotNetOpenId.Test.Extensions { Assert.AreNotEqual(req, req2);
req2.MaximumAuthenticationAge = req.MaximumAuthenticationAge;
Assert.AreEqual(req, req2);
+
+ // Test PreferredAuthLevelTypes comparison.
+ req.PreferredAuthLevelTypes.Add("authlevel1");
+ Assert.AreNotEqual(req, req2);
+ req2.PreferredAuthLevelTypes.Add("authlevel2");
+ Assert.AreNotEqual(req, req2);
+ req.PreferredAuthLevelTypes.Add("authlevel2");
+ req2.PreferredAuthLevelTypes.Add("authlevel1");
+ Assert.AreEqual(req, req2);
}
[Test]
@@ -105,14 +123,16 @@ namespace DotNetOpenId.Test.Extensions { // Test with all fields set
req2 = new PolicyRequest();
req.PreferredPolicies.Add(AuthenticationPolicies.MultiFactor);
+ req.PreferredAuthLevelTypes.Add(Constants.AuthenticationLevels.NistTypeUri);
req.MaximumAuthenticationAge = TimeSpan.FromHours(1);
fields = ((IExtensionRequest)req).Serialize(null);
Assert.IsTrue(((IExtensionRequest)req2).Deserialize(fields, null, Constants.TypeUri));
Assert.AreEqual(req, req2);
- // Test with an extra policy
+ // Test with an extra policy and auth level
req2 = new PolicyRequest();
req.PreferredPolicies.Add(AuthenticationPolicies.PhishingResistant);
+ req.PreferredAuthLevelTypes.Add("customAuthLevel");
fields = ((IExtensionRequest)req).Serialize(null);
Assert.IsTrue(((IExtensionRequest)req2).Deserialize(fields, null, Constants.TypeUri));
Assert.AreEqual(req, req2);
@@ -121,13 +141,54 @@ namespace DotNetOpenId.Test.Extensions { // the doubled policies out.
req2 = new PolicyRequest();
req.PreferredPolicies.Add(AuthenticationPolicies.PhishingResistant);
+ req.PreferredAuthLevelTypes.Add(Constants.AuthenticationLevels.NistTypeUri);
fields = ((IExtensionRequest)req).Serialize(null);
Assert.IsTrue(((IExtensionRequest)req2).Deserialize(fields, null, Constants.TypeUri));
Assert.AreNotEqual(req, req2);
// Now go ahead and add the doubled one so we can do our equality test.
req2.PreferredPolicies.Add(AuthenticationPolicies.PhishingResistant);
+ req2.PreferredAuthLevelTypes.Add(Constants.AuthenticationLevels.NistTypeUri);
Assert.AreEqual(req, req2);
+ }
+ [Test]
+ public void Serialize() {
+ PolicyRequest req = new PolicyRequest();
+ var fields = ((IExtensionRequest)req).Serialize(null);
+ Assert.AreEqual(1, fields.Count);
+ Assert.IsTrue(fields.ContainsKey("preferred_auth_policies"));
+ Assert.IsEmpty(fields["preferred_auth_policies"]);
+
+ req.MaximumAuthenticationAge = TimeSpan.FromHours(1);
+ fields = ((IExtensionRequest)req).Serialize(null);
+ Assert.AreEqual(2, fields.Count);
+ Assert.IsTrue(fields.ContainsKey("max_auth_age"));
+ Assert.AreEqual(TimeSpan.FromHours(1).TotalSeconds.ToString(CultureInfo.InvariantCulture), fields["max_auth_age"]);
+
+ req.PreferredPolicies.Add("http://pol1/");
+ fields = ((IExtensionRequest)req).Serialize(null);
+ Assert.AreEqual("http://pol1/", fields["preferred_auth_policies"]);
+
+ req.PreferredPolicies.Add("http://pol2/");
+ fields = ((IExtensionRequest)req).Serialize(null);
+ Assert.AreEqual("http://pol1/ http://pol2/", fields["preferred_auth_policies"]);
+
+ req.PreferredAuthLevelTypes.Add("http://authtype1/");
+ fields = ((IExtensionRequest)req).Serialize(null);
+ Assert.AreEqual(4, fields.Count);
+ Assert.IsTrue(fields.ContainsKey("auth_level.ns.alias1"));
+ Assert.AreEqual("http://authtype1/", fields["auth_level.ns.alias1"]);
+ Assert.IsTrue(fields.ContainsKey("preferred_auth_level_types"));
+ Assert.AreEqual("alias1", fields["preferred_auth_level_types"]);
+
+ req.PreferredAuthLevelTypes.Add(Constants.AuthenticationLevels.NistTypeUri);
+ fields = ((IExtensionRequest)req).Serialize(null);
+ Assert.AreEqual(5, fields.Count);
+ Assert.IsTrue(fields.ContainsKey("auth_level.ns.alias2"));
+ Assert.AreEqual("http://authtype1/", fields["auth_level.ns.alias2"]);
+ Assert.IsTrue(fields.ContainsKey("auth_level.ns.nist"));
+ Assert.AreEqual(Constants.AuthenticationLevels.NistTypeUri, fields["auth_level.ns.nist"]);
+ Assert.AreEqual("alias2 nist", fields["preferred_auth_level_types"]);
}
}
}
diff --git a/src/DotNetOpenId.Test/Extensions/PolicyResponseTests.cs b/src/DotNetOpenId.Test/Extensions/PolicyResponseTests.cs index 572672a..7fe240b 100644 --- a/src/DotNetOpenId.Test/Extensions/PolicyResponseTests.cs +++ b/src/DotNetOpenId.Test/Extensions/PolicyResponseTests.cs @@ -88,6 +88,19 @@ namespace DotNetOpenId.Test.Extensions { }
[Test]
+ public void AssuranceLevels() {
+ PolicyResponse resp = new PolicyResponse();
+ Assert.AreEqual(0, resp.AssuranceLevels.Count);
+ resp.NistAssuranceLevel = NistAssuranceLevel.Level2;
+ Assert.AreEqual(1, resp.AssuranceLevels.Count);
+ Assert.AreEqual("2", resp.AssuranceLevels[Constants.AuthenticationLevels.NistTypeUri]);
+ resp.AssuranceLevels[Constants.AuthenticationLevels.NistTypeUri] = "3";
+ Assert.AreEqual(NistAssuranceLevel.Level3, resp.NistAssuranceLevel);
+ resp.AssuranceLevels.Clear();
+ Assert.IsNull(resp.NistAssuranceLevel);
+ }
+
+ [Test]
public void EqualsTest() {
PolicyResponse resp = new PolicyResponse();
PolicyResponse resp2 = new PolicyResponse();
@@ -129,6 +142,18 @@ namespace DotNetOpenId.Test.Extensions { Assert.AreNotEqual(resp, resp2);
resp2.NistAssuranceLevel = NistAssuranceLevel.Level2;
Assert.AreEqual(resp, resp2);
+
+ // Test AssuranceLevels comparison.
+ resp.AssuranceLevels.Add("custom", "b");
+ Assert.AreNotEqual(resp, resp2);
+ resp2.AssuranceLevels.Add("custom", "2");
+ Assert.AreNotEqual(resp, resp2);
+ resp2.AssuranceLevels["custom"] = "b";
+ Assert.AreEqual(resp, resp2);
+ resp.AssuranceLevels[Constants.AuthenticationLevels.NistTypeUri] = "1";
+ Assert.AreNotEqual(resp, resp2);
+ resp2.AssuranceLevels[Constants.AuthenticationLevels.NistTypeUri] = "1";
+ Assert.AreEqual(resp, resp2);
}
[Test]
@@ -166,6 +191,7 @@ namespace DotNetOpenId.Test.Extensions { // Test with an extra policy
resp2 = new PolicyResponse();
resp.ActualPolicies.Add(AuthenticationPolicies.PhishingResistant);
+ resp.AssuranceLevels.Add("customlevel", "ABC");
fields = ((IExtensionResponse)resp).Serialize(null);
Assert.IsTrue(((IExtensionResponse)resp2).Deserialize(fields, null, Constants.TypeUri));
Assert.AreEqual(resp, resp2);
@@ -181,5 +207,52 @@ namespace DotNetOpenId.Test.Extensions { resp2.ActualPolicies.Add(AuthenticationPolicies.PhishingResistant);
Assert.AreEqual(resp, resp2);
}
+
+ [Test]
+ public void Serialize() {
+ PolicyResponse resp = new PolicyResponse(), resp2 = new PolicyResponse();
+ var fields = ((IExtensionResponse)resp).Serialize(null);
+ Assert.AreEqual(1, fields.Count);
+ Assert.IsTrue(fields.ContainsKey("auth_policies"));
+ Assert.AreEqual(AuthenticationPolicies.None, fields["auth_policies"]);
+
+ resp.ActualPolicies.Add(AuthenticationPolicies.PhishingResistant);
+ fields = ((IExtensionResponse)resp).Serialize(null);
+ Assert.AreEqual(1, fields.Count);
+ Assert.AreEqual(AuthenticationPolicies.PhishingResistant, fields["auth_policies"]);
+
+ resp.ActualPolicies.Add(AuthenticationPolicies.PhysicalMultiFactor);
+ fields = ((IExtensionResponse)resp).Serialize(null);
+ Assert.AreEqual(1, fields.Count);
+ Assert.AreEqual(
+ AuthenticationPolicies.PhishingResistant + " " + AuthenticationPolicies.PhysicalMultiFactor,
+ fields["auth_policies"]);
+
+ resp.AuthenticationTimeUtc = DateTime.UtcNow;
+ fields = ((IExtensionResponse)resp).Serialize(null);
+ Assert.AreEqual(2, fields.Count);
+ Assert.IsTrue(fields.ContainsKey("auth_time"));
+
+ resp.NistAssuranceLevel = NistAssuranceLevel.Level3;
+ fields = ((IExtensionResponse)resp).Serialize(null);
+ Assert.AreEqual(4, fields.Count);
+ Assert.IsTrue(fields.ContainsKey("auth_level.ns.nist"));
+ Assert.AreEqual(Constants.AuthenticationLevels.NistTypeUri, fields["auth_level.ns.nist"]);
+ Assert.IsTrue(fields.ContainsKey("auth_level.nist"));
+ Assert.AreEqual("3", fields["auth_level.nist"]);
+
+ resp.AssuranceLevels.Add("custom", "CU");
+ fields = ((IExtensionResponse)resp).Serialize(null);
+ Assert.AreEqual(6, fields.Count);
+ Assert.IsTrue(fields.ContainsKey("auth_level.ns.alias2"));
+ Assert.AreEqual("custom", fields["auth_level.ns.alias2"]);
+ Assert.IsTrue(fields.ContainsKey("auth_level.alias2"));
+ Assert.AreEqual("CU", fields["auth_level.alias2"]);
+ // and make sure the NIST is still there.
+ Assert.IsTrue(fields.ContainsKey("auth_level.ns.nist"));
+ Assert.AreEqual(Constants.AuthenticationLevels.NistTypeUri, fields["auth_level.ns.nist"]);
+ Assert.IsTrue(fields.ContainsKey("auth_level.nist"));
+ Assert.AreEqual("3", fields["auth_level.nist"]);
+ }
}
}
diff --git a/src/DotNetOpenId/ExtensionArgumentsManager.cs b/src/DotNetOpenId/ExtensionArgumentsManager.cs index 23f18a7..4f8cdc2 100644 --- a/src/DotNetOpenId/ExtensionArgumentsManager.cs +++ b/src/DotNetOpenId/ExtensionArgumentsManager.cs @@ -22,6 +22,7 @@ namespace DotNetOpenId { /// </summary>
static readonly Dictionary<string, string> typeUriToAliasAffinity = new Dictionary<string, string> {
{ Extensions.SimpleRegistration.Constants.sreg_ns, Extensions.SimpleRegistration.Constants.sreg_compatibility_alias },
+ { Extensions.ProviderAuthenticationPolicy.Constants.TypeUri, Extensions.ProviderAuthenticationPolicy.Constants.pape_compatibility_alias },
};
private ExtensionArgumentsManager() { }
diff --git a/src/DotNetOpenId/Extensions/AliasManager.cs b/src/DotNetOpenId/Extensions/AliasManager.cs index 52308d5..f4bbb04 100644 --- a/src/DotNetOpenId/Extensions/AliasManager.cs +++ b/src/DotNetOpenId/Extensions/AliasManager.cs @@ -37,6 +37,61 @@ namespace DotNetOpenId.Extensions { aliasToTypeUriMap.Add(alias, typeUri);
typeUriToAliasMap.Add(typeUri, alias);
}
+ /// <summary>
+ /// Takes a sequence of type URIs and assigns aliases for all of them.
+ /// </summary>
+ /// <param name="typeUris">The type URIs to create aliases for.</param>
+ /// <param name="preferredTypeUriToAliases">An optional dictionary of URI/alias pairs that suggest preferred aliases to use if available for certain type URIs.</param>
+ public void AssignAliases(IEnumerable<string> typeUris, IDictionary<string, string> preferredTypeUriToAliases) {
+ // First go through the actually used type URIs and see which ones have matching preferred aliases.
+ if (preferredTypeUriToAliases != null) {
+ foreach (string typeUri in typeUris) {
+ if (typeUriToAliasMap.ContainsKey(typeUri)) {
+ // this Type URI is already mapped to an alias.
+ continue;
+ }
+
+ string preferredAlias;
+ if (preferredTypeUriToAliases.TryGetValue(typeUri, out preferredAlias) && !IsAliasUsed(preferredAlias)) {
+ SetAlias(preferredAlias, typeUri);
+ }
+ }
+ }
+
+ // Now go through the whole list again and assign whatever is left now that the preferred ones
+ // have gotten their picks where available.
+ foreach (string typeUri in typeUris) {
+ if (typeUriToAliasMap.ContainsKey(typeUri)) {
+ // this Type URI is already mapped to an alias.
+ continue;
+ }
+
+ assignNewAlias(typeUri);
+ }
+ }
+ /// <summary>
+ /// Sets up aliases for any Type URIs in a dictionary that do not yet have aliases defined,
+ /// and where the given preferred alias is still available.
+ /// </summary>
+ /// <param name="preferredTypeUriToAliases">A dictionary of type URI keys and alias values.</param>
+ public void SetPreferredAliasesWhereNotSet(IDictionary<string, string> preferredTypeUriToAliases) {
+ if (preferredTypeUriToAliases == null) throw new ArgumentNullException("preferredTypeUriToAliases");
+
+ foreach (var pair in preferredTypeUriToAliases) {
+ if (typeUriToAliasMap.ContainsKey(pair.Key)) {
+ // type URI is already mapped
+ continue;
+ }
+
+ if (aliasToTypeUriMap.ContainsKey(pair.Value)) {
+ // alias is already mapped
+ continue;
+ }
+
+ // The type URI and alias are as yet unset, so go ahead and assign them.
+ SetAlias(pair.Value, pair.Key);
+ }
+ }
/// <summary>
/// Gets the Type Uri encoded by a given alias.
@@ -57,6 +112,11 @@ namespace DotNetOpenId.Extensions { public IEnumerable<string> Aliases {
get { return aliasToTypeUriMap.Keys; }
}
+ /// <summary>
+ /// Returns a value indicating whether an alias has already been assigned to a type URI.
+ /// </summary>
+ /// <param name="alias">The alias in question.</param>
+ /// <returns>True if the alias has already been assigned. False otherwise.</returns>
public bool IsAliasUsed(string alias) {
if (string.IsNullOrEmpty(alias)) throw new ArgumentNullException("alias");
return aliasToTypeUriMap.ContainsKey(alias);
diff --git a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs index 517525f..f505a9b 100644 --- a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs +++ b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs @@ -13,6 +13,13 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { /// </remarks>
public static class AuthenticationPolicies {
/// <summary>
+ /// Used in a PAPE response to indicate that no PAPE authentication policies could be satisfied.
+ /// </summary>
+ /// <remarks>
+ /// Used internally by the PAPE extension, so that users don't have to know about it.
+ /// </remarks>
+ internal const string None = "http://schemas.openid.net/pape/policies/2007/06/none";
+ /// <summary>
/// An authentication mechanism where the End User does not provide a shared secret to a party potentially under the control of the Relying Party. (Note that the potentially malicious Relying Party controls where the User-Agent is redirected to and thus may not send it to the End User's actual OpenID Provider).
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Phishing")]
diff --git a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/Constants.cs b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/Constants.cs index 13fc4cf..395ea36 100644 --- a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/Constants.cs +++ b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/Constants.cs @@ -12,6 +12,24 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { /// </summary>
internal const string TypeUri = "http://specs.openid.net/extensions/pape/1.0";
/// <summary>
+ /// The namespace alias to use for OpenID 1.x interop, where aliases are not defined in the message.
+ /// </summary>
+ internal const string pape_compatibility_alias = "pape";
+ /// <summary>
+ /// The string to prepend on an Auth Level Type alias definition.
+ /// </summary>
+ internal const string AuthLevelNamespaceDeclarationPrefix = "auth_level.ns.";
+
+ internal static class AuthenticationLevels {
+ internal static readonly IDictionary<string, string> PreferredTypeUriToAliasMap = new Dictionary<string, string> {
+ { NistTypeUri, nist_compatibility_alias },
+ };
+
+ internal const string nist_compatibility_alias = "nist";
+ internal const string NistTypeUri = "http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf";
+ }
+
+ /// <summary>
/// Parameters to be included with PAPE requests.
/// </summary>
internal static class RequestParameters {
@@ -31,6 +49,10 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { /// If no policies are requested, the RP may be interested in other information such as the authentication age.
/// </remarks>
internal const string PreferredAuthPolicies = "preferred_auth_policies";
+ /// <summary>
+ /// The space separated list of the name spaces of the custom Assurance Level that RP requests, in the order of its preference.
+ /// </summary>
+ internal const string PreferredAuthLevelTypes = "preferred_auth_level_types";
}
/// <summary>
/// Parameters to be included with PAPE responses.
@@ -58,13 +80,11 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { /// </remarks>
internal const string AuthTime = "auth_time";
/// <summary>
- /// Optional. The Assurance Level as defined by the National Institute of Standards and Technology (NIST) in Special Publication 800-63 (Burr, W., Dodson, D., and W. Polk, Ed., “Electronic Authentication Guideline,” April 2006.) [NIST_SP800‑63] corresponding to the authentication method and policies employed by the OP when authenticating the End User.
+ /// The first part of a parameter name that gives the custom string value for
+ /// the assurance level. The second part of the parameter name is the alias for
+ /// that assurance level.
/// </summary>
- /// <value>Integer value between 0 and 4 inclusive.</value>
- /// <remarks>
- /// Level 0 is not an assurance level defined by NIST, but rather SHOULD be used to signify that the OP recognizes the parameter and the End User authentication did not meet the requirements of Level 1. See Appendix A.1.2 (NIST Assurance Levels) for high-level example classifications of authentication methods within the defined levels.
- /// </remarks>
- internal const string NistAuthLevel = "nist_auth_level";
+ internal const string AuthLevelAliasPrefix = "auth_level.";
}
}
}
diff --git a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs index 2afc118..6358294 100644 --- a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs +++ b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs @@ -13,6 +13,8 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { /// before asserting or interpreting what these levels signify, notwithstanding
/// the brief summaries attached to each level in DotNetOpenId documentation.
/// http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf
+ ///
+ /// See PAPE spec Appendix A.1.2 (NIST Assurance Levels) for high-level example classifications of authentication methods within the defined levels.
/// </remarks>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Nist")]
public enum NistAssuranceLevel {
diff --git a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequest.cs b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequest.cs index 5fff170..eb78e1f 100644 --- a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequest.cs +++ b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequest.cs @@ -14,6 +14,7 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { /// </summary>
public PolicyRequest() {
PreferredPolicies = new List<string>(1);
+ PreferredAuthLevelTypes = new List<string>(1);
}
/// <summary>
@@ -33,6 +34,11 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { public IList<string> PreferredPolicies { get; private set; }
/// <summary>
+ /// Zero or more name spaces of the custom Assurance Level the RP requests, in the order of its preference.
+ /// </summary>
+ public IList<string> PreferredAuthLevelTypes { get; private set; }
+
+ /// <summary>
/// Tests equality between two <see cref="PolicyRequest"/> instances.
/// </summary>
public override bool Equals(object obj) {
@@ -43,6 +49,10 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { foreach(string policy in PreferredPolicies) {
if (!other.PreferredPolicies.Contains(policy)) return false;
}
+ if (PreferredAuthLevelTypes.Count != other.PreferredAuthLevelTypes.Count) return false;
+ foreach (string authLevel in PreferredAuthLevelTypes) {
+ if (!other.PreferredAuthLevelTypes.Contains(authLevel)) return false;
+ }
return true;
}
@@ -66,6 +76,19 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { // Even if empty, this parameter is required as part of the request message.
fields.Add(Constants.RequestParameters.PreferredAuthPolicies, SerializePolicies(PreferredPolicies));
+ if (PreferredAuthLevelTypes.Count > 0) {
+ AliasManager authLevelAliases = new AliasManager();
+ authLevelAliases.AssignAliases(PreferredAuthLevelTypes, Constants.AuthenticationLevels.PreferredTypeUriToAliasMap);
+
+ // Add a definition for each Auth Level Type alias.
+ foreach (string alias in authLevelAliases.Aliases) {
+ fields.Add(Constants.AuthLevelNamespaceDeclarationPrefix + alias, authLevelAliases.ResolveAlias(alias));
+ }
+
+ // Now use the aliases for those type URIs to list a preferred order.
+ fields.Add(Constants.RequestParameters.PreferredAuthLevelTypes, SerializeAuthLevels(PreferredAuthLevelTypes, authLevelAliases));
+ }
+
return fields;
}
@@ -84,6 +107,16 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { PreferredPolicies.Add(policy);
}
+ PreferredAuthLevelTypes.Clear();
+ AliasManager authLevelAliases = FindIncomingAliases(fields);
+ string preferredAuthLevelAliases;
+ if (fields.TryGetValue(Constants.RequestParameters.PreferredAuthLevelTypes, out preferredAuthLevelAliases)) {
+ foreach (string authLevelAlias in preferredAuthLevelAliases.Split(' ')) {
+ if (authLevelAlias.Length == 0) continue;
+ PreferredAuthLevelTypes.Add(authLevelAliases.ResolveAlias(authLevelAlias));
+ }
+ }
+
return true;
}
@@ -102,20 +135,54 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { #endregion
static internal string SerializePolicies(IList<string> policies) {
- Debug.Assert(policies != null);
- StringBuilder policyList = new StringBuilder();
- foreach (string policy in GetUniqueItems(policies)) {
- if (policy.Contains(" ")) {
+ return ConcatenateListOfElements(policies);
+ }
+
+ private static string SerializeAuthLevels(IList<string> preferredAuthLevelTypes, AliasManager aliases) {
+ var aliasList = new List<string>();
+ foreach (string typeUri in preferredAuthLevelTypes) {
+ aliasList.Add(aliases.GetAlias(typeUri));
+ }
+
+ return ConcatenateListOfElements(aliasList);
+ }
+
+ /// <summary>
+ /// Looks at the incoming fields and figures out what the aliases and name spaces for auth level types are.
+ /// </summary>
+ internal static AliasManager FindIncomingAliases(IDictionary<string, string> fields) {
+ AliasManager aliasManager = new AliasManager();
+
+ foreach (var pair in fields) {
+ if (!pair.Key.StartsWith(Constants.AuthLevelNamespaceDeclarationPrefix, StringComparison.Ordinal)) {
+ continue;
+ }
+
+ string alias = pair.Key.Substring(Constants.AuthLevelNamespaceDeclarationPrefix.Length);
+ aliasManager.SetAlias(alias, pair.Value);
+ }
+
+ aliasManager.SetPreferredAliasesWhereNotSet(Constants.AuthenticationLevels.PreferredTypeUriToAliasMap);
+
+ return aliasManager;
+ }
+
+ internal static string ConcatenateListOfElements(IList<string> values) {
+ Debug.Assert(values != null);
+ StringBuilder valuesList = new StringBuilder();
+ foreach (string value in GetUniqueItems(values)) {
+ if (value.Contains(" ")) {
throw new FormatException(string.Format(CultureInfo.CurrentCulture,
- Strings.InvalidUri, policy));
+ Strings.InvalidUri, value));
}
- policyList.Append(policy);
- policyList.Append(" ");
+ valuesList.Append(value);
+ valuesList.Append(" ");
}
- if (policyList.Length > 0)
- policyList.Length -= 1; // remove trailing space
- return policyList.ToString();
+ if (valuesList.Length > 0)
+ valuesList.Length -= 1; // remove trailing space
+ return valuesList.ToString();
}
+
static internal IEnumerable<T> GetUniqueItems<T>(IList<T> list) {
List<T> itemsSeen = new List<T>(list.Count);
foreach (T item in list) {
diff --git a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs index 96d3efc..0f5922f 100644 --- a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs +++ b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs @@ -17,6 +17,7 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { /// </summary>
public PolicyResponse() {
ActualPolicies = new List<string>(1);
+ AssuranceLevels = new Dictionary<string, string>(1);
}
/// <summary>
@@ -46,13 +47,37 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { }
}
}
+
+ /// <summary>
+ /// Optional. The Assurance Level as defined by the National Institute of Standards and Technology (NIST) in Special Publication 800-63 (Burr, W., Dodson, D., and W. Polk, Ed., “Electronic Authentication Guideline,” April 2006.) [NIST_SP800‑63] corresponding to the authentication method and policies employed by the OP when authenticating the End User.
/// </summary>
+ /// <remarks>
/// See PAPE spec Appendix A.1.2 (NIST Assurance Levels) for high-level example classifications of authentication methods within the defined levels.
/// </remarks>
+ public NistAssuranceLevel? NistAssuranceLevel {
+ get {
+ string levelString;
+ if (AssuranceLevels.TryGetValue(Constants.AuthenticationLevels.NistTypeUri, out levelString)) {
+ return (NistAssuranceLevel)Enum.Parse(typeof(NistAssuranceLevel), levelString);
+ } else {
+ return null;
+ }
+ }
+ set {
+ if (value != null) {
+ AssuranceLevels[Constants.AuthenticationLevels.NistTypeUri] = ((int)value).ToString(CultureInfo.InvariantCulture);
+ } else {
+ AssuranceLevels.Remove(Constants.AuthenticationLevels.NistTypeUri);
+ }
+ }
+ }
+
/// <summary>
- /// Optional. The Assurance Level as defined by the National Institute of Standards and Technology (NIST) in Special Publication 800-63 (Burr, W., Dodson, D., and W. Polk, Ed., “Electronic Authentication Guideline,” April 2006.) [NIST_SP800‑63] corresponding to the authentication method and policies employed by the OP when authenticating the End User.
+ /// Gets a dictionary where keys are the authentication level type URIs and
+ /// the values are the per authentication level defined custom value.
/// </summary>
/// <remarks>
- /// See PAPE spec Appendix A.1.2 (NIST Assurance Levels) for high-level example classifications of authentication methods within the defined levels.
+ /// A very common key is <see cref="Constants.AuthenticationLevels.NistTypeUri"/>
+ /// and values for this key are available in <see cref="NistAssuranceLevel"/>.
/// </remarks>
- public NistAssuranceLevel? NistAssuranceLevel { get; set; }
+ public IDictionary<string, string> AssuranceLevels { get; private set; }
/// <summary>
/// Tests equality between two <see cref="PolicyResponse"/> instances.
@@ -61,7 +86,10 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { PolicyResponse other = obj as PolicyResponse;
if (other == null) return false;
if (AuthenticationTimeUtc != other.AuthenticationTimeUtc) return false;
- if (NistAssuranceLevel != other.NistAssuranceLevel) return false;
+ if (AssuranceLevels.Count != other.AssuranceLevels.Count) return false;
+ foreach (var pair in AssuranceLevels) {
+ if (!other.AssuranceLevels.Contains(pair)) return false;
+ }
if (ActualPolicies.Count != other.ActualPolicies.Count) return false;
foreach (string policy in ActualPolicies) {
if (!other.ActualPolicies.Contains(policy)) return false;
@@ -81,12 +109,24 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { IDictionary<string, string> IExtensionResponse.Serialize(DotNetOpenId.Provider.IRequest authenticationRequest) {
var fields = new Dictionary<string, string>();
- fields.Add(Constants.ResponseParameters.AuthPolicies, PolicyRequest.SerializePolicies(ActualPolicies));
+ fields.Add(Constants.ResponseParameters.AuthPolicies, SerializePolicies(ActualPolicies));
if (AuthenticationTimeUtc.HasValue) {
fields.Add(Constants.ResponseParameters.AuthTime, AuthenticationTimeUtc.Value.ToUniversalTime().ToString(PermissibleDateTimeFormats[0], CultureInfo.InvariantCulture));
}
- if (NistAssuranceLevel.HasValue) {
- fields.Add(Constants.ResponseParameters.NistAuthLevel, ((int)NistAssuranceLevel).ToString(CultureInfo.InvariantCulture));
+
+ if (AssuranceLevels.Count > 0) {
+ AliasManager aliases = new AliasManager();
+ aliases.AssignAliases(AssuranceLevels.Keys, Constants.AuthenticationLevels.PreferredTypeUriToAliasMap);
+
+ // Add a definition for each Auth Level Type alias.
+ foreach (string alias in aliases.Aliases) {
+ fields.Add(Constants.AuthLevelNamespaceDeclarationPrefix + alias, aliases.ResolveAlias(alias));
+ }
+
+ // Now use the aliases for those type URIs to list the individual values.
+ foreach (var pair in AssuranceLevels) {
+ fields.Add(Constants.ResponseParameters.AuthLevelAliasPrefix + aliases.GetAlias(pair.Key), pair.Value);
+ }
}
return fields;
@@ -99,7 +139,7 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { ActualPolicies.Clear();
string[] actualPolicies = fields[Constants.ResponseParameters.AuthPolicies].Split(' ');
foreach (string policy in actualPolicies) {
- if (policy.Length > 0)
+ if (policy.Length > 0 && policy != AuthenticationPolicies.None)
ActualPolicies.Add(policy);
}
@@ -116,15 +156,13 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { }
}
- NistAssuranceLevel = null;
- string nistAuthLevel;
- if (fields.TryGetValue(Constants.ResponseParameters.NistAuthLevel, out nistAuthLevel)) {
- int nistAuthLevelNumber;
- if (int.TryParse(nistAuthLevel, out nistAuthLevelNumber) &&
- nistAuthLevelNumber >= 0 && nistAuthLevelNumber <= 4) {
- NistAssuranceLevel = (NistAssuranceLevel)nistAuthLevelNumber;
- } else {
- Logger.Error("Invalid NIST level.");
+ AssuranceLevels.Clear();
+ AliasManager authLevelAliases = PolicyRequest.FindIncomingAliases(fields);
+ foreach (string authLevelAlias in authLevelAliases.Aliases) {
+ string authValue;
+ if (fields.TryGetValue(Constants.ResponseParameters.AuthLevelAliasPrefix + authLevelAlias, out authValue)) {
+ string authLevelType = authLevelAliases.ResolveAlias(authLevelAlias);
+ AssuranceLevels[authLevelType] = authValue;
}
}
@@ -144,5 +182,13 @@ namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy { }
#endregion
+
+ static internal string SerializePolicies(IList<string> policies) {
+ if (policies.Count == 0) {
+ return AuthenticationPolicies.None;
+ } else {
+ return PolicyRequest.ConcatenateListOfElements(policies);
+ }
+ }
}
}
|