I'm attempting to check if a given user has access to a specific Custom Table.
Based on the example listed on the kentico documentation to check permissions for a custom table, I have setup a similar call, using my custom table class name and userinfo, but the call to "UserInfoProvider.IsAuthorizedPerClass" always return false:
private bool CheckCustomTableReadPermission(UserInfo user = null)
{
// Gets the user object
//UserInfo user = UserInfoProvider.GetUserInfo("CMSEditor");
//UserInfo user = UserInfoProvider.GetUserInfo("someothervalidusername");
//UserInfo user = CurrentUser;
//normally outside of this function
UserInfo CurrentUser = MembershipContext.AuthenticatedUser;
string CustomTableClassName = "Namespc.TblName";
if (user == null)
{
user = CurrentUser;
}
if (user != null)
{
// Checks whether the user has the Read permission for the CMS.MenuItem page type
if (UserInfoProvider.IsAuthorizedPerClass(CustomTableClassName, "Read", SiteContext.CurrentSiteName, user))
{
// Perform an action according to the result
return true;
}
}
return false;
}
Can anyone also mention what the valid permission name strings are, other than "Read"? (e.g.: "Modify"? "Delete"? "Insert"?)
Does UserInfoProvider.IsAuthorizedPerClass resolve all memberships of the given user, or does it only check if the user is explicitly added to the Custom Table?
Any suggestions? We're using Kentico v8.2.25
Thanks!
Victor
What about doing it the same way as it's done in
CMS\CMSModules\CustomTables\Tools\CustomTable_Data_EditItem.aspx.cs
which is:
DataClassInfo dci = DataClassInfoProvider.GetDataClassInfo(customTableId);
dci.CheckPermissions(PermissionsEnum.Read, SiteContext.CurrentSiteName, MembershipContext.AuthenticatedUser)
And the possible permissions are located in CMS.DataEngine.PermissionsEnum. (Read, Modify, Create, Delete, Destroy)
EDIT:
I'm, dumb. You're assigning a default value to the user param, not an auto-assigned value. I would still check to make sure you're getting the user info you're expecting, because that seems to be the most likely cause for the problem.
You seem to be running into a problem here:
private bool CheckCustomTableReadPermission(UserInfo user = null)
Since you're auto-assigning your user parameter to null when your method is called, the following statement will always be true:
if (user == null)
{
user = CurrentUser;
}
And you will never reach your other statement:
if (user != null)
{
// Checks whether the user has the Read permission for the CMS.MenuItem page type
if (UserInfoProvider.IsAuthorizedPerClass(CustomTableClassName, "Read", SiteContext.CurrentSiteName, user))
{
// Perform an action according to the result
return true;
}
}
So your method will always return false.
The IsAuthorizedPerClass() function checks only the user's permissions for the class that you provide to check against and only the specific permission you provide for it to check (e.g. "Read"). So yes, it's only going to see if the user has the Read permission for your custom table.
I'm not 100% certain what all the permissions are, although it appears to be stored in an enum. I'll get back to you on that one in a bit. Hope this helps :)
The IsAuthorizedPerClass() method will return true only if the user's role has been granted permission explicitly within the role's Permissions for that class. All other times, it will return false even if the user is in fact able to Read/Modify/etc. the custom table.
To get the correct permission strings, you can use CMS.DataEngine.PermissionsEnum.<type>.ToString()
To check whether a user has permissions to Read a specific custom table, you will need to make the following 3 checks in order:
UserInfoProvider.IsAuthorizedPerUIElement("CMS.CustomTables","CustomTables",SiteContext.CurrentSiteName,user)
UserInfoProvider.IsAuthorizedPerResource("CMS.CustomTables", PermissionsEnum.Read.ToString(), SiteContext.CurrentSiteName, user)
UserInfoProvider.IsAuthorizedPerClass(CustomTableClassName, PermissionsEnum.Read.ToString(), SiteContext.CurrentSiteName, user)
Related
I am trying to create a page in my MVC .NET CORE application in which a user can change the roles of other users. At this point the view and model bindings all work great, but when actually trying to save the roles back to the database I am getting an error saying that the roles don't exists. My approach is this:
Get list of roles from the model.
Delete existing roles for specified user.
Add roles back using list from model.
Do this all in a transaction in case of error.
My code in the controller is as follows:
var user = await _userManager.FindByIdAsync(id);
if (user == null)
{
return View("Error");
}
using (_context.Database.BeginTransaction())
{
var removeResult = await _userManager.RemoveFromRolesAsync(user, await _userManager.GetRolesAsync(user));
if (!removeResult.Succeeded)
{
return View("Error");
}
var addResult = await _userManager.AddToRolesAsync(user, model.MemberRoles);
if (!addResult.Succeeded)
{
return View("Error");
}
}
Where model.MemberRoles as a List<string> of roles. The strange part is is that this process is failing on _userManager.RemoveFromRolesAsync, even though I am passing the existing roles of the user directly into the function. I have tried ditching the UserManager class and going with ef but had no luck. I also verified that the roles in the model match what is in the database. Is there anything obviously wrong that would cause this to fail? Thanks.
Edit
Here is the error I am running into:
InvalidOperationException: Role ADMINISTRATOR does not exist.
Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`4.<AddToRoleAsync>d__32.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
And here are the roles defined in my database:
When you try to add a user to a role, it first tries to find the role. In Sql Profiler, you can see that it is actually searching by the NormalizedName field rather than the Name field.
exec sp_executesql N'
SELECT TOP(2) [r].[Id], [r].[ConcurrencyStamp], [r].[Name], [r].[NormalizedName]
FROM [AspNetRoles] AS [r]
WHERE [r].[NormalizedName] = #__normalizedRoleName_0',N'#__normalizedRoleName_0 nvarchar(256)',#__normalizedRoleName_0=N'ADMINISTRATOR'
The solution is to set the NormalizedName field to the Uppercase of the Name field when you create the Roles:
So in the SeedData class:
/* Create Roles */
IEnumerable<UserRole> allRoles = Enum.GetValues(typeof(UserRole)).Cast<UserRole>();
foreach (UserRole role in allRoles)
{
if (!await roleManager.RoleExistsAsync(role.ToString()))
{
await roleManager.CreateAsync(new IdentityRole {
Name = role.ToString(),
NormalizedName = role.ToString().ToUpper()
});
}
}
I'm actually surprised you're not getting an InvalidCastException instead, as it's not possible to directly cast string[] to List<string>. Not really sure why you're doing it that way in the first place, since you could just as easily just call .ToList(), instead of .ToArray(), and call it a day, if you want a list. Or, since the return type of GetRolesAsync is IList<string>, anyways, you don't even really need to do that. Right now, that's the only problem I see with your code, though.
I am working on a password change method, this view is enforced by one of many conditions. The first condition is if the LastLoggedin field in the database is null. I have this field set to null in my test database to force the password change events.
I have coded a method to change the password after performing some tests, but I cannot get past the first test because the controller method that the click event calls returns the user back to the log on method, which in turns checks the LastLoggedin field in the database which is still null because I have not yet logged in. I tried to change that value from within my change password method using the following code:
public bool SavePassword(UserModel user, PasswordRecoveryModel password)
{
try
{
string newPass = password.PasswordNew;
string newHash = PasswordManager.EncryptPassword(newPass);
User domainObject = UnitOfWork.UserRepository.GetItem(user.EntityId);
bool hasUsedPassword = UnitOfWork.UserRepository.HasHadPassword(domainObject, newHash, DateTime.Now.AddMonths(-6));
if (hasUsedPassword.Equals(true))
{
return true;
}
User lastLogged = UnitOfWork.UserRepository.GetItem(user.EntityId);
lastLogged.LastLoggedIn = DateTime.Now;
lastLogged.VerifiedOn = DateTime.Now;
UnitOfWork.UserRepository.Update(lastLogged);
lastLogged.VerifiedOn.Equals(DateTime.Now);
lastLogged.LastLoggedIn.Equals(DateTime.Now);
user.Password = newHash;
user = SaveModel(UnitOfWork.UserRepository,
user,
Mapper.User.ModelToDomain,
Mapper.User.DomainToUserModel);
CommitTransaction();
return false;
}
catch (Exception ex)
{
OnServiceException(ex);
throw;
}
}
But I am still not getting anything other than the new password saved in the database, I am verifying this by running a query on my username. How do I update this field so I can get by this test on password change, and is it even acceptable to do this? In our old product it was done in a very complex manner creating a temporary user object ant using that to get past the test and on to the next test. I am trying to simplify the process.
I am new to MVC and programming in general any help would be appreciated.
EDIT found my problem, as usual over thinking the issue, here is the updated code:
if (hasUsedPassword.Equals(true))
{
return false;
}
user.LastLoggedIn = DateTime.Now.ToString();
user.Password = newHash;
user = SaveModel(UnitOfWork.UserRepository,
user,
Mapper.User.ModelToDomain,
Mapper.User.DomainToUserModel);
CommitTransaction();
return true;
i have developing project in c# for creating a user in AD.
i create a user and i want to create a attribute,like "mobilenumber"for this user.
when,i create this,the below error will occured.
here my code.
if (userDetails.GetUnderlyingObjectType() == typeof(DirectoryEntry))
{
dEntry = (DirectoryEntry)userDetails.GetUnderlyingObject();
if (User.UsrPassword != null && User.UsrPassword.Trim() != "")
{
if (dEntry.Properties.Contains("mobilenumber"))
{
Console.WriteLine("mobilenumberAttribute:Already created");
dEntry.Properties["mobilenumber"][0] = User.UsrPassword;
dEntry.CommitChanges();
}
else
{
Console.WriteLine("mobilenumber Attribute: Adding");
dEntry.Properties["mobilenumber"].Add(User.UsrPassword);
dEntry.CommitChanges();
}
userDetails.Save();
result = true;
}
}
The requested operation did not satisfy one or more constraints associated with the class of the object. (Exception from HRESULT: 0x80072014)
How can i resolve this?
Create an attribute? You mean like extending the schema? You can't do that by just adding it to an object. As you can see here, there is no such attribute as "mobilenumber". Maybe you want otherMobile (Phone-Mobile-Other) or mobile (Phone-Mobile-Primary)?
What are you trying to do? Why keep a copy of the password in the user object. If the user changes it, your copy will not be updated. If you need it to somehow inform the user, do something different like infoming his supervisor... Just a thought.
I want to check if a user is logged in and if they are, deny them access to the registration and login pages. When a user logs in I'm setting these session variables:
HttpContext.Current.Session["LoggedIn"] = true;
HttpContext.Current.Session["FullName"] = (string)Reader["FirstName"] + " " + (string)Reader["LastName"];
Response.Redirect("Default.aspx");
And I'm checking them at the top of the register and login pages like so:
if ((bool)HttpContext.Current.Session["LoggedIn"])
{
Response.Redirect("Default.aspx");
}
However, when I try to go to the page while not logged in this exception gets thrown:
Object reference not set to an instance of an object.
I'm assuming it's ebcause the LoggedIn key doesn't exist because I only create it after a successful login.
So, how can I check if the LoggedIn key exists and if it doesn't, redirect the user to Default.aspx?
Thanks!
I think you can do a simple null check on this like....
if (HttpContext.Current.Session["LoggedIn"] != null)
{
// once inside this loop
// you can now read the value from Session["LoggedIn"]
Response.Redirect("Default.aspx");
}
you need to make shure that the object is not null before unboxing it
if(HttpContext.Current.Session["LoggedIn"]!=null)
{
if ((bool)HttpContext.Current.Session["LoggedIn"])
{
Response.Redirect("Default.aspx");
}
}
Why to avoid the default webforms authentication model altogether? Simply use web.config to define a restricted area, set all the settings correctly and you won't have to perform checks like this for every page.
But, if you want to reinvent the wheel....
You check for something that probably doesn't exist yet. You must modify your if-statement like this:
bool isLoggedIn = (HttpContext.Current.Session["LoggedIn"] == null ? false : (bool)HttpContenxt.Current.Session["LoggedIn"];
if (isLoggedIn)
{
Response.Redirect("Default.aspx");
}
I am developing a web application which shows an admin a different menu bar than it shows the normal user. I mean if the user is the admin, he will see some options related to the settings of the system itself.
I created the menu bar as a user control (ascx) and I defind the following method in its code-behind:
public bool DisplayAdminOnlyMenuItems
{
get { return menuItem1ToHide.Visible; }
set
{
menuItem1ToHide.Visible = value;
}
}
then in the in the site.master page, I put the menu bar user control and set the DisplayAdminMenuItems as false
and in the code-behind, I use the following logic:
if(user.username == "John"){
MenuBar1.DisplayAdminOnlyMenuItems = true;
}
Everything works well and fine. Now, I got new requirements which complicates my life a little bit.
I have the three tables in my database which structure as following:
User table: Name, Username, Department (Username is the primary key)
Roles table: RoleID, RoleName (RoleID is the primary key)
UserRole table: UserRoleID, Username, RoleID (UserRoleID is the primary key)
I have three roles: Admin, Organizer and User
I want to display the Admin menu just for the user who has Admin role, so how can I do that?
if(user.username == "John"){
MenuBar1.DisplayAdminOnlyMenuItems = true;
}
The approach you used above is not right because it is not scalable. What if there are two admins? Will you put an OR condition for each admin?
Right approach is your new requirement which is based on Role rather than users.
When you get a user check it's role and display UI based on role
if(user.RoleName == "Admin"){
MenuBar1.DisplayAdminOnlyMenuItems = true;
}
I am assuming you can get RoleName if you have UserName
What you should do is add a new method or two to your user class to perform this logic so that you don't have references to roles spread throughout your code and so that future changes can be accommodated.
For example, if you get a request next week to add a super admin role and this role should also be able to have access to the admin role functionality without the user being assigned the admin role, you will have a hard time updating your code.
However, if you add a method like this to your user class:
public const string ADMIN_ROLE = "ADMIN";
public const string SUPER_ADMIN_ROLE = "SUPERADMIN";
public bool HasAdminAuthority() {
// Assuming roles are not indexed by name
foreach (string sRole in this.Roles) {
switch (sRole.ToUpper()) {
case ADMIN_ROLE:
return true;
case SUPER_ADMIN_ROLE:
return true;
}
}
return false;
}
Another useful method to add is something like:
public bool IsInRole(string sRoleToFind) {
foreach (string sRole in this.Roles) {
if (sRoleToFind.Equals(sRole, StringComparison.InvariantCultureIgnoreCase) {
return true;
}
}
return false;
}
Then you can use one of these methods in your UI logic to control the behavior (I would strongly recommend the first one).
Also note that in both of the above examples, if the roles in the user are in dictionaries, for example, the loops can be replaced by .ContainsKey method calls on the dictionary.
You can use the LoginView Control.
<asp:LoginView ID="lgvAdmins" runat="server">
<asp:rolegroup roles="Administrators">
<contenttemplate>
...controls...
</contenttemplate>
</rolegroup>
<asp:rolegroup roles="Users">
<contenttemplate>
...controls...
</contenttemplate>
</rolegroup>
</asp:LoginView>
I think you need to enable the RoleManager in your web.config though. You can also specify controls for anonymous and other normal logged-in users. Or you could use the Application_BeginRequest to get the user's roles at that point.