You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
915 lines
26 KiB
915 lines
26 KiB
using System;
|
|
using System.IO;
|
|
using System.Web;
|
|
using System.Data;
|
|
using System.Text;
|
|
using System.Xml;
|
|
using System.Data.SqlClient;
|
|
using System.Xml.Serialization;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.Security.Principal;
|
|
using System.Web.Services.Protocols;
|
|
using System.Web.Security;
|
|
using UDDI;
|
|
using UDDI.Diagnostics;
|
|
using UDDI.API.Authentication;
|
|
|
|
namespace UDDI.API
|
|
{
|
|
public class UDDIExtension : SoapExtension
|
|
{
|
|
Data data;
|
|
DateTime begin;
|
|
|
|
private class Data
|
|
{
|
|
public bool log = true;
|
|
public bool https = false;
|
|
public bool validate = true;
|
|
public bool performance = true;
|
|
public bool authenticate = false;
|
|
public bool transaction = false;
|
|
public bool certificate = false;
|
|
public string messageType = "";
|
|
|
|
public Data(
|
|
bool log,
|
|
bool validate,
|
|
bool performance,
|
|
bool authenticate,
|
|
bool transaction,
|
|
bool https,
|
|
bool certificate,
|
|
string messageType )
|
|
{
|
|
this.log = log;
|
|
this.https = https;
|
|
this.validate = validate;
|
|
this.performance = performance;
|
|
this.authenticate = authenticate;
|
|
this.transaction = transaction;
|
|
this.certificate = certificate;
|
|
this.messageType = messageType;
|
|
}
|
|
}
|
|
|
|
static UDDIExtension()
|
|
{
|
|
Context.ContextType = ContextType.SOAP;
|
|
}
|
|
|
|
private void CheckForHttps( SoapMessage message )
|
|
{
|
|
Debug.Enter();
|
|
|
|
if( 1 == Config.GetInt( "Security.HTTPS", 1 ) )
|
|
{
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "URL: " + message.Url );
|
|
Debug.Verify( message.Url.ToLower().StartsWith( "https" ), "UDDI_ERROR_FATALERROR_HTTPSREQUIREDFORPUBLISH" );
|
|
}
|
|
else
|
|
{
|
|
Debug.Write( SeverityType.Warning, CategoryType.Soap, "HTTPS check is turned off. Content may be published without SSL. To turn this check on remove or modify the Security.HTTPS configuration setting" );
|
|
}
|
|
|
|
Debug.Leave();
|
|
}
|
|
|
|
private void CheckCertificate( SoapMessage message )
|
|
{
|
|
HttpClientCertificate httpCert = HttpContext.Current.Request.ClientCertificate;
|
|
X509Certificate requestCert = new X509Certificate( httpCert.Certificate );
|
|
|
|
Debug.Verify( !Utility.StringEmpty( httpCert.Issuer ), "UDDI_ERROR_FATALERROR_CLIENTCERTREQUIRED" );
|
|
Debug.Verify( !Utility.StringEmpty( httpCert.Subject ), "UDDI_ERROR_FATALERROR_CLIENTCERTREQUIRED" );
|
|
|
|
SqlStoredProcedureAccessor sp = new SqlStoredProcedureAccessor( "net_operatorCert_get" );
|
|
|
|
sp.Parameters.Add( "@certSerialNo", SqlDbType.NVarChar, UDDI.Constants.Lengths.CertSerialNo );
|
|
|
|
sp.Parameters.SetString( "@certSerialNo", requestCert.GetSerialNumberString() );
|
|
|
|
SqlDataReaderAccessor reader = sp.ExecuteReader();
|
|
|
|
try
|
|
{
|
|
if( reader.Read() )
|
|
{
|
|
Context.RemoteOperator = reader.GetGuidString( "operatorKey" );
|
|
|
|
byte[] operatorCertRaw = reader.GetBinary( "certificate" );
|
|
byte[] requestCertRaw = httpCert.Certificate;
|
|
|
|
Debug.Verify(
|
|
null != operatorCertRaw,
|
|
"UDDI_ERROR_FATALERROR_CLIENTCERTNOTSTORED",
|
|
ErrorType.E_fatalError,
|
|
Context.RemoteOperator );
|
|
|
|
if( operatorCertRaw.Length != requestCertRaw.Length )
|
|
{
|
|
throw new UDDIException(
|
|
ErrorType.E_unknownUser,
|
|
"UDDI_ERROR_UNKNOWNUSER_UNKOWNCERT" );
|
|
}
|
|
|
|
for( int i = 0; i < operatorCertRaw.Length; i ++ )
|
|
{
|
|
if( operatorCertRaw[ i ] != requestCertRaw[ i ] )
|
|
{
|
|
throw new UDDIException(
|
|
ErrorType.E_unknownUser,
|
|
"UDDI_ERROR_UNKNOWNUSER_UNKOWNCERT" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* TODO: Check to see if this works instead
|
|
*
|
|
|
|
X509Certificate operatorCert = new X509Certificate( operatorCertRaw );
|
|
X509Certificate requestCert = new X509Certificate( requestCertRaw );
|
|
|
|
if( !requestCert.Equals( operatorCert ) )
|
|
{
|
|
throw new UDDIException(
|
|
ErrorType.E_unknownUser,
|
|
"Unknown certificate" );
|
|
}
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
throw new UDDIException(
|
|
ErrorType.E_unknownUser,
|
|
"UDDI_ERROR_UNKNOWNUSER_UNKOWNCERT" );
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
reader.Close();
|
|
}
|
|
}
|
|
|
|
private void Validate( SoapMessage message )
|
|
{
|
|
Debug.Enter();
|
|
|
|
StreamReader srdr = new StreamReader( message.Stream, System.Text.Encoding.UTF8 );
|
|
#if DEBUG
|
|
Debug.Write( SeverityType.Verbose, CategoryType.None, srdr.ReadToEnd() );
|
|
message.Stream.Seek( 0,System.IO.SeekOrigin.Begin );
|
|
#endif
|
|
//
|
|
// Validate incoming XML, ValidateStream will rewind stream when finished
|
|
// so I don't have to.
|
|
//
|
|
SchemaCollection.Validate( message.Stream );
|
|
|
|
Debug.Leave();
|
|
}
|
|
|
|
private void PublishMethodBegin( SoapMessage message )
|
|
{
|
|
Debug.Enter();
|
|
begin = DateTime.Now;
|
|
Debug.Leave();
|
|
}
|
|
|
|
private void PublishMethodEnd( SoapMessage message )
|
|
{
|
|
Debug.Enter();
|
|
|
|
TimeSpan duration = DateTime.Now - begin;
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "Message took " + duration.TotalMilliseconds.ToString() + " ms" );
|
|
|
|
Performance.PublishMessageData( data.messageType, duration );
|
|
|
|
Debug.Leave();
|
|
}
|
|
|
|
//
|
|
// What follows is the logic for selection of the authentication algorithm
|
|
// Enjoy boys and girls
|
|
//
|
|
// Bit 3 - Anonymous User
|
|
// Bit 2 - UDDI Authentication Mode
|
|
// Bit 1 - Windows Authentication Mode
|
|
// Bit 0 - Ticket Present
|
|
// |
|
|
// | Authentication Module Used
|
|
// 0000 X
|
|
// 0001 X
|
|
// 0010 Windows
|
|
// 0011 Exception ( UDDI authentication turned off )
|
|
// 0100 UDDI ( will fail authentication due to invalid credentials )
|
|
// 0101 UDDI
|
|
// 0110 Windows
|
|
// 0111 UDDI
|
|
// 1000 X
|
|
// 1001 X
|
|
// 1010 Exception UDDI authentication turned off
|
|
// 1011 Exception ""
|
|
// 1100 UDDI ( will fail authentication due to invalid credentials )
|
|
// 1101 UDDI
|
|
// 1110 UDDI ( will fail authentication due to invalid credentials )
|
|
// 1111 UDDI
|
|
//
|
|
//
|
|
// Reduction Work
|
|
//
|
|
// A - Anonymous User
|
|
// B - UDDI Authentication Mode
|
|
// C - Windows Authentication Mode
|
|
// D - Ticket Present
|
|
//
|
|
// Key
|
|
// e - throw exception invalid configuration
|
|
// x - invalid state
|
|
// w - windows authentication
|
|
// u - uddi authentication
|
|
//
|
|
// CD
|
|
// AB 00 01 11 10
|
|
// 00 x x e w
|
|
// 01 u u u w
|
|
// 11 u u u u
|
|
// 10 x x e e
|
|
//
|
|
// if( !A && C && !D )
|
|
// w - windows authentication
|
|
// else if( B )
|
|
// u - uddi authentication
|
|
// else
|
|
// throw exception
|
|
//
|
|
|
|
private void Authenticate( SoapMessage message )
|
|
{
|
|
Debug.Enter();
|
|
|
|
IAuthenticateable authenticate = (IAuthenticateable) message.GetInParameterValue(0);
|
|
//WindowsIdentity identity = (WindowsIdentity)HttpContext.Current.User.Identity;
|
|
IIdentity identity = HttpContext.Current.User.Identity;
|
|
int mode = Config.GetInt( "Security.AuthenticationMode", (int) AuthenticationMode.Both );
|
|
|
|
if( mode == (int) AuthenticationMode.Passport )
|
|
{
|
|
if( identity is PassportIdentity )
|
|
{
|
|
string ticket = authenticate.AuthInfo.Trim();
|
|
|
|
//
|
|
// Authentication the user using the attached passport ticket
|
|
//
|
|
PassportAuthenticator pa = new PassportAuthenticator();
|
|
pa.Authenticate( ticket, Config.GetInt( "Security.TimeOut", 60 ) );
|
|
}
|
|
else
|
|
{
|
|
throw new UDDIException( ErrorType.E_fatalError,
|
|
"UDDI_ERROR_FATALERROR_PASSPORTBADCONFIG" ) ;
|
|
}
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "Authenticated user: using Passport based authentication Identity is " + identity.Name );
|
|
|
|
}
|
|
else if( !( (WindowsIdentity)identity ).IsAnonymous &&
|
|
( ( mode & (int) AuthenticationMode.Windows ) != 0 ) &&
|
|
Utility.StringEmpty( authenticate.AuthInfo ) )
|
|
{
|
|
/* 0X10 Case */
|
|
//
|
|
// Authenticate the user using the currently impersonated credentials
|
|
//
|
|
WindowsAuthenticator wa = new WindowsAuthenticator();
|
|
wa.Authenticate( authenticate.AuthInfo, Config.GetInt( "Security.TimeOut", 60 ) );
|
|
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "Authenticated user: using Windows based authentication Identity is " + identity.Name );
|
|
}
|
|
else if( ( mode & (int) AuthenticationMode.Uddi ) != 0 )
|
|
{
|
|
/* X1XX Case for leftovers */
|
|
//
|
|
// If windows authentication is turned off or the
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "Anonymous user: using UDDI authentication" );
|
|
|
|
//
|
|
// Authenticate the user using the authToken
|
|
//
|
|
UDDIAuthenticator ua = new UDDIAuthenticator();
|
|
ua.Authenticate( authenticate.AuthInfo, Config.GetInt( "Security.TimeOut", 60 ) );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Throw exception for the rest
|
|
//
|
|
throw new UDDIException( UDDI.ErrorType.E_unsupported,
|
|
"UDDI_ERROR_UNSUPPORTED_BADAUTHENTICATIONCONFIG" );
|
|
}
|
|
|
|
//
|
|
// Check to make sure the authenticated user has publisher credentials
|
|
//
|
|
Debug.Verify( Context.User.IsPublisher,
|
|
"UDDI_ERROR_FATALERROR_USERNOPUBLISHERCRED",
|
|
UDDI.ErrorType.E_fatalError,
|
|
Context.User.ID );
|
|
|
|
//
|
|
// The server can be configured for automatic registration of publishers with credentials
|
|
//
|
|
if( !Context.User.IsRegistered )
|
|
{
|
|
if( 1 == Config.GetInt( "Security.AutoRegister", 0 ) )
|
|
{
|
|
//
|
|
// Mark the user as verified.
|
|
//
|
|
Context.User.TrackPassport = false;
|
|
Context.User.Verified = true;
|
|
|
|
Context.User.Register();
|
|
}
|
|
else
|
|
{
|
|
throw new UDDIException( UDDI.ErrorType.E_unknownUser,
|
|
"UDDI_ERROR_UNKNOWNUSER_NOTREGISTERED" );
|
|
}
|
|
}
|
|
|
|
Context.User.Login();
|
|
#if DEBUG
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "Windows Identity is " + WindowsIdentity.GetCurrent().Name );
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "Thread Identity is " + System.Threading.Thread.CurrentPrincipal.Identity.Name );
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "HttpContext Identity is " + identity.Name );
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "IsAdministrator = " + Context.User.IsAdministrator );
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "IsCoordinator = " + Context.User.IsCoordinator );
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "IsPublisher = " + Context.User.IsPublisher );
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "IsUser = " + Context.User.IsUser );
|
|
#endif
|
|
Debug.Leave();
|
|
}
|
|
|
|
|
|
public override object GetInitializer( Type t )
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public override object GetInitializer( LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute )
|
|
{
|
|
UDDIExtensionAttribute attr = (UDDIExtensionAttribute) attribute;
|
|
return new Data( attr.log, attr.validate, attr.performance, attr.authenticate, attr.transaction, attr.https, attr.certificate, attr.messageType );
|
|
}
|
|
|
|
public override void Initialize( object initializer )
|
|
{
|
|
data = (UDDIExtension.Data) initializer;
|
|
}
|
|
|
|
public override void ProcessMessage(SoapMessage message)
|
|
{
|
|
Debug.Enter();
|
|
|
|
#if DEBUG
|
|
string info = "log: " + data.log.ToString() +
|
|
"; https: " + data.https.ToString() +
|
|
"; validate: " + data.validate.ToString() +
|
|
"; performance: " + data.performance.ToString() +
|
|
"; authenticate: " + data.authenticate.ToString() +
|
|
"; transaction: " + data.transaction.ToString() +
|
|
"; messageType: " + data.messageType;
|
|
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, info );
|
|
#endif
|
|
try
|
|
{
|
|
switch( message.Stage )
|
|
{
|
|
//
|
|
// First Event
|
|
//
|
|
case SoapMessageStage.BeforeDeserialize:
|
|
//
|
|
// Initialize our context.
|
|
//
|
|
Context.Current.Initialize();
|
|
|
|
Config.CheckForUpdate();
|
|
|
|
//
|
|
// TODO: Since we are using DispositionReport.ThrowFinal() I don't think this is
|
|
// needed anymore.
|
|
//
|
|
|
|
//
|
|
// Check to make sure the authenticated user has user credentials
|
|
//
|
|
Debug.Verify( "1" != HttpContext.Current.Request.ServerVariables[ "Exception" ],
|
|
"UDDI_ERROR_FATALERROR_VERSIONCHECKERROR",
|
|
UDDI.ErrorType.E_fatalError );
|
|
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "URL: " + message.Url );
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "SOAPAction: " + HttpContext.Current.Request.Headers[ "SOAPAction" ] );
|
|
|
|
string contentType = HttpContext.Current.Request.ContentType.ToLower();
|
|
|
|
bool validEncoding = ( contentType.IndexOf( "charset=\"utf-8\"" ) >= 0 ) ||
|
|
( contentType.IndexOf( "charset=utf-8" ) >= 0 );
|
|
|
|
Debug.Verify( validEncoding, "UDDI_ERROR_UNSUPPORTED_CONTENTTYPEHEADERMISSING", ErrorType.E_unsupported );
|
|
|
|
if( data.performance )
|
|
PublishMethodBegin( message );
|
|
|
|
if( data.https )
|
|
CheckForHttps( message );
|
|
|
|
//
|
|
// Validation has been moved into the other SOAP extension
|
|
//
|
|
// if( data.validate )
|
|
// Validate( message );
|
|
|
|
break;
|
|
|
|
//
|
|
// Second Event
|
|
//
|
|
case SoapMessageStage.AfterDeserialize:
|
|
|
|
ConnectionManager.Open( data.transaction, data.transaction );
|
|
|
|
if( data.certificate )
|
|
CheckCertificate( message );
|
|
|
|
if( data.authenticate )
|
|
Authenticate( message );
|
|
else if( 0 != ( Config.GetInt( "Security.AuthenticationMode", (int) AuthenticationMode.Both )
|
|
& (int) AuthenticationMode.AuthenticatedRead ) )
|
|
{
|
|
//
|
|
// Authenticated reads are turned on and this is a read request
|
|
// Make sure the caller is authenticated using Windows and is at least a user
|
|
//
|
|
WindowsIdentity identity = (WindowsIdentity) HttpContext.Current.User.Identity;
|
|
WindowsAuthenticator wa = new WindowsAuthenticator();
|
|
wa.Authenticate( "", 0 /* not used */ );
|
|
|
|
Debug.Write( SeverityType.Info, CategoryType.Soap, "Authenticated user: using Windows based authentication Identity is " + identity.Name );
|
|
|
|
//
|
|
// Check to make sure the authenticated user has user credentials
|
|
//
|
|
Debug.Verify( Context.User.IsUser,
|
|
"UDDI_ERROR_FATALERROR_NOUSERCREDS",
|
|
UDDI.ErrorType.E_fatalError,
|
|
Context.User.ID );
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// Third Event
|
|
//
|
|
case SoapMessageStage.BeforeSerialize:
|
|
break;
|
|
|
|
//
|
|
// Last Event
|
|
//
|
|
case SoapMessageStage.AfterSerialize:
|
|
|
|
//
|
|
// Cleanup the connection and commit the database activity
|
|
//
|
|
if( data.transaction &&
|
|
( null != (object) ConnectionManager.GetConnection() ) &&
|
|
( null != (object) ConnectionManager.GetTransaction() ) )
|
|
{
|
|
if( null == (object) message.Exception )
|
|
{
|
|
ConnectionManager.Commit();
|
|
}
|
|
else
|
|
{
|
|
ConnectionManager.Abort();
|
|
}
|
|
}
|
|
|
|
ConnectionManager.Close();
|
|
|
|
try
|
|
{
|
|
if( data.performance )
|
|
PublishMethodEnd( message );
|
|
}
|
|
catch
|
|
{
|
|
Debug.OperatorMessage(
|
|
SeverityType.Warning,
|
|
CategoryType.None,
|
|
OperatorMessageType.UnableToPublishCounter,
|
|
"An error occurred while trying to publish a performance counter, the system will continue" );
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_UNKNOWNEXTSTAGE" );
|
|
}
|
|
}
|
|
catch( Exception e )
|
|
{
|
|
DispositionReport.Throw( e );
|
|
}
|
|
|
|
Debug.Leave();
|
|
}
|
|
|
|
public override Stream ChainStream( Stream stream )
|
|
{
|
|
return base.ChainStream( stream );
|
|
}
|
|
}
|
|
|
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
|
|
public class UDDIExtensionAttribute : SoapExtensionAttribute
|
|
{
|
|
private int priority;
|
|
|
|
//
|
|
// The default constructor should be configured for the inquire API set
|
|
//
|
|
public UDDIExtensionAttribute() : this( true, true, true, false, false, false, false, "" ){}
|
|
public UDDIExtensionAttribute(
|
|
bool log,
|
|
bool validate,
|
|
bool performance,
|
|
bool authenticate,
|
|
bool transaction,
|
|
bool https,
|
|
bool certificate,
|
|
string messageType )
|
|
{
|
|
this.log = log;
|
|
this.https = https;
|
|
this.validate = validate;
|
|
this.performance = performance;
|
|
this.authenticate = authenticate;
|
|
this.transaction = transaction;
|
|
this.certificate = certificate;
|
|
this.messageType = messageType;
|
|
}
|
|
|
|
public override Type ExtensionType
|
|
{
|
|
get { return typeof(UDDIExtension); }
|
|
}
|
|
|
|
public override int Priority
|
|
{
|
|
get { return priority; }
|
|
set { priority = value; }
|
|
}
|
|
|
|
public bool log;
|
|
public bool https;
|
|
public bool validate;
|
|
public bool performance;
|
|
public bool authenticate;
|
|
public bool transaction;
|
|
public bool certificate;
|
|
public string messageType;
|
|
}
|
|
|
|
/// ********************************************************************
|
|
/// public class VersionSupportExtension
|
|
/// --------------------------------------------------------------------
|
|
/// <summary>
|
|
/// </summary>
|
|
/// ********************************************************************
|
|
///
|
|
public class VersionSupportExtension : SoapExtension
|
|
{
|
|
Stream oldStream;
|
|
Stream newStream;
|
|
|
|
public override object GetInitializer( LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute )
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public override object GetInitializer( Type type )
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public override void Initialize( object initializer )
|
|
{
|
|
}
|
|
|
|
public override void ProcessMessage(SoapMessage message)
|
|
{
|
|
try
|
|
{
|
|
switch( message.Stage )
|
|
{
|
|
case SoapMessageStage.BeforeDeserialize:
|
|
//
|
|
// Check to see if the server has been manually stopped.
|
|
//
|
|
if( 0 == Config.GetInt( "Run", 1 ) )
|
|
{
|
|
DispositionReport.ThrowFinal( new UDDIException( ErrorType.E_busy, "UDDI_ERROR_BUSY_SERVICENOTAVAILABLE" ) );
|
|
|
|
//
|
|
// DispositionReport.ThrowFinal will close the HTTP stream so there is no point going on in this method
|
|
//
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
//
|
|
// Validate against the UDDI schemas
|
|
//
|
|
SchemaCollection.Validate( oldStream );
|
|
}
|
|
catch( Exception e )
|
|
{
|
|
DispositionReport.ThrowFinal( new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_SCHEMAVALIDATIONFAILED", e.Message ) );
|
|
|
|
//
|
|
// DispositionReport.ThrowFinal will close the HTTP stream so there is no point going on in this method
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Make sure we only have 1 UDDI request in the SOAP body. This method will also set the versionMajor
|
|
// member.
|
|
//
|
|
CheckForSingleRequest( oldStream );
|
|
|
|
//
|
|
// If this is a v1 message, we'll first map it to the v2
|
|
// namespace so that it can be processed by the new
|
|
// library.
|
|
//
|
|
if( 1 == Context.ApiVersionMajor || 2 == Context.ApiVersionMajor)
|
|
{
|
|
TextReader reader = new StreamReader( oldStream );
|
|
TextWriter writer = new StreamWriter( newStream, new System.Text.UTF8Encoding( false ) );
|
|
string xml = reader.ReadToEnd();
|
|
|
|
if( 1 == Context.ApiVersionMajor )
|
|
{
|
|
xml = xml.Replace( "=\"urn:uddi-org:api\"", "=\"urn:uddi-org:api_v2\"" );
|
|
xml = xml.Replace( "='urn:uddi-org:api'", "=\"urn:uddi-org:api_v2\"" );
|
|
}
|
|
writer.Write( xml );
|
|
writer.Flush();
|
|
|
|
newStream.Position = 0;
|
|
}
|
|
|
|
break;
|
|
|
|
case SoapMessageStage.AfterDeserialize:
|
|
//
|
|
// After the message is deserialized is the earliest place where we
|
|
// have access to our SOAP headers.
|
|
//
|
|
CheckSOAPHeaders( message );
|
|
|
|
//
|
|
// Now that the message has been deserialized, make
|
|
// sure that the generic and xmlns attributes agree.
|
|
//
|
|
IMessage obj = message.GetInParameterValue( 0 ) as IMessage;
|
|
if( null != obj )
|
|
{
|
|
//
|
|
// We only need to do this if the deserialized object supports IMessage
|
|
//
|
|
string expected = Context.ApiVersionMajor + ".0";
|
|
string actual = obj.Generic.Trim();
|
|
|
|
if( expected != actual )
|
|
throw new UDDIException( ErrorType.E_unrecognizedVersion, "UDDI_ERROR_UNKNOWNVERSION_GENERICNAMESPACEMISMATCH" );
|
|
}
|
|
|
|
break;
|
|
|
|
case SoapMessageStage.BeforeSerialize:
|
|
break;
|
|
|
|
case SoapMessageStage.AfterSerialize:
|
|
|
|
//
|
|
// There may have been exceptions thrown during serialization.
|
|
//
|
|
if( null != message.Exception &&
|
|
( null == message.Exception.Detail ||
|
|
0 == message.Exception.Detail.ChildNodes.Count ) )
|
|
{
|
|
DispositionReport.ThrowFinal( new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_FAILEDDESERIALIZATION" ) );
|
|
|
|
//
|
|
// DispositionReport.ThrowFinal will close the HTTP stream so there is no point going on in this method
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If the original request was v1, then we'll need to
|
|
// remap the output to use the v1 namespace.
|
|
//
|
|
if( 1 == Context.ApiVersionMajor || 2 == Context.ApiVersionMajor )
|
|
{
|
|
newStream.Position = 0;
|
|
|
|
TextReader reader = new StreamReader( newStream );
|
|
TextWriter writer = new StreamWriter( oldStream, new System.Text.UTF8Encoding( false ) );
|
|
|
|
string xml = reader.ReadToEnd();
|
|
|
|
//
|
|
// We don't have to use the same 'loose' replacement as we did on the incoming request
|
|
// because our response will be serialized such that the default namespace is our UDDI
|
|
// namespace.
|
|
//
|
|
|
|
if( 1 == Context.ApiVersionMajor )
|
|
{
|
|
xml = xml.Replace( "xmlns=\"urn:uddi-org:api_v2\"", "xmlns=\"urn:uddi-org:api\"" );
|
|
xml = xml.Replace( "generic=\"2.0\"", "generic=\"1.0\"" );
|
|
}
|
|
writer.Write( xml );
|
|
writer.Flush();
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_UNKNOWNEXTSTAGE" );
|
|
}
|
|
}
|
|
catch( Exception e )
|
|
{
|
|
DispositionReport.Throw( e );
|
|
}
|
|
}
|
|
|
|
public override Stream ChainStream( Stream stream )
|
|
{
|
|
oldStream = stream;
|
|
newStream = new MemoryStream();
|
|
|
|
return newStream;
|
|
}
|
|
|
|
private void CheckSOAPHeaders( SoapMessage message )
|
|
{
|
|
// We want to check the following:
|
|
//
|
|
// - no SOAP Actor attribute exists
|
|
// - no SOAP headers can have a must_understand attribute set to true
|
|
//
|
|
// Go through each header in our message
|
|
//
|
|
foreach( SoapHeader header in message.Headers )
|
|
{
|
|
if( header.MustUnderstand )
|
|
{
|
|
//
|
|
// No headers can have this attribute set.
|
|
//
|
|
DispositionReport.ThrowFinal( new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_SOAP_MUSTUNDERSTANDATT" ) );
|
|
|
|
return;
|
|
}
|
|
|
|
if( header.Actor.Length > 0 )
|
|
{
|
|
//
|
|
// Can't have a SOAP Actor attribute set, generate a SOAP fault with
|
|
// no detail element and a 'Client' fault code
|
|
//
|
|
DispositionReport.ThrowFinal( new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_SOAP_ACTORATT" ) );
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// TODO: see if there is a way to better modularize this method and rename it.
|
|
//
|
|
private void CheckForSingleRequest( Stream stream )
|
|
{
|
|
try
|
|
{
|
|
//
|
|
// Move to the start of our stream
|
|
//
|
|
stream.Position = 0;
|
|
XmlTextReader requestReader = new XmlTextReader( oldStream );
|
|
requestReader.MoveToContent();
|
|
|
|
//
|
|
// TODO: should not hard-code SOAP names and namespaces
|
|
//
|
|
|
|
//
|
|
// Move to the beginning of the SOAP envelope
|
|
//
|
|
requestReader.ReadStartElement( "Envelope", "http://schemas.xmlsoap.org/soap/envelope/" );
|
|
|
|
//
|
|
// Move to the SOAP body
|
|
//
|
|
while( !requestReader.IsStartElement( "Body", "http://schemas.xmlsoap.org/soap/envelope/" ) && !requestReader.EOF )
|
|
{
|
|
requestReader.Skip();
|
|
}
|
|
|
|
//
|
|
// Advance the current node to the first child of Body. This is presumably the UDDI message
|
|
//
|
|
requestReader.ReadStartElement( "Body", "http://schemas.xmlsoap.org/soap/envelope/" );
|
|
requestReader.MoveToContent();
|
|
|
|
//
|
|
// This element MUST have a UDDI namespace
|
|
//
|
|
string uddiNamespace = requestReader.LookupNamespace( requestReader.Prefix );
|
|
|
|
switch( uddiNamespace )
|
|
{
|
|
case "urn:uddi-org:api":
|
|
{
|
|
Context.ApiVersionMajor = 1;
|
|
break;
|
|
}
|
|
case "urn:uddi-org:api_v2":
|
|
{
|
|
Context.ApiVersionMajor = 2;
|
|
break;
|
|
}
|
|
case "urn:uddi-microsoft-com:api_v2_extensions":
|
|
{
|
|
Context.ApiVersionMajor = 2;
|
|
break;
|
|
}
|
|
case "urn:uddi-org:repl":
|
|
{
|
|
Context.ApiVersionMajor = 2;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
//
|
|
// This is a problem, we don't have a UDDI namespace. Throw an exception and get out of here. The
|
|
// exception will be caught in our outer catch and sent to our client using DispositionReport.ThrowFinal.
|
|
//
|
|
|
|
throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_MISSINGUDDINS" );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Skip the children of this node
|
|
//
|
|
requestReader.Skip();
|
|
requestReader.MoveToContent();
|
|
|
|
//
|
|
// Reset our stream so someone else can use it.
|
|
//
|
|
stream.Position = 0;
|
|
|
|
//
|
|
// If we are not at the end of the Body tag, then we have multiple requests, we should reject the message.
|
|
//
|
|
if( false == requestReader.LocalName.Equals( "Body" ) )
|
|
{
|
|
DispositionReport.ThrowFinal( new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_SOAP_MULTIPLEREQUEST" ) );
|
|
}
|
|
}
|
|
catch( UDDIException uddiException )
|
|
{
|
|
DispositionReport.ThrowFinal( uddiException );
|
|
}
|
|
catch
|
|
{
|
|
//
|
|
// We'll get this exception if the message contains any invalid elements
|
|
//
|
|
DispositionReport.ThrowFinal( new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_SOAP_INVALIDELEMENT" ) );
|
|
}
|
|
}
|
|
}
|
|
}
|