//-----------------------------------------------------------------------
//
// Copyright (c) Andrew Arnott. All rights reserved.
//
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId.Extensions {
using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using DotNetOpenAuth.Messaging;
internal class ExtensionArgumentsManager : IIncomingExtensions, IOutgoingExtensions {
private Protocol protocol;
///
/// Whether extensions are being read or written.
///
private bool isReadMode;
private Extensions.AliasManager aliasManager = new Extensions.AliasManager();
///
/// A complex dictionary where the key is the Type URI of the extension,
/// and the value is another dictionary of the name/value args of the extension.
///
private Dictionary> extensions = new Dictionary>();
///
/// This contains a set of aliases that we must be willing to implicitly
/// match to namespaces for backward compatibility with other OpenID libraries.
///
private static readonly Dictionary typeUriToAliasAffinity = new Dictionary {
// TODO: re-enable these lines.
////{ Extensions.SimpleRegistration.Constants.sreg_ns, Extensions.SimpleRegistration.Constants.sreg_compatibility_alias },
////{ Extensions.ProviderAuthenticationPolicy.Constants.TypeUri, Extensions.ProviderAuthenticationPolicy.Constants.pape_compatibility_alias },
};
private ExtensionArgumentsManager() { }
public static ExtensionArgumentsManager CreateIncomingExtensions(IDictionary query) {
if (query == null) throw new ArgumentNullException("query");
var mgr = new ExtensionArgumentsManager();
mgr.protocol = Protocol.Detect(query);
mgr.isReadMode = true;
string aliasPrefix = mgr.protocol.openid.ns + ".";
// First pass looks for namespace aliases
foreach (var pair in query) {
if (pair.Key.StartsWith(aliasPrefix, StringComparison.Ordinal)) {
mgr.aliasManager.SetAlias(pair.Key.Substring(aliasPrefix.Length), pair.Value);
}
}
// For backwards compatibility, add certain aliases if they aren't defined.
foreach (var pair in typeUriToAliasAffinity) {
if (!mgr.aliasManager.IsAliasAssignedTo(pair.Key) &&
!mgr.aliasManager.IsAliasUsed(pair.Value)) {
mgr.aliasManager.SetAlias(pair.Value, pair.Key);
}
}
// Second pass looks for extensions using those aliases
foreach (var pair in query) {
if (!pair.Key.StartsWith(mgr.protocol.openid.Prefix, StringComparison.Ordinal)) continue;
string possibleAlias = pair.Key.Substring(mgr.protocol.openid.Prefix.Length);
int periodIndex = possibleAlias.IndexOf(".", StringComparison.Ordinal);
if (periodIndex >= 0) possibleAlias = possibleAlias.Substring(0, periodIndex);
string typeUri;
if ((typeUri = mgr.aliasManager.TryResolveAlias(possibleAlias)) != null) {
if (!mgr.extensions.ContainsKey(typeUri))
mgr.extensions[typeUri] = new Dictionary();
string key = periodIndex >= 0 ? pair.Key.Substring(mgr.protocol.openid.Prefix.Length + possibleAlias.Length + 1) : string.Empty;
mgr.extensions[typeUri].Add(key, pair.Value);
}
}
return mgr;
}
public static ExtensionArgumentsManager CreateOutgoingExtensions(Protocol protocol) {
var mgr = new ExtensionArgumentsManager();
mgr.protocol = protocol;
// Affinity for certain alias for backwards compatibility
foreach (var pair in typeUriToAliasAffinity) {
mgr.aliasManager.SetAlias(pair.Value, pair.Key);
}
return mgr;
}
///
/// Gets the actual arguments to add to a querystring or other response,
/// where type URI, alias, and actual key/values are all defined.
///
public IDictionary GetArgumentsToSend(bool includeOpenIdPrefix) {
if (isReadMode) throw new InvalidOperationException();
Dictionary args = new Dictionary();
foreach (var typeUriAndExtension in extensions) {
string typeUri = typeUriAndExtension.Key;
var extensionArgs = typeUriAndExtension.Value;
if (extensionArgs.Count == 0) continue;
string alias = aliasManager.GetAlias(typeUri);
// send out the alias declaration
string openidPrefix = includeOpenIdPrefix ? protocol.openid.Prefix : string.Empty;
args.Add(openidPrefix + protocol.openidnp.ns + "." + alias, typeUri);
string prefix = openidPrefix + alias;
foreach (var pair in extensionArgs) {
string key = prefix;
if (pair.Key.Length > 0) key += "." + pair.Key;
args.Add(key, pair.Value);
}
}
return args;
}
public void AddExtensionArguments(string extensionTypeUri, IDictionary arguments) {
if (isReadMode) {
throw new InvalidOperationException();
}
ErrorUtilities.VerifyNonZeroLength(extensionTypeUri, "extensionTypeUri");
ErrorUtilities.VerifyArgumentNotNull(arguments, "arguments");
if (arguments.Count == 0) return;
IDictionary extensionArgs;
if (!extensions.TryGetValue(extensionTypeUri, out extensionArgs)) {
extensions.Add(extensionTypeUri, extensionArgs = new Dictionary());
}
ErrorUtilities.VerifyProtocol(extensionArgs.Count == 0, OpenIdStrings.ExtensionAlreadyAddedWithSameTypeURI, extensionTypeUri);
foreach (var pair in arguments) {
extensionArgs.Add(pair.Key, pair.Value);
}
}
///
/// Gets the fields carried by a given OpenId extension.
///
/// The fields included in the given extension, or null if the extension is not present.
public IDictionary GetExtensionArguments(string extensionTypeUri) {
if (!isReadMode) throw new InvalidOperationException();
if (string.IsNullOrEmpty(extensionTypeUri)) throw new ArgumentNullException("extensionTypeUri");
IDictionary extensionArgs;
extensions.TryGetValue(extensionTypeUri, out extensionArgs);
return extensionArgs;
}
public bool ContainsExtension(string extensionTypeUri) {
if (!isReadMode) throw new InvalidOperationException();
return extensions.ContainsKey(extensionTypeUri);
}
}
}