How to Check the duplicate in application state in mvc asp.net - asp.net-mvc

how to check the duplicate in Application state
My problem scenario:
I stored the username and password in the application[""] variable.
another user enters the username password i want to check for each and every user.
i tried for for loop but hard to find the count..
could you help me check the duplicate
for (int j = 0; j < (int)System.Web.HttpContext.Current.Application["Userlogin"].ToString().Length - 1; j++)
{
if (System.Web.HttpContext.Current.Application[i].ToString() == sKey)
{
Session["duplicateuser"] = "logout";
Returnmsg = "-3";
}
}
but it shows the string length of the application[]
Thank in Advance

I stored the username and password in the application[""] variable
Wow, don't. The Application state is shared among all users of your application. Never store user specific data in application state. Use Session state instead.
UPDATE:
If you want to check for concurrent user access you could store a collection of users into the application state such as IEnumerable<string>. Then you could check if a user is already logged in easily:
public bool IsUserLoggedIn(string username, HttpApplicationStateBase application)
{
var users = application["users"] as IEnumerable<string>;
if (users == null)
{
users = new ConcurrentBag<string>();
application["users"] = users;
}
return users.Any(u => u == username);
}

Related

How to detect and logout the user from an application if he logins from another browser/device in IdentityServer4?

I have implemented the IdentityServer4 SSO in my application. SSO works fine as well as Logout for all the clients,However there is a new requirement where if the user is already logged in into an application and if he tries to login again (From different devise/browser) then he should be automatically logged out of the previous browser. I am not getting my head around this.How to implement this and if it is possible at all to track the user login sessions?
Update:-
We have tried following way of doing it, We have added the Session info into the Global Static variables using "Action" filter attribute.Here we stored the Login Session Info after user gets logged in.
private class LoginSession
{
internal string UserId { get; set; }
internal string SessionId { get; set; }
internal string AuthTime { get; set; }
internal DateTimeOffset AuthDateTime
{
get
{
if (!string.IsNullOrEmpty(AuthTime))
return DateTimeOffset.FromUnixTimeSeconds(long.Parse(AuthTime));
else
return DateTimeOffset.UtcNow;
}
}
}
private static List<LoginSession> LoginSessions = new List<LoginSession>();
In "Action Filter" methods we check if the user's session id is already present or not. If the session is present and it's SessionId is not matching with claims session id then we check the Login time of the Session. If the login time is less than the current login time then the user is logged out of the system else we update the login session with the latest session id and login time. Due to this workflow for the second login the Login Session will be updated as the Login Time is always Greater than the saved Login Session Info. And for the old Logged in session the user will be logged out of the system as the login time would always be less than the updated session info.
public class SessionValidationAttribute : ActionFilterAttribute
{
public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
string action = context.RouteData.Values["action"].ToString();
if (!string.IsNullOrEmpty(action) &&
context.Controller.GetType().GetMethod(action).GetCustomAttributes(typeof(AllowAnonymousAttribute), true).Length == 0)
{
var claims = ((ClaimsIdentity)((Microsoft.AspNetCore.Mvc.ControllerBase)context.Controller).User.Identity).Claims;
var sessionId = claims.Where(x => x.Type == "sid").First().Value; // context.HttpContext.Request.Cookies.TryGetValue("idsrv.session", out var sessionId);
var userId = claims.Where(x => x.Type == "sub").First().Value;
var authTime = claims.Where(x => x.Type == "auth_time").First().Value;
var authDateTime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(authTime));
if (LoginSessions.Where(x => x.UserId.Contains(userId)).Count() > 0) // if already logged in
{
var latestLogin = LoginSessions.Where(x => x.UserId == userId).OrderByDescending(x => x.AuthDateTime).First();
if (sessionId != latestLogin.SessionId)
{
if(authDateTime > latestLogin.AuthDateTime) // login using new browser(session)
{
latestLogin.SessionId = sessionId; // assign latest sessionId
latestLogin.AuthTime = authTime; // assign latest authTime
}
else if (authDateTime < latestLogin.AuthDateTime) // login using old browser(session)
{
LoginSessions.RemoveAll(x => x.UserId == userId && x.SessionId!=latestLogin.SessionId);
context.Result = ((Microsoft.AspNetCore.Mvc.ControllerBase)context.Controller)
.RedirectToAction(actionName: "Logout", controllerName: "Home",
routeValues: new { tenant = string.Empty, isRemoteError = false });
}
}
}
else
{
var newLogin = new LoginSession() { UserId = userId, SessionId = sessionId, AuthTime = authTime };
LoginSessions.Add(newLogin);
}
}
return base.OnActionExecutionAsync(context, next);
}
}
This works as we tested for few users but Will this solution work in actual scenario where there are thousands of users login into the system?Is it a good idea to use Static variable globally for storing session info? What will be potential drawbacks of using this.Please advice. We are open to new ideas also,if there is any new methods of implementing this functionality please let us know.
Thanks!!!
Disclaimer: I have no practical experience with IS4.
You probably have a good reason but I'm failing to understand why you are overwriting the latestLogin session's details when you are validating the current latest login?
If I'm not mistaken this line will loop through all sessions in your application, which you have multiple alike of in the lines that follow.
if (LoginSessions.Where(x => x.UserId.Contains(userId)).Count() > 0)
This is indeed something you wouldn't want to do in an application you expect to scale.
Unfortunately I'm not familiar with IS4 and I can not tell you if there is a possibility to solve this problem entirely by utilizing its APIs, instead I can give you practical advice.
You can use a separate centralized storage, the possibilities are endless but something along the lines of memcached is perfect. Then the algorithm is fairly simple:
Whenever a user tries to log in, retrieve the value stored under user's ID from storage.
If present, that would be the current session ID, then destroy it in IS4 and continue.
Create new login session and store the session ID in memcached under the user's ID.
This way there will never be more than 1 session for a given user and you've successfully reduced the complexity of the algorithm from O(n), or worse in your case, to O(1).

does Session.Clear(); clears all sessions data

I Want to clear one session Data but it's clearing both Sessions data. below is my 2 MVC action methods which are used to clear Session data.
1)
public int Clearsesson()
{
int i = 0;
List<Wish_Product> products = new List<Wish_Product>();
if (**Session["WishItem"]** != null)
{
Session.Clear();
i = 1;
}
else
{
i = 0;
}
return i;
}
2)
public int Clearsesson2()
{
int i = 0;
List<Cart_Product> products = new List<Cart_Product>();
if (**Session["CartItem"]** != null)
{
// Session.Remove("CartItem");
Session.Clear();
i = 1;
}
else
{
i = 0;
}
return i;
}
So like this I am storing Data in two Sessions but when I run this asp.net MVC Action method to clear One session data, it automatically clears second one Session Data too.
Why so like this? Please make it clear to me why it's happening.
Or what I've to do to solve this problem?
The short answer is yes, on the current collection.
According to ASP.NET documentation, HttpSessionState.Clear method removes all keys and values from currently active session state collection (it doesn't destroy user's session state base), so that both Session["WishItem"] & Session["CartItem"] values are cleared together from collection when the method is executed, but the session state base kept alive.
There are 2 different session collection keys in this case & actually both of them using same session state. To clear single session data, there are 2 different ways:
1) Remove session key using Session.Remove.
Session.Remove("WishItem");
2) Assign null value to the session state based with its key.
Session["WishItem"] = null;
The difference between them is that the former deletes both keys and values so that it won't appear as key-value pair in session collection anymore, and the latter overwrites current collection value with specified key but the key name still unchanged.
Therefore, you can remove each session data separately without clearing another session key like this:
public int ClearSession()
{
int i = 0;
// other code logic
if (Session["WishItem"] != null)
{
// only WishItem key has cleared
Session.Remove("WishItem");
i = 1;
}
// other code logic
}
public int ClearSession2()
{
int i = 0;
// other code logic
if (Session["CartItem"] != null)
{
// only CartItem key has cleared
Session.Remove("CartItem");
i = 1;
}
// other code logic
}
Note that each authenticated user always be assigned with one session state base, hence using Session.Clear removes all existing key-value pairs stored in session collection for current user (doesn't affect other user's session state).

how to store data into temporary instead of database

i want to insert my data into temporary in mvc
I dont want to store data directly into database
For an example, as u visit to a mall and buy 5-6 things, so first it wont store all data in database, instead it stores into somewhere in application.
Like this what logic may i use to make this type of site
public ActionResult Education_Detail(Education objEducation)
{
sp.Reg_Can_Education(ref objEducation);
objEducation.CorColl = (CourseCollection)TempData["objCourseColl"];
TempData["objCourseColl"] = objEducation.CorColl;
for (int item = 0; item < objEducation.CorColl.Count; item++)
{
if (objEducation.CorColl.Item(item).CourseId.ToString() == objEducation.objCourse.CourseId)
{
objEducation.objCourse.CourseNm = objEducation.CorColl.Item(item).CourseNm.ToString();
}
if (objEducation.objCourse.CourseNm != null)
{
break;
}
}
Thank you
Store your data in cookie or session and when user check out store it to database.

Duplicate users added to database

I try to add a new value to my database. UserPassword and RePassword must have the same value and a user with UserName must not already exist in the database.
public User NewUser(int HotelID, string UserName, string UserPassword, string RePassword, string FullName, string Email, bool Active, bool MasterUser)
{
User user = new User();
user.HotelID = HotelID;
user.UserName = UserName;
user.UserPassword = UserPassword;
user.FullName = FullName;
user.Email = Email;
user.IsActiveq = Active;
user.IsMaster = MasterUser;
var cekUser = (from c in _UserRepository.All()
where c.HotelID == HotelID
select c.UserName).ToList();
if (UserPassword == RePassword)
{
foreach (string cek in cekUser)
{
var x = cek;
if (UserName != x)
{
_UserRepository.Add(user);
}
}
}
_UserRepository.CommitChanges();
return user;
}
Every time I run my code a new line is added to the database, although a user with the supplied user name already exists in the database.
Why does this happen? Which part of my code is wrong?
I think your code should be something like this:
if (UserPassword == RePassword)
{
// Also I thinks you should finish whether user existed logic in database
// but for now, let's follow your original logic
var existedUsers = (from c in _UserRepository.All()
where c.HotelID == HotelID
select c.UserName).ToList();
if (!existedUsers.Any(u => u == UserName))
{
_UserRepository.Add(user);
_UserRepository.CommitChanges();
}
}
You have your logic wrong. If there is more than one user in a given hotel, your code will be adding more users for all users with names different from UserName.
bool found = false;
foreach(string cek in cekUser)
{
if ( UserName == cek)
{
found = true;
break;
}
}
if (!found)
_UserRepository.Add(user);
Just offering an alternate idea.
If you have access to the database, the best approach will be to make the Username field UNIQUE. That way, even if you get your code wrong, a duplicate insert will fail. Then you capture that fail gracefully in your Repository, and Bob's your uncle.

Put data from database into Session in MVC

I currently working on an extension of the filter [Authorize], so that I can retrieve the permissions from the database. Everything works, but it is sure a performance issue. Every time I send a query to the database, asking for permission, and that is not the best way to determine this. So I thought to put these data in the Session. What is the fastest way to put a data from database in to Session object which I could ask (LINQ) as well as databases.
Now this looks like:
var _allowedRolesDB = context.sec_RolesInCAs
.Where(rl => rl.MenuControlName == controllRights && rl.MenuActionName == actionRights)
.Select(rl => rl.RoleName);
foreach (var r in _allowedRolesDB)
{
RolesDB = RolesDB + r.ToString() + ",";
}
but I want change to
var _allowedRolesDB = MySuperSessionSomethink
.Where(rl => rl.MenuControlName == controllRights && rl.MenuActionName == actionRights)
.Select(rl => rl.RoleName);
foreach (var r in _allowedRolesDB)
{
RolesDB = RolesDB + r.ToString() + ",";
}
where MySuperSessionSomethink will keep one-time-retrieved data from database. Any idea how I can do this? Tx for help.
BIGGER PICTURE
Ok. I will show bigger picture.
Whole idea is to create custom authorize filter.
[CustomAuthAttribute("Home,Index", Roles = "SuperAdministrator")]
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
What is a goal of this. Create authorize attribute which has all benefits, plus additional features like keep information about rights in database.
Now what I do is:
public CustomAuthAttribute(params string[] controllerAction)
{
IPrincipal user = HttpContext.Current.User;
string userName = user.Identity.Name;
**... some code .. and take all allowed roles and check it have permissions**
var _allowedRolesDB = context.sec_RolesInCAs
.Where(rl => rl.MenuControlName == controllRights && rl.MenuActionName == actionRights)
.Select(rl => rl.RoleName);
foreach (var r in _allowedRolesDB)
{
RolesDB = RolesDB + r.ToString() + ",";
}
**... some code .. thesame withs single users**
}
After this i use
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
**... can acces or not part of code ...**
if (_rolesSplit.Any(user.IsInRole))
{
return true;
}
}
But there is the problem. Everytime I ask database about permissions, and this IMHO is not best way. Now my idea is take all permissions for one user and put him into his session when hi is authorized. Maybe I'm wrong and this way will make problems, but keep in database and ask all the time about permissions is not good idea too :). So maybe better way to take data and use in code after only one or two questions to the database?
First of all, it seems like you would want to add these to the cache, and not the session. They seem global to the application, not specific to the user. Since you're doing a lookup of roles by menu control/action, I would just add them to the cache as a lookup:
string action = controlRights + actionRights;
string allowedRoles = Cache[action];
if (allowedRoles == null)
{
allowedRoles = String.Join(",", context.sec_RolesInCAs
.Where(rl => rl.MenuControlName == controlRights && rl.MenuActionName == actionRights)
.Select(rl => rl.RoleName)
.ToArray());
Cache[action] = allowedRoles;
}
This will give you the allowed roles for the given control/action, from the cache on the second request.

Resources