I need to filter users by Roles, but all users belong to two Roles
Example: Admin and Group1 for one user, and User and Group1 for the other.
Now I want to filter them by Group1 Role and ignore Admin and User.
public ViewResult Index()
{
string[] roles = Roles.GetRolesForUser();
var group = string.Join(" ", roles );
group = group.Replace("Admin", "");//Used this to single out Group1 from Admin user
return View(new UserViewModel
{
Users = _userService.FindAll().Where(x => Roles.GetRolesForUser(x.UserName).Contains(group)),
Roles = roles
});
}
This doesn't error out but it shows it empty. I think I know why but still can't figure out how to go around it...
public ViewResult Index()
{
var roleFilter = Roles.GetRolesForUser().First(r => !r.equals("Admin"));
return View(new UserViewModel
{
Users = _userService.FindAll().Where(x => Roles.GetRolesForUser(x.UserName).Contains(roleFilter)),
Roles = new [] {roleFilter}
});
}
Alright, here it goes. My understanding of your problem is that you want to check if the user has one or more specific roles. If so, allow access; otherwise, deny them access and send them into a black hole. It sounds like in your example that you want to only check one specific role. Reading some of your comments you may also want to check multiple roles at some point. The code example I will show you allows you to check a user against a set of one or more roles that the user must be assigned to in order to gain access to the page.
string[] requiredRoles = new string[] { "Awesome", "Pancake" };
if (requiredRoles.Except(theUsersAssignedRoles).Any())
{
// Authorization has failed!
// The user is not awesome and they are not a pancake.
}
else
{
// User is awesome and a pancake so let them through.
}
The idea here is to take a list of all required roles and subtract out all of the roles the user has assigned to him or her. If there are any roles left in the list after the subtraction, then the user doesn't have all of the required roles. Here are a few examples. The roles that are required for authorization are on the left of the subtraction sign while the user's assigned roles are on the right of the subtraction sign just like the above code.
User is awesome and a pancake so they are allowed access. Notice we end up with an empty set which means the user meets the requirements:
{ "Awesome", "Pancake" } - { "Awesome", "Pancake" } = { }
User is only a pancake. They are not allowed access. The resulting set contains "Awesome" so that means they are missing the "Awesome" role:
{ "Awesome", "Pancake" } - { "Pancake" } = { "Awesome" }
This user is not awesome or a pancake but she is an admin. Still, we require awesome pancakes in order to enter the page so this user is denied access:
{ "Awesome", "Pancake" } - { "Admin" } = { "Awesome", "Pancake" }
If you have any questions or this isn't what you want, please leave a comment. I'll be happy to help you out further if needed.
It really sounds like you're trying to use this method instead:
http://msdn.microsoft.com/en-us/library/system.web.security.roleprovider.getusersinrole.aspx
Parameters
roleName
Type: System.String
The name of the role to get the list of users for.
Related
I can search the user and find only the groups that the user belongs to. And now i want to return all the groups/roles and assign a user to a specific group/role.
DirectoryEntry and PrincipalContext doesn't work in my case and i have tried that for days.
This is my working code for searching user group/roles which is working fine.
How can i get all the groups/roles?
And Add user to a group/role
Container = “ou=containername,ou=xx,ou=xx,O=xxxxx”
Domain = “mydomain.com”
group = ou=groups,ou=containername,ou=xx,ou=xx,O=xxxx
List<string> roles = new List<string>();
SearchRequest request = new SearchRequest("", "(&(objectClass=person)(mail=myusername))", System.DirectoryServices.Protocols.SearchScope.Subtree);
SearchResponse response = (SearchResponse)con.SendRequest(request);
if (response.Entries.Count == 0)
{
return null;
}
else
{
foreach (SearchResultEntry entry in response.Entries)
{
if (entry.Attributes["member"] != null)
{
roles = (entry.Attributes["member"].GetValues(typeof(string))).ToArray().Select(r => r.ToString()
.Substring(r.ToString().IndexOf("cn=") + 3,
r.ToString().IndexOf(",") - 3))
.ToList();
}
}
}
return roles;
I don't think you're using Active Directory. What tipped me off is that you're getting data from the member attribute of a user. That's not how it works with Active Directory (it would be memberOf).
I'm not entirely sure what you're asking for. Your title mentioned "nested groups", which means when one group is a member of another group. So I assume that would mean that you want to find every group the user is a member of and all groups that those groups are members of, etc. If that's the case, you will really have to find out what type of server you're connecting to before anyone can give you a good answer on that.
But in your question you say "How can i get all the groups/roles?" So does that mean you just want to find every group that exists? To do that, you can just do a new search and use this as the filter:
(objectClass=group)
For adding a user to a group, I think it would be something like this (where userDn is the distinguishedName of the user you want to add, and groupDn is that of the group):
var mod = new DirectoryAttributeModification {
Name = "member",
Operation = DirectoryAttributeOperation.Add
}
mod.Add(userDn);
var response = (ModifyResponse) connectionObject.SendRequest(
new ModifyRequest {
DistinguishedName = groupDn,
Modifications = { mod }
}
);
But I've never actually used LdapConnection, so you might need to tweak it.
By default, the ADLDS or AD MemberOf (User object) Member (Group object) attribute is not retrieved.
Example Solution for User
SearchRequest request = new SearchRequest("", "(&(objectClass=user)(mail=myusername))", System.DirectoryServices.Protocols.SearchScope.Subtree);
request.Attributes.Add("memberOf");
or Group
SearchRequest request = new SearchRequest("", "(&(objectClass=group))", System.DirectoryServices.Protocols.SearchScope.Subtree);
request.Attributes.Add("member");
Default LDAP Filters and Attributes for Users, Groups and Containers
I need to show all my security groups and the users that are members of the security groups in a listView.
At the moment I can display all the security groups but not sure how to display the users in the security group.
Here is my code I am currently using:
private void Security_group_btn_Click(object sender, EventArgs e)
{
DirectorySearcher searcher = new DirectorySearcher(DomainName);
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, DomainName);
UserPrincipal userPrin = new UserPrincipal(ctx);
userPrin.Name = "*";
var search = new System.DirectoryServices.AccountManagement.PrincipalSearcher();
search.QueryFilter = userPrin;
var results = searcher.FindAll();
ListView lvwListView = this.security_listView;
lvwListView.Clear();
lvwListView.Columns.Add("Security Group", 175, HorizontalAlignment.Left);
lvwListView.Columns.Add("Users", 175, HorizontalAlignment.Left);
searcher.Filter = "(&(objectClass=group))";
searcher.SearchScope = SearchScope.Subtree;
searcher.PropertiesToLoad.Add("sAMAccountName");
SearchResultCollection result = searcher.FindAll();
foreach (SearchResult entry in result)
{
lvwListView.Items.Add(entry.GetDirectoryEntry().Properties["sAMAccountName"].Value.ToString());
}
result.Dispose();
searcher.Dispose();
}
}
}
So basically I would like to display something like this in my ListView:
Security Group Name
Users User1
User2
User3
Administrators User1
User2
User3
User4
Thanks
First, be careful of this:
lvwListView.Items.Add(entry.GetDirectoryEntry().Properties["sAMAccountName"].Value.ToString());
Particularly, using GetDirectoryEntry() just to get a value. When you get a value using DirectoryEntry.Properties, it checks to see if it already has the value you're asking for in the cache. If not, it asks AD for every attribute that has a value, which will usually be a whole lot more data than you actually need. Since you're looping over a bunch of accounts, that can significantly slow you down.
You are already asking for the sAMAccountName in the search results, since you used searcher.PropertiesToLoad.Add("sAMAccountName"), so you can pull the value from the SearchResult object directly:
lvwListView.Items.Add((string) entry.Properties["sAMAccountName"][0]);
I talk more about that in an article I wrote about better performance when programming with Active Directory.
Now to actually answer your question:
Getting the members is a different story. You can ask for the member attribute in the search, but you get problems when a group has more than 1500 members. You have to ask for the members in pages. But also, you have to decide how you are going to handle nested groups: when a group is a member of a group. Do you want to just list that group name? Or do you want to look inside that group and get those members too?
I wrote a whole article about this too: Find all the members of a group
But it's more than likely you can just use one of the sample methods I show in that article. This will take a DirectoryEntry object of a group, and give you a list of strings containing the DOMAIN\username of each object inside the group (you can change that if you want). You can use the recursive parameter to decide what you want to do with nested groups.
public static IEnumerable<string> GetGroupMemberList(DirectoryEntry group, bool recursive = false) {
var members = new List<string>();
group.RefreshCache(new[] { "member" });
while (true) {
var memberDns = group.Properties["member"];
foreach (string member in memberDns) {
using (var memberDe = new DirectoryEntry($"LDAP://{member.Replace("/", "\\/")}")) {
memberDe.RefreshCache(new[] { "objectClass", "msDS-PrincipalName", "cn" });
if (recursive && memberDe.Properties["objectClass"].Contains("group")) {
members.AddRange(GetGroupMemberList(memberDe, true));
} else {
var username = memberDe.Properties["msDS-PrincipalName"].Value.ToString();
if (!string.IsNullOrEmpty(username)) {
members.Add(username);
}
}
}
}
if (memberDns.Count == 0) break;
try {
group.RefreshCache(new[] {$"member;range={members.Count}-*"});
} catch (COMException e) {
if (e.ErrorCode == unchecked((int) 0x80072020)) { //no more results
break;
}
throw;
}
}
return members;
}
You would call it from your code like this:
var members = GetGroupMemberList(entry.GetDirectoryEntry());
Then you can display them however you want.
This will work fine if you are only working in a single AD forest. But if your domain trusts other domains outside your forest and you can expect to see users from those domains in the groups you are looking at, then you need to account for that. But I cover that in that article. I also cover primary groups there too, which is rare that you need to care about, but you might.
Update: To add two columns in a ListViewItem, you have to create it separately. You can use the constructor that takes a string array of the "subitems", as they call it.
You could put something like this inside your loop where you loop through the members, where groupName is the name of the group and member is the name of the group member.
lvwListView.Items.Add(new ListViewItem(new [] {
groupName,
member
});
If you only want the groupName on the first one, then you can just put an empty string ("") instead of groupName for the others.
I have a list view control that I want to add accounts to. The first column of the list view is for an account's username, and the second column is for the account's password.
I want avoid adding duplicate rows of login credentials. What I mean by that is that I don't care if a username appears in the list view control more than once. Just as long as each instance of the username has a different password. The same rule applies to passwords. I don't care if multiple accounts have the same password.
I just want to avoid duplicate ROWS.
The following code is something I have tried with no success:
private void AddAccounts()
{
List<string> usernames = new List<string>();
usernames.Add("Margaret Parker");
usernames.Add("Steven Stewart");
usernames.Add("Heather Powell");
usernames.Add("Denise Simmons");
usernames.Add("Ronald Moore");
List<string> passwords = new List<string>();
passwords.Add("mExEvHb3");
passwords.Add("muFLdtHu");
passwords.Add("GrcSNCyY");
passwords.Add("S8qenUZY");
passwords.Add("PVAzFYyu");
// usernames.Count will always be the same as passwords.Count
// so it doesn't matter which property I use.
for (int i = 0; i < usernames.Count; i++)
{
ListViewItem lvi = new ListViewItem(usernames[i]);
lvi.SubItems.Add(passwords[i]);
if (!accountsListView.Items.Contains(lvi))
{
// It is unsafe to call a control from a thread other
// than the one that created the control without using
// the Invoke method.
Invoke((MethodInvoker)delegate { accountsListView.Items.Add(lvi); });
}
}
}
When AddAccounts() is called, it will add the following items to accountsListView:
/*
# Margaret Parker mExEvHb3
# Steven Stewart muFLdtHu
# Heather Powell GrcSNCyY
# Denise Simmons S8qenUZY
# Ronald Moore PVAzFYyu
*/
It will not take into consideration if an account has already been added to the list view control. Which is why I'm coming to you folks for help.
How can I avoid adding duplicate rows to a list view control?
Thank you for taking the time to read my question.
I want to ALLOW multiple instances of the same username OR password:
/*
# JamesEdwards ---- LZsDVQ7A ---- different password (GOOD!)
# GeraldLopez ---- LZsDVQ7A
# JamesEdwards ---- 7cbrPRzt ---- different password (GOOD!)
*/
I want to avoid EXACT duplicates:
/*
# PhillipAnderson ---- 4ZN5TKfM ---- exact duplicate (BAD!)
# NicholasPowell ---- 4ZN5TKfM
# PhillipAnderson ---- 4ZN5TKfM ---- exact duplicate (BAD!)
*/
If an account's password ever gets changed, then I want to be able to add that account again to my list view control with the new password. I want to keep the old account information in my list view control as well. That is why I want to allow duplicate usernames in my list view control.
You are creating a new instance of ListViewItem and validating existence in the collection, which performs reference check and returns false all the time.
instead of this accountsListView.Items.Contains(lvi)
do this
ListViewItem item = accountsListView.FindItemWithText(userNames[i]);
if (item == null)
{
// logic goes here.
}
or
bool found = false;
foreach(ListViewItem lv in accountsListView.Items)
{
if(lv.Text ==userNames[i])
{
found = true;
break;
}
}
if(!found)
{
// logic goes here.
}
Make a note ListView also provides FindItemWithText to validate existence of an item.
There are few things that you need to change in your code. You do not have to use 2 lists for this purpose. One list should be enough. You can use available features in C# to do this kind of things more efficiently.
So as the first step; create a class and inherit it from IEqualityComparer. We have to override Equals and GetHashCode methods to make it possible to be used in Linq etc.
public class UserAccount : IEqualityComparer<UserAccount>
{
public string UserName { get; set; }
public string Password { get; set; }
public bool Equals(UserAccount x, UserAccount y)
{
return (x.UserName == y.UserName && x.Password == y.Password);
}
public int GetHashCode(UserAccount obj)
{
return obj.UserName.GetHashCode() + obj.Password.GetHashCode();
}
}
Then use the above created class to create a List of user accounts.
var userAccounts = new List<UserAccount>
{
new UserAccount() {UserName = "PhillipAnderson", Password = "4ZN5TKfM"},
new UserAccount() {UserName = "NicholasPowell", Password = "4ZN5TKfM"},
new UserAccount() {UserName = "PhillipAnderson", Password = "4ZN5TKfM"}
};
Now you can use Distinct method to filter out duplicate user accounts. you will notice uniqueUserAccounts has only 2 user accounts after applying Distinct.
var uniqueUserAccounts = userAccounts.Distinct(new UserAccount()).ToList();
foreach (var uniqueUserAccount in uniqueUserAccounts)
{
//your code to add user accounts to the list view
}
I have an ASP.NET WebForms application with a (EF) database.
Basically the two tables I'm concerned with are Users and Roles.
In Roles there's: Id (pk), UserId (fk), Type : String - which contains either Admin, User, Moderator, Publisher, etc.
In Users there's: Id (pk), Email, NameFirst, NameLast, Password, Username.
In designer I connected Users with Roles so that in Roles -> UserId == Id of User.
And now, after creating a class that inherits from RoleProvider, in function GetRolesForUser(string username) I want to get the enum of all the roles of a user whose id is that of the username.
So for instance if I get a user Agon, I want to be able to get an enum of all his roles for later use and also return them as string[] in said method.
So for after hours of head-numbing attempts I've been getting consistent errors. Not sure where to go from here:
public override string[] GetRolesForUser(string username)
{
using (SMEntities db = new SMEntities())
{
User user = db.Users.First(x => x.Username == username);
}
//throw new NotImplementedException();
}
I'm not sure where enums come into play really on this one, but how about the following:
using (SMEntities db = new SMEntities())
{
User user = db.Users.First(x => x.Username == username);
return user.Roles.Select(r => r.Type).ToArray();
}
I'm adding onto my DNN module a check to exclude certain users from having to answer some questions when logging in. Instead of hard coding each individual role I'd like to instead just exclude anyone within a particular role group. That way if we have more roles in the future we can just add them into the role group if we want them to be excluded.
However, I don't know how you check if a user is in role group. I know how to check the role, but not the group if they are in one.
SOLUTION: Here's the code I put together based on the answers I got. Should work.
RoleGroupInfo RoleGrp = RoleController.GetRoleGroupByName(this.PortalId, "Role Group");
bool bShouldSkipQuestions = false;
if (RoleGrp != null)
{
Dictionary<string, RoleInfo> GroupChk = RoleGrp.Roles;
if (GroupChk.Count > 0)
{
foreach (var item in GroupChk.Values)
{
if (_user.IsInRole(item.RoleName))
{
bShouldSkipQuestions = true;
break;
}
}
}
}
Role groups aren't really intended to be used like that (they're intended just for end-user organization), so there isn't a direct way to check that. You'll want to get all of the roles in the group (RoleController.GetRolesByRoleGroup) and then check PortalSecurity.IsInRoles, passing in a comma-separated string of the role names.
Try this code:
var roleGroup = RoleController.GetRoleGroupByName(this.PortalId, "Role Group");
var shouldSkipQuestions = roleGroup != null
&& roleGroup.Roles.Keys.Any(role => _user.IsInRole(role));