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