How can I isolate users' data in ASP.net MVC application? - asp.net

So I have an asp.net application (using MVC5, ASP 4.5, EF) deployed on Azure. The application allows users to register and login.
However, once logged in, anyone can see everyone else's data.
What is the best practice to isolate the data belonging to different users so that each user can only see the data he/she creates?
Another small question is how does Facebook separates its users' data?
Thanks

For every piece of data a user creates, store their user ID and only allow users to see data that has their user ID. A couple of examples:
SQL:
SELECT UserDataID, Value FROM UserData WHERE UserID = #UserID;
Pass in the user's id to the #UserID parameter.
LINQ:
using (var entityFrameworkContext = new MyDataEntities())
{
var currentUserData = entityFrameworkContext.UserData.Where(userData -> userData.UserID = currentUserID);
}
Where currentUserID could be the user name or ID from forms authentication, for example: HttpContext.Current.User.Identity.Name.

The way in which I accomplished this was by the following.
In your controller you will need to use
public ActionResult Index()
{
var currentUser = manager.FindById(User.Identity.GetUserId());
return View(db.ToDoes.ToList().Where(todo => todo.User.Id == currentUser.Id));
}
You can then also create an admin role which can then view all the details of users and return ToList(). You then might want to put an [Authorize] method on it to only allow Admins access.
[Authorize(Roles="Admin")]
public async Task<ActionResult> All()
{
return View(await db.ToDoes.ToListAsync());
}
I found the following project of great help in understanding. https://github.com/rustd/AspnetIdentitySample
Hope this is of some help

Related

Using claims or just roles in ASP.NET Core MVC

In my ASP.NET Core MVC project, I have this scenario: a manager can do anything in the web application, you can call it super manager, and there is only one user. This "super" manager can add other managers that are more restricted, for example these managers cannot create users of type manager, or cannot see some information.
Technically, they have a lot in common. I already have many role types so I do not want to create another one called super manager where I will be creating only one user. So in this case should I use claims? or is it better to just create two roles? I know it will be less complicated but I want to know the best practice.
I'm new to ASP.NET Core so I appreciate examples or articles that could help me, thank you!
In my opinion, there is no difference between add the superadmin by claims or role claim, the role claim is also a type of claims.
In my opinion, if you don't have special requirement which need to add the user superadmin by claims, the best way is using claims. Since you could directly use the [Authorize(Roles = "Superadmin")] and no need to write another codes to add the claims by using identity factory.
If you want to add the superadmin by claims, you should use UserClaimsPrincipalFactory like this answer and add the claims policy like this article shows.
I suggest if you need to create many roles, you use
Claims-based authorization .
You can use Authorize[Roles = "Admin"] property or you can create a custom AuthorizeAttribute, for example:
public class AuthorizeAccess : AuthorizeAttribute, IAuthorizationFilter
{
public string UniqueCode { get; set; }
public void OnAuthorization(AuthorizationFilterContext context)
{
if (!context.HttpContext.User.HasClaim(c => c.Type == "CustomAccess" && c.Value.Equals(UniqueCode)))
{
// Redirect AccessDenied when the claim not exist.
context.Result = new RedirectToPageResult("/AccessDenied");
return;
}
}
}
And we can use it
[AuthorizeAccess(UniqueCode = "EDIT")]
public class IndexModel : PageModel
{
....
}
In this case you need to load a claim list in the login access
identity.AddClaim(new Claim("CustomAccess", "HOME_PAGE"));
identity.AddClaim(new Claim("CustomAccess", "EDIT"));
identity.AddClaim(new Claim("CustomAccess", "DELETE"));
identity.AddClaim(new Claim("CustomAccess", "INSERT"));

okta - asp.net - How do I get the User Name / Log In Id / Unique Identifier of the current user sign in? - Standard Way?

Using okta, how do I get the User Name / Log In Id / Unique Identifier of the current user signed in?
I am currently working on an ASP.Net Web Forms App. After the user successfully signs in, I can get the name of the user with the code below.
var name = User.Identity.Name;
However, I want the Log In Id / Unique Identifier of the User. Looking at the Profile form on the okta web site, it looks like okta calls that field User Name.
After doing some research, I did write up some code that gets what I want using the ClaimsPrincipal class and claimType == "preferred_username". But the code seems like a hack to me. Looking for the Standard Way to get this value.
Yes, you're on the right track. You do have to locate a claim based on its "type" but it's simple enough:
[Authorize]
public ActionResult SomeMethod()
{
var userId = HttpContext.User.FindFirstValue("preferred_username");
...
}
This is the code I added:
var userId = principal.Claims.FirstOrDefault(c => c.Type == "preferred_username");

Is there a better way to implement role based access in ASP.NET framework?

Basically I've spent the last few days trying to figure out how to add simple Admin and Member roles onto a website I'm developing for a friend. (I am using ASP.NET Framework 5.2.7.0). I know that Microsoft has a nice role based access feature built in which allows you to put something like [Authorize Role=("Admin") at the top of the controller; however I have not been able to get it to work at all and most of the resources I've found are for ASP.NET Core.
I've tried modifying my web.config file to enable the role based access (and hopefully migrate the roles and such to my database). But since I've been unable to figure any of this out, I've tried going a more hacky route. (**I am not an advanced programmer, I've been doing this for about a year now and am in no way a pro). This is what I've basically come up with in my attempt to verify if a user is an admin (which also didn't work).
[Authorize]
public class AdminController : Controller
{
private LDSXpressContext db = new LDSXpressContext();
public ActionResult AdminPortal()
{
IsAdmin();
return View();
}
private ActionResult IsAdmin()
{
string name = User.Identity.Name;
//The User.Identity.Name stores the user email when logged in
var currentUserObject = db.accounts.Where(x => x.clientEmail == name);
Account currentUser = new Account();
foreach (var user in currentUserObject)
{
//I loop through the results, even though only one user should
//be stored in the var CurrentUserObject because it's the only
//way I know how to assign it to an object and get its values.
currentUser = user;
}
if (currentUser.role == 2) //the number 2 indicates admin in my db
{
return null;
}
else
{
//Even when this is hit, it just goes back and returns the
//AdminPortal view
return RedirectToAction("Index", "Home");
}
}
}
Now I'm nearly positive that is is NOT a very secure way to check if a signed in user is an admin, but I was hoping that it would at least work. My idea was when someone attempted to access the AdminPortal, the IsAdmin method would run and check if the user is an admin in the database. If they are, then it returns null and the AdminPortal view is displayed, if they are not an Admin, then they are redirected to the Index view on the home page. However, the AdminPortal page is always displayed to any user and this doesn't seem to work either. I've even stepped into the code and watched it run over the return RedirectToAction("Index", "Home"); action, but then it jumps back to the AdminPortal method and just returns the AdminPortal view. So my question is:
1) If anyone happens to have experience with Role Based access in ASP.NET Framework, I would love some tips on how to get it set up
or,
2) If all else fails and I need to use my hacky method, why does it continue to return the AdminView even when the user is not an admin.
**Note: I know I could create a function that returns true or false if the user is an Admin or not, and then have an if/else statement in the AdminPortal controller that will return on view for true and another for false, however I don't want to have to implement that onto every ActionMethod, it'd be nice to keep it down to one line, or just the [Authorize Role="Admin] above the controller if possible.
Thank you guys so much for any help provided, I've been trying to research and fix this for days now and decided to reach out and ask the community!
At a minimum, you'll want to make some adjustments to what you're doing:
[Authorize]
public class AdminController : Controller
{
public ActionResult AdminPortal()
{
if(IsAdmin())
{
return View();
}
return RedirectToAction("Index", "Home");
}
private bool IsAdmin()
{
bool isAdmin = false;
using(LDSXpressContext db = new LDSXpressContext())
{
string name = User.Identity.Name;
//The User.Identity.Name stores the user email when logged in
// #see https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.singleordefault
var currentUser = db.accounts.SingleOrDefault(x => x.clientEmail.Equals(name, StringComparison.OrdinalIgnoreCase));
// If the email doesn't match a user, currentUser will be null
if (currentUser != null)
{
//the number 2 indicates admin in my db
isAdmin = currentUser.role == 2;
}
}
return isAdmin;
}
}
First off, DbContext instances are meant to used, at most, per the lifetime of an HTTP request. Moving it from the class / controller level and placing it within a using block makes sure that it's properly disposed.
Next, your IsAdmin function really just needs to return a true/false value based on your lookup, and then the AdminPortal action can decide what to do with that result.
Since email seems to be a unique field in your table, use the SingleOrDefault or FirstOrDefault LINQ extension to fetch a single matching record. Which one you use is up to you, but if it's truly a unique value, SingleOrDefault makes more sense (it will throw an exception if more than one row matches). Using the StringComparison flag with the String.Equals extension method makes your search case-insensitive. There are a few culture-specific versions of that, but ordinal matching is what I would normally use, here.
Implementing some version of the Identity framework is a bit too long for an answer here, but it's possible to implement a claims-based authentication scheme without too much work. That's something that probably needs a separate answer, though.

ASP.NET SQL Profile Provider - Does the ProfileBase.Create() method hit DB?

I am working with the SQLMemebershipProvider and using Profiles. I have a custom class called UserProfile that inherits from the ProfileBase class and I use this to set custom properties like "FullName". I am wanting to loop through all the users in the database and get access to their profile properties. On each iteration I am calling ProfileBase.Create() to get a new profile and then access the properties.
It looks to me like every time ProfileBase.Create() is called it hits my SQL database. But I am just looking for confirmation of this. So, does anyone know if this does in fact hit the DB each time?
And better yet, does anyone have a better solution of how I could make one call to the DB to get all users with their custom profile attributes?
I know I could write my own stored proc, but I am wondering if there is a way built in to the Membership Provider.
Mike, I believe what you observed is true. I am working with a ProfileProvider that uses Azure TableStorage as data store. I wanted to get a list of user profiles from database and merge them with information from membership provider.
It took some time until I realized that calling ProfileBase.Create() with a username as argument performs a lookup against TableStorage and actually retrieves the data associated with that username. As far as I'm concerned, calling this method Create() is misleading, I would expect Load() or Get().
Currently my code looks like this:
public IEnumerable<AggregatedUser> GetAllAggregatedUsers()
{
ProfileInfoCollection allProfiles = this.GetAllUsersCore(
ProfileManager.GetAllProfiles(ProfileAuthenticationOption.All)
);
//AggregatedUser is simply a custom Class that holds all the properties (Email, FirstName) that are being used
var allUsers = new List<AggregatedUser>();
AggregatedUser currentUser = null;
MembershipUser currentMember = null;
foreach (ProfileInfo profile in allProfiles)
{
currentUser = null;
// Fetch profile information from profile store
ProfileBase webProfile = ProfileBase.Create(profile.UserName);
// Fetch core information from membership store
currentMember = Membership.FindUsersByName(profile.UserName)[profile.UserName];
if (currentMember == null)
continue;
currentUser = new AggregatedUser();
currentUser.Email = currentMember.Email;
currentUser.FirstName = GetStringValue(webProfile, "FirstName");
currentUser.LastName = GetStringValue(webProfile, "LastName");
currentUser.Roles = Roles.GetRolesForUser(profile.UserName);
currentUser.Username = profile.UserName;
allUsers.Add(currentUser);
}
return allUsers;
}
private String GetStringValue(ProfileBase profile, String valueName)
{
if (profile == null)
return String.Empty;
var propValue = profile.PropertyValues[valueName];
if (propValue == null)
return String.Empty;
return propValue.PropertyValue as String;
}
Is there a better (more straightforward, more performant) way to
retrieve all the custom profile information from profile provider and
merge them with membership provider info to show them e.g. in an administrator page?
I have had a look at Web Profile Builder but IMO this only provides design-time intellisense for custom profile properties by generating a proxy class.
You don't persist to the database until you call Save:
The Save method writes modified
profile property values to the data
source. The profile provider can
reduce the amount of activity at the
data source by performing updates only
when the IsDirty property is set to
true. This is the case for the default
SqlProfileProvider.

How do you get the UserID of a User object in ASP.Net MVC?

I have some tables that have a uniqueidentifier UserID that relates to aspnet_Users.UserID. When the user submits some data for those tables, since the controller method has an [Authorize] I get a User object. I can get the username with User.Identity.Name, but how do I get the UserID to be able to establish (the ownership) relationship?
It seems you cannot get it from the User object but you can get it this way:
Guid userGuid = (Guid)Membership.GetUser().ProviderUserKey;
Here is the solution:
Include:
using Microsoft.AspNet.Identity;
Then use extension methods:
User.Identity.GetUserId();
Firstly, this answer is not strictly an MVC answer, but an ASP.NET answer. The fact that your site is MVC is irrelevant to solving the problem, in this case.
Hmm. I'm not very sure how you are handling your users in your system but it sounds like you using the (very evil) asp.net membership provider that comes out of the box with .net. This is hinted by the fact that you said
aspnet_Users.UserID
UserID is a uniqueidentifier (read: GUID).
With the default forms authentication system, which uses the default FormsIdentity, it only has a single property called Name (as you correctly noted). This means it has only one value where to place some unique user information. In your case, you are putting Name/UserName/DisplayName, in the Name property. I'm assuming this name is their Display Name and it is unique. Whatever value you are putting in here, it HAS TO BE UNIQUE.
From this, you can grab the user's guid.
Check this out.
using System.Web.Security;
....
// NOTE: This is a static method .. which makes things easier to use.
MembershipUser user = Membership.GetUser(User.Identity.Name);
if (user == null)
{
throw new InvalidOperationException("User [" +
User.Identity.Name + " ] not found.");
}
// Do whatever u want with the unique identifier.
Guid guid = (Guid)user.ProviderUserKey;
So, every time you wish to grab the user information, you need to grab it from the database using the static method above.
Read all about the Membership class and MembershipUser class on MSDN.
Bonus Answer / Suggestion
As such, i would CACHE that result so you don't need to keep hitting the database.
... cont from above....
Guid guid = (Guid)user.ProviderUserKey;
Cache.Add(User.Identity.Name, user.UserID); // Key: Username; Value: Guid.
Otherwise, you can create your own Identity class (which inherits from IIdentity) and add your own custom properties, like UserID. Then, whenever you authenticate (and also on every request) you can set this value. Anyway, this is a hard core solution, so go with the caching, right now.
HTH
User.Identity is an IPrincipal - typically of type System.Web.Security.FormsIdentity
It doesn't know anything about UserIDs - it's just an abstraction of the concept of an 'identity'.
The IIdentity interface only has 'Name' for a user, not even 'Username'.
If you're using MVC4 with the default SimpleMembershipProvider you can do this:
WebSecurity.GetUserId(User.Identity.Name) // User is on ControllerBase
(Where WebSecurity is in the nuget package Microsoft.AspNet.WebPages.WebData in WebMatrix
You can also use
WebSecurity.CurrentUserName
WebSecurity.CurrentUserId
(if you're using ASPNetMembershipProvider which is the older more complex ASPNET membership system then see the answer by #eduncan911)
If you are using the ASP.NET Membership (which in turn uses the IPrincipal object):
using System.Web.Security;
{
MembershipUser user = Membership.GetUser(HttpContext.User.Identity.Name);
Guid guid = (Guid)user.ProviderUserKey;
}
User.Identity always returns the state of the current user, logged in or not.
Anonymous or not, etc. So a check for is logged in:
if (User.Identity.IsAuthenticated)
{
...
}
So, putting it all together:
using System.Web.Security;
{
if (User.Identity.IsAuthenticated)
{
MembershipUser user = Membership.GetUser(HttpContext.User.Identity.Name);
Guid guid = (Guid)user.ProviderUserKey;
}
}
Best Option to Get User ID
Add Below references
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security;*
public myFunc()
{
.....
// Code which will give you user ID is
var tmp = User.Identity.GetUserId();
}
If you are using your own IPrincipal object for authorization, you just need to cast it to access the Id.
For example:
public class MyCustomUser : IPrincipal
{
public int UserId {get;set;}
//...Other IPrincipal stuff
}
Here is a great tutorial on creating your own Form based authentication.
http://www.codeproject.com/KB/web-security/AspNetCustomAuth.aspx
That should get you on the right path to creating an authentication cookie for your user and accessing your custom user data.
using System.Web.Security;
MembershipUser user = Membership.GetUser(User.Identity.Name);
int id = Convert.ToInt32(user.ProviderUserKey);
Its the ProviderUserKey property.
System.Web.Security.MembershipUser u;
u.ProviderUserKey
Simple....
int userID = WebSecurity.CurrentUserId;
Usually you can just use WebSecurity.currentUserId, but if you're in AccountController just after the account has been created and you want to use the user id to link the user to some data in other tables then WebSecurity.currentUserId (and all of the solutions above), unfortunately, in that case returns -1, so it doesn't work.
Luckily in this case you have the db context for the UserProfiles table handy, so you can get the user id by the following:
UserProfile profile = db.UserProfiles.Where(
u => u.UserName.Equals(model.UserName)
).SingleOrDefault();
I came across this case recently and this answer would have saved me a whole bunch of time, so just putting it out there.

Resources