//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IdentityModel;
using System.IdentityModel.Policy;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Security.Claims;
using System.Runtime;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.ServiceModel.Security.Tokens;
using System.Xml;
namespace System.ServiceModel.Security
{
///
/// This class derives from System.ServiceModel.Security.WSSecurityTokenSerializer and wraps a collection of SecurityTokenHandlers.
/// Any call to this serilaizer is delegated to the token handler and delegated to the base class if no token handler
/// is registered to handle this particular token or KeyIdentifier.
///
class WsSecurityTokenSerializerAdapter : WSSecurityTokenSerializer
{
SecureConversationVersion _scVersion;
SecurityTokenHandlerCollection _securityTokenHandlers;
bool _mapExceptionsToSoapFaults;
ExceptionMapper _exceptionMapper = new ExceptionMapper();
///
/// Initializes an instance of
///
///
/// The containing the set of
/// objects used for serializing and validating tokens found in WS-Trust messages.
///
public WsSecurityTokenSerializerAdapter( SecurityTokenHandlerCollection securityTokenHandlerCollection )
: this( securityTokenHandlerCollection, MessageSecurityVersion.Default.SecurityVersion )
{
}
///
/// Initializes an instance of
///
///
/// The containing the set of
/// objects used for serializing and validating tokens found in WS-Trust messages.
///
/// The SecurityTokenVersion of the base WSSecurityTokenSerializer.
public WsSecurityTokenSerializerAdapter( SecurityTokenHandlerCollection securityTokenHandlerCollection, SecurityVersion securityVersion )
: this( securityTokenHandlerCollection, securityVersion, true, new SamlSerializer(), null, null )
{
}
///
/// Initializes an instance of
///
///
/// The containing the set of
/// objects used for serializing and validating tokens found in WS-Trust messages.
///
/// The SecurityVersion of the base WSSecurityTokenSerializer.
/// Flag that determines if the serailization shoudl be BSP compliant.
/// Serializer for SAML 1.1 tokens.
/// SecurityStateEncoder used for resolving SCT.
/// The collection of known claim types.
public WsSecurityTokenSerializerAdapter( SecurityTokenHandlerCollection securityTokenHandlerCollection, SecurityVersion securityVersion, bool emitBspAttributes, SamlSerializer samlSerializer, SecurityStateEncoder stateEncoder, IEnumerable knownTypes )
: this( securityTokenHandlerCollection, securityVersion, TrustVersion.WSTrust13, SecureConversationVersion.WSSecureConversation13, emitBspAttributes, samlSerializer, stateEncoder, knownTypes )
{
}
///
/// Initializes an instance of
///
///
/// The containing the set of
/// objects used for serializing and validating tokens found in WS-Trust messages.
///
/// The SecurityVersion of the base WSSecurityTokenSerializer.
/// The TrustVersion of the serializer uses.
/// The SecureConversationVersion of the serializer.
/// Flag that determines if the serailization shoudl be BSP compliant.
/// Serializer for SAML 1.1 tokens.
/// SecurityStateEncoder used for resolving SCT.
/// The collection of known claim types.
public WsSecurityTokenSerializerAdapter( SecurityTokenHandlerCollection securityTokenHandlerCollection, SecurityVersion securityVersion, TrustVersion trustVersion, SecureConversationVersion secureConversationVersion, bool emitBspAttributes, SamlSerializer samlSerializer, SecurityStateEncoder stateEncoder, IEnumerable knownTypes )
: base( securityVersion, trustVersion, secureConversationVersion, emitBspAttributes, samlSerializer, stateEncoder, knownTypes )
{
if ( securityTokenHandlerCollection == null )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "securityTokenHandlerCollection" );
}
_scVersion = secureConversationVersion;
_securityTokenHandlers = securityTokenHandlerCollection;
}
///
/// Gets and Sets the property that describes if exceptions
/// should be mapped to SOAP Fault exceptions. Default is false.
///
public bool MapExceptionsToSoapFaults
{
get
{
return _mapExceptionsToSoapFaults;
}
set
{
_mapExceptionsToSoapFaults = value;
}
}
///
/// Gets the SecurityTokenHandlerCollection.
///
public SecurityTokenHandlerCollection SecurityTokenHandlers
{
get
{
return _securityTokenHandlers;
}
}
///
/// Gets or sets the ExceptionMapper to be used when throwing exceptions.
///
public ExceptionMapper ExceptionMapper
{
get
{
return _exceptionMapper;
}
set
{
if ( value == null )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "value" );
}
_exceptionMapper = value;
}
}
///
/// Checks if one of the wrapped SecurityTokenHandlers or the base WSSecurityTokenSerializer
/// can read the security token.
///
/// Reader to a Security token.
/// 'True' if the serializer can read the given Security Token.
/// The input parameter 'reader' is null.
protected override bool CanReadTokenCore( XmlReader reader )
{
if ( reader == null )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "reader" );
}
if ( _securityTokenHandlers.CanReadToken( reader ) )
{
return true;
}
return base.CanReadTokenCore( reader );
}
///
/// Checks if one of the wrapped SecurityTokenHandlers or the base WSSecurityTokenSerializer
/// can write the given security token.
///
/// SecurityToken instance.
/// 'True' if the serializer can write the given security token.
/// The input parameter 'token' is null.
protected override bool CanWriteTokenCore( SecurityToken token )
{
if ( token == null )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "token" );
}
if ( _securityTokenHandlers.CanWriteToken( token ) )
{
return true;
}
return base.CanWriteTokenCore( token );
}
///
/// Deserializes the SecurityToken from the given XmlReader.
///
/// Reader to a Security token.
/// Instance of SecurityTokenResolver.
/// 'True' if the serializer can read the given Security Token.
/// The input parameter 'reader' is null.
protected override SecurityToken ReadTokenCore( XmlReader reader, SecurityTokenResolver tokenResolver )
{
if ( reader == null )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "reader" );
}
try
{
foreach ( SecurityTokenHandler securityTokenHandler in _securityTokenHandlers )
{
if ( securityTokenHandler.CanReadToken( reader ) )
{
SecurityToken token = securityTokenHandler.ReadToken( reader, tokenResolver );
SessionSecurityToken sessionToken = token as SessionSecurityToken;
if ( sessionToken != null )
{
if ( sessionToken.SecureConversationVersion.AbsoluteUri != _scVersion.Namespace.Value )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperInvalidOperation( SR.GetString( SR.ID4053, sessionToken.SecureConversationVersion, _scVersion ) );
}
return SecurityContextSecurityTokenHelper.ConvertSessionTokenToSecurityContextSecurityToken(sessionToken);
}
else
{
return token;
}
}
}
return base.ReadTokenCore( reader, tokenResolver );
}
catch ( Exception ex )
{
if ( !( MapExceptionsToSoapFaults && _exceptionMapper.HandleSecurityTokenProcessingException( ex ) ) )
{
throw;
}
Fx.Assert( false, "ExceptionMapper did not handle an exception correctly." );
// This should never happen. ExceptionMapper will handle the exception, in which case,
// a fault exception is thrown or the original exception gets thrown.
}
return null;
}
///
/// Serializes the SecurityToken to the XmlWriter.
///
/// XmlWriter to write to.
/// The SecurityToken to serializer.
/// The input parameter 'writer' or 'token' is null.
protected override void WriteTokenCore( XmlWriter writer, SecurityToken token )
{
if ( writer == null )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "writer" );
}
if ( token == null )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "token" );
}
try
{
//
// Wire the session handler for SCT
//
SecurityContextSecurityToken sct = token as SecurityContextSecurityToken;
if ( sct != null )
{
//
// Bare SCT tokens are wrapped with a SessionSecurityToken.
// The property SessionSecurityToken.IsSecurityContextSecurityTokenWrapper will be true.
//
token = SecurityContextSecurityTokenHelper.ConvertSctToSessionToken( sct, _scVersion );
}
SecurityTokenHandler securityTokenHandler = _securityTokenHandlers[token];
if ( ( securityTokenHandler != null ) && ( securityTokenHandler.CanWriteToken ) )
{
securityTokenHandler.WriteToken( writer, token );
return;
}
base.WriteTokenCore( writer, token );
}
catch ( Exception ex )
{
if ( !( MapExceptionsToSoapFaults && _exceptionMapper.HandleSecurityTokenProcessingException( ex ) ) )
{
throw;
}
Fx.Assert( false, "ExceptionMapper did not handle an exception correctly." );
// This should never happen. ExceptionMapper will handle the exception, in which case,
// a fault exception is thrown or the original exception gets thrown.
}
}
///
/// Checks if one of the wrapped SecurityTokenHandlers or the base WSSecurityTokenSerializer
/// can read the security key identifier.
///
/// Reader pointing at a Security Key Identifier {ds:Keyinfo}.
/// 'True' if the serializer can read the given Security Key Identifier.
/// The is null.
protected override bool CanReadKeyIdentifierCore( XmlReader reader )
{
if ( reader == null )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "reader" );
}
if ( reader.IsStartElement( XmlSignatureConstants.Elements.KeyInfo, XmlSignatureConstants.Namespace ) )
{
return true;
}
else
{
return base.CanReadKeyIdentifierCore( reader );
}
}
///
/// Reads an SecurityKeyIdentifier from a XML stream.
///
/// An XML reader positioned at an SecurityKeyIdentifier (ds: KeyInfo) as defined in 'http://www.w3.org/TR/xmldsig-core'.
/// SecurityKeyIdentifier.
/// The is null.
/// If the is not positioned at KeyInfo element.
protected override SecurityKeyIdentifier ReadKeyIdentifierCore( XmlReader reader )
{
if ( reader == null )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "reader" );
}
if ( reader.IsStartElement( XmlSignatureConstants.Elements.KeyInfo, XmlSignatureConstants.Namespace ) )
{
KeyInfo keyInfo = new KeyInfo( this );
keyInfo.ReadXml( XmlDictionaryReader.CreateDictionaryReader( reader ) );
return keyInfo.KeyIdentifier;
}
else
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperXml( reader, SR.GetString( SR.ID4192 ) );
}
}
///
/// 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 override bool CanReadKeyIdentifierClauseCore( XmlReader reader )
{
if ( reader == null )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "reader" );
}
foreach ( SecurityTokenHandler securityTokenHandler in _securityTokenHandlers )
{
if ( securityTokenHandler.CanReadKeyIdentifierClause( reader ) )
{
return true;
}
}
return base.CanReadKeyIdentifierClauseCore( reader );
}
///
/// Checks if the wrapped SecurityTokenHandler or the base WSSecurityTokenSerializer can write the
/// given SecurityKeyIdentifierClause.
///
/// SecurityKeyIdentifierClause to be checked.
/// 'True' if the SecurityTokenKeyIdentifierClause can be written.
/// The input parameter 'keyIdentifierClause' is null.
protected override bool CanWriteKeyIdentifierClauseCore( SecurityKeyIdentifierClause keyIdentifierClause )
{
if ( keyIdentifierClause == null )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "keyIdentifierClause" );
}
foreach ( SecurityTokenHandler securityTokenHandler in _securityTokenHandlers )
{
if ( securityTokenHandler.CanWriteKeyIdentifierClause( keyIdentifierClause ) )
{
return true;
}
}
return base.CanWriteKeyIdentifierClauseCore( keyIdentifierClause );
}
///
/// Deserializes a SecurityKeyIdentifierClause from the given reader.
///
/// XmlReader to a SecurityKeyIdentifierClause.
/// The deserialized SecurityKeyIdentifierClause.
/// The input parameter 'reader' is null.
protected override SecurityKeyIdentifierClause ReadKeyIdentifierClauseCore( XmlReader reader )
{
if ( reader == null )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "reader" );
}
try
{
foreach ( SecurityTokenHandler securityTokenHandler in _securityTokenHandlers )
{
if ( securityTokenHandler.CanReadKeyIdentifierClause( reader ) )
{
return securityTokenHandler.ReadKeyIdentifierClause( reader );
}
}
return base.ReadKeyIdentifierClauseCore( reader );
}
catch ( Exception ex )
{
if ( !( MapExceptionsToSoapFaults && _exceptionMapper.HandleSecurityTokenProcessingException( ex ) ) )
{
throw;
}
Fx.Assert( false, "ExceptionMapper did not handle an exception correctly." );
// This should never happen. ExceptionMapper will handle the exception, in which case,
// a fault exception is thrown or the original exception gets thrown.
}
return null;
}
///
/// Serializes the given SecurityKeyIdentifierClause in a XmlWriter.
///
/// XmlWriter to write into.
/// SecurityKeyIdentifierClause to be written.
/// The input parameter 'writer' or 'keyIdentifierClause' is null.
protected override void WriteKeyIdentifierClauseCore( XmlWriter writer, SecurityKeyIdentifierClause keyIdentifierClause )
{
if ( writer == null )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "writer" );
}
if ( keyIdentifierClause == null )
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "keyIdentifierClause" );
}
try
{
foreach ( SecurityTokenHandler securityTokenHandler in _securityTokenHandlers )
{
if ( securityTokenHandler.CanWriteKeyIdentifierClause( keyIdentifierClause ) )
{
securityTokenHandler.WriteKeyIdentifierClause( writer, keyIdentifierClause );
return;
}
}
base.WriteKeyIdentifierClauseCore( writer, keyIdentifierClause );
}
catch ( Exception ex )
{
if ( !( MapExceptionsToSoapFaults && _exceptionMapper.HandleSecurityTokenProcessingException( ex ) ) )
{
throw;
}
Fx.Assert( false, "ExceptionMapper did not handle an exception correctly." );
// This should never happen. ExceptionMapper will handle the exception, in which case,
// a fault exception is thrown or the original exception gets thrown.
}
}
}
}