//----------------------------------------------------------------------- // // 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