using System; using System.IO; using System.Net; using System.Text; using System.Xml; using System.Xml.Serialization; using System.Security.Principal; using System.Security.Cryptography; using System.Runtime.InteropServices; using Microsoft.Win32; using UDDI; using UDDI.Diagnostics; namespace UDDI.API.Authentication { /// ******************************************************************** /// public DiscardAuthToken /// -------------------------------------------------------------------- /// /// Represents a discard_authToken message. /// /// ******************************************************************** /// [XmlRootAttribute("discard_authToken", Namespace=UDDI.API.Constants.Namespace)] public class DiscardAuthToken : IMessage { // // Attribute: generic // private string generic; [XmlAttribute("generic")] public string Generic { get { return generic; } set { generic = value; } } // // Element: authInfo // [XmlElement("authInfo")] public string AuthInfo = ""; } /// ******************************************************************** /// public GetAuthToken /// -------------------------------------------------------------------- /// /// Represents a get_authToken message. /// /// ******************************************************************** /// [XmlRootAttribute("get_authToken", Namespace=UDDI.API.Constants.Namespace)] public class GetAuthToken : IMessage { // // Attribute: generic // private string generic; [XmlAttribute("generic")] public string Generic { get { return generic; } set { generic = value; } } // // Attribute: userID // [XmlAttribute("userID")] public string UserID = ""; // // Attribute: cred // [XmlAttribute("cred")] public string Cred = ""; } /// ******************************************************************** /// public AuthToken /// -------------------------------------------------------------------- /// /// Represents an authToken. /// /// ******************************************************************** /// [XmlRootAttribute("authToken", Namespace=UDDI.API.Constants.Namespace)] public class AuthToken { // // Attribute: operator // [XmlAttribute("operator")] public string Operator = Config.GetString( "Operator" ); // // Attribute: generic // [XmlAttribute("generic")] public string Generic = Constants.Version; // // Element: authInfo // [XmlElement("authInfo")] public string AuthInfo; } public interface IAuthenticator { bool GetAuthenticationInfo( string userid, string password, out string ticket ); bool Authenticate( string strTicket, int timeWindow ); } public class WindowsAuthenticator : IAuthenticator { public bool GetAuthenticationInfo( string userid, string password, out string ticket ) { try { // // Verify that a userid and password were not specifed // Debug.Verify( Utility.StringEmpty( userid ) && Utility.StringEmpty( password ), "UDDI_ERROR_FATALERROR_USERIDANDPASSINWINAUTH" ); // // This form of authentication requires the impersonation of the caller // on the current thread of activity. // // // The ticket will not be used for this form of authentication // ticket = ""; // // Setup the user credentials so we can verify that the user is a publisher // IPrincipal principal = System.Threading.Thread.CurrentPrincipal; Context.User.SetRole( principal ); } catch( Exception exception ) { // // Log the real exception // Debug.Write( SeverityType.FailAudit, CategoryType.Authorization, exception.ToString() ); // // Preserve the error number if it was a UDDIException; but DO NOT preserve the error // message. We want the error message in the log, but not shown to the user for security // reasons. // UDDIException uddiException = exception as UDDIException; if( null != uddiException ) { throw new UDDIException( uddiException.Number, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } else { throw new UDDIException( UDDI.ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } } return true; } public bool Authenticate( string strTicket, int timeWindow ) { try { // // TODO: Verify strTicket is empty // // // No timeout check is possible for Windows Authentication // IPrincipal principal = System.Threading.Thread.CurrentPrincipal; Context.User.SetRole( principal ); } catch( Exception exception ) { // // Log the real exception // Debug.Write( SeverityType.FailAudit, CategoryType.Authorization, exception.ToString() ); // // Preserve the error number if it was a UDDIException; but DO NOT preserve the error // message. We want the error message in the log, but not shown to the user for security // reasons. // UDDIException uddiException = exception as UDDIException; if( null != uddiException ) { throw new UDDIException( uddiException.Number, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } else { throw new UDDIException( UDDI.ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } } return true; } } public class UDDIAuthenticator : IAuthenticator { const string ResetKeyDateFormat = "MM/dd/yyyy HH:mm:ss"; [DllImport("advapi32.dll", SetLastError=true)] public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out int phToken); private string DomainFromUserID( string userid ) { return userid.Substring( 0, userid.IndexOf( '\\' ) ); } private string BaseUserNameFromUserID( string userid ) { return userid.Substring( userid.IndexOf( '\\' ) + 1 ); } public bool GetAuthenticationInfo( string userid, string password, out string ticket ) { try { Debug.VerifySetting( "Security.Key" ); Debug.VerifySetting( "Security.IV" ); ticket = null; // // TODO: Need to support UPN formed user names // // // TODO: Need to look at use of this call on Domain controllers // // // The user account must have Log On Locally permission on the local computer. // This permission is granted to all users on workstations and servers, // but only to administrators on domain controllers. // // // Check userid and password by logging in // int windowstoken; // The Windows NT user token. string baseName = BaseUserNameFromUserID( userid ); string domain = DomainFromUserID( userid ); bool loggedOn = LogonUser( baseName,// User name. domain, // Domain name. password, // Password. 3, // Logon type = LOGON32_LOGON_NETWORK 0, // Logon provider = LOGON32_PROVIDER_DEFAULT out windowstoken ); // The user token for the specified user is returned here. if( !loggedOn ) { Debug.Write( SeverityType.FailAudit, CategoryType.Authorization, userid + " failed logon. Error code was " + Marshal.GetLastWin32Error().ToString() ); throw new UDDIException( UDDI.ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } WindowsIdentity wi = new WindowsIdentity( new IntPtr( windowstoken ) ); WindowsPrincipal principal = new WindowsPrincipal( wi ); Context.User.SetRole( principal ); // // Generate the ticket // MemoryStream strm = new MemoryStream(); Context.User.Serialize( strm ); // // Get a key and initialization vector. // byte[] key = null; byte[] iv = null; GetSecurityPair( out key, out iv ); // // Encrypt the ticket information // MemoryStream strmEncrypted = new MemoryStream(); EncryptData( strm, strmEncrypted, key, iv ); ticket = Convert.ToBase64String( strmEncrypted.GetBuffer(), 0, (int) strmEncrypted.Length ); #if DEBUG Debug.Write( SeverityType.Info, CategoryType.Soap, "Ticket Out:----------\n" + ticket ); #endif } catch( Exception exception ) { // // Log the real exception // Debug.Write( SeverityType.FailAudit, CategoryType.Authorization, exception.ToString() ); // // Preserve the error number if it was a UDDIException; but DO NOT preserve the error // message. We want the error message in the log, but not shown to the user for security // reasons. // UDDIException uddiException = exception as UDDIException; if( null != uddiException ) { throw new UDDIException( uddiException.Number, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } else { throw new UDDIException( UDDI.ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } } return true; } public bool Authenticate( string strTicket, int timeWindow ) { try { Debug.VerifySetting( "Security.Key" ); Debug.VerifySetting( "Security.IV" ); Debug.Verify( null != strTicket && strTicket.Length > 0, "UDDI_ERROR_AUTHTOKENREQUIRED_NOTOKENPUBLISHATTEMPT", ErrorType.E_authTokenRequired ); #if DEBUG Debug.Write( SeverityType.Info, CategoryType.Soap, "Ticket In:----------\n" + strTicket ); #endif // // Get a key and initialization vector. // byte[] key = null; byte[] iv = null; GetSecurityPair( out key, out iv ); // // If the ticket cannot be decoded or decrypted throw an E_authTokenRequired // MemoryStream strm = new MemoryStream(); try { byte[] ticket = Convert.FromBase64String( strTicket ); // // Decrypt the ticket into a stream // MemoryStream strmEncrypted = new MemoryStream( ticket ); DecryptData( strmEncrypted, strm, key, iv ); } catch( Exception ) { throw new UDDIException( UDDI.ErrorType.E_authTokenRequired, "UDDI_ERROR_AUTHTOKENREQUIRED_INVALIDOREXPIRED" ); } // // Deserialize the stream into the user class // and setup the context's user information // XmlSerializer serializer = XmlSerializerManager.GetSerializer( typeof( UserInfo ) ); Context.User = (UserInfo) serializer.Deserialize( strm ); // // Check the age of the token // Context.User.CheckAge( timeWindow ); } catch( Exception exception ) { // // Log the real exception // Debug.Write( SeverityType.FailAudit, CategoryType.Authorization, exception.ToString() ); // // Preserve the error number if it was a UDDIException; but DO NOT preserve the error // message. We want the error message in the log, but not shown to the user for security // reasons. // UDDIException uddiException = exception as UDDIException; if( null != uddiException ) { throw new UDDIException( uddiException.Number, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } else { throw new UDDIException( UDDI.ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } } return true; } // // This method returns a security key and initialization vector to be used in decryption or encryption // algorithms. If the key has timed out, a new one is created. This method was added as part of the changes needed to remove our dependency on SQLAgent. // private void GetSecurityPair( out byte[] key, out byte[] iv ) { // // Make sure we have the settings that we need. // Debug.VerifySetting( "Security.KeyAutoReset" ); Debug.VerifySetting( "Security.KeyLastResetDate" ); Debug.VerifySetting( "Security.KeyTimeout" ); Debug.VerifySetting( "Security.Key" ); Debug.VerifySetting( "Security.IV" ); key = null; iv = null; // // If we aren't supposed to automatically generate keys, then we don't care if the key has expired, so // just use the current values. // if( 1 == Config.GetInt( "Security.KeyAutoReset" ) ) { // // Since we are allowed to generate keys, see if we need to. // // // Get the current date // DateTime current = DateTime.Now; // // Get the last time the key was reset. // // DateTime lastReset = DateTime.Parse( Config.GetString( "Security.KeyLastResetDate" ) ); // // 739955 - Make sure date is parsed in the same format it was written. // DateTime lastReset = UDDILastResetDate.Get(); // // Get the timeout (days) // int timeOutDays = Config.GetInt( "Security.KeyTimeout" ); // // Has the key expired? // DateTime expiration = lastReset.AddDays( timeOutDays ); if( current > expiration ) { try { // // Generate new security information. // SymmetricAlgorithm sa = SymmetricAlgorithm.Create(); sa.GenerateKey(); key = sa.Key; sa.GenerateIV(); iv = sa.IV; // // Store the new key, initialization vector, and current time. // // // 739955 - Make sure date is parsed in the same format it was written. // UDDILastResetDate.Set( current ); Config.SetString( "Security.Key", Convert.ToBase64String( key ) ); Config.SetString( "Security.IV", Convert.ToBase64String( iv ) ); // // Make sure our current configuration is reading these values. TODO AddSetting is not // public, so we have to Refresh, this is pretty expensive, but it should only happen // once a week (by default). // Config.Refresh(); } catch( Exception exception ) { // // Don't let any exceptions propogate here, we'll catch them // and just throw a generic one. We don't want to reveal too much // information if we don't have to. // key = null; iv = null; // // Log the real exception // Debug.Write( SeverityType.FailAudit, CategoryType.Authorization, exception.ToString() ); // // Preserve the error number if it was a UDDIException; but DO NOT preserve the error // message. We want the error message in the log, but not shown to the user for security // reasons. // UDDIException uddiException = exception as UDDIException; if( null != uddiException ) { throw new UDDIException( uddiException.Number, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } else { throw new UDDIException( UDDI.ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } } } } // // If we don't have a value for the pair at this point, use the original values. // if( key == null && iv == null ) { key = Convert.FromBase64String( Config.GetString( "Security.Key" ) ); iv = Convert.FromBase64String( Config.GetString( "Security.IV" ) ); } } private static void EncryptData( Stream input, Stream output, byte[] key, byte[] iv ) { byte[] buffer = new byte[100]; long bytesRead = 0; long bytesTotal = input.Length; // // Creates the default implementation, which is RijndaelManaged (AES). // SymmetricAlgorithm sa = SymmetricAlgorithm.Create(); CryptoStream encodingStream = new CryptoStream( output, sa.CreateEncryptor( key, iv ), CryptoStreamMode.Write ); // // Encrypt the bytes in the buffer in 100 bytes at a time // while( bytesRead < bytesTotal ) { int n = input.Read( buffer, 0, 100 ); encodingStream.Write( buffer, 0, n ); bytesRead = bytesRead + n; } encodingStream.FlushFinalBlock(); input.Position = 0; output.Position = 0; } private static void DecryptData( Stream input, Stream output, byte[] key, byte[] iv ) { byte[] buffer = new byte[100]; long bytesRead = 0; long bytesTotal = input.Length; // // Creates the default implementation, which is RijndaelManaged. // SymmetricAlgorithm sa = SymmetricAlgorithm.Create(); CryptoStream decodingStream = new CryptoStream( output, sa.CreateDecryptor( key, iv ), CryptoStreamMode.Write ); // // Encrypt the bytes in the buffer in 100 bytes at a time // while( bytesRead < bytesTotal ) { int n = input.Read( buffer, 0, 100 ); decodingStream.Write( buffer, 0, n ); bytesRead = bytesRead + n; } decodingStream.FlushFinalBlock(); input.Position = 0; output.Position = 0; } } public class PassportAuthenticator : IAuthenticator { public bool GetAuthenticationInfo( string userid, string password, out string ticket ) { try { // // TODO: Should we generate and use our own ticket? // PassportAuthenticationHelper helper = new PassportAuthenticationHelper(); ticket = helper.AuthenticateUser( userid, password, false ); Context.User.SetPublisherRole( userid ); } catch( Exception exception ) { // // Log the real exception // Debug.Write( SeverityType.FailAudit, CategoryType.Authorization, exception.ToString() ); // // Preserve the error number if it was a UDDIException; but DO NOT preserve the error // message. We want the error message in the log, but not shown to the user for security // reasons. // UDDIException uddiException = exception as UDDIException; if( null != uddiException ) { throw new UDDIException( uddiException.Number, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } else { throw new UDDIException( UDDI.ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } } return true; } public bool Authenticate( string strTicket, int timeWindow ) { try { PassportAuthenticationHelper helper = new PassportAuthenticationHelper(); helper.ValidateAuthInfo( strTicket, timeWindow ); string userID; string email; helper.GetUserInfo( strTicket, out userID, out email ); Context.User.SetPublisherRole( userID ); Context.User.Email = email; } catch( Exception exception ) { // // Log the real exception // Debug.Write( SeverityType.FailAudit, CategoryType.Authorization, exception.ToString() ); // // Preserve the error number if it was a UDDIException; but DO NOT preserve the error // message. We want the error message in the log, but not shown to the user for security // reasons. // UDDIException uddiException = exception as UDDIException; if( null != uddiException ) { throw new UDDIException( uddiException.Number, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } else { throw new UDDIException( UDDI.ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_LOGINFAILED" ); } } return true; } } /// **************************************************************** /// class PassportAuthenticationHelper /// ---------------------------------------------------------------- /// /// Methods for authenticating users against the Passport /// authentication site. /// /// **************************************************************** /// class PassportAuthenticationHelper { private const int PassportEmailValidated = 0x0001; // // SECURITY: This interval should be configurable // private readonly TimeSpan refreshInterval = TimeSpan.FromDays( 1 ); private PassportLib.Manager passport = null; private XmlDocument clientXml = null; private int keyVersion = 0; /// **************************************************************** /// public Authenticator [constructor] /// ---------------------------------------------------------------- /// /// Constructs a new authentication object. /// /// **************************************************************** /// public PassportAuthenticationHelper() { Debug.Enter(); // // Retrieve Passport key version. // RegistryKey key = Registry.LocalMachine.OpenSubKey( @"Software\Microsoft\Passport" ); try { keyVersion = Convert.ToInt32( key.GetValue( "CurrentKey", 1 ) ); } finally { key.Close(); } // // Create an instance of the Passport Manager for this object. // passport = new PassportLib.Manager(); // // TODO: Need to centralize default values such as 100 for connection limit // // // Set the connection limit for the webclient. // ServicePointManager.DefaultConnectionLimit = Config.GetInt( "Passport.ConnectionLimit", 100 ); Debug.Leave(); } /// **************************************************************** /// public AuthenticateUser /// ---------------------------------------------------------------- /// /// Authenticates a user by sending the specified userId and /// password to the appropriate Passport authentication site. /// /// ---------------------------------------------------------------- /// /// The full user id for the user to be verified. This must be /// of the form user@domain. /// /// /// /// The password of the user to authenticate. /// /// /// /// Specifies whether the user should remain logged into the /// site. /// /// ---------------------------------------------------------------- /// /// Returns an authorization token, if successful. /// /// ---------------------------------------------------------------- /// /// An authorization token is simply a concatenation of the /// passport ticket and profile, separated by a semicolon. /// /// **************************************************************** /// public string AuthenticateUser( string userId, string password, bool savePassword ) { Debug.Enter(); Debug.Verify( !Utility.StringEmpty( userId ), "UDDI_ERROR_FATALERROR_NULLUSERID" ); Debug.Verify( !Utility.StringEmpty( password ), "UDDI_ERROR_FATALERROR_NULLPASSWORD" ); Debug.VerifySetting( "Passport.SiteID" ); Debug.VerifySetting( "Passport.ReturnURL" ); string domain = ""; string authUrl = ""; string authInfo = ""; try { // // The authentication URL depends on the user's domain. First parse off the // domain (i.e. user@domain) using the Passport manager. // try { domain = passport.DomainFromMemberName( userId ); } catch( Exception ) { Debug.OperatorMessage( SeverityType.FailAudit, CategoryType.Authorization, OperatorMessageType.InvalidUserId, "Invalid format for user ID. Must be in the form of user@domain: " + userId ); throw new UDDIException( ErrorType.E_unknownUser, "UDDI_ERROR_UNKNOWNUSER_BADFORMAT" ); } Debug.Write( SeverityType.Info, CategoryType.Authorization, "Got domain=" + domain + " for user=" + userId ); // // Lookup the URL we should use to authenticate users from this // domain. // authUrl = GetLoginURL( domain ); Debug.Write( SeverityType.Info, CategoryType.Authorization, "Obtained authorization URL=" + authUrl ); // // Append the site id and return url to the authorization url. // authUrl += "?id=" + Config.GetInt( "Passport.SiteID" ).ToString() + "&ru=" + Config.GetString( "Passport.ReturnURL" ) + "&kv=" + keyVersion.ToString(); // // Create the login message. // string loginMessage = "" + "" + "" + "" + userId + "" + "" + password + "" + "" + ( true == savePassword ? "true" : "false" ) + "" + "" + ""; byte[] requestData = Encoding.ASCII.GetBytes( loginMessage ); // // Prepare the web request. // WebRequest webRequest = WebRequest.Create( authUrl ); string proxy = Config.GetString( "Proxy", null ); if( !Utility.StringEmpty( proxy ) ) webRequest.Proxy = new WebProxy( proxy, true ); webRequest.Method = "POST"; webRequest.ContentType = "text/xml"; webRequest.ContentLength = requestData.Length; webRequest.Timeout = Config.GetInt( "Passport.Timeout", 30000 ); Stream stream; stream = webRequest.GetRequestStream(); stream.Write( requestData, 0, requestData.Length ); stream.Close(); // // Post the data to the server. // #if DEBUG Debug.Write( SeverityType.Info, CategoryType.Authorization, "Posting XML login message: " + loginMessage ); #endif // // SECURITY: try/finally for managing the stream // and webrequest in cases of failure // WebResponse webResponse = webRequest.GetResponse(); stream = webResponse.GetResponseStream(); // // Retrieve the response. // StreamReader reader = new StreamReader( stream ); string response = reader.ReadToEnd(); reader.Close(); stream.Close(); webResponse.Close(); // // Process the response. If the response data contains Success="true", // then the user has been authenticated // if( "true" != Utility.ParseDelimitedToken( "Success=\"", "\"", response ) ) { // // SECURITY: FailAudit this login failure // Debug.Write( SeverityType.Info, CategoryType.Authorization, "Login failed for user " + userId + "; response did not include attribute Success=true.\r\n" + response ); throw new UDDIException( ErrorType.E_unknownUser, "UDDI_ERROR_UNKNOWNUSER_FAILED" ); } // // Retrieve the associated ticket and profile. // string redirectUrl = Utility.ParseDelimitedToken( "", "", response ); string ticket = Utility.ParseDelimitedToken( "&t=", "&p=", redirectUrl ); string profile = Utility.ParseDelimitedToken( "&p=", null, redirectUrl ); if( null == ticket || null == profile ) { Debug.Write( SeverityType.FailAudit, CategoryType.Authorization, "Login failed for user " + userId + "; response did not include Success=true.\r\n" + response ); throw new UDDIException( ErrorType.E_unknownUser, "UDDI_ERROR_UNKNOWNUSER_FAILED" ); } // // Build the authInfo from the ticket and profile. // authInfo = ticket + ";" + profile; Debug.Write( SeverityType.PassAudit, CategoryType.Authorization, "User " + userId + " authenticated.\r\nauthInfo=" + authInfo ); Debug.Leave(); return authInfo; } catch( UDDIException ) { throw; } catch( WebException e ) { // // A web exception is thrown when the Passport server is // unavailable or returns an error message. // string message = "Error authenticating user\r\n\r\n"; if( null != e.Response ) { StreamReader reader = new StreamReader( e.Response.GetResponseStream(), Encoding.UTF8 ); string response = reader.ReadToEnd(); reader.Close(); string errorCode = Utility.ParseDelimitedToken( "Error Code=\"", "\"", response ); // // Check to make sure an error code was returned. If it was, // we can provide a little more fidelity in our error // reporting. // if( null != errorCode ) message += "Passport error: " + ParsePassportErrorCode( errorCode ) + "\r\n\r\n"; message += "Response stream:\r\n" + response + "\r\n\r\n"; } else { message += "Passport error: unknown error communicating with Passport\r\n\r\n"; } Debug.OperatorMessage( SeverityType.Error, CategoryType.Authorization, OperatorMessageType.PassportSiteUnavailable, message + e.ToString() ); throw new UDDIException( ErrorType.E_unknownUser, "UDDI_ERROR_UNKNOWNUSER_AUTHENTICATIONFAILED" ); } catch( Exception e ) { // // General exception handling. // Debug.OperatorMessage( SeverityType.Error, CategoryType.Authorization, OperatorMessageType.PassportSiteUnavailable, "Error authenticating user.\r\n\r\n" + e.ToString() ); throw new UDDIException( ErrorType.E_unknownUser, "UDDI_ERROR_UNKNOWNUSER_AUTHENTICATIONFAILED" ); } } /// **************************************************************** /// public ValidateAuthInfo /// ---------------------------------------------------------------- /// /// Validates an authorization token. /// /// ---------------------------------------------------------------- /// /// The authorization token. /// /// /// /// Time in seconds since login after which an authorization /// token is considered expired. /// /// **************************************************************** /// public void ValidateAuthInfo( string authInfo, int timeWindow ) { Debug.Enter(); Debug.Verify( null != authInfo, "UDDI_ERROR_FATALERROR_NULLAUTHINFO" ); // // Parse the ticket and profile from the authInfo string. // int separator = authInfo.IndexOf( ";" ); if( -1 == separator ) { Debug.OperatorMessage( SeverityType.FailAudit, CategoryType.Authorization, OperatorMessageType.InvalidTicketFormat, "Invalid ticket format: " + authInfo ); throw new UDDIException( ErrorType.E_authTokenRequired, "UDDI_ERROR_AUTHTOKENREQUIRED_INVALIDOREXPIRED" ); } string ticket = authInfo.Substring( 0, separator ); string profile = authInfo.Substring( separator + 1 ); // // Attempt to authenticate the ticket and profile. // try { passport.OnStartPageManual( ticket, null, null, null, null, null ); if( false == passport.IsAuthenticated( timeWindow, false, false ) ) { Debug.Write( SeverityType.Info, CategoryType.Authorization, "Authentication failed; ticket is invalid or has expired" ); throw new UDDIException( ErrorType.E_authTokenExpired, "UDDI_ERROR_AUTHTOKENREQUIRED_INVALIDOREXPIRED" ); } Debug.Write( SeverityType.PassAudit, CategoryType.Authorization, "Authentication successful" ); Debug.Leave(); } catch( UDDIException ) { throw; } catch( Exception e ) { Debug.Write( SeverityType.Error, CategoryType.Authorization, "Authentication failed; " + e.Message ); throw new UDDIException( ErrorType.E_unknownUser, "UDDI_ERROR_UNKNOWNUSER_AUTHENTICATIONFAILED" ); } } /// **************************************************************** /// public GetUserInfo /// ---------------------------------------------------------------- /// /// Gets the user information associated with a specified /// authorization token. /// /// ---------------------------------------------------------------- /// /// The authorization token. /// /// /// /// [out] The member id for the user. This is different than /// the user id which was used to login. /// /// /// /// [out] The user's preferred email address. /// /// **************************************************************** /// public void GetUserInfo( string authInfo, out string puid, out string userEmail ) { Debug.Enter(); Debug.Verify( null != authInfo, "UDDI_ERROR_FATALERROR_NULLAUTHINFO" ); // // Parse the ticket and profile from the authInfo string. // int separator = authInfo.IndexOf( ";" ); if( -1 == separator ) { Debug.OperatorMessage( SeverityType.FailAudit, CategoryType.Authorization, OperatorMessageType.InvalidTicketFormat, "Invalid ticket format: " + authInfo ); throw new UDDIException( ErrorType.E_authTokenRequired, "UDDI_ERROR_AUTHTOKENREQUIRED_INVALIDFORMAT" ); } string ticket = authInfo.Substring( 0, separator ); string profile = authInfo.Substring( separator + 1 ); // // Get the user's preferred email. This could change over time // since the user could change this on the Passport site, so // we'll always get the most recent value from the user's profile. // userEmail = (string)GetPropertyFromProfile( ticket, profile, "PreferredEmail" ); // // Get the user's PUID. Since the PUID is a 64-bit value, we'll need // to build this from the low and high 32-bit values (Passport doesn't // store it as a single 64-bit value). // /* object idHigh = GetPropertyFromProfile( ticket, profile, "MemberIDHigh" ); Debug.Verify( null != idHigh, "Unable to retrieve Passport member ID." ); object idLow = GetPropertyFromProfile( ticket, profile, "MemberIDLow" ); Debug.Verify( null != idLow, "Unable to retrieve Passport member ID." ); puid = ((int)idHigh).ToString( "X8" ) + ((int)idLow).ToString( "X8" ); */ // // they now provide a single property to get the PUID // puid = passport.HexPUID; Debug.Leave(); } /// **************************************************************** /// private GetPropertyFromProfile /// ---------------------------------------------------------------- /// /// Retrieves a property from the given user profile. /// /// ---------------------------------------------------------------- /// /// Ticket for the authenticated user. /// /// /// /// Profile for the authenticated user. /// /// /// /// The name of the property to retrieve. /// /// ---------------------------------------------------------------- /// /// The value of the property. /// /// **************************************************************** /// private object GetPropertyFromProfile( string ticket, string profile, string propertyName ) { Debug.Assert( null != ticket, "ticket cannot be null" ); Debug.Assert( null != profile, "profile cannot be null" ); Debug.Assert( null != propertyName, "propertyName cannot be null" ); object val = null; try { passport.OnStartPageManual( ticket, profile, null, null, null, null ); val = passport[ propertyName ]; Debug.Write( SeverityType.Info, CategoryType.Authorization, "Current Property: " + propertyName + "\r\n" + "Current Value: " + ((null!=val)?val:"(null)" ) + "\r\n" + "MemberIDHigh: " + passport[ "MemberIDHigh" ] + "\r\n" + "MemberIDLow: " + passport[ "MemberIDLow" ] + "\r\n" + "PreferredEmail: " + passport[ "PreferredEmail" ] + "\r\n" + "ProfileVersion: " + passport[ "ProfileVersion" ] + "\r\n" ); return val; } catch( Exception ) { throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_ERRORRETRIEVINGPROFILEDATA", propertyName ); } } /// **************************************************************** /// public GetLoginURL /// ---------------------------------------------------------------- /// /// Retrieves the XML Login URL for the given domain. /// /// ---------------------------------------------------------------- /// /// The domain name. /// /// ---------------------------------------------------------------- /// /// The XML Login URL. /// /// **************************************************************** /// public string GetLoginURL( string domain ) { Debug.VerifySetting( "Passport.ClientXmlFile" ); string location = Config.GetString( "Passport.ClientXmlFile" ); // // Make sure the Client.xml file exists and is loaded. // if( !File.Exists( location ) ) { UpdateClientXml(); } // // Check to see if it is time for a periodic refresh // of the Client.xml file. // TimeSpan age = DateTime.Now.Subtract( File.GetLastWriteTime( location ) ); if( age >= refreshInterval ) { UpdateClientXml(); } // // If the Client.xml file is still not loaded, load it now. // if( null == clientXml ) { clientXml = new XmlDocument(); clientXml.Load( location ); } // // Make sure the Client.xml hasn't expired. // DateTime validUntil = DateTime.Parse( clientXml.DocumentElement.Attributes.GetNamedItem( "ValidUntil" ).Value ); if( validUntil < DateTime.Now ) UpdateClientXml(); // // Retrieve the XMLLogin element text for the specified domain. // if( clientXml.HasChildNodes ) { XmlNode node = clientXml.SelectSingleNode( "//Domain[@Name=\"" + domain + "\"]/XMLLogin" ); if( null != node ) return node.InnerText; // // We couldn't find the specified domain, so ll try the default // XML login URL. // node = clientXml.SelectSingleNode( "//Domain[@Name=\"default\"]/XMLLogin" ); if( null != node ) return node.InnerText; } // // We were unable to find a login URL for the domain. // Debug.OperatorMessage( SeverityType.FailAudit, CategoryType.Authorization, OperatorMessageType.UnknownLoginURL, "Could not find login URL for user from domain '" + domain + "'" ); throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_ERRORAUTHENTICATINGINDOMAIN", domain); } /// **************************************************************** /// public UpdateClientXml /// ---------------------------------------------------------------- /// /// Retrieves a fresh copy of the Client.xml file from /// Passport. /// /// **************************************************************** /// public void UpdateClientXml() { Debug.VerifySetting( "Passport.ClientXmlURL" ); Debug.VerifySetting( "Passport.ClientXmlFile" ); string url = Config.GetString( "Passport.ClientXmlURL" ); try { WebRequest request = WebRequest.Create( url ); string proxy = Config.GetString( "Proxy", null ); if( !Utility.StringEmpty( proxy ) ) request.Proxy = new WebProxy( proxy, true ); request.Timeout = Config.GetInt( "Passport.Timeout", 30000 ); WebResponse response = request.GetResponse(); try { clientXml = new XmlDocument(); clientXml.Load( response.GetResponseStream() ); clientXml.Save( Config.GetString( "Passport.ClientXmlFile" ) ); } finally { response.Close(); } } catch( Exception e ) { Debug.OperatorMessage( SeverityType.Error, CategoryType.Authorization, OperatorMessageType.CannotRetrieveClientXml, "Error retrieving Client.xml from '" + url + "'\n\nDetails:\n" + e.ToString() ); throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_ERRORCOMMUNICATINGWITHPASSPORT" ); } } /// **************************************************************** /// private ParsePassportErrorCode [static] /// ---------------------------------------------------------------- /// /// Returns an appropriate message for the Passport error /// code. /// /// ---------------------------------------------------------------- /// /// The error code returned by Passport. /// /// ---------------------------------------------------------------- /// /// The error message. /// /// **************************************************************** /// private static string ParsePassportErrorCode( string errorCode ) { // // Check for a general network error. This indicates the service // is unavailable. // if( 'n' == errorCode[0] ) return "Service unavailable"; // // Return an appropriate message for the error code. // switch( errorCode ) { case "e1": return "Missing member name and password"; case "e2": return "Missing member name"; case "e3": return "Missing password"; case "e5a": return "Incorrect password for given member name"; case "e5b": return "Member name does not exist"; case "e5d": return "Member name incomplete"; case "e8": return "Missing Passport site ID (configuration error)"; case "e8a": return "Missing Passport return URL (configuration error)"; case "e6": goto case "e9"; case "e9": return "Member has a KIDS Passport that does not have parental consent"; case "e10": return "Password lockout. Several incorrect password attempted for member in a short time duration"; case "e11": return "Member name or domain exceed 64 characters or password exceeded 16 characters"; case "e13": return "General XML parsing or validation failure"; case "e13a": return "Login sent to wrong domain authority (see referral tag)"; case "e14": return "Malformed request (missing LoginRequest element)"; case "g1": return "Malformed request (missing ClientInfo element)"; case "p1": return "Invalid version attribute specified in ClientInfo element"; default: return "Unknown Passport error code '" + errorCode + "'."; } } } }