//******************************************************************************************************
// SecurityPortal.aspx.cs - Gbtc
//
// Copyright © 2012, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may
// not use this file except in compliance with the License. You may obtain a copy of the License at:
//
// http://www.opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 05/18/2010 - Pinal C. Patel
// Generated original version of source code.
// 06/02/2010 - Pinal C. Patel
// Added sample customization config file entries to code comments.
// Modified to set the text cursor focus on input fields for ease of use.
// 01/05/2011 - Pinal C. Patel
// Added HelpPage and FooterText customization settings that can be specified in the config file.
// Added the ability to change and reset passwords.
// Updated UI for compatibility across multiple browsers including mobile devices.
// 01/06/2011 - Pinal C. Patel
// Fixed a issue that required users to login before password could be reset.
// 07/20/2011 - Pinal C. Patel
// Added tracing for diagnosing unexpected error conditions.
// 10/20/2011 - Pinal C. Patel
// Updated GetReferrerUrl() and GetRedirectUrl() methods to generate relative URLs instead
// of absolute ones so redirection would work when reverse proxy is involved.
// 12/20/2012 - Starlynn Danyelle Gilliam
// Modified Header.
//
//******************************************************************************************************
using System;
using System.Security;
using System.Text;
using System.Threading;
using System.Web;
using System.Web.UI;
using GSF.Configuration;
using GSF.Security;
using GSF.Web.UI;
// No actual passwords store here
#pragma warning disable S2068
namespace GSF.Web.Embedded
{
///
/// Embedded web page used by secure ASP.NET web sites for security related tasks.
///
///
/// Config file entries for customizing the page:
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
/// ]]>
///
///
public partial class SecurityPortal : Page
{
#region [ Members ]
// Constants
private const string UsernameKey = "Username";
private const string CookieName = "SecurityPortal";
private const string StaticPageTitle = "Security Portal";
private const string SettingsCategory = "SecurityPortal";
private const string StatusCodeRequestKey = "s";
private const string ReturnUrlRequestKey = "r";
private const string UnauthorizedStatusCode = "401";
private const string AccessDeniedStatusCode = "403";
private const string PasswordResetStatusCode = "401.11";
private const string PasswordChangeStatusCode = "403.11";
private const string EmbeddedHelpFile = "GSF.Web.Embedded.Files.Help.pdf";
private const string EmbeddedHelpImage = "GSF.Web.Embedded.Images.Help.png";
private const string EmbeddedWarningImage = "GSF.Web.Embedded.Images.Warning.png";
private const string EmbeddedCompanyLogo = "GSF.Web.Embedded.Images.Logo.png";
private const string EmbeddedStyleSheet = "GSF.Web.Embedded.Styles.SecurityPortal.css";
private const string DefaultCompanyLink = "http://www.gridprotectionalliance.org/";
private const string DefaultFooterText = "© Grid Protection Alliance. All rights reserved.";
#endregion
#region [ Properties ]
private ISecurityProvider CurrentProvider
{
get
{
return (Thread.CurrentPrincipal as SecurityPrincipal)?.Identity.Provider;
}
}
#endregion
#region [ Methods ]
///
/// Initializes the web page.
///
/// Source of this event.
/// Arguments of this event.
protected void Page_Load(object sender, EventArgs e)
{
ConfigurationFile config = ConfigurationFile.Current;
CategorizedSettingsElementCollection settings = config.Settings[SettingsCategory];
CategorizedSettingsElement setting;
// Setup company logo.
setting = settings["CompanyLogo"];
if ((object)setting != null)
LogoImage.ImageUrl = setting.Value;
else
LogoImage.ImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(SecurityPortal), EmbeddedCompanyLogo);
// Setup company link.
setting = settings["CompanyLink"];
if ((object)setting != null)
LogoLink.NavigateUrl = setting.Value;
else
LogoLink.NavigateUrl = DefaultCompanyLink;
// Setup help link.
setting = settings["HelpPage"];
if ((object)setting != null)
HelpLink.NavigateUrl = setting.Value;
else
HelpLink.NavigateUrl = Page.ClientScript.GetWebResourceUrl(typeof(SecurityPortal), EmbeddedHelpFile);
// Setup footer information.
setting = settings["FooterText"];
if ((object)setting != null)
FooterLabel.Text = setting.Value;
else
FooterLabel.Text = DefaultFooterText;
HelpImage.ImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(SecurityPortal), EmbeddedHelpImage);
WarningImage.ImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(SecurityPortal), EmbeddedWarningImage);
StyleSheet.Attributes["href"] = Page.ClientScript.GetWebResourceUrl(typeof(SecurityPortal), EmbeddedStyleSheet);
if (Request[StatusCodeRequestKey] == AccessDeniedStatusCode)
{
// Show access denied.
Page.Title = StaticPageTitle + " :: Access Denied";
AccessDeniedPanel.Visible = true;
ContentPlaceHolder.Controls.Add(AccessDeniedPanel);
}
else if (Request[StatusCodeRequestKey] == PasswordChangeStatusCode)
{
// Show change password.
Page.Title = StaticPageTitle + " :: Change Password";
ChangePasswordPanel.Visible = true;
ContentPlaceHolder.Controls.Add(ChangePasswordPanel);
// Setup UI.
ChangeButton.SetSubmitOnce();
ChangePasswordPanel.DefaultButton = ChangeButton.ID;
if (!Page.IsPostBack)
{
ChangePasswordUsername.Text = GetSavedUsername();
if (string.IsNullOrEmpty(ChangePasswordUsername.Text))
ChangePasswordUsername.Focus();
else
ChangePasswordOldPassword.Focus();
}
}
else if (Request[StatusCodeRequestKey] == PasswordResetStatusCode)
{
// Show reset password.
Page.Title = StaticPageTitle + " :: Reset Password";
if ((object)ViewState[UsernameKey] == null)
{
// Check for reset support.
ResetPasswordCheckPanel.Visible = true;
ContentPlaceHolder.Controls.Add(ResetPasswordCheckPanel);
// Setup UI.
ResetCheckButton.SetSubmitOnce();
ResetPasswordUsername.Focus();
ResetPasswordCheckPanel.DefaultButton = ResetCheckButton.ID;
}
else
{
// Perform password reset.
ResetPasswordFinalPanel.Visible = true;
ContentPlaceHolder.Controls.Add(ResetPasswordFinalPanel);
// Setup UI.
ResetFinalButton.SetSubmitOnce();
ResetPasswordSecurityAnswer.Focus();
ResetPasswordFinalPanel.DefaultButton = ResetFinalButton.ID;
ResetPasswordSecurityQuestion.Text = ViewState["SecurityQuestion"].ToString();
MessageLabel.Text = string.Empty;
}
}
else if (Request[StatusCodeRequestKey] == UnauthorizedStatusCode || (object)CurrentProvider == null || !User.Identity.IsAuthenticated)
{
// Show login.
Page.Title = StaticPageTitle + " :: Login";
LoginPanel.Visible = true;
ContentPlaceHolder.Controls.Add(LoginPanel);
// Setup UI.
LoginButton.SetSubmitOnce();
LoginPanel.DefaultButton = LoginButton.ID;
ForgotPassword.NavigateUrl = GetRedirectUrl(PasswordResetStatusCode);
ChangePassword.NavigateUrl = GetRedirectUrl(PasswordChangeStatusCode);
if (!Page.IsPostBack)
{
LoginUsername.Text = GetSavedUsername();
if (string.IsNullOrEmpty(LoginUsername.Text))
{
LoginUsername.Focus();
}
else
{
LoginPassword.Focus();
RememberUsername.Checked = true;
}
}
}
else
{
// Show my account.
Page.Title = StaticPageTitle + " :: My Account";
MyAccountPanel.Visible = true;
ContentPlaceHolder.Controls.Clear();
ContentPlaceHolder.Controls.Add(MyAccountPanel);
// Setup UI.
UpdateButton.SetSubmitOnce();
MyAccountPanel.DefaultButton = UpdateButton.ID;
if (!Page.IsPostBack)
{
ISecurityProvider provider = CurrentProvider;
ShowUserData(provider);
// The UpdateData() method was never implemented in
// any security provider so it has been removed
//if (!provider.CanUpdateData)
//{
// AccountUserFirstName.Enabled = false;
// AccountUserLastName.Enabled = false;
// AccountUserEmailAddress.Enabled = false;
// AccountUserPhoneNumber.Enabled = false;
// AccountUserSecurityAnswer.Enabled = false;
// UpdateButton.Enabled = false;
//}
}
}
}
///
/// Logins the user.
///
/// Source of this event.
/// Arguments of this event.
protected void LoginButton_Click(object sender, EventArgs e)
{
try
{
// Initialize the security provider.
ISecurityProvider securityProvider = SecurityProviderUtility.CreateProvider(LoginUsername.Text);
securityProvider.Password = LoginPassword.Text;
if (securityProvider.Authenticate())
{
// Credentials were authenticated successfully.
SecurityIdentity securityIdentity = new SecurityIdentity(securityProvider);
Thread.CurrentPrincipal = new SecurityPrincipal(securityIdentity);
if (RememberUsername.Checked)
{
Response.Cookies[CookieName][UsernameKey] = LoginUsername.Text;
Response.Cookies[CookieName].Expires = DateTime.Now.AddYears(1);
}
else
{
Response.Cookies[CookieName][UsernameKey] = string.Empty;
Response.Cookies[CookieName].Expires = DateTime.Now.AddYears(-1);
}
// Redirect to the referring page.
Response.Redirect(GetReferrerUrl(), false);
}
else
{
// Check why authentication failed.
if (securityProvider.UserData.PasswordChangeDateTime != DateTime.MinValue &&
securityProvider.UserData.PasswordChangeDateTime <= DateTime.UtcNow)
{
// User must change password.
if (securityProvider.CanChangePassword)
Response.Redirect(GetRedirectUrl(PasswordChangeStatusCode), false);
else
ShowMessage("Account password has expired.", true);
}
else
{
// Show why login failed.
if (!ShowFailureReason(securityProvider))
ShowMessage("Authentication was not successful.", true);
}
}
}
catch (SecurityException ex)
{
// Show security related error messages.
ShowMessage(ex.Message.EnsureEnd('.'), true);
}
catch (Exception ex)
{
// Show ambiguous message for other errors.
ShowMessage("Login failed due to an unexpected error.", true);
System.Diagnostics.Trace.WriteLine(string.Format("Login error: \r\n {0}", ex));
}
finally
{
LoginPassword.Focus();
}
}
///
/// Updates user data.
///
/// Source of this event.
/// Arguments of this event.
protected void UpdateButton_Click(object sender, EventArgs e)
{
ISecurityProvider provider = null;
try
{
provider = CurrentProvider;
// The UpdateData() method was never implemented in
// any security provider so it has been removed
//if (provider.CanUpdateData)
//{
// provider.UserData.FirstName = AccountUserFirstName.Text;
// provider.UserData.LastName = AccountUserLastName.Text;
// provider.UserData.EmailAddress = AccountUserEmailAddress.Text;
// provider.UserData.PhoneNumber = AccountUserPhoneNumber.Text;
// provider.UserData.SecurityAnswer = AccountUserSecurityAnswer.Text;
// provider.UpdateData();
// ShowMessage("Information has been updated successfully!", false);
//}
//else
{
ShowMessage("Account does not support updating of information.", true);
}
}
catch (SecurityException ex)
{
// Show security related error messages.
ShowMessage(ex.Message.EnsureEnd('.'), true);
}
catch (Exception ex)
{
// Show ambiguous message for other errors.
ShowMessage("Update failed due to an unexpected error.", true);
System.Diagnostics.Trace.WriteLine(string.Format("Update information error: \r\n {0}", ex));
}
finally
{
ShowUserData(provider);
}
}
///
/// Changes user password.
///
/// Source of this event.
/// Arguments of this event.
protected void ChangeButton_Click(object sender, EventArgs e)
{
try
{
// Initialize the security provider.
ISecurityProvider securityProvider = SecurityProviderUtility.CreateProvider(ChangePasswordUsername.Text);
if (securityProvider.CanChangePassword)
{
// Attempt to change password.
if (securityProvider.ChangePassword(ChangePasswordOldPassword.Text, ChangePasswordNewPassword.Text))
{
securityProvider.Password = ChangePasswordNewPassword.Text;
// Password changed successfully.
if (securityProvider.Authenticate())
{
// Password authenticated successfully.
SecurityIdentity securityIdentity = new SecurityIdentity(securityProvider);
Thread.CurrentPrincipal = new SecurityPrincipal(securityIdentity);
Response.Redirect(GetReferrerUrl(), false);
}
else
{
// Show why authentication failed.
if (!ShowFailureReason(securityProvider))
ShowMessage("Authentication was not successful.", true);
}
}
else
{
// Show why password change failed.
if (!ShowFailureReason(securityProvider))
ShowMessage("Password change was not successful.", true);
}
}
else
{
// Changing password is not supported.
ShowMessage("Account does not support password change.", true);
}
}
catch (SecurityException ex)
{
// Show security related error messages.
ShowMessage(ex.Message.EnsureEnd('.'), true);
}
catch (Exception ex)
{
// Show ambiguous message for other errors.
ShowMessage("Password change failed due to an unexpected error.", true);
System.Diagnostics.Trace.WriteLine(string.Format("Password change error: \r\n {0}", ex));
}
finally
{
ChangePasswordOldPassword.Focus();
}
}
///
/// Checks if user password can be reset.
///
/// Source of this event.
/// Arguments of this event.
protected void ResetCheckButton_Click(object sender, EventArgs e)
{
try
{
// Initialize the security provider.
ISecurityProvider provider = SecurityProviderUtility.CreateProvider(ResetPasswordUsername.Text);
if (provider.CanResetPassword)
{
// Proceed to resetting password.
if (!string.IsNullOrEmpty(provider.UserData.SecurityQuestion) && !string.IsNullOrEmpty(provider.UserData.SecurityAnswer))
{
ViewState.Add(UsernameKey, ResetPasswordUsername.Text);
ViewState.Add("SecurityQuestion", provider.UserData.SecurityQuestion);
Page.ClientScript.RegisterStartupScript(Page.GetType(), "PostBack", Page.ClientScript.GetPostBackEventReference(Page, null), true);
}
else
{
ShowMessage("Security question and answer must be set to reset password.", true);
}
}
else
{
// Resetting password is not supported.
ShowMessage("Account does not support password reset.", true);
}
}
catch (SecurityException ex)
{
// Show security related error messages.
ShowMessage(ex.Message.EnsureEnd('.'), true);
}
catch (Exception ex)
{
// Show ambiguous message for other errors.
ShowMessage("Password reset failed due to an unexpected error.", true);
System.Diagnostics.Trace.WriteLine(string.Format("Password reset error: \r\n {0}", ex));
}
finally
{
ResetPasswordUsername.Focus();
}
}
///
/// Resets user password.
///
/// Source of this event.
/// Arguments of this event.
protected void ResetFinalButton_Click(object sender, EventArgs e)
{
try
{
// Initialize the security provider.
ISecurityProvider provider = SecurityProviderUtility.CreateProvider(ResetPasswordUsername.Text);
if (provider.CanResetPassword)
{
// Attempt to reset password.
if (provider.ResetPassword(ResetPasswordSecurityAnswer.Text))
{
// Password reset was successful.
ShowMessage("Password reset request has been processed.", false);
}
else
{
// Show why password reset failed.
if (!ShowFailureReason(provider))
ShowMessage("Password reset was not successful.", true);
}
}
else
{
// Resetting password is not supported.
ShowMessage("Account does not support password reset.", true);
}
}
catch (SecurityException ex)
{
// Show security related error messages.
ShowMessage(ex.Message.EnsureEnd('.'), true);
}
catch (Exception ex)
{
// Show ambiguous message for other errors.
ShowMessage("Password reset failed due to an unexpected error.", true);
System.Diagnostics.Trace.WriteLine(string.Format("Password reset error: \r\n {0}", ex));
}
finally
{
ResetPasswordSecurityAnswer.Focus();
}
}
private string GetSavedUsername()
{
if (Request.Cookies[CookieName] == null)
return string.Empty;
return Request.Cookies[CookieName][UsernameKey];
}
private string GetReferrerUrl()
{
// Redirect to specified return URL.
if (Request[ReturnUrlRequestKey] != null)
return Request[ReturnUrlRequestKey];
// Redirect to referring URL.
if (Request.UrlReferrer != null)
return VirtualPathUtility.ToAppRelative(Request.UrlReferrer.AbsolutePath);
// Redirect to self.
return VirtualPathUtility.ToAppRelative(Request.Url.AbsolutePath);
}
private string GetRedirectUrl(string newStatusCode)
{
StringBuilder url = new StringBuilder();
url.AppendFormat("{0}?", VirtualPathUtility.ToAppRelative(Request.Url.AbsolutePath));
if (!string.IsNullOrEmpty(Request.Url.Query))
{
// Query parameters are present in the current request.
foreach (string pair in Request.Url.Query.TrimStart('?').Split('&'))
{
string[] pairSplit = pair.Split('=');
if (string.Compare(pairSplit[0], StatusCodeRequestKey, StringComparison.OrdinalIgnoreCase) != 0)
url.AppendFormat("{0}={1}&", pairSplit[0], pairSplit[1]);
}
}
if (!string.IsNullOrEmpty(newStatusCode))
url.AppendFormat("{0}={1}", StatusCodeRequestKey, newStatusCode);
return url.ToString().TrimEnd('?', '&');
}
private void ShowUserData(ISecurityProvider provider)
{
AccountUserFirstName.Focus();
AccountUsername.Text = provider.UserData.Username;
AccountUserCompany.Text = provider.UserData.CompanyName;
AccountUserFirstName.Text = provider.UserData.FirstName;
AccountUserLastName.Text = provider.UserData.LastName;
AccountUserEmailAddress.Text = provider.UserData.EmailAddress;
AccountUserPhoneNumber.Text = provider.UserData.PhoneNumber;
if (string.IsNullOrEmpty(provider.UserData.SecurityQuestion))
{
AccountUserSecurityQuestion.Visible = false;
AccountUserSecurityAnswer.Visible = false;
AccountUserSecurityAnswerValidator.Visible = false;
}
else
{
AccountUserSecurityQuestion.Text = provider.UserData.SecurityQuestion;
AccountUserSecurityAnswer.Text = provider.UserData.SecurityAnswer;
}
}
private void ShowMessage(string message, bool isError)
{
MessageLabel.Text = string.Format("{0}
", message);
if (isError)
MessageLabel.CssClass = "ErrorMessage";
else
MessageLabel.CssClass = "InformationMessage";
}
private bool ShowFailureReason(ISecurityProvider provider)
{
if (!provider.UserData.IsDefined)
ShowMessage("Account does not exist.", true); // No such account.
else if (provider.UserData.IsDisabled)
ShowMessage("Account is currently disabled.", true); // Account is disabled.
else if (provider.UserData.IsLockedOut)
ShowMessage("Account is currently locked out.", true); // Account is locked out.
else
return false;
return true;
}
#endregion
}
}