Login control's loggedin event not firing - security

I've noticed that forums are littered with examples of this problem, but with the only solutions referring to events not actually being hooked up, or typos. I'm pretty sure that my problem relates to neither of these!
I have a login control with an opening tag that looks like this:
<asp:Login ID="signInControl" FailureText="Sorry, your login has not been successful" runat="server" DisplayRememberMe="false" OnLoggedIn="signInControl_LoggedIn" OnAuthenticate="signInControl_Authenticate" OnLoggingIn="signInControl_LoggingIn">
Here are my events in the code-behind:
protected void signInControl_LoggedIn(object sender, EventArgs e)
{
MembershipProvider provider = Membership.Providers[GlobalConstants.MembershipProviderName];
MembershipUser user = provider.GetUser(this.signInControl.UserName, true);
int expiryInDays = Utility.GetConfigurationValueAsInt(SPContext.Current.Web, "PasswordExpiryLengthInDays");
// provide a mechanism to stop password expiry (i.e. if -1);
if (expiryInDays > 0)
{
expiryInDays = expiryInDays * -1;
// If the user hasn't changed their password within the last x days, send them to update it.
DateTime minimumPasswordChange = DateTime.Now.AddDays(expiryInDays);
TimeSpan due = user.LastPasswordChangedDate.Subtract(minimumPasswordChange);
Logging.LogTrace(string.Format("The user {0} next password change is in {1} days.", user.UserName, due.Days));
if (user.LastPasswordChangedDate >= minimumPasswordChange)
{
SPUtility.Redirect("/_layouts/ExpiredPassword.aspx", SPRedirectFlags.Trusted, this.Context);
}
}
else
{
SPUtility.Redirect("/_layouts/ExpiredPassword.aspx", SPRedirectFlags.Trusted, this.Context);
}
}
protected void signInControl_Authenticate(object sender, AuthenticateEventArgs e)
{
SPClaimsUtility.AuthenticateFormsUser(new Uri(SPContext.Current.Web.Url), this.signInControl.UserName, this.signInControl.Password);
e.Authenticated = Membership.ValidateUser(this.signInControl.UserName, this.signInControl.Password);
}
protected void signInControl_LoggingIn(object sender, LoginCancelEventArgs e)
{
Logging.LogTrace(string.Format("User {0} attempting to sign in.", this.signInControl.UserName));
e.Cancel = false;
}
And as you can see in the authenticate event, e.authenticated is being set by whether the user can be validated. I've confirmed by stepping into the code that true is returned. So why isn't my signInControl_LoggedIn method firing? I'm seriously confused!
This is the markup for the front-end:
Thanks for any help!
Karl.

There are number of posts that describe how to create your own custom login page for SharePoint 2010. Here is the one I followed. In general, they create an application page that inherits from FormsSignInPage. For a page that just needs to change the look and feel, this works well. In some cases, like this one, creating the login page in this way may not be enough.
The way it is described at the end of this post, you need to inherit from IdentityModelSignInPageBase instead of FormsSignInPage. You also need to create an event handler for the authenticate event and register this in the OnLoad event of the page.
This is because the Authenticate method in the control will automatically take the user to the "ReturnUrl" page before it ever calls the OnLoggedIn event. It only takes two lines of code to authenticate the user in this method.
void signInControl_Authenticate(object sender, AuthenticateEventArgs e)
{
Login signInControl = sender as Login;
e.Authenticated = SPClaimsUtility.AuthenticateFormsUser(this.Page.Request.Url, signInControl.UserName, signInControl.Password);
}
Since your code now does not send the user to the ReturnUrl value, the OnLoggedIn event is reached.
You may ask why do I need the OnLoggedIn event if I have to create a custom Authenticate method. Well, there are still functions the control will handle for you. For instance, if the user does not authenticate (ie bad UN and PW combination), the control will display the error message and the OnLoggedIn never fires.

If you inherit from FormsSignInPage you may override your function RedirectToSuccessUrl:
protected override void RedirectToSuccessUrl()
{
//Your code here if successful login
base.RedirectToSuccessUrl();
}

Related

Java - Spring MVC - Redirect to route without button/change url dynamically

Please excuse me for my bad english.
This is my problem using Spring MVC:
Let assume that a user is clicking on a button and arrived to a route called "/randomRoute".
I want him to have the possibility to keep doing his job (for example filling a form). About a certain amount of time (represented by the Thread.sleep in the thread), and whatever he did before, I want him to be redirected to a new route called "/newRandomRoute".
I thought about using Thread.run so that the user can continue his work until the delay is done but I don't know how to implement the redirect to the new route.
Is that possible with Spring? Or is there a way to change the url after the Thread.sleep method?
If you want more precision (I am trying to do something on the "Go to "newRandomRoute" page" comment:
#Controller
#RequestMapping("/home")
public class Chat {
#GetMapping("/randomRoute")
public String displayRandomRoute() {
new Thread() {
#Override
public void run() {
try {
Thread.sleep(5000);
// Go to "/newRandomRoute" newPage
} catch (Exception e) {}
}
}.start();
return "page";
}
#GetMapping("/newRandomRoute")
public String displayNewRandomRoute() {
return "newPage";
}
}
Thanks for your help :)

Acumatica: Sign Out User After Process Completion

I am trying to sign out a user once a process is completed, I tried using the PXAccess or the PXAccessInfo classes in order to do so but did not manage to find a correct way in logging out a user. Are there any other means in signing out a user which I might have glossed over?
I adapted the standard SignOut code so it can be run from a graph extension instead of a Aspx.cs web page. It is equivalent to this SignOut menu item:
In this example I put the code in SOOrderEntry Initialize override so it signs out the current user as soon as you navigate to the SalesOrderEntry graph. You can put it in an Action event handler but I haven't tested it in a PXLongOperation context which runs in a separate thread context:
public class SOOrderEntry_Extension:PXGraphExtension<SOOrderEntry>
{
public override void Initialize()
{
System.Web.UI.Page page = System.Web.HttpContext.Current.Handler as System.Web.UI.Page;
if (page != null)
{
PX.Data.PXLogin.LogoutUser(PX.Data.PXAccess.GetUserName(), page.Session.SessionID);
PX.Common.PXContext.Session.SetString("UserLogin", string.Empty);
string absoluteLoginUrl = PX.Export.Authentication.AuthenticationManagerModule.Instance.SignOut();
page.Session.Abandon();
PX.Data.Auth.ExternalAuthHelper.SignOut(System.Web.HttpContext.Current, absoluteLoginUrl);
PX.Export.Authentication.FormsAuthenticationModule.
RedirectToLoginPage(PX.Data.Auth.ExternalAuthHelper.SILENT_LOGIN + "=None", true);
}
}
}

Stop users going from one page to another page

I want to stop users going from one frame/page back to the main previous page.
For example, when the user successfully logs in they are to go to the users list page.
If a user presses the hardware back button from the users list page, then they shouldn't go back to the login screen. If they do, the program should either prompt with two buttons, yes to logout and go back to the login screen, or no and stay on the current screen.
private void HardwareButtons_BackPressed(object sender, Windows.Phone.UI.Input.BackPressedEventArgs e)
{
Frame frame = Window.Current.Content as Frame;
if (frame == null) return;
//the current frame is UserList
if (frame.Content is UserList)
{
messageBox("yes");
e.Handled = true;
}
else if (frame.CanGoBack)
{
frame.GoBack();
e.Handled = true;
}
}
In theory, if the current frame is the user list page, then do not go back.
How can I stop a user from going back a page?
First you need to attach the event to HardwareButtons back button which is available in Windows.UI.Xaml.Input namespace. Add this below InitializeComponent call in page constructor.
HardwareButtons.BackPressed += HardwareButtons_BackPressed;
If you just handle the event it is enough to prevent user from going back with hardware back button.
private void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
{
e.Handled = true;
}
If you don't want to allow the user to go back if the previous page is Login page you can try this.
private void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
{
e.Handled = true;
if (Frame.BackStack.Last().SourcePageType.Equals(typeof(LoginPage)))
{
//TODO: handle prompt
} else {
Frame.GoBack();
}
}
ps: if you handle e you should provide Frame.GoBack(); manually otherwise users will be stuck in that page.

What is the best place to detect user sign in when using azure acs and mvc3?

I want to be able to detect when a user signs on to my application using passive acs, so that I can add them to my database if this is the first time using my app. Right now I am subscribing to WSFederationAuthenticationModule.SignedIn but I feel I'm missing something. Mainly I'm not sure the best place to subscribe to the event, I got it to work inside PostAuthenticateRequest but its a bit hacky. Any suggestions?
this code is from global.asax
public override void Init()
{
base.Init();
PostAuthenticateRequest += (s, e) =>
{
try
{
FederatedAuthentication.WSFederationAuthenticationModule.SignedIn -= SignedIn;
}
finally
{
FederatedAuthentication.WSFederationAuthenticationModule.SignedIn += SignedIn;
}
};
}
private void SignedIn(object sender, EventArgs e)
{
//do something
}
EDIT:
For now I'm going to use a flag variable to make sure I only subscribe once to SignedIn. Unless someone has any other suggestions that is :) thanks for the help Sandrino. Here is what I have at the moment.
private static bool isFirstRequest = true;
public override void Init()
{
base.Init();
PostAuthenticateRequest += (s, e) => {
if (isFirstRequest)
{
FederatedAuthentication
.WSFederationAuthenticationModule.SignedIn += SignedIn;
isFirstRequest = false;
}
};
}
private void SignedIn(object sender, EventArgs e)
{
//do something
}
EDIT:
A little more info. This problem happens if I'm using the azure emulator, it probably happens when deployed as well but I haven't tried that. I have tested if I am just not able to debug by trying to write to a text file and no text file was created.
Why do you subscribe to the SignedIn event each time the PostAuthenticateRequest event is raised? You can simple subscribe to it when the application starts (in the Global.asax) and it will be raised for each user that signed in:
public class MvcApplication : System.Web.HttpApplication
{
...
protected void Application_Start()
{
...
FederatedAuthentication.ServiceConfigurationCreated += (s, e) =>
{
FederatedAuthentication.WSFederationAuthenticationModule.SignedIn += new EventHandler(OnUserSignedIn);
};
}
private void OnUserSignedIn(object sender, EventArgs e)
{
// Custom logic here.
}
}
The SignedIn event is the best way to detect a user sign in before the application continues. Take a look at the following diagram. Before redirecting back to a page, the SignedIn event is raised to allow you to detect an user sign in:
Reference: http://msdn.microsoft.com/en-us/library/ee517293.aspx
I created a class that derives from ClaimsAuthenticationManager. There is only one method that you have to override, which is
public virtual IClaimsPrincipal Authenticate(string resourceName, IClaimsPrincipal incomingPrincipal);
In my app, I use this method to check if the user, who has successfully authenticated, is really a user of my app (i.e. they exist in my database). If not, I direct them to a signup page.
My class looks something like this:
public override IClaimsPrincipal Authenticate(string resourceName, IClaimsPrincipal incomingPrincipal)
{
if (incomingPrincipal.Identity.IsAuthenticated)
{
var identity = incomingPrincipal.Identity as IClaimsIdentity;
User user = null;
// Get name identifier and identity provider
var nameIdentifierClaim = identity.Claims.SingleOrDefault(c => c.ClaimType.Equals(ClaimTypes.NameIdentifier, StringComparison.OrdinalIgnoreCase));
var identityProviderClaim = identity.Claims.SingleOrDefault(c => c.ClaimType.Equals(CustomClaimTypes.IdentityProviderClaimType, StringComparison.OrdinalIgnoreCase));
if (nameIdentifierClaim == null || identityProviderClaim == null)
{
throw new AuthenticationErrorException("Invalid claims", "The claims provided by your Identity Provider are invalid. Please contact your administrator.");
}
try
{
//checking the database here...
using (var context = new CloudContext())
{
user = (from u in context.Users
where u.IdentityProvider == identityProviderClaim.Value &&
u.NameIdentifier == nameIdentifierClaim.Value &&
!u.Account.PendingDelete
select u).FirstOrDefault();
}
}
catch (System.Data.DataException ex)
{
Console.WriteLine(ex.Message);
if (ex.InnerException != null)
Console.WriteLine(ex.InnerException);
throw;
}
}
return incomingPrincipal;
}
Then, in your web.config, you add a section to the <microsoft.identitymodel> area, as so:
<claimsAuthenticationManager type="CloudAnalyzer.UI.Security.CloudAnalyzerClaimsAuthenticationManager" />
I learned this trick from the sample app located here: Windows Azure Marketplace. Even if you're not going to publish in the Window Azure Marketplace it's a good sample with some helpful code snippets you can use for ACS integration.

SPItemEvent: abort the event from the EventHandler

How can I abort an item even (in my case, ItemDeleting) so that it doesn'r get executed? I want the deletion not to take place if certain conditions are matched and do it silently for the use (no messages, no exceptions). Thanks
EDIT:
SP 2010
public override void ItemDeleting(SPItemEventProperties properties) {
properties.Cancel = true;
properties.ErrorMessage = "Something went wrong!";
}
If you cancel it though, it will be reported back to the user, nothing you can do about that.
UPDATE
For use the Status property
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.speventpropertiesbase.status.aspx
public override void ItemDeleting(SPItemEventProperties properties) {
properties.Status = SPEventReceiverStatus.CancelNoError;
}

Resources