//-----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//-----------------------------------------------------------------------
namespace System.IdentityModel.Tokens
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IdentityModel.Selectors;
using System.Security.Claims;
using System.Xml;
///
/// Defines a collection of SecurityTokenHandlers.
///
public class SecurityTokenHandlerCollection : Collection
{
internal static int defaultHandlerCollectionCount = 8;
private Dictionary handlersByIdentifier = new Dictionary();
private Dictionary handlersByType = new Dictionary();
private SecurityTokenHandlerConfiguration configuration;
private KeyInfoSerializer keyInfoSerializer;
///
/// Creates an instance of .
/// Creates an empty set.
///
public SecurityTokenHandlerCollection()
: this(new SecurityTokenHandlerConfiguration())
{
}
///
/// Creates an instance of .
/// Creates an empty set.
///
/// The configuration to associate with the collection.
public SecurityTokenHandlerCollection(SecurityTokenHandlerConfiguration configuration)
{
if (configuration == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("configuration");
}
this.configuration = configuration;
this.keyInfoSerializer = new KeyInfoSerializer(true);
}
///
/// Creates an instance of
///
/// List of SecurityTokenHandlers to initialize from.
///
/// Do not use this constructor to attempt to clone an instance of a SecurityTokenHandlerCollection,
/// use the Clone method instead.
///
public SecurityTokenHandlerCollection(IEnumerable handlers)
: this(handlers, new SecurityTokenHandlerConfiguration())
{
}
///
/// Creates an instance of
///
/// List of SecurityTokenHandlers to initialize from.
/// The in effect.
///
/// Do not use this constructor to attempt to clone an instance of a SecurityTokenHandlerCollection,
/// use the Clone method instead.
///
public SecurityTokenHandlerCollection(IEnumerable handlers, SecurityTokenHandlerConfiguration configuration)
: this(configuration)
{
if (handlers == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("handlers");
}
foreach (SecurityTokenHandler handler in handlers)
{
Add(handler);
}
}
///
/// Gets an instance of
///
public SecurityTokenHandlerConfiguration Configuration
{
get { return this.configuration; }
}
///
/// Gets the List of System.Type of the Token Handlers in this collection.
///
public IEnumerable TokenTypes
{
get { return this.handlersByType.Keys; }
}
///
/// Gets the list of Token type Identifier of the Token Handlers.
///
public IEnumerable TokenTypeIdentifiers
{
get
{
return this.handlersByIdentifier.Keys;
}
}
///
/// Gets a Token Handler by its Token Type Identifier.
///
/// The Token Type Identfier string to search for.
/// Instance of a SecurityTokenHandler.
public SecurityTokenHandler this[string tokenTypeIdentifier]
{
get
{
if (string.IsNullOrEmpty(tokenTypeIdentifier))
{
return null;
}
SecurityTokenHandler handler;
this.handlersByIdentifier.TryGetValue(tokenTypeIdentifier, out handler);
return handler;
}
}
///
/// Gets a Token Handler that can handle a given SecurityToken.
///
/// SecurityToken for which a Token Handler is requested.
/// Instance of SecurityTokenHandler.
public SecurityTokenHandler this[SecurityToken token]
{
get
{
if (null == token)
{
return null;
}
return this[token.GetType()];
}
}
///
/// Gets a Token Handler based on the System.Type of the token.
///
/// System.Type of the Token that needs to be handled.
/// Instance of SecurityTokenHandler.
public SecurityTokenHandler this[Type tokenType]
{
get
{
SecurityTokenHandler handler = null;
if (tokenType != null)
{
this.handlersByType.TryGetValue(tokenType, out handler);
}
return handler;
}
}
///
/// Creates a system default collection of basic SecurityTokenHandlers, each of which has the system default configuration.
/// The SecurityTokenHandlers in this collection must be configured with service specific data before they can be used.
///
/// A SecurityTokenHandlerCollection with default basic SecurityTokenHandlers.
public static SecurityTokenHandlerCollection CreateDefaultSecurityTokenHandlerCollection()
{
return CreateDefaultSecurityTokenHandlerCollection(new SecurityTokenHandlerConfiguration());
}
///
/// Creates a system default collection of basic SecurityTokenHandlers, each of which has the system default configuration.
/// The SecurityTokenHandlers in this collection must be configured with service specific data before they can be used.
///
/// The configuration to associate with the collection.
/// A SecurityTokenHandlerCollection with default basic SecurityTokenHandlers.
public static SecurityTokenHandlerCollection CreateDefaultSecurityTokenHandlerCollection(SecurityTokenHandlerConfiguration configuration)
{
SecurityTokenHandlerCollection collection = new SecurityTokenHandlerCollection(new SecurityTokenHandler[] {
new KerberosSecurityTokenHandler(),
new RsaSecurityTokenHandler(),
new SamlSecurityTokenHandler(),
new Saml2SecurityTokenHandler(),
new WindowsUserNameSecurityTokenHandler(),
new X509SecurityTokenHandler(),
new EncryptedSecurityTokenHandler(),
new SessionSecurityTokenHandler(),
},
configuration);
defaultHandlerCollectionCount = collection.Count;
return collection;
}
internal SecurityTokenSerializer KeyInfoSerializer
{
get { return this.keyInfoSerializer; }
}
///
/// Adds a new handler or replace the existing handler with the same token type identifier
/// with with the new handler.
///
/// The SecurityTokenHandler to add or replace
/// When the input parameter is null.
public void AddOrReplace(SecurityTokenHandler handler)
{
if (handler == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("handler");
}
// Remove the old one if it exists
Type tokenType = handler.TokenType;
if (tokenType != null && this.handlersByType.ContainsKey(tokenType))
{
Remove(this[tokenType]);
}
else
{
string[] identifiers = handler.GetTokenTypeIdentifiers();
if (identifiers != null)
{
foreach (string tokenTypeIdentifier in identifiers)
{
if (tokenTypeIdentifier != null && this.handlersByIdentifier.ContainsKey(tokenTypeIdentifier))
{
Remove(this[tokenTypeIdentifier]);
break;
}
}
}
}
// Add the new handler in the collection
Add(handler);
}
///
/// Checks if a token can be read using the SecurityTokenHandlers.
///
/// XmlReader pointing at token.
/// True if the token can be read, false otherwise
/// The input argument 'reader' is null.
public bool CanReadToken(XmlReader reader)
{
if (reader == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
}
foreach (SecurityTokenHandler handler in this)
{
if (null != handler && handler.CanReadToken(reader))
{
return true;
}
}
return false;
}
///
/// Checks if a token can be read using the SecurityTokenHandlers.
///
/// The token string thats needs to be read.
/// True if the token can be read, false otherwise
/// The input argument 'tokenString' is null or empty.
public bool CanReadToken(string tokenString)
{
if (String.IsNullOrEmpty(tokenString))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("tokenString");
}
foreach (SecurityTokenHandler handler in this)
{
if (null != handler && handler.CanReadToken(tokenString))
{
return true;
}
}
return false;
}
///
/// Checks if a token can be written using the SecurityTokenHandlers.
///
/// SecurityToken to be written out.
/// True if the token can be written, false otherwise
/// The input argument 'token' is null.
public bool CanWriteToken(SecurityToken token)
{
if (token == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
}
SecurityTokenHandler handler = this[token];
if (null != handler && handler.CanWriteToken)
{
return true;
}
return false;
}
///
/// Creates a SecurityToken from the given SecurityTokenDescriptor using the list of
/// SecurityTokenHandlers.
///
/// SecurityTokenDescriptor for the token to be created.
/// Instance of
/// The input argument 'tokenDescriptor' is null.
public SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor)
{
if (tokenDescriptor == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenDescriptor");
}
SecurityTokenHandler handler = this[tokenDescriptor.TokenType];
if (null == handler)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4020, tokenDescriptor.TokenType)));
}
return handler.CreateToken(tokenDescriptor);
}
///
/// Validates a given token using the SecurityTokenHandlers.
///
/// The SecurityToken to be validated.
/// A of representing the identities contained in the token.
/// The input argument 'token' is null.
/// A cannot be found that can validate the .
public ReadOnlyCollection ValidateToken(SecurityToken token)
{
if (token == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
}
SecurityTokenHandler handler = this[token];
if (null == handler || !handler.CanValidateToken)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4011, token.GetType())));
}
return handler.ValidateToken(token);
}
///
/// Reads a token using the TokenHandlers.
///
/// XmlReader pointing at token.
/// Instance of
/// The input argument 'reader' is null.
public SecurityToken ReadToken(XmlReader reader)
{
if (reader == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
}
foreach (SecurityTokenHandler handler in this)
{
if (null != handler && handler.CanReadToken(reader))
{
return handler.ReadToken(reader);
}
}
return null;
}
///
/// Reads a token using the TokenHandlers.
///
/// The token string to be deserialized.
/// Instance of
/// The input argument 'tokenString' is null or empty.
public SecurityToken ReadToken(string tokenString)
{
if (String.IsNullOrEmpty(tokenString))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("tokenString");
}
foreach (SecurityTokenHandler handler in this)
{
if (null != handler && handler.CanReadToken(tokenString))
{
return handler.ReadToken(tokenString);
}
}
return null;
}
///
/// Writes a given SecurityToken to the XmlWriter.
///
/// XmlWriter to write the token into.
/// SecurityToken to be written out.
/// The input argument 'writer' or 'token' is null.
public void WriteToken(XmlWriter writer, SecurityToken token)
{
if (writer == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
}
if (token == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
}
SecurityTokenHandler handler = this[token];
if (null == handler || !handler.CanWriteToken)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4010, token.GetType())));
}
handler.WriteToken(writer, token);
}
///
/// Writes a given SecurityToken to a string.
///
/// SecurityToken to be written out.
/// The serialized token.
/// The input argument 'token' is null.
public string WriteToken(SecurityToken token)
{
if (token == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
}
SecurityTokenHandler handler = this[token];
if (null == handler || !handler.CanWriteToken)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4010, token.GetType())));
}
return handler.WriteToken(token);
}
///
/// Override. (Inherited from Collection<T>"/>
///
protected override void ClearItems()
{
base.ClearItems();
this.handlersByIdentifier.Clear();
this.handlersByType.Clear();
}
///
/// Override. (Inherited from Collection<T>"/>
///
/// The zero-based index at which item should be inserted.
/// The object to insert. The value can be null for reference types.
protected override void InsertItem(int index, SecurityTokenHandler item)
{
base.InsertItem(index, item);
try
{
this.AddToDictionaries(item);
}
catch
{
base.RemoveItem(index);
throw;
}
}
///
/// Override. (Inherited from Collection<T>"/>
///
/// The zero-based index of the element to remove.
protected override void RemoveItem(int index)
{
SecurityTokenHandler removedItem = Items[index];
base.RemoveItem(index);
this.RemoveFromDictionaries(removedItem);
}
///
/// Override. (Inherited from Collection<T>"/>
///
/// The zero-based index of the element to replace.
/// The new value for the element at the specified index. The value can be null for reference types.
protected override void SetItem(int index, SecurityTokenHandler item)
{
SecurityTokenHandler replaced = Items[index];
base.SetItem(index, item);
this.RemoveFromDictionaries(replaced);
try
{
this.AddToDictionaries(item);
}
catch
{
base.SetItem(index, replaced);
this.AddToDictionaries(replaced);
throw;
}
}
public bool CanReadKeyIdentifierClause(XmlReader reader)
{
if (reader == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
}
return CanReadKeyIdentifierClauseCore(reader);
}
///
/// Checks if the wrapped SecurityTokenHandler or the base WSSecurityTokenSerializer can read the
/// SecurityKeyIdentifierClause.
///
/// Reader to a SecurityKeyIdentifierClause.
/// 'True' if the SecurityKeyIdentifierCause can be read.
/// The input parameter 'reader' is null.
protected virtual bool CanReadKeyIdentifierClauseCore(XmlReader reader)
{
if (reader == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
}
foreach (SecurityTokenHandler securityTokenHandler in this)
{
if (securityTokenHandler.CanReadKeyIdentifierClause(reader))
{
return true;
}
}
return false;
}
public SecurityKeyIdentifierClause ReadKeyIdentifierClause(XmlReader reader)
{
if (reader == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
}
return ReadKeyIdentifierClauseCore(reader);
}
///
/// Deserializes a SecurityKeyIdentifierClause from the given reader.
///
/// XmlReader to a SecurityKeyIdentifierClause.
/// The deserialized SecurityKeyIdentifierClause.
/// The input parameter 'reader' is null.
protected virtual SecurityKeyIdentifierClause ReadKeyIdentifierClauseCore(XmlReader reader)
{
if (reader == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
}
foreach (SecurityTokenHandler securityTokenHandler in this)
{
if (securityTokenHandler.CanReadKeyIdentifierClause(reader))
{
return securityTokenHandler.ReadKeyIdentifierClause(reader);
}
}
return this.keyInfoSerializer.ReadKeyIdentifierClause(reader);
}
public void WriteKeyIdentifierClause(XmlWriter writer, SecurityKeyIdentifierClause keyIdentifierClause)
{
if (writer == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
}
if (keyIdentifierClause == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("keyIdentifierClause");
}
WriteKeyIdentifierClauseCore(writer, keyIdentifierClause);
}
///
/// Serializes the given SecurityKeyIdentifierClause in a XmlWriter.
///
/// XmlWriter to write into.
/// SecurityKeyIdentifierClause to be written.
/// The input parameter 'writer' or 'keyIdentifierClause' is null.
protected virtual void WriteKeyIdentifierClauseCore(XmlWriter writer, SecurityKeyIdentifierClause keyIdentifierClause)
{
if (writer == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
}
if (keyIdentifierClause == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("keyIdentifierClause");
}
foreach (SecurityTokenHandler securityTokenHandler in this)
{
if (securityTokenHandler.CanWriteKeyIdentifierClause(keyIdentifierClause))
{
securityTokenHandler.WriteKeyIdentifierClause(writer, keyIdentifierClause);
return;
}
}
this.keyInfoSerializer.WriteKeyIdentifierClause(writer, keyIdentifierClause);
}
private void AddToDictionaries(SecurityTokenHandler handler)
{
if (handler == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("handler");
}
bool firstSucceeded = false;
string[] identifiers = handler.GetTokenTypeIdentifiers();
if (identifiers != null)
{
foreach (string typeId in identifiers)
{
if (typeId != null)
{
this.handlersByIdentifier.Add(typeId, handler);
firstSucceeded = true;
}
}
}
Type type = handler.TokenType;
if (handler.TokenType != null)
{
try
{
this.handlersByType.Add(type, handler);
}
catch
{
if (firstSucceeded)
{
this.RemoveFromDictionaries(handler);
}
throw;
}
}
// Ensure that the handler knows which collection it is in.
handler.ContainingCollection = this;
// Propagate this collection's STH configuration to the handler
// if the handler's configuration is unset.
if (handler.Configuration == null)
{
handler.Configuration = this.configuration;
}
}
private void RemoveFromDictionaries(SecurityTokenHandler handler)
{
string[] identifiers = handler.GetTokenTypeIdentifiers();
if (identifiers != null)
{
foreach (string typeId in identifiers)
{
if (typeId != null)
{
this.handlersByIdentifier.Remove(typeId);
}
}
}
Type type = handler.TokenType;
if (type != null && this.handlersByType.ContainsKey(type))
{
this.handlersByType.Remove(type);
}
// Ensure that the handler knows that it is no longer
// in a collection.
handler.ContainingCollection = null;
handler.Configuration = null;
}
}
}