//-----------------------------------------------------------------------
//
// Copyright (c) Outercurve Foundation. All rights reserved.
//
//-----------------------------------------------------------------------
[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedScriptResourceName, "text/javascript")]
[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedStylesheetResourceName, "text/css")]
[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedSpinnerResourceName, "image/gif")]
[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName, "image/png")]
[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginFailureResourceName, "image/png")]
#pragma warning disable 0809 // marking inherited, unsupported properties as obsolete to discourage their use
namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing.Design;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using DotNetOpenAuth.Logging;
using DotNetOpenAuth.Messaging;
using Validation;
///
/// An ASP.NET control that provides a minimal text box that is OpenID-aware and uses AJAX for
/// a premium login experience.
///
[DefaultProperty("Text"), ValidationProperty("Text")]
[ToolboxData("<{0}:OpenIdAjaxTextBox runat=\"server\" />")]
public class OpenIdAjaxTextBox : OpenIdRelyingPartyAjaxControlBase, IEditableTextControl, ITextControl, IPostBackDataHandler {
///
/// The name of the manifest stream containing the OpenIdAjaxTextBox.js file.
///
internal const string EmbeddedScriptResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.OpenIdAjaxTextBox.js";
///
/// The name of the manifest stream containing the OpenIdAjaxTextBox.css file.
///
internal const string EmbeddedStylesheetResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.OpenIdAjaxTextBox.css";
///
/// The name of the manifest stream containing the spinner.gif file.
///
internal const string EmbeddedSpinnerResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.spinner.gif";
///
/// The name of the manifest stream containing the login_success.png file.
///
internal const string EmbeddedLoginSuccessResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.login_success.png";
///
/// The name of the manifest stream containing the login_failure.png file.
///
internal const string EmbeddedLoginFailureResourceName = Util.DefaultNamespace + ".OpenId.RelyingParty.login_failure.png";
///
/// The default value for the property.
///
internal const bool DownloadYahooUILibraryDefault = true;
///
/// The default value for the property.
///
internal const int ThrottleDefault = 3;
#region Property viewstate keys
///
/// The viewstate key to use for storing the value of the property.
///
private const string AutoPostBackViewStateKey = "AutoPostback";
///
/// The viewstate key to use for the property.
///
private const string TextViewStateKey = "Text";
///
/// The viewstate key to use for storing the value of the property.
///
private const string ColumnsViewStateKey = "Columns";
///
/// The viewstate key to use for the property.
///
private const string CssClassViewStateKey = "CssClass";
///
/// The viewstate key to use for storing the value of the property.
///
private const string OnClientAssertionReceivedViewStateKey = "OnClientAssertionReceived";
///
/// The viewstate key to use for storing the value of the property.
///
private const string AuthenticatedAsToolTipViewStateKey = "AuthenticatedAsToolTip";
///
/// The viewstate key to use for storing the value of the property.
///
private const string AuthenticationSucceededToolTipViewStateKey = "AuthenticationSucceededToolTip";
///
/// The viewstate key to use for storing the value of the property.
///
private const string LogOnInProgressMessageViewStateKey = "BusyToolTip";
///
/// The viewstate key to use for storing the value of the property.
///
private const string AuthenticationFailedToolTipViewStateKey = "AuthenticationFailedToolTip";
///
/// The viewstate key to use for storing the value of the property.
///
private const string IdentifierRequiredMessageViewStateKey = "BusyToolTip";
///
/// The viewstate key to use for storing the value of the property.
///
private const string BusyToolTipViewStateKey = "BusyToolTip";
///
/// The viewstate key to use for storing the value of the property.
///
private const string LogOnTextViewStateKey = "LoginText";
///
/// The viewstate key to use for storing the value of the property.
///
private const string ThrottleViewStateKey = "Throttle";
///
/// The viewstate key to use for storing the value of the property.
///
private const string LogOnToolTipViewStateKey = "LoginToolTip";
///
/// The viewstate key to use for storing the value of the property.
///
private const string LogOnPostBackToolTipViewStateKey = "LoginPostBackToolTip";
///
/// The viewstate key to use for storing the value of the property.
///
private const string NameViewStateKey = "Name";
///
/// The viewstate key to use for storing the value of the property.
///
private const string TimeoutViewStateKey = "Timeout";
///
/// The viewstate key to use for storing the value of the property.
///
private const string TabIndexViewStateKey = "TabIndex";
///
/// The viewstate key to use for the property.
///
private const string EnabledViewStateKey = "Enabled";
///
/// The viewstate key to use for storing the value of the property.
///
private const string RetryToolTipViewStateKey = "RetryToolTip";
///
/// The viewstate key to use for storing the value of the property.
///
private const string RetryTextViewStateKey = "RetryText";
///
/// The viewstate key to use for storing the value of the property.
///
private const string DownloadYahooUILibraryViewStateKey = "DownloadYahooUILibrary";
///
/// The viewstate key to use for storing the value of the property.
///
private const string ShowLogOnPostBackButtonViewStateKey = "ShowLogOnPostBackButton";
#endregion
#region Property defaults
///
/// The default value for the property.
///
private const bool AutoPostBackDefault = false;
///
/// The default value for the property.
///
private const int ColumnsDefault = 40;
///
/// The default value for the property.
///
private const string CssClassDefault = "openid";
///
/// The default value for the property.
///
private const string LogOnInProgressMessageDefault = "Please wait for login to complete.";
///
/// The default value for the property.
///
private const string AuthenticationSucceededToolTipDefault = "Authenticated by {0}.";
///
/// The default value for the property.
///
private const string AuthenticatedAsToolTipDefault = "Authenticated as {0}.";
///
/// The default value for the property.
///
private const string AuthenticationFailedToolTipDefault = "Authentication failed.";
///
/// The default value for the property.
///
private const string LogOnTextDefault = "LOG IN";
///
/// The default value for the property.
///
private const string BusyToolTipDefault = "Discovering/authenticating";
///
/// The default value for the property.
///
private const string IdentifierRequiredMessageDefault = "Please correct errors in OpenID identifier and allow login to complete before submitting.";
///
/// The default value for the property.
///
private const string NameDefault = "openid_identifier";
///
/// Default value for property.
///
private const short TabIndexDefault = 0;
///
/// The default value for the property.
///
private const string RetryToolTipDefault = "Retry a failed identifier discovery.";
///
/// The default value for the property.
///
private const string LogOnToolTipDefault = "Click here to log in using a pop-up window.";
///
/// The default value for the property.
///
private const string LogOnPostBackToolTipDefault = "Click here to log in immediately.";
///
/// The default value for the property.
///
private const string RetryTextDefault = "RETRY";
///
/// The default value for the property.
///
private const bool ShowLogOnPostBackButtonDefault = false;
#endregion
///
/// The path where the YUI control library should be downloaded from for HTTP pages.
///
private const string YuiLoaderHttp = "http://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/yuiloader/yuiloader-min.js";
///
/// The path where the YUI control library should be downloaded from for HTTPS pages.
///
private const string YuiLoaderHttps = "https://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/yuiloader/yuiloader-min.js";
///
/// Initializes a new instance of the class.
///
public OpenIdAjaxTextBox() {
this.HookFormSubmit = true;
}
#region Events
///
/// Fired when the content of the text changes between posts to the server.
///
[Description("Occurs when the content of the text changes between posts to the server."), Category(BehaviorCategory)]
public event EventHandler TextChanged;
///
/// Gets or sets the client-side script that executes when an authentication
/// assertion is received (but before it is verified).
///
///
/// In the context of the executing javascript set in this property, the
/// local variable sender is set to the openid_identifier input box
/// that is executing this code.
/// This variable has a getClaimedIdentifier() method that may be used to
/// identify the user who is being authenticated.
/// It is very important to note that when this code executes,
/// the authentication has not been verified and may have been spoofed.
/// No security-sensitive operations should take place in this javascript code.
/// The authentication is verified on the server by the time the
/// server-side event fires.
///
[Description("Gets or sets the client-side script that executes when an authentication assertion is received (but before it is verified).")]
[Bindable(true), DefaultValue(""), Category(BehaviorCategory)]
public string OnClientAssertionReceived {
get { return this.ViewState[OnClientAssertionReceivedViewStateKey] as string; }
set { this.ViewState[OnClientAssertionReceivedViewStateKey] = value; }
}
#endregion
#region Properties
///
/// Gets or sets the value in the text field, completely unprocessed or normalized.
///
[Bindable(true), DefaultValue(""), Category(AppearanceCategory)]
[Description("The content of the text box.")]
public string Text {
get {
return this.Identifier != null ? this.Identifier.OriginalString : (this.ViewState[TextViewStateKey] as string ?? string.Empty);
}
set {
// Try to store it as a validated identifier,
// but failing that at least store the text.
Identifier id;
if (Identifier.TryParse(value, out id)) {
this.Identifier = id;
} else {
// Be sure to set the viewstate AFTER setting the Identifier,
// since setting the Identifier clears the viewstate in OnIdentifierChanged.
this.Identifier = null;
this.ViewState[TextViewStateKey] = value;
}
}
}
///
/// Gets or sets a value indicating whether a postback is made to fire the
/// event as soon as authentication has completed
/// successfully.
///
///
/// true if a postback should be made automatically upon authentication;
/// otherwise, false to delay the
/// event from firing at the server until a postback is made by some other control.
///
[Bindable(true), Category(BehaviorCategory), DefaultValue(AutoPostBackDefault)]
[Description("Whether the LoggedIn event fires on the server as soon as authentication completes successfully.")]
public bool AutoPostBack {
get { return (bool)(this.ViewState[AutoPostBackViewStateKey] ?? AutoPostBackDefault); }
set { this.ViewState[AutoPostBackViewStateKey] = value; }
}
///
/// Gets or sets the width of the text box in characters.
///
[Bindable(true), Category(AppearanceCategory), DefaultValue(ColumnsDefault)]
[Description("The width of the text box in characters.")]
public int Columns {
get {
return (int)(this.ViewState[ColumnsViewStateKey] ?? ColumnsDefault);
}
set {
Requires.Range(value >= 0, "value");
this.ViewState[ColumnsViewStateKey] = value;
}
}
///
/// Gets or sets the CSS class assigned to the text box.
///
[Bindable(true), DefaultValue(CssClassDefault), Category(AppearanceCategory)]
[Description("The CSS class assigned to the text box.")]
public string CssClass {
get { return (string)this.ViewState[CssClassViewStateKey]; }
set { this.ViewState[CssClassViewStateKey] = value; }
}
///
/// Gets or sets the tab index of the text box control. Use 0 to omit an explicit tabindex.
///
[Bindable(true), Category(BehaviorCategory), DefaultValue(TabIndexDefault)]
[Description("The tab index of the text box control. Use 0 to omit an explicit tabindex.")]
public virtual short TabIndex {
get { return (short)(this.ViewState[TabIndexViewStateKey] ?? TabIndexDefault); }
set { this.ViewState[TabIndexViewStateKey] = value; }
}
///
/// Gets or sets a value indicating whether this is enabled
/// in the browser for editing and will respond to incoming OpenID messages.
///
/// true if enabled; otherwise, false.
[Bindable(true), DefaultValue(true), Category(BehaviorCategory)]
[Description("Whether the control is editable in the browser and will respond to OpenID messages.")]
public bool Enabled {
get { return (bool)(this.ViewState[EnabledViewStateKey] ?? true); }
set { this.ViewState[EnabledViewStateKey] = value; }
}
///
/// Gets or sets the HTML name to assign to the text field.
///
[Bindable(true), DefaultValue(NameDefault), Category("Misc")]
[Description("The HTML name to assign to the text field.")]
public string Name {
get {
return (string)(this.ViewState[NameViewStateKey] ?? NameDefault);
}
set {
Requires.NotNullOrEmpty(value, "value");
this.ViewState[NameViewStateKey] = value ?? string.Empty;
}
}
///
/// Gets or sets the time duration for the AJAX control to wait for an OP to respond before reporting failure to the user.
///
[Browsable(true), DefaultValue(typeof(TimeSpan), "00:00:08"), Category(BehaviorCategory)]
[Description("The time duration for the AJAX control to wait for an OP to respond before reporting failure to the user.")]
public TimeSpan Timeout {
get {
return (TimeSpan)(this.ViewState[TimeoutViewStateKey] ?? TimeoutDefault);
}
set {
Requires.Range(value.TotalMilliseconds > 0, "value");
this.ViewState[TimeoutViewStateKey] = value;
}
}
///
/// Gets or sets the maximum number of OpenID Providers to simultaneously try to authenticate with.
///
[Browsable(true), DefaultValue(ThrottleDefault), Category(BehaviorCategory)]
[Description("The maximum number of OpenID Providers to simultaneously try to authenticate with.")]
public int Throttle {
get {
return (int)(this.ViewState[ThrottleViewStateKey] ?? ThrottleDefault);
}
set {
Requires.Range(value > 0, "value");
this.ViewState[ThrottleViewStateKey] = value;
}
}
///
/// Gets or sets the text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.
///
[Bindable(true), DefaultValue(LogOnTextDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.")]
public string LogOnText {
get {
return (string)(this.ViewState[LogOnTextViewStateKey] ?? LogOnTextDefault);
}
set {
Requires.NotNullOrEmpty(value, "value");
this.ViewState[LogOnTextViewStateKey] = value ?? string.Empty;
}
}
///
/// Gets or sets the rool tip text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.
///
[Bindable(true), DefaultValue(LogOnToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears on the LOG IN button in cases where immediate (invisible) authentication fails.")]
public string LogOnToolTip {
get { return (string)(this.ViewState[LogOnToolTipViewStateKey] ?? LogOnToolTipDefault); }
set { this.ViewState[LogOnToolTipViewStateKey] = value ?? string.Empty; }
}
///
/// Gets or sets the rool tip text that appears on the LOG IN button when clicking the button will result in an immediate postback.
///
[Bindable(true), DefaultValue(LogOnPostBackToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears on the LOG IN button when clicking the button will result in an immediate postback.")]
public string LogOnPostBackToolTip {
get { return (string)(this.ViewState[LogOnPostBackToolTipViewStateKey] ?? LogOnPostBackToolTipDefault); }
set { this.ViewState[LogOnPostBackToolTipViewStateKey] = value ?? string.Empty; }
}
///
/// Gets or sets the text that appears on the RETRY button in cases where authentication times out.
///
[Bindable(true), DefaultValue(RetryTextDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The text that appears on the RETRY button in cases where authentication times out.")]
public string RetryText {
get {
return (string)(this.ViewState[RetryTextViewStateKey] ?? RetryTextDefault);
}
set {
Requires.NotNullOrEmpty(value, "value");
this.ViewState[RetryTextViewStateKey] = value ?? string.Empty;
}
}
///
/// Gets or sets the tool tip text that appears on the RETRY button in cases where authentication times out.
///
[Bindable(true), DefaultValue(RetryToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears on the RETRY button in cases where authentication times out.")]
public string RetryToolTip {
get { return (string)(this.ViewState[RetryToolTipViewStateKey] ?? RetryToolTipDefault); }
set { this.ViewState[RetryToolTipViewStateKey] = value ?? string.Empty; }
}
///
/// Gets or sets the tool tip text that appears when authentication succeeds.
///
[Bindable(true), DefaultValue(AuthenticationSucceededToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears when authentication succeeds.")]
public string AuthenticationSucceededToolTip {
get { return (string)(this.ViewState[AuthenticationSucceededToolTipViewStateKey] ?? AuthenticationSucceededToolTipDefault); }
set { this.ViewState[AuthenticationSucceededToolTipViewStateKey] = value ?? string.Empty; }
}
///
/// Gets or sets the tool tip text that appears on the green checkmark when authentication succeeds.
///
[Bindable(true), DefaultValue(AuthenticatedAsToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears on the green checkmark when authentication succeeds.")]
public string AuthenticatedAsToolTip {
get { return (string)(this.ViewState[AuthenticatedAsToolTipViewStateKey] ?? AuthenticatedAsToolTipDefault); }
set { this.ViewState[AuthenticatedAsToolTipViewStateKey] = value ?? string.Empty; }
}
///
/// Gets or sets the tool tip text that appears when authentication fails.
///
[Bindable(true), DefaultValue(AuthenticationFailedToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears when authentication fails.")]
public string AuthenticationFailedToolTip {
get { return (string)(this.ViewState[AuthenticationFailedToolTipViewStateKey] ?? AuthenticationFailedToolTipDefault); }
set { this.ViewState[AuthenticationFailedToolTipViewStateKey] = value ?? string.Empty; }
}
///
/// Gets or sets the tool tip text that appears over the text box when it is discovering and authenticating.
///
[Bindable(true), DefaultValue(BusyToolTipDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The tool tip text that appears over the text box when it is discovering and authenticating.")]
public string BusyToolTip {
get { return (string)(this.ViewState[BusyToolTipViewStateKey] ?? BusyToolTipDefault); }
set { this.ViewState[BusyToolTipViewStateKey] = value ?? string.Empty; }
}
///
/// Gets or sets the message that is displayed if a postback is about to occur before the identifier has been supplied.
///
[Bindable(true), DefaultValue(IdentifierRequiredMessageDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The message that is displayed if a postback is about to occur before the identifier has been supplied.")]
public string IdentifierRequiredMessage {
get { return (string)(this.ViewState[IdentifierRequiredMessageViewStateKey] ?? IdentifierRequiredMessageDefault); }
set { this.ViewState[IdentifierRequiredMessageViewStateKey] = value ?? string.Empty; }
}
///
/// Gets or sets the message that is displayed if a postback is attempted while login is in process.
///
[Bindable(true), DefaultValue(LogOnInProgressMessageDefault), Localizable(true), Category(AppearanceCategory)]
[Description("The message that is displayed if a postback is attempted while login is in process.")]
public string LogOnInProgressMessage {
get { return (string)(this.ViewState[LogOnInProgressMessageViewStateKey] ?? LogOnInProgressMessageDefault); }
set { this.ViewState[LogOnInProgressMessageViewStateKey] = value ?? string.Empty; }
}
///
/// Gets or sets a value indicating whether the Yahoo! User Interface Library (YUI)
/// will be downloaded in order to provide a login split button.
///
///
/// true to use a split button; otherwise, false to use a standard HTML button
/// or a split button by downloading the YUI library yourself on the hosting web page.
///
///
/// The split button brings in about 180KB of YUI javascript dependencies.
///
[Bindable(true), DefaultValue(DownloadYahooUILibraryDefault), Category(BehaviorCategory)]
[Description("Whether a split button will be used for the \"log in\" when the user provides an identifier that delegates to more than one Provider.")]
public bool DownloadYahooUILibrary {
get { return (bool)(this.ViewState[DownloadYahooUILibraryViewStateKey] ?? DownloadYahooUILibraryDefault); }
set { this.ViewState[DownloadYahooUILibraryViewStateKey] = value; }
}
///
/// Gets or sets a value indicating whether the "Log in" button will be shown
/// to initiate a postback containing the positive assertion.
///
[Bindable(true), DefaultValue(ShowLogOnPostBackButtonDefault), Category(AppearanceCategory)]
[Description("Whether the log in button will be shown to initiate a postback containing the positive assertion.")]
public bool ShowLogOnPostBackButton {
get { return (bool)(this.ViewState[ShowLogOnPostBackButtonViewStateKey] ?? ShowLogOnPostBackButtonDefault); }
set { this.ViewState[ShowLogOnPostBackButtonViewStateKey] = value; }
}
#endregion
///
/// Gets or sets a value indicating whether the ajax text box should hook the form's submit event for special behavior.
///
internal bool HookFormSubmit { get; set; }
///
/// Gets the name of the open id auth data form key.
///
///
/// A concatenation of and "_openidAuthData".
///
protected override string OpenIdAuthDataFormKey {
get { return this.Name + "_openidAuthData"; }
}
///
/// Gets the default value for the property.
///
/// 8 seconds; or eternity if the debugger is attached.
private static TimeSpan TimeoutDefault {
get {
if (Debugger.IsAttached) {
Logger.OpenId.Warn("Debugger is attached. Inflating default OpenIdAjaxTextbox.Timeout value to infinity.");
return TimeSpan.MaxValue;
} else {
return TimeSpan.FromSeconds(8);
}
}
}
#region IPostBackDataHandler Members
///
/// When implemented by a class, processes postback data for an ASP.NET server control.
///
/// The key identifier for the control.
/// The collection of all incoming name values.
///
/// true if the server control's state changes as a result of the postback; otherwise, false.
///
bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection) {
return this.LoadPostData(postDataKey, postCollection);
}
///
/// When implemented by a class, signals the server control to notify the ASP.NET application that the state of the control has changed.
///
void IPostBackDataHandler.RaisePostDataChangedEvent() {
this.RaisePostDataChangedEvent();
}
#endregion
///
/// Raises the event.
///
/// The instance containing the event data.
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
this.Page.RegisterRequiresPostBack(this);
}
///
/// Called when the property is changed.
///
protected override void OnIdentifierChanged() {
this.ViewState.Remove(TextViewStateKey);
base.OnIdentifierChanged();
}
///
/// Prepares to render the control.
///
/// An object that contains the event data.
protected override void OnPreRender(EventArgs e) {
base.OnPreRender(e);
if (!this.Visible) {
return;
}
if (this.DownloadYahooUILibrary) {
// Although we'll add the