Membership.DeleteUser(UserName,true) not removing user from role - c#

When I click the "delete" linkbutton, it can delete the all User info from my "UserDetail" table in my "JobPost.mdf", it also delete the corresponding "aspnet_Users" & "aspnet_Membership",but the "UserInRole" still contain that UserName. Even though I specified the Code:Membership.DeleteUser(UserName, true);
I thought true is for bool deleteallrelated data, but it doesn't really delete the userInRole. So next time the user registers with the same name, it automatically get the "admin" role right.
This "deleteUser" page I keep it inside a protected "admin"folder.
How to solve it? Why Membership.DeleteUser(UserName, true) doesn't delete UserInRole?
protected void GridView2_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Delete")
{
string UserName = e.CommandArgument.ToString();
Membership.DeleteUser(UserName, true);
JobPostDataContext db = new JobPostDataContext();
var query = from u in db.UserDetails
where u.UserName == UserName
select u;
foreach (var item in query)
{
db.UserDetails.DeleteOnSubmit(item);
}
db.SubmitChanges();
FormsAuthentication.SignOut();
}
}
My web.config inside the protected Admin folder:
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<authorization>
<allow roles="Administrators" />
<deny users="*" />
</authorization>
</system.web>
</configuration>

The UserInRole table contains two Guid fields. The username is not stored. Whenever a new User is created, they are assigned a new, completely unique UserId.
Why do you think the user-role association is not being deleted? You can test this accurately by performing a query for the SELECT COUNT(*) FROM aspnet_UserInRoles WHERE UserId={DeletedUserId}.
I have included the ASP.NET membership SQL database schema below for your reference.
Also, the Roles API allows you to delete roles manually. So, to delete all the roles for a given user would looke like:
void DeleteUserRoles(string username)
{
foreach (var role in Roles.GetRolesForUser(username))
Roles.RemoveUserFromRole(username, role);
}

Related

System.Web.HttpContext.Current.User.Identity.IsAuthenticated fails sometimes

I have been having trouble with my production site (not my development sites). Every now and then both Firefox and Chrome fail to log users in (all users both on our client network and general web). But the strange part is that Internet Explorer always works correctly and has NEVER failed once (I have delete cache and cookies in browsers but still the same thing happens).
Then after an hour or X amount of time, Firefox and Chrome start behaving normally again.
I have a narrowed it down to function below that always returns false even after login.
public bool isLoggedIn()
{
return System.Web.HttpContext.Current.User.Identity.IsAuthenticated;
}
So the process goes below with the user going to login with this function:
public void Login_OnClick(object sender, EventArgs args)
{
string email = UserName.Text;
string password = Password.Text;
string errorMsg = string.Empty;
bool cb = cb_agreeterms.Checked;
if (tests)
{
// The code in here tests to see if email, password, etc. have been filled out.
// This works 100% of the time and is NOT a problem.
}
else
{
// Validate user.
if (Membership.ValidateUser(email, password))
{
// Get the logged in user
MembershipUser user = Membership.GetUser(email);
if (user.IsLockedOut)
{
user.UnlockUser();
}
// Gets a datatable of the user details in our general database
DataTable dtUserData = this.dbData.GetUserByEmail(user.UserName);
if (dtUserData.Rows.Count > 0)
{
FormsAuthentication.SetAuthCookie(user.UserName, true);
// The details for the userId, screenName, etc. below get set by looking at the row 0 in datatable
// The LoginSession function intializes a session with a guid and saves all the data into an Application Context. This creates a SessionGuid cookie which I see get created on FF and Chrome (and always on IE).
LoginSession(userId, screenName, permissionLevel, user.UserName);
Response.Redirect("../myinternalsite.aspx");
}
}
else if (UserExistsInMembership(email))
{
// Tested this out and entering bad credentials fails the login and error is shown correctly on screen in the login control.
// We have failed to login.
ShowLoginError("E-mail or password is incorrect.");
}
}
}
So when the user authenticates, the redirect goes to ../myinternalsite.aspx. On the page in Page Load a VerifyLogin function gets called and calls:
public bool isLoggedIn()
The above ALWAYS returns falso in Chrome and FF which prompts a redirect to the home page. After a couple hours this fixes itself. IE works 100% of the time.
The web.config is this:
// authenticationConnection works and links correctly to the auth database just fine.
<sessionState timeout="120"/>
<membership defaultProvider="SqlProvider">
<providers>
<add connectionStringName="authenticationConnection" applicationName="Auth" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" name="SqlProvider" type="System.Web.Security.SqlMembershipProvider" requiresQuestionAndAnswer="false" passwordFormat="Hashed" enablePasswordReset="true" maxInvalidPasswordAttempts="1000" passwordAttemptWindow="1" />
</providers>
</membership>
<roleManager enabled="true" defaultProvider="SqlRoleManager">
<providers>
<add name="SqlRoleManager" type="System.Web.Security.SqlRoleProvider" connectionStringName="authenticationConnection" applicationName="MyApp"/>
</providers>
</roleManager>
<identity impersonate="true"/>
The cookies in Chrome and Firefox get set. I deleted them and saw them get reset correctly. But what is this issue? Why is IsAuthenticated failing for only some browsers and working for others and then fixes itself?
My login template with all my different steps is something like this too:
<asp:UpdatePanel ID="updateTheLogin" runat="server">
<ContentTemplate>
<asp:TextBox ID="UserName" runat="server" CssClass="loginTextbox"></asp:TextBox>
<asp:TextBox id="Password" runat="server" textMode="Password" CssClass="loginTextbox"></asp:TextBox>
<input type="button" class="btn-small pull-right disabled" id="LoginButton" value="Log In" onserverclick="Login_Click" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>
If you use MembershipProvider, you do not need to create Form Authentication cookie by yourself.
I answered one of your question, but after reading this, ignore that answer since you are using Membership Provider which will automatically create IPrincipal object for you.
All you have to do is to use ASP.Net Login control.
<asp:Login ID="Login" runat="server"></asp:Login>
Note: applicationName should be same for both membership and roleManager. They are different in your web.config.
How to View Authenticated User's Information
protected void Page_Load(object sender, EventArgs e)
{
if (User.Identity.IsAuthenticated)
{
var sb = new StringBuilder();
var id = (FormsIdentity) User.Identity;
var ticket = id.Ticket;
sb.Append("Authenticated");
sb.Append("<br/>CookiePath: " + ticket.CookiePath);
sb.Append("<br/>Expiration: " + ticket.Expiration);
sb.Append("<br/>Expired: " + ticket.Expired);
sb.Append("<br/>IsPersistent: " + ticket.IsPersistent);
sb.Append("<br/>IssueDate: " + ticket.IssueDate);
sb.Append("<br/>Name: " + ticket.Name);
sb.Append("<br/>UserData: " + ticket.UserData);
sb.Append("<br/>Version: " + ticket.Version);
Label1.Text = sb.ToString();
}
else
Label1.Text = "Not Authenticated";
}

asp.net custom user authentication with master page

I am working on a small Inventory management system. I have almost created it on my local machine. It works as it is designed. I just moved it to my hosting account and now I am facing some problems.
1, After login, when user goes to different pages, open and closes forms, after some time it redirects to login page. I don't know why. Seems like some sort of exception was occurred or the session had gone empty. How to handle this condition?
2, Is this the right way to keep a check on the user login using a check in Page_Load event of master page?
My site uses a master page that has separate layout for top navigation menu and bottom body area. When the first time user lands on the site it login into the system and upon successful login I store his information in session. I am heavily using Session in all pages for ADD, DELETE, UPDATE purposes. When an add record is performed, I pass success of failure message in session to show after post back. Code behind of login page is given below:
protected void loginForm_OnAuthenticate(object sender, AuthenticateEventArgs e)
{
string error = "";
sMethodName = "loginForm_OnAuthenticate";
_objLoginBLL = new LoginBLL();
int iRetVal = _objLoginBLL.ValidateUser(loginForm.UserName, loginForm.Password, ref error);
if (iRetVal >= 0)
{
Session.Clear(); //Remove all stored Session variables.
Session[Constant.Session.LOGGED_IN_DATETIME] = DateTime.Now.ToString("yyyyMMddHHmmssfff");
Session[Constant.Session.LOGIN_USERNAME] = loginForm.UserName;
Session[Constant.Session.LOGIN_USER_ID] = iRetVal;
Session[Constant.Session.LOGIN_COMPANY] = ddlCompanies.SelectedValue;
Session[Constant.Session.LOGIN_FISCAL_YEAR] = ddlFiscalYear.SelectedValue;
Session[Constant.Session.IS_DIRECT_ACCESS] = "NO";
FormsAuthentication.RedirectFromLoginPage(loginForm.UserName, loginForm.RememberMeSet);
}
else
{
Logger.Log("User validation failed.", sClassName, sMethodName, DEBUG);
switch (iRetVal)
{
case -1:
loginForm.FailureText = Constant.Messages.INCORRECT_USER_OR_PASSWORD;
loginForm.Focus();
break;
case -2:
loginForm.FailureText = Constant.Messages.ACCOUNT_LOCKED;
loginForm.Focus();
break;
//case -3:
//TODO: Account doesn't exists
default:
var randToken = new Random().Next(1000);
Session[Constant.Session.TOKEN] = randToken;
var myHashtable = new Hashtable
{
{Constant.Session.TOKEN, randToken},
{Constant.Fields.ERROR_KEY, iRetVal}
};
Response.Redirect(WebFunctions.CreateQueryString(Constant.Urls.Error, myHashtable));
break;
}
}
}
I am continuously checking if the session doesn't contain any user id then redirect it to the login page. The code behind of my master page is given below:
protected void Page_Load(object sender, EventArgs e)
{
if (Session[Constant.Session.LOGIN_USER_ID] == null)
{
FormsAuthentication.RedirectToLoginPage();
return;
}
CheckDBConnection();
Initialize();
}
Any help or tips will be appreciated.
You can view the site here: www.paracha.net (I can share guest account credentials in private message if anyone is interested)
First of all, keep in mind that the session cookie is not encrypted, so you should not be using the session to store any confidential information.
Secondly, you should not be checking the authentication on every Page_Load. Instead, you should configure the page access in web.config:
<configuration>
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</configuration>
This will protect all your pages so only authenticated (i.e. logged in) users will see the page, while all others will be redirected to the login page.
If you have some pages (e.g. a splash page) or folders (e.g. the images folder) that you want them to be accessible to all users, then add a section for each page or folder:
<configuration>
<location path="splash.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
<location path="images">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
</configuration>
In order for this to work, you should be using forms authentication. Here are the settings in web.config:
<configuration>
<system.web>
<authentication mode="Forms">
<forms
name=".YOURNAME_AUTH"
loginUrl="login"
defaultUrl="/"
protection="All"
timeout="30"
path="/"
requireSSL="true"
slidingExpiration="true"
cookieless="UseCookies"
domain=""
enableCrossAppRedirects="false">
</forms>
</authentication>
</system.web>
</configuration>
Obviously, you will need a login.aspx page, and when you click the Log in button, you need to authenticate the user like this:
protected void btnLogIn_Click(object sender, EventArgs e) {
string Username = txtUsername.Text;
string Password = txtPassword.Text;
try {
if (ValidateUser(Username, Password)) {
FormsAuthentication.RedirectFromLoginPage(Username, false);
}
else {
lblMessage.Text = "Incorrect Credentials.";
lblMessage.ForeColor = Color.Red;
}
}
catch {
lblMessage.Text = "Login Failed.";
lblMessage.ForeColor = Color.Red;
}
}
The function ValidateUser() can do anything you want for authentication. You can validate the credentials against your database if you like.
If you use FormsAuthentication, you do not need to check Session[Constant.Session.LOGIN_USER_ID] manually. It will redirect to Login page automatically for which you can configure in web.config.
Another thought
It is not directly related to your question. It is just an alternative approach.
Instead of creating multiple session states, you can create custom Context to keep track of the current logged-in user's information
E.g. You can store Company and Fiscal Year properties inside MyUser class.
void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (HttpContext.Current.User != null &&
HttpContext.Current.User.Identity.IsAuthenticated)
{
MyContext.Current.MyUser =
YOUR_BLL.GetUserByUsername(HttpContext.Current.User.Identity.Name);
}
}
public class MyContext
{
private MyUser _myUser;
public static MyContext Current
{
get
{
if (HttpContext.Current.Items["MyContext"] == null)
{
MyContext context = new MyContext();
HttpContext.Current.Items.Add("MyContext", context);
return context;
}
return (MyContext) HttpContext.Current.Items["MyContext"];
}
}
public MyUser MyUser
{
get { return _myUser; }
set { _myUser = value; }
}
}
}
Addition
C# is strongly type language, so you should not encode the variable name with type of variable. E.g. objLoginBLL and iRetVal. Please read C# Design Guideline or Essential C# 6.0 (Page 7).

Basic login with one database table

I am currently creating a CMS and have a section where a user can create, edit and delete users. The information is generated from a database, where I have made a table with User_ID, User_Name and User_Password. This means I do not want to use the automatically generated database tables VS gives you for their log ins.
With this I am trying to develop a really basic log in but I am having trouble understanding the process.
This is my web.config for the whole application:
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="websiteContent" connectionString="uid=AAA;pwd=AAA;Initial Catalog=AAA;Data Source=.\SQLEXPRESS"/>
</connectionStrings>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
<authentication mode="Forms">
<forms loginUrl="~/tools/default.aspx" timeout="2880"/>
</authentication>
</system.web>
</configuration>
Web.config for login:
<?xml version="1.0"?>
<configuration>
<location path="default.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</configuration>
This is my log in on the front end:
<asp:Login ID="Login1" runat="server" CssClass="loginSec" TextLayout="TextOnTop"
TitleText="" OnAuthenticate="Login1_Authenticate">
<LabelStyle CssClass="lblLogin" />
<TextBoxStyle CssClass="txtLogin" />
</asp:Login>
Log in from the back end:
protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
{
string userName = Login1.UserName;
string passWord = Login1.Password;
bool rememberUserName = Login1.RememberMeSet;
using (SqlConnection sqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["websiteContent"].ConnectionString))
{
sqlCon.Open();
string SQL = "SELECT CMS_Username, CMS_Password FROM CMS_Users WHERE CMS_Username ='" + userName + "' AND CMS_Password ='" + passWord + "'";
using (SqlCommand sqlComm = new SqlCommand(SQL, sqlCon))
{
sqlComm.ExecuteScalar();
if (sqlComm.ExecuteScalar() != null)
{
Response.Redirect("cms.aspx");
}
else
{
Session["UserAuthentication"] = "";
}
}
sqlCon.Close();
}
}
What I have done so far has prevented access to the page cms.aspx, but the log in never redirects to the page.
Any insight would be appreciated!!
I have added the settings of Authenticated as required by the docs
Custom authentication schemes should set the Authenticated property to
true to indicate that a user has been authenticated.
More research has led me to the neccessity to add this line in your code
FormsAuthentication.SetAuthCookie(Login1.UserName, true);
Also I will try to change your code in such a way that ExecuteScalar returns the count of user with that username and password. In this way ExecuteScalar will never return NULL, but a value that could be zero if no user exists or 1 if user exists (I suppose that you don't have two records with the same user and password)
using (SqlConnection sqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["websiteContent"].ConnectionString))
{
sqlCon.Open();
string SQL = "SELECT COUNT(*) As LoginFound FROM CMS_Users " +
"WHERE CMS_Username =#usr AND CMS_Password = #pwd";
using (SqlCommand sqlComm = new SqlCommand(SQL, sqlCon))
{
sqlComm.Parameters.AddWithValue("#usr", userName);
sqlComm.Parameters.AddWithValue("#pwd", password);
int result = (int)sqlComm.ExecuteScalar();
if (result > 0)
{
// In case of success you need to communicate this
e.Authenticated = Authenticated;
FormsAuthentication.SetAuthCookie(Login1.UserName, true);
Response.Redirect("~/tools/cms.aspx");
}
else
{
Session["UserAuthentication"] = "";
}
}
}
Also, I have removed the string concatenation from your sql command. This is the right way to pass string text to the database. Particularly if the values comes from your user input.
(See Sql Injection )
EDIT
Of course the cmd.aspx page should check if the user has been authenticated because otherwise one could type directly the url of the cms.aspx page bypassing the login control.
So in the Page_Load event of cms.aspx add this code
protected void Page_Load(object sender, EventArgs e)
{
if ( !Request.IsAuthenticated)
{
Response.Redirect("~/tools/default.aspx");
}
}

How to secure a link to be downloaded only by specific users?

I have the following case , and i wanna to ask what's the best solution ?
I have a specific file i wanna specific users(according to some permissions) to download this file .
so i show this file only for the authorized users, but what if someone(not authorized) recognize the file link(knows the link url) and download it !!
How to allow this file to be downloaded only by the authorized users .
Put the file into a directory which is not served by the web server and implement a handler for the "virtual url" which in turn checks for permissions etc. - a possible way would be an ASHX handler (see here for sample code and here for MSDN reference).
My answer would be:
Dont use direct links!
Create a Download.aspx and have the links for downloads post to Download.aspx?params
The params should be encrypted/hashed containing the filepath+name to download and session_id.
On Download.aspx validate that the session_id is valid and active on the browser.
This should allow you to allow downloads to the correct folks only:
If you add to the params also the user_id or the user_type you can deny/permit download on the onLoad of Download.aspx
The following link provides details on Authorization Rules in iis and asp.net, it seems pertinent to your question.
Firstly you want to ensure ASP.NET handles request for your specified file type. You can configure this in IIS (see link below).
Secondly, you will then need to update your web.config to deny anonymous users from reaching your url, providing that you are using rolemanager :
<roleManager defaultProvider="SqlProvider" enabled="true" cacheRolesInCookie="false"
cookieName=".ASPROLES" cookieTimeout="30" cookiePath="/" cookieRequireSSL="false"
cookieSlidingExpiration="true" cookieProtection="All">
<providers>
<add name="SqlProvider" type="System.Web.Security.SqlRoleProvider"
connectionStringName="membership" applicationName="yourApplication"/>
</providers>
</roleManager>
<location path="path/file.extension">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</location>
IIS 6 ignores Web.config authorization settings
The best way would be to add httphandlers and check whether the requested file have special permissions or not, an example for what I said would be:
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class MyHTTPHandler : IHttpHandler, IRequiresSessionState
{
string myFile;
public bool IsReusable {
get { return true; }
}
public void ProcessRequest(System.Web.HttpContext context)
{
myFile = context.Request.Path;
if (myFile.ToLower().Contains("members private files") || myFile.ToLower().Contains("members%20private%20files")) {
if (System.Web.HttpContext.Current.Session["Login"] == null) {
context.Response.Redirect("~/NotAuthorized.aspx");
} else {
if (myFile.ToLower().Contains("privatefiles")) {
StartDownload(context, myFile);
} else {
if (IsMemberAuthoraizedToDownloadFile(context)) {
StartDownload(context, myFile);
} else {
context.Response.Redirect("~/NotAuthorized.aspx");
}
}
}
} else {
StartDownload(context, myFile);
}
}
private void StartDownload(HttpContext context, string downloadFile)
{
context.Response.Buffer = true;
context.Response.Clear();
context.Response.AddHeader("content-disposition", "attachment; filename=" + downloadFile);
context.Response.ContentType = "application/pdf";
context.Response.WriteFile(downloadFile);
}
// just my own function to check if user is valid
private bool IsMemberAuthoraizedToDownloadFile(HttpContext context)
{
GroupMembersControl MyGroupMemberc = new GroupMembersControl();
System.Collections.Generic.List<GroupMembers> MemberGroupsL = MyGroupMemberc.GetMemberGroups(System.Web.HttpContext.Current.Session["Login"]);
MemberGroupControl MyGroupC = new MemberGroupControl();
MemberGroup MyGroup = default(MemberGroup);
foreach (GroupMembers groupmember in MemberGroupsL) {
MyGroup = MyGroupC.GetMemberGroup(groupmember.GroupID);
if (myFile.ToLower().Contains(MyGroup.Name.ToLower)) {
return true;
}
}
return false;
}
}

IsInRole problem

I'm working for the first time with Forms Authentication, I'm using an example from the web to learn, I included in my web.config
<authentication mode="Forms">
<forms name="MYWEBAPP.ASPXAUTH" loginUrl="Login.aspx" protection="All" path="/"/>
</authentication>
<authorization>
<deny users="?"/>
</authorization>
Then I created a page for logging in "login.aspx", and coded this on a button, just to start;
private void btnLogin_Click(Object sender, EventArgs e)
{
// Initialize FormsAuthentication
FormsAuthentication.Initialize();
// Create a new ticket used for authentication
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // Ticket version
Username.Value, // Username associated with ticket
DateTime.Now, // Date/time issued
DateTime.Now.AddMinutes(30), // Date/time to expire
true, // "true" for a persistent user cookie
"accountants, seekers, copiers, typers", // User-data, in this case the roles
FormsAuthentication.FormsCookiePath);// Path cookie valid for
// Encrypt the cookie using the machine key for secure transport
string hash = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(
FormsAuthentication.FormsCookieName, // Name of auth cookie
hash); // Hashed ticket
// Set the cookie's expiration time to the tickets expiration time
if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;
// Add the cookie to the list for outgoing response
Response.Cookies.Add(cookie);
}
Also I coded in Global.asax;
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if(HttpContext.Current.User != null)
{
if(HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
// Get the stored user-data, in this case, our roles
string userData = ticket.UserData;
string[] roles = userData.Split(',');
HttpContext.Current.User = new GenericPrincipal(id, roles);
}
}
}
}
And finally in another page I tried to confirm the roles aquired;
protected void Page_Load(object sender, EventArgs e)
{
string str = null;
if (User.IsInRole("seekers"))
{
str += " seekers ";
}
if (User.IsInRole("accountants"))
{
str += " accountants ";
}
if (User.IsInRole("copiers"))
{
str += "copiers";
}
Response.Write(str);
}
But something strange happens cause it only writes "accountants" (note that "accountants" is the firts element in the delimited comma string) and not the other roles, which were supposed to be showed. I changed the order of the role list in the btnlogin click event writing "copiers" as the first element and it's written only "copiers" in the page.
I've tried with different combinations and always is printed the first element of the delimited comma string.
Sorry by my ignorance but what is happening here, are all the roles there or not? is normal? or there's something I'm forgetting here?
Thanks in advance.
Drop the spaces in
"accountants, seekers, copiers, typers"
Try it without the spaces after the commas:
"accountants,seekers,copiers,typers"
The Split will be creating strings like "accountants", " seekers", " copiers", " typers",
You're splitting on ',' ... but when you initialize your string of roles it's actually ", " (comma space).
A tip on this is to use debugger and use the immediate window to actually "see" what's happening as it happens.

Categories

Resources