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));
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 have the name of my AD Group and I need to return a list of every single email address of the users found inside. I've been looking around trying to find the answer to this but all of the results I've found are aimed at checking an AD Group for a single users email address. Often by using their windows log on username.
However, what I'm wanting to do is to grab every email address inside my target active directory group. As eventually this will be turned into an automated email that needs to be sent to only those individuals within the active directory group.
The project I'm building is making use of MVC 5.
Could someone please point me in the right direction for this?
You need to split this into two steps:
Find the group (I assume you just have the name of the group)
Get all the members.
Finding the group
You can use GroupPrincipal.FindByIdentity, which is arguably easier, but I find that's quite slow (slower the bigger your group is). I prefer to use DirectorySearcher/DirectoryEntry (which is what GroupPrincipal uses behind the scenes anyway).
To find the group, your code would look something like this:
var groupName = "MyGroup";
var search = new DirectorySearcher() {
Filter = $"(&(objectClass=group)(cn={groupName}))"
};
search.PropertiesToLoad.Add("cn"); //this is just to prevent it from returning every attribute
//This will throw an exception if the group is not found
var group = ((SearchResult)search.FindOne()).GetDirectoryEntry();
If you already have the distinguishedName of the group, you can actually skip that and just make a DirectoryEntry:
var group = new DirectoryEntry($"LDAP://{disginguishedName}");
Getting the members
I wrote an article on how to do this, with example C# code: Find all the members of a group
The example code in that article returns the DOMAIN\username, so here is the same method, modified to return a list of email addresses for all the members that have one.
Pass this method the DirectoryEntry object we found for the group. Set recursive to true if you want it to look inside 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", "mail" });
if (recursive && memberDe.Properties["objectClass"].Contains("group")) {
members.AddRange(GetGroupMemberList(memberDe, true));
} else {
var email = memberDe.Properties["mail"].Value.ToString();
if (!string.IsNullOrEmpty(email)) {
members.Add(email);
}
}
}
}
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;
}
In my article, I also talk about primary groups and Foreign Security Principals, but you don't need to worry about that here since it's not applicable to distribution lists.
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 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.
I am trying to do a very simple AD query to see if a computer is in a group. The following code seems intuitive enough but does not work. The LDAPString is a fully distinguised name for the group that the computer referenced by NetBIOSName is a memberOf.
public bool IsComputerInADGroup(String LDAPString, String NetBIOSName)
{
using (DirectoryEntry entry = new DirectoryEntry(String.Format(#"LDAP://{0}", LDAPString)))
using (DirectorySearcher computerSearch = new DirectorySearcher(entry))
{
ComputerSearch.Filter = String.Format("(&(objectCategory=computer)(CN={0}))", NetBIOSName);
SearchResult match = ComputerSearch.FindOne();
if (match != null)
{
return true;
}
}
return false;
}
Can someone please explain why this is incorrect and what the correct/fastest way to to perform this search is.
Thanks
P
Your basic assumption is wrong - a computer (or user) cannot be in a group implying "containment" inside a group; a user or computer is only inside an OU.
A user or computer can be member of any number of groups - but you need to check this against the member property of the group (or the memberOf attribute of the element that is a member of that group).
So the easiest way, really, is to
bind to the object in question
refresh its property cache to get the latest entries in memberOf
enumerate of its memberOf entries and see if the group you're looking for is present
Something like:
public static bool IsAccountMemberOfGroup(string account, string group)
{
bool found = false;
using (DirectoryEntry entry = new DirectoryEntry(account))
{
entry.RefreshCache(new string[] { "memberOf" });
foreach (string memberOf in entry.Properties["memberOf"])
{
if (string.Compare(memberOf, group, true) == 0)
{
found = true;
break;
}
}
}
return found;
}
Call this like so:
bool isMemberOf =
IsAccountMemberOfGroup("LDAP://cn=YourComputer,dc=Corp,dc=com",
"CN=yourGroupInQuestion,OU=SomeOU,dc=corp,dc=com");
and you should be fine.
Update: if you're on .NET 3.5, you could also use the new System.DirectoryServices.AccountManagement namespace and LINQ to make things even easier:
public static bool IsAccountMemberOfGroup2(PrincipalContext ctx, string account, string groupName)
{
bool found = false;
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, groupName);
if (group != null)
{
found = group.GetMembers()
.Any(m => string.Compare(m.DistinguishedName, account, true) == 0);
}
return found;
}
and call this:
// establish default domain context
PrincipalContext domain = new PrincipalContext(ContextType.Domain);
// call your function
bool isMemberOf =
IsAccountMemberOfGroup2(domain,
"cn=YourComputer,dc=Corp,dc=com",
"CN=yourGroupInQuestion,OU=SomeOU,dc=corp,dc=com");
when you say it doesn't work, you mean that you can't find the computer? If so, check first if the computer is in the group, there is a nice tool out there named Active directory exporer wich can help you: http://technet.microsoft.com/en-us/sysinternals/bb963907.aspx
If it is in the group what you can try is to eliminate the filter for the computer name on the filter and iterate over the resultset in order to find out if your element is there:
ComputerSearch.Filter = ("(&(objectCategory=computer))";
SearchResult match = ComputerSearch.FindAll();
Here's some infos on how to query AD : http://www.codeproject.com/KB/system/everythingInAD.aspx