I am creating some data in the seed method. I am creating a few users with a few permissions (roles)
WebSecurity.InitializeDatabaseConnection("MibContext", "UserProfile", "UserId", "UserName", autoCreateTables: true);
Roles.CreateRole(Constants.Roles.Administrator);
Roles.CreateRole(Constants.Roles.ContentManager);
Roles.CreateRole(Constants.Roles.KeyAccountManager);
I then have variable for the username (to avoid any typo possibility)
var adminusername = "admin#domain.com";
var userusername = "user#domain.com";
I create the Userprofile record via EF and the UnitOfWork Pattern
var admin = new UserProfile
{
FirstName = "Admin",
LastName = "Account",
UserName = adminusername
};
unitOfWork.UserRepository.Insert(admin);
unitOfWork.Save();
Then create the user Membership account, and assign roles:
WebSecurity.CreateAccount(adminusername, "123456");
WebSecurity.CreateAccount(userusername, "123456");
Roles.AddUserToRole(userusername, Constants.Roles.Administrator);
Roles.AddUserToRole(userusername, Constants.Roles.KeyAccountManager);
The last roles command causes the error, Foreign Key Violation between UsersInRoles and UserProfile. Which is very strange since it can map a role to the first user (which was the admin user first time around, but wanted to see if it could create a permission for that user, which it does)
I then removed the foreign key constraint to see what was going on, and it put a 3 in there. Testing by repeating but adding a 4th role, it seems that the (username, role) parameters are the wrong way around!?!? The new role has Id=4, and on repeating the test, that is what it put in the UserId column.
RoleId UserId
2 1
2 4
NULL NULL
Roles are configured :
<roleManager enabled="true" defaultProvider="SimpleRoleProvider">
<providers>
<clear/>
<add name="SimpleRoleProvider"
type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData" />
</providers>
</roleManager>
Am I missing something?
The order of fields in the webpages_UsersInRole table is obviously very important.
I looked at the mapping to see what was there, and the relationship (many -> many) was defined on the ROLES table side. I swapped the mapping to the Users side and it is now working correctly. Here is the mapping I used on the UserProfile object.
// Relationships
this.HasMany(t => t.webpages_Roles)
.WithMany(t => t.UserProfiles)
.Map(m =>
{
m.ToTable("webpages_UsersInRoles");
m.MapRightKey("RoleId");
m.MapLeftKey("UserId");
});
Really, the Role Provider should not assume on the order of columns and should specify their names. At least I can still use the default functionality by amending the mapping this way. This is pretty poor SQL:
INSERT INTO webpages_UsersInRoles VALUES (4,1);
Related
I am using this article as an example
https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-model-partition-example with a Users container with userId and username and the partition key as userId.
{
"id": "54c7da13-f4b8-4668-90dc-7c1aa968a73e",
"userId": "54c7da13-f4b8-4668-90dc-7c1aa968a73e",
"type": "user",
"username": "jeffw"
}
In my create user page I want to make sure the username is unique before adding a new user. I tried a pre-trigger but found that "You can't run stored procedures or triggers across multiple logical partitions." How do I make sure that when a user is created that they have selected a unique username? I think I could change the partition key to username but why does the article use userId instead?
SOLUTION
See answer from #mark-brown.
Create a unique key on the Users container and /username:
await database.Database.DefineContainer(name: "Users", partitionKeyPath: "/userId")
.WithUniqueKey().Path("/username").Attach()
.CreateIfNotExistsAsync();
Then try to create a new User with userId as "unique_username" and the new username that is attempting to be created:
{
"id": "06af2937-4677-4d27-a167-5517aa6d0ffd",
"userId": "unique_username",
"type": "unique_username",
"username": "jeffw"
}
await _usersContainer.CreateItemAsync(uniqueUser, new PartitionKey("unique_username"));
This will return a Conflict status if the username already exists. Example is here https://github.com/jwidmer/AzureCosmosDbBlogExample/blob/master/BlogWebApp/Services/BlogCosmosDbService.cs
Changing the partition key to username won't help because you can have multiples of that value in your container. One way you could do this is to have a new partition where you store a unique instance for every user name and use a unique index on the container (unique indexes are unique within a logical partition).
Create a new type = "unique_user" and a userId = "unique_user". Then add a new record of that type with the new username as they register. This should get you millions of users without going over the 20GB limit. Then when creating a new user do an insert on the container with the "unique_user" type and id with the new username. If you get a 201 then do another insert with type= "user" and the rest of the user data.
hope that helps.
You can set up an index policy for unique values.
I have users in membership and one user can have one or more than one roles assigned. I want to check for specific roles of a page for example:
Role1, Role2, Role3, Role4, Role5
Users who have access of Role2 and Role3 can access Page1.aspx and Also if user who have access of Role1 and Role2 also can access because Role2 is there available.
I have implemented membership and have list of user's roles string array with the help of
string[] roles = Roles.GetRolesForUser(User.Identity.Name);
How can I check against multiple roles? May I need to do one by one check with using Roles.IsUserInRole function?
I have used; It returns 0 count because of Partner in capital. How can I do ignore case? And is below is right way to check array against array?
string[] userroles = { "Partner", "testsetsr" };
string[] requiredroles = { "contractor", "customer", "sims", "nonclientau", "partner" };
var countInRoles = userroles.Intersect(requiredroles).Count();
You would have to implement your own Authorization Filter Attribure. To do that you can create a class extending 'AuthorizationAttribute', then override OnAuthorization where you can specify your required role-checks.
A small example on how to do that would be here in Nimesh's .Net Blog
The example is not a solution to your personal problem, but should give you the idea about what you need to do.
Edit:
The same goes for your applications role-provider, extend it and override IsUserInRole (or maybe better even add a new method) to provide the checks you need in order to have Roles.IsUserInRole work the way you want.
To your 2nd question:
if you have 2 arrays and want to count how many members of 1st array are members of the 2nd while ignoring case you can do that using linq
var countInRoles = userroles.Select(u => u.ToLower()).Count(u => requiredroles.Select(r => r.ToLower()).Contains(u));
On the other hand, lets vice versa the lists if you have a list of required roles to access some action you can also check if a user has all required roles using linq (again with ignoring case)
string[] requiredroles = { "Partner", "testsetsr" };
string[] userroles = { "contractor", "testsetsr", "customer", "sims", "nonclientau", "partner" };
var userHasAllRequiredRoles = requiredroles.Select(r => r.ToLower()).All(r => userroles.Select(u => u.ToLower()).Contains(r));
I have this table named Person :
FirstName,LastName,BirthDate,UserId(FK,int,not null)
userId field is joint to userProfile table of SqlMembership
and 2 other tables named Category and News besides SqlMembership tables like UserInRole,... in a database
I create a .edmx file and add my own tables (Category,News,Person) to it.cause my approach is database first.
now I except when I get current logged in user,I can access it's related person:
if (Request.IsAuthenticated)
{
var context = new UsersContext();
var user = context.UserProfiles.SingleOrDefault(u => u.UserName == "asma");
ViewBag.fullName= user.person.FirstName+' '+user.person.LastName; //ERROR
}
but when I write user. the intellisense just gives me 2 fields:UserId and UserName
where is user.person....
on the other hand I cannot add UserProfile to my .edmx file.adding UserProfile to .edmx file causes 'MVC4 duplicated UserProfile table' problem
What's my fault?adding my own tables to the database that is created by SqlMembership?
This question is a bit of a structural/design question as I'm having trouble working out the best way to perform the task.
In my MVC app, I am using DotNetOpenAuth (3.4) as my login information provider and just using the standard FormsAuthentication for cookies etc.
The current user table in the DB has:
UserId (PK, uniqueidentifier)
OpenIdIdentifier (nvarchar(255))
OpenIdDisplay (nvarchar(255))
Displayname (nvarchar(50))
Email (nvarchar(50))
PhoneNumber (nvarchar(50))
As the UserId is the clear identifier for a user (they should be able to change their OpenId provider at a later date), it is the key that other tables link to (for a user).
This is the current code, that on a successfull authentication, creates a temporary user and redirects to Create Action.
switch (response.Status)
{
case AuthenticationStatus.Authenticated:
FormsAuthentication.SetAuthCookie(response.ClaimedIdentifier, false);
var users = new UserRepository();
if (!users.IsOpenIdAssociated(response.ClaimedIdentifier))
{
var newUser = new DueDate.Models.User();
newUser.OpenIdIdentifer = response.ClaimedIdentifier;
newUser.OpenIdDisplay = response.FriendlyIdentifierForDisplay;
TempData["newUser"] = newUser;
return this.RedirectToAction("Create");
}
And now for the crux of the question:
Is the response.ClaimedIdentifier the correct piece of information to be storing against a user?
Is FormAuthentication.SetAuthCookie the preferred way to forms authentication? Or is there a better way?
When I call SetAuthCookie, there is no data relating to the user except for the ClaimedIdentifier. If I'm consistently referring to their UserId, is a better idea to create the user, then store that UserId in the cookie instead of the ClaimedIdentifier?
If I'm using that UserId in a number of places, how do I either retrieve it from the cookie, or store it somewhere else more logical/useful?
A bit long winded but I've been having trouble trying to work out the best way to do this/
1.Is the response.ClaimedIdentifier the correct piece of information to be storing against a user?
Yes. And make sure the column you store it in the database with is case sensitive. Here is a table schema that demonstrates how to make sure it is case sensitive. This comes out of the DotNetOpenAuth project template's database schema. The "CS" bit of the specified collation stand for Case Sensitive.
CREATE TABLE [dbo].[AuthenticationToken] (
[AuthenticationTokenId] INT IDENTITY (1, 1) NOT NULL,
[UserId] INT NOT NULL,
[OpenIdClaimedIdentifier] NVARCHAR (250) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL,
[OpenIdFriendlyIdentifier] NVARCHAR (250) NULL,
[CreatedOn] DATETIME NOT NULL,
[LastUsed] DATETIME NOT NULL,
[UsageCount] INT NOT NULL
);
2.Is FormAuthentication.SetAuthCookie the preferred way to forms authentication? Or is there a better way?
For MVC apps it definitely is, since you still can return your preferred ActionResult from the method.
3.When I call SetAuthCookie, there is no data relating to the user except for the ClaimedIdentifier. If I'm consistently referring to their UserId, is a better idea to create the user, then store that UserId in the cookie instead of the ClaimedIdentifier?
That sounds like personal preference. But I would typically go with user_id, since it might result in a faster database lookup every time an HTTP request comes in that requires you to look up any user information.
4.If I'm using that UserId in a number of places, how do I either retrieve it from the cookie, or store it somewhere else more logical/useful?
FormsAuthentication does provide a way to store more information in its encrypted cookie than just username, but it is harder than you'd expect to use it. This snippet comes out of DotNetOpenAuth's web SSO RP sample:
const int TimeoutInMinutes = 100; // TODO: look up the right value from the web.config file
var ticket = new FormsAuthenticationTicket(
2, // magic number used by FormsAuth
response.ClaimedIdentifier, // username
DateTime.Now,
DateTime.Now.AddMinutes(TimeoutInMinutes),
false, // "remember me"
"your extra data goes here");
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
Response.SetCookie(cookie);
Response.Redirect(Request.QueryString["ReturnUrl"] ?? FormsAuthentication.DefaultUrl);
Then you can get at that extra data in a future HTTP request with this:
var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (cookie != null) {
var ticket = FormsAuthentication.Decrypt(cookie.Value);
if (!string.IsNullOrEmpty(ticket.UserData)) {
// do something cool with the extra data here
}
}
First of all, let's define a few tables:
Users table will store information about a user:
Users
- UserID (int, PK)
- ...
UserTasks is a table that stores a task associated with a user:
UserTasks
- userID (int, FK to Users table)
- taskName (varchar)
When I generate the UserTasks table using the ADO Entity Framework, I'll get a class that looks like this:
UserTasks
- taskName (string)
- Users (collection of Users objects)
Note: There's no userID that is generated in the UserTasks table. So let's assume now that I need to insert a new user task... how do I do it? I don't have access to the userID FK field, so my only option is to do a lookup and populate a Users object, then pass that to my task object, like this:
//get the user we are interested in
var user = (from u in context.Users
where u.userID == 2
select u).FirstOrDefault();
//create the new task
UserTasks newTask = new UserTasks();
newTask.taskName = "New Task";
newTask.User = user;
The problem with the above is that I'm doing an extra and needless database call to populate my user object. Is there anyway to somehow create a new mapping to my userID field and still keep the User object?
Thanks
--Ryan
Not now, but maybe in the next version.