diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2009-01-25 07:40:15 -0800 |
---|---|---|
committer | Andrew <andrewarnott@gmail.com> | 2009-01-25 21:13:41 -0800 |
commit | 100cda0a7f2f5528cbc409c2357a61404ce38dbf (patch) | |
tree | 6c40956521b58252be69b3de7c700e2fe3b21d51 /src | |
parent | 0f8c85a92f63fc67227a93b53d51b20a6236e188 (diff) | |
download | DotNetOpenAuth-100cda0a7f2f5528cbc409c2357a61404ce38dbf.zip DotNetOpenAuth-100cda0a7f2f5528cbc409c2357a61404ce38dbf.tar.gz DotNetOpenAuth-100cda0a7f2f5528cbc409c2357a61404ce38dbf.tar.bz2 |
Added sreg client javascript support for responses.
Diffstat (limited to 'src')
4 files changed, 129 insertions, 109 deletions
diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs index 6c40fba..48922f6 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs @@ -9,6 +9,7 @@ namespace DotNetOpenAuth.Messaging { using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; + using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -28,6 +29,29 @@ namespace DotNetOpenAuth.Messaging { internal static readonly RandomNumberGenerator CryptoRandomDataGenerator = new RNGCryptoServiceProvider(); /// <summary> + /// A set of escaping mappings that help secure a string from javscript execution. + /// </summary> + /// <remarks> + /// The characters to escape here are inspired by + /// http://code.google.com/p/doctype/wiki/ArticleXSSInJavaScript + /// </remarks> + private static readonly Dictionary<string, string> javascriptStaticStringEscaping = new Dictionary<string, string> { + { "\\", @"\\" }, // this WAS just above the & substitution but we moved it here to prevent double-escaping + { "\t", @"\t" }, + { "\n", @"\n" }, + { "\r", @"\r" }, + { "\u0085", @"\u0085" }, + { "\u2028", @"\u2028" }, + { "\u2029", @"\u2029" }, + { "'", @"\x27" }, + { "\"", @"\x22" }, + { "&", @"\x26" }, + { "<", @"\x3c" }, + { ">", @"\x3e" }, + { "=", @"\x3d" }, + }; + + /// <summary> /// Gets the original request URL, as seen from the browser before any URL rewrites on the server if any. /// Cookieless session directory (if applicable) is also included. /// </summary> @@ -558,6 +582,52 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Constructs a Javascript expression that will create an object + /// on the user agent when assigned to a variable. + /// </summary> + /// <param name="namesAndValues">The untrusted names and untrusted values to inject into the JSON object.</param> + /// <returns>The Javascript JSON object as a string.</returns> + internal static string CreateJsonObject(IEnumerable<KeyValuePair<string, string>> namesAndValues) { + StringBuilder builder = new StringBuilder(); + builder.Append("{ "); + + foreach (var pair in namesAndValues) { + builder.Append(MessagingUtilities.GetSafeJavascriptValue(pair.Key)); + builder.Append(": "); + builder.Append(MessagingUtilities.GetSafeJavascriptValue(pair.Value)); + builder.Append(","); + } + + if (builder[builder.Length - 1] == ',') { + builder.Length -= 1; + } + builder.Append("}"); + return builder.ToString(); + } + + /// <summary> + /// Prepares what SHOULD be simply a string value for safe injection into Javascript + /// by using appropriate character escaping. + /// </summary> + /// <param name="value">The untrusted string value to be escaped to protected against XSS attacks. May be null.</param> + /// <returns>The escaped string.</returns> + internal static string GetSafeJavascriptValue(string value) { + if (value == null) { + return "null"; + } + + // We use a StringBuilder because we have potentially many replacements to do, + // and we don't want to create a new string for every intermediate replacement step. + StringBuilder builder = new StringBuilder(value); + foreach (var pair in javascriptStaticStringEscaping) { + builder.Replace(pair.Key, pair.Value); + } + builder.Insert(0, '\''); + builder.Append('\''); + return builder.ToString(); + } + + /// <summary> /// A class to convert a <see cref="Comparison<T>"/> into an <see cref="IComparer<T>"/>. /// </summary> /// <typeparam name="T">The type of objects being compared.</typeparam> diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs index 082fd52..876315e 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs @@ -20,7 +20,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration { /// A struct storing Simple Registration field values describing an /// authenticating user. /// </summary> - public sealed class ClaimsResponse : ExtensionBase { + public sealed class ClaimsResponse : ExtensionBase, IClientScriptExtensionResponse { /// <summary> /// The factory method that may be used in deserialization of this message. /// </summary> @@ -221,48 +221,6 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration { } } - #region IClientScriptExtension Members - - // TODO: re-enable this - ////string IClientScriptExtensionResponse.InitializeJavaScriptData(IDictionary<string, string> sreg, IAuthenticationResponse response, string typeUri) { - //// StringBuilder builder = new StringBuilder(); - //// builder.Append("{ "); - - //// string nickname, email, fullName, dob, genderString, postalCode, country, language, timeZone; - //// if (sreg.TryGetValue(Constants.nickname, out nickname)) { - //// builder.Append(createAddFieldJS(Constants.nickname, nickname)); - //// } - //// if (sreg.TryGetValue(Constants.email, out email)) { - //// builder.Append(createAddFieldJS(Constants.email, email)); - //// } - //// if (sreg.TryGetValue(Constants.fullname, out fullName)) { - //// builder.Append(createAddFieldJS(Constants.fullname, fullName)); - //// } - //// if (sreg.TryGetValue(Constants.dob, out dob)) { - //// builder.Append(createAddFieldJS(Constants.dob, dob)); - //// } - //// if (sreg.TryGetValue(Constants.gender, out genderString)) { - //// builder.Append(createAddFieldJS(Constants.gender, genderString)); - //// } - //// if (sreg.TryGetValue(Constants.postcode, out postalCode)) { - //// builder.Append(createAddFieldJS(Constants.postcode, postalCode)); - //// } - //// if (sreg.TryGetValue(Constants.country, out country)) { - //// builder.Append(createAddFieldJS(Constants.country, country)); - //// } - //// if (sreg.TryGetValue(Constants.language, out language)) { - //// builder.Append(createAddFieldJS(Constants.language, language)); - //// } - //// if (sreg.TryGetValue(Constants.timezone, out timeZone)) { - //// builder.Append(createAddFieldJS(Constants.timezone, timeZone)); - //// } - //// if (builder[builder.Length - 1] == ',') builder.Length -= 1; - //// builder.Append("}"); - //// return builder.ToString(); - ////} - - #endregion - /// <summary> /// Tests equality of two <see cref="ClaimsResponse"/> objects. /// </summary> @@ -320,5 +278,42 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration { public override int GetHashCode() { return (this.Nickname != null) ? this.Nickname.GetHashCode() : base.GetHashCode(); } + + #region IClientScriptExtension Members + + /// <summary> + /// Reads the extension information on an authentication response from the provider. + /// </summary> + /// <param name="response">The incoming OpenID response carrying the extension.</param> + /// <returns> + /// A Javascript snippet that when executed on the user agent returns an object with + /// the information deserialized from the extension response. + /// </returns> + /// <remarks> + /// This method is called <b>before</b> the signature on the assertion response has been + /// verified. Therefore all information in these fields should be assumed unreliable + /// and potentially falsified. + /// </remarks> + string IClientScriptExtensionResponse.InitializeJavaScriptData(IProtocolMessageWithExtensions response) { + var sreg = new Dictionary<string, string>(15); + + // Although we could probably whip up a trip with MessageDictionary + // to avoid explicitly setting each field, doing so would likely + // open ourselves up to security exploits from the OP as it would + // make possible sending arbitrary javascript in arbitrary field names. + sreg[Constants.nickname] = this.Nickname; + sreg[Constants.email] = this.Email; + sreg[Constants.fullname] = this.FullName; + sreg[Constants.dob] = this.BirthDateRaw; + sreg[Constants.gender] = this.Gender.HasValue ? this.Gender.Value.ToString() : null; + sreg[Constants.postcode] = this.PostalCode; + sreg[Constants.country] = this.Country; + sreg[Constants.language] = this.Language; + sreg[Constants.timezone] = this.TimeZone; + + return MessagingUtilities.CreateJsonObject(sreg); + } + + #endregion } }
\ No newline at end of file diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs index 9bdf2f8..5172800 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs @@ -871,29 +871,29 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { try { List<IAuthenticationRequest> requests = this.CreateRequests(userSuppliedIdentifier, true); if (requests.Count > 0) { - discoveryResultBuilder.AppendFormat("claimedIdentifier: {0},", Util.GetSafeJavascriptValue(requests[0].ClaimedIdentifier)); + discoveryResultBuilder.AppendFormat("claimedIdentifier: {0},", MessagingUtilities.GetSafeJavascriptValue(requests[0].ClaimedIdentifier)); discoveryResultBuilder.Append("requests: ["); foreach (IAuthenticationRequest request in requests) { this.OnLoggingIn(request); discoveryResultBuilder.Append("{"); - discoveryResultBuilder.AppendFormat("endpoint: {0},", Util.GetSafeJavascriptValue(request.Provider.Uri.AbsoluteUri)); + discoveryResultBuilder.AppendFormat("endpoint: {0},", MessagingUtilities.GetSafeJavascriptValue(request.Provider.Uri.AbsoluteUri)); request.Mode = AuthenticationRequestMode.Immediate; UserAgentResponse response = request.RedirectingResponse; - discoveryResultBuilder.AppendFormat("immediate: {0},", Util.GetSafeJavascriptValue(response.DirectUriRequest.AbsoluteUri)); + discoveryResultBuilder.AppendFormat("immediate: {0},", MessagingUtilities.GetSafeJavascriptValue(response.DirectUriRequest.AbsoluteUri)); request.Mode = AuthenticationRequestMode.Setup; response = request.RedirectingResponse; - discoveryResultBuilder.AppendFormat("setup: {0}", Util.GetSafeJavascriptValue(response.DirectUriRequest.AbsoluteUri)); + discoveryResultBuilder.AppendFormat("setup: {0}", MessagingUtilities.GetSafeJavascriptValue(response.DirectUriRequest.AbsoluteUri)); discoveryResultBuilder.Append("},"); } discoveryResultBuilder.Length -= 1; // trim off last comma discoveryResultBuilder.Append("]"); } else { discoveryResultBuilder.Append("requests: new Array(),"); - discoveryResultBuilder.AppendFormat("error: {0}", Util.GetSafeJavascriptValue(OpenIdStrings.OpenIdEndpointNotFound)); + discoveryResultBuilder.AppendFormat("error: {0}", MessagingUtilities.GetSafeJavascriptValue(OpenIdStrings.OpenIdEndpointNotFound)); } } catch (ProtocolException ex) { discoveryResultBuilder.Append("requests: new Array(),"); - discoveryResultBuilder.AppendFormat("error: {0}", Util.GetSafeJavascriptValue(ex.Message)); + discoveryResultBuilder.AppendFormat("error: {0}", MessagingUtilities.GetSafeJavascriptValue(ex.Message)); } discoveryResultBuilder.Append("}"); this.discoveryResult = discoveryResultBuilder.ToString(); @@ -1122,24 +1122,24 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { startupScript.AppendFormat( CultureInfo.InvariantCulture, "initAjaxOpenId(box, {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, function({18}, {19}, {20}) {{{21}}});{22}", - Util.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), OpenIdTextBox.EmbeddedLogoResourceName)), - Util.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedDotNetOpenIdLogoResourceName)), - Util.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedSpinnerResourceName)), - Util.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedLoginSuccessResourceName)), - Util.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedLoginFailureResourceName)), + MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), OpenIdTextBox.EmbeddedLogoResourceName)), + MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedDotNetOpenIdLogoResourceName)), + MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedSpinnerResourceName)), + MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedLoginSuccessResourceName)), + MessagingUtilities.GetSafeJavascriptValue(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedLoginFailureResourceName)), this.Throttle, this.Timeout.TotalMilliseconds, string.IsNullOrEmpty(this.OnClientAssertionReceived) ? "null" : "'" + this.OnClientAssertionReceived.Replace(@"\", @"\\").Replace("'", @"\'") + "'", - Util.GetSafeJavascriptValue(this.LogOnText), - Util.GetSafeJavascriptValue(this.LogOnToolTip), - Util.GetSafeJavascriptValue(this.RetryText), - Util.GetSafeJavascriptValue(this.RetryToolTip), - Util.GetSafeJavascriptValue(this.BusyToolTip), - Util.GetSafeJavascriptValue(this.IdentifierRequiredMessage), - Util.GetSafeJavascriptValue(this.LogOnInProgressMessage), - Util.GetSafeJavascriptValue(this.AuthenticationSucceededToolTip), - Util.GetSafeJavascriptValue(this.AuthenticatedAsToolTip), - Util.GetSafeJavascriptValue(this.AuthenticationFailedToolTip), + MessagingUtilities.GetSafeJavascriptValue(this.LogOnText), + MessagingUtilities.GetSafeJavascriptValue(this.LogOnToolTip), + MessagingUtilities.GetSafeJavascriptValue(this.RetryText), + MessagingUtilities.GetSafeJavascriptValue(this.RetryToolTip), + MessagingUtilities.GetSafeJavascriptValue(this.BusyToolTip), + MessagingUtilities.GetSafeJavascriptValue(this.IdentifierRequiredMessage), + MessagingUtilities.GetSafeJavascriptValue(this.LogOnInProgressMessage), + MessagingUtilities.GetSafeJavascriptValue(this.AuthenticationSucceededToolTip), + MessagingUtilities.GetSafeJavascriptValue(this.AuthenticatedAsToolTip), + MessagingUtilities.GetSafeJavascriptValue(this.AuthenticationFailedToolTip), identifierParameterName, discoveryCallbackResultParameterName, discoveryErrorCallbackParameterName, diff --git a/src/DotNetOpenAuth/Util.cs b/src/DotNetOpenAuth/Util.cs index 9004cbc..cb5581f 100644 --- a/src/DotNetOpenAuth/Util.cs +++ b/src/DotNetOpenAuth/Util.cs @@ -21,29 +21,6 @@ namespace DotNetOpenAuth { internal const string DefaultNamespace = "DotNetOpenAuth"; /// <summary> - /// A set of escaping mappings that help secure a string from javscript execution. - /// </summary> - /// <remarks> - /// The characters to escape here are inspired by - /// http://code.google.com/p/doctype/wiki/ArticleXSSInJavaScript - /// </remarks> - private static readonly Dictionary<string, string> javascriptStaticStringEscaping = new Dictionary<string, string> { - { "\\", @"\\" }, // this WAS just above the & substitution but we moved it here to prevent double-escaping - { "\t", @"\t" }, - { "\n", @"\n" }, - { "\r", @"\r" }, - { "\u0085", @"\u0085" }, - { "\u2028", @"\u2028" }, - { "\u2029", @"\u2029" }, - { "'", @"\x27" }, - { "\"", @"\x22" }, - { "&", @"\x26" }, - { "<", @"\x3c" }, - { ">", @"\x3e" }, - { "=", @"\x3d" }, - }; - - /// <summary> /// Gets a human-readable description of the library name and version, including /// whether the build is an official or private one. /// </summary> @@ -80,28 +57,6 @@ namespace DotNetOpenAuth { } /// <summary> - /// Prepares what SHOULD be simply a string value for safe injection into Javascript - /// by using appropriate character escaping. - /// </summary> - /// <param name="value">The untrusted string value to be escaped to protected against XSS attacks.</param> - /// <returns>The escaped string.</returns> - internal static string GetSafeJavascriptValue(string value) { - if (value == null) { - return "null"; - } - - // We use a StringBuilder because we have potentially many replacements to do, - // and we don't want to create a new string for every intermediate replacement step. - StringBuilder builder = new StringBuilder(value); - foreach (var pair in javascriptStaticStringEscaping) { - builder.Replace(pair.Key, pair.Value); - } - builder.Insert(0, '\''); - builder.Append('\''); - return builder.ToString(); - } - - /// <summary> /// Prepares a dictionary for printing as a string. /// </summary> /// <typeparam name="K">The type of the key.</typeparam> |