145 lines
6.2 KiB
C#
145 lines
6.2 KiB
C#
using Microsoft.Owin;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Owin.Security.Providers.OpenIDBase.Infrastructure
|
|
{
|
|
public class Message
|
|
{
|
|
public Message(IReadableStringCollection parameters, bool strict)
|
|
{
|
|
Namespaces = new Dictionary<string, Property>(StringComparer.Ordinal);
|
|
Properties = new Dictionary<string, Property>(parameters.Count(), StringComparer.Ordinal);
|
|
Add(parameters, strict);
|
|
}
|
|
|
|
public Dictionary<string, Property> Namespaces { get; }
|
|
public Dictionary<string, Property> Properties { get; }
|
|
|
|
/// <summary>
|
|
/// Adds the openid parameters from querystring or form body into Namespaces and Properties collections.
|
|
/// This normalizes the parameter name, by replacing the variable namespace alias with the
|
|
/// actual namespace in the collection's key, and will optionally skip any parameters that are
|
|
/// not signed if the strict argument is true.
|
|
/// </summary>
|
|
/// <param name="parameters">The keys and values of the incoming querystring or form body</param>
|
|
/// <param name="strict">True if keys that are not signed should be ignored</param>
|
|
private void Add(IReadableStringCollection parameters, bool strict)
|
|
{
|
|
IEnumerable<KeyValuePair<string, string[]>> addingParameters;
|
|
|
|
// strict is true if keys that are not signed should be strict
|
|
if (strict)
|
|
{
|
|
var signed = parameters.GetValues("openid.signed");
|
|
if (signed == null ||
|
|
signed.Count != 1)
|
|
{
|
|
// nothing is added if the signed parameter is not present
|
|
return;
|
|
}
|
|
|
|
// determine the set of keys that are signed, or which may be used without
|
|
// signing. ns, mode, signed, and sig each may be used without signing.
|
|
var strictKeys = new HashSet<string>(signed[0]
|
|
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
|
.Select(value => "openid." + value)
|
|
.Concat(new[] { "openid.ns", "openid.mode", "openid.signed", "openid.sig" }));
|
|
|
|
// the parameters to add are only the parameters what are in this set
|
|
addingParameters = parameters.Where(kv => strictKeys.Contains(kv.Key));
|
|
}
|
|
else
|
|
{
|
|
// when strict is false all of the incoming parameters are to be added
|
|
addingParameters = parameters;
|
|
}
|
|
|
|
// convert the incoming parameter strings into Property objects. the
|
|
// Key is the raw key name. The Name starts of being equal to Key with a
|
|
// trailing dot appended. The Value is the query or form value, with a comma delimiter
|
|
// inserted between multiply occuring values.
|
|
var addingProperties = addingParameters.Select(kv => new Property
|
|
{
|
|
Key = kv.Key,
|
|
Name = kv.Key + ".",
|
|
Value = string.Join(",", kv.Value)
|
|
}).ToArray();
|
|
|
|
// first, recognize which parameters are namespace declarations
|
|
|
|
var namespacePrefixes = new Dictionary<string, Property>(StringComparer.Ordinal);
|
|
foreach (var item in addingProperties.Where(item => item.Name.StartsWith("openid.ns.", StringComparison.Ordinal)))
|
|
{
|
|
// the value of the parameter is the uri of the namespace
|
|
item.Namespace = item.Value;
|
|
item.Name = "openid." + item.Name.Substring("openid.ns.".Length);
|
|
|
|
// the namespaces collection is keyed by the ns uri
|
|
Namespaces.Add(item.Namespace, item);
|
|
|
|
// and the prefixes collection is keyed by "openid.alias."
|
|
namespacePrefixes.Add(item.Name, item);
|
|
}
|
|
|
|
// second, recognize which parameters are property values
|
|
|
|
foreach (var item in addingProperties)
|
|
{
|
|
// anything with a namespace was already added to Namespaces
|
|
if (item.Namespace != null) continue;
|
|
// look for the namespace match for this property.
|
|
Property match = null;
|
|
|
|
// try finding where openid.alias.arg2 matches openid.ns.alies namespace
|
|
if (item.Name.StartsWith("openid.", StringComparison.Ordinal))
|
|
{
|
|
var dotIndex = item.Name.IndexOf('.', "openid.".Length);
|
|
if (dotIndex != -1)
|
|
{
|
|
var namespacePrefix = item.Name.Substring(0, dotIndex + 1);
|
|
namespacePrefixes.TryGetValue(namespacePrefix, out match);
|
|
}
|
|
}
|
|
|
|
// then try finding where openid.arg1 should match openid.ns namespace
|
|
if (match == null)
|
|
{
|
|
namespacePrefixes.TryGetValue("openid.", out match);
|
|
}
|
|
|
|
// when a namespace is found
|
|
if (match != null)
|
|
{
|
|
// the property's namespace is defined, and the namespace's prefix is removed
|
|
item.Namespace = match.Namespace;
|
|
item.Name = item.Name.Substring(match.Name.Length);
|
|
}
|
|
|
|
// the resulting property key is keyed by the local name and namespace
|
|
// so "openid.arg1" becomes "arg1.namespace-uri-of-openid"
|
|
// and "openid.alias.arg2" becomes "arg2.namespace-uri-of-alias"
|
|
Properties.Add(item.Name + item.Namespace, item);
|
|
}
|
|
}
|
|
|
|
public bool TryGetValue(string key, out string value)
|
|
{
|
|
Property property;
|
|
if (Properties.TryGetValue(key, out property))
|
|
{
|
|
value = property.Value;
|
|
return true;
|
|
}
|
|
value = null;
|
|
return false;
|
|
}
|
|
|
|
public IEnumerable<KeyValuePair<string, string>> ToFormValues()
|
|
{
|
|
return Namespaces.Concat(Properties).Select(pair => new KeyValuePair<string, string>(pair.Value.Key, pair.Value.Value));
|
|
}
|
|
}
|
|
}
|