I have a legacy UserID (int32) which I wish to link to asp.net membership. I have set up link tables on the database and I'm happy with that part. The question is where to store the UserID in the web application so that it is easily accessible when required.
I decided that the best place to store it is in the UserData part of the FormsAuthenticationTicket in the LoggedIn event of the Login Control. My first attempt to make this accessible was to extract it in the PreInit of my BasePage class. The trouble with this is that it gets messy when the UserID is required by UserControls.
Is it acceptable to just wrap it in a static method or property in a Utilities class, something like this:
public static int UserID
{
get
{
int userID = 0;
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
userID = Int32.Parse(ticket.UserData);
}
return userID;
}
}
This seems to work but I don't know if I'm breaking some unwritten rule here. I presume all this stuff is in memory so there's no great overhead in this access.
Your code looks fine from a functional perspective (though I would clean it up a bit, but that's more a style thing).
You might consider making it an extension method, though, rather than just sticking it in a random utility class. Maybe an extension for the IIdentity class?
int myUserId = HttpContext.Current.User.Identity.MyUserId();
Using the UserData field is fine, I guess. Another option is to create your own IIdentity object with a custom Ticket and wrap them in a GenericPrincipal -- might be too much work for what you're after, though.
Related
So I created a class to hold user settings and i'm saving it as a JSON string so that I can modify it whenever I please without doing migrations every time I add one setting.
Anyways everything is working wonderful, well until, exception, I look further into it, and I've found things about it being thread safe but I don't really understand what they mean, how is my simple function that accesses entity identity dbcontext, not thread safe?
public static AppUserSettings GetUserSettings(this IIdentity identity)
{
var user = IdentityDbContext.Users.Find(identity.GetUserId());
return user.SettingsClass != null ? JsonConvert.DeserializeObject<AppUserSettings>(user.SettingsClass) : new AppUserSettings();
}
and the sister function:
public static void SetUserSettings(this IIdentity identity, AppUserSettings toSaveUserSettings)
{
var user = IdentityDbContext.Users.Find(identity.GetUserId());
user.SettingsClass = JsonConvert.SerializeObject(toSaveUserSettings);
DatabaseUtil.SaveChanges(IdentityDbContext);
}
Any help is appreciated, if need be I can attach other classes and/or snippets, but I feel like it is largely a Identity/Entity issue.
My web method returns object 'User' that has a lot of properties: Id, Nick, Email, FirstName, SecondName, etc...
[WebMethod]
public User GetUserById(int userId)
{
vfm_elita.elita_table.user.User user =
vfm_elita.elita_table.user.User.GetUserById(userId);
return user;
}
Is there any possibility to limit amount of properties returned? Or I should to create a separate class (UserForClientForParticular) that has only required properties?
Thanks.
P.S. Guess, my last idea will be the suggested answer :), but anyway, any thoughts are welcome
It's a good idea to use a data transfer object, specialized to expose only the properties that the client-side should know about. Alternatively, if you're prototyping, in a hurry, or don't want to create a DTO class for some other reason, you can use an anonymous type to pare down the response:
[WebMethod]
public object GetUserById(int userId)
{
vfm_elita.elita_table.user.User user =
vfm_elita.elita_table.user.User.GetUserById(userId);
// Will return something like {"Nick":"Encosia","FirstName":"Dave"}
return new { Nick = user.Nick, FirstName = user.FirstName };
}
You should always be very aware of what data you expose in your APIs, especially if there's a chance that they'll be consumed by external parties but even if they are only used internally. I would definitely recommend what you mentioned in your post and create a construct designed to expose only the data that is necessary in this context.
Update: also recommend taking a look at the AutoMapper project on codeplex that handles automatically mapping property values from one object (User) to another object (UserSummary).
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.
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.
Is there any good way of combining ASP.NET Windows Authentication with a custom IPrincipal/IIdentity object? I need to store the user's email address and have done so for Forms Authentication using a custom IIdentity/IPrincipal pair that I added to the Context.CurrentUser during the AuthenticateRequest event.
How would I best go by to accomplish this using WindowsAuthentication?
Maybe you could create your "ExtendedWindowsPrincipal" as a derived class based on WindowsPrincipal, and just add your extra data to the derived class?
That way, your ExtendedWindowsPrincipal would still be recognized anywhere where a WindowsPricinpal is needed.
OR: since you're talking about using Windows Authentication, you're probably in a Windows network - is there an Active Directory or a user database somewhere, where you could look up your e-mail address that you're interested in instead of storing it in the principal?
Marc
I ended up refactoring my initial solution into replacing the Principal instead of the Identity as I originally thought. Replacing the Identity proved troublesome, since i ran into security problems when creating an instance of a new extended WindowsPrincipal.
public class ExtendedWindowsPrincipal : WindowsPrincipal
{
private readonly string _email;
public ExtendedWindowsPrincipal(WindowsIdentity ntIdentity,
string email) : base(ntIdentity)
{
_email = email;
}
public string Email
{
get { return _email; }
}
}
In my Authentication module i replaced the principal on the HttpContext like this:
var currentUser = (WindowsIdentity)HttpContext.Current.User.Identity;
HttpContext.Current.User =
new ExtendedWindowsPrincipal(currentUser, userEmail);