I have an In-house C# application that will be run on lap-tops in several remote locations. The most common users will have admin rights to the lap-tops, but sometimes it will be run by users without admin rights. For operational reasons, we want just one copy of the application per computer, so it will be installed under Program Files instead of the user accounts.
I am creating an auto-update routine. I would like it to have this behavior:
It checks if there are any updates available.
If there are updates and the user has no admin rights, They will be informed of the updates.
If the user has admin rights, the updates will be loaded.
In all cases, the application will be launched. Non-admin users can decide if the updates warrant shutting down and finding someone with admin rights.
99% of the time, there will be no updates, and I would prefer not to request privileges in the manifest when they usually will not be needed. So I plan on starting a separate process to actually load the updates. But in that case, I'd rather not bother non-admin users with requests for admin privileges that they cannot provide (no - they will not have another account they themselves can log into that has admin privileges).
Is there some reliable way I can have it determine - once it has found updates - whether the current user is in the administrators group, so that it will know whether to bother with launching the update process, or just report updates available and move on?
I've been searching for hours, but have only turned up one method (checking if the user has a split token) that is apparently unreliable and warned against.
Edit:
For completeness, the final solution I found based on Wheels73's post with corrections for the error I was getting is:
bool CurrentUserIsAdmin()
{
UserPrincipal user = UserPrincipal.Current;
using (IEnumerator<Principal> groups = user.GetAuthorizationGroups().GetEnumerator())
{
while (groups.MoveNext())
{
try
{
if (groups.Current.ToString() == "Administrators")
{
return true;
}
}
catch (NoMatchingPrincipalException)
{
continue;
}
}
return false;
}
}
As discussed, this is the routine I use to list all the AD directories for a given login.
public List<string> GetUsersActiveDirectoryGroups(string windowsUserName)
{
var allUserGroups = new List<string>();
var domainConnection = new DirectoryEntry();
var samSearcher = new DirectorySearcher
{
SearchRoot = domainConnection,
Filter = "(samAccountName=" + windowsUserName + ")"
};
samSearcher.PropertiesToLoad.Add("displayName");
var samResult = samSearcher.FindOne();
if (samResult == null) //User not found
return allUserGroups;
//Get groups
var theUser = samResult.GetDirectoryEntry();
theUser.RefreshCache(new[] {"tokenGroups"});
foreach (byte[] resultBytes in theUser.Properties["tokenGroups"])
{
var mySid = new SecurityIdentifier(resultBytes, 0);
var sidSearcher = new DirectorySearcher
{
SearchRoot = domainConnection,
Filter = "(objectSid=" + mySid.Value + ")"
};
sidSearcher.PropertiesToLoad.Add("name");
var sidResult = sidSearcher.FindOne();
if (sidResult != null)
{
allUserGroups.Add(sidResult.Properties["name"][0].ToString());
}
}
return allUserGroups;
}
You could then check the contents of the groups to return a bool based upon the group name you are looking for.
var myUsersGroups = GetUsersActiveDirectoryGroups("YOURLOGINNAME");
var usersIsInAdmin = myUsersGroups.Any(g => g == "Administrator");
To detect if a user simply has loca admin rights, you can use the below
WindowsIdentity user = null;
user = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(user);
var isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
OK.. final shout :)
To find out if another user has local admin rights, you can do the below
var usersPrincipal = UserPrincipal.FindByIdentity(UserPrincipal.Current.Context, IdentityType.SamAccountName, "YOURLOGINNAME");
var otherUserIsAdmin = usersPrincipal.GetAuthorizationGroups().Any(p => p.ToString() == "Administrators");
Hope that helps.
Related
I have looked at many search results but I am struggling to find a way to programmatically (using C#) create a custom permission and a custom group in Active Directory.
I have an application that will need to have about 50 individual permissions, such as: can encrypt data, can decrypt data, can export private key, can delete keypair, etc. These permissions will be assigned to a custom group. For instance, the group may be called: standard user, security manager, etc.
Users will be assigned one or more of these groups. I need all of this to be managed through Active Directory. The software that is being written is in C#. The users will be in Active Directory.
The software will check that the user has a particular permission when a function on the application is to be executed. If the user does not have permission then they will be required to enter an override. This override is simply a prompt for the credentials of another user who DOES have the relevant permissions.
I want to emphasise that this needs to be managed through Active Directory because the software is running on a domain and the permissions will be managed by the Domain Administrator.
As such, I believe the ASP.Net Roles functionality is not sufficient? In addition, I am not sure if Azure AD is the same as Windows AD.
I would very much appreciate any guidance as to which .NET assembly/namespace will provide the following capability:
Create permission
Create group
Assign permission to group
Assign user to group
Remove user from group
Remove permission from group
I need to to do this programatically because the software will have an installer and will be responsible for adding the application-specific custom permissions and groups during installation if they do not already exist.
It may be possible that I am approaching this wrong so I am open to suggestions otherwise. As long as I am able to perform the above then great!
Thank you!
As I understood,
Here you can try below code
Try once
1) Create Group
PrincipalContext principalContext =
new PrincipalContext(ContextType.Domain, LDAPDomain, LDAPContainer,
LDAPAdmin, LDAPPassword);
GroupPrincipal group = GroupPrincipal.FindByIdentity(principalContext, "groupName");
if (group == null)
{
GroupPrincipal groupPrincipal = new GroupPrincipal(principalContext);
groupPrincipal.Name = "groupName";
groupPrincipal.SamAccountName = "samAccountName";
groupPrincipal.UserPrincipalName = "userPrincipleName";
groupPrincipal.GroupScope = GroupScope.Global;
groupPrincipal.Description = "groupNameDescription";
groupPrincipal.DisplayName = "groupNameDisplayName";
groupPrincipal.Save();
}
2) Add User To Group
GroupPrincipal group = GroupPrincipal.FindByIdentity(principalContext, "groupName");
UserPrincipal user = UserPrincipal.FindByIdentity(principalContext, "userName");
bool isUserAdded = false;
if (user != null & group != null)
{
if (user.IsMemberOf(group))
{
//Do Code
}
else
{
group.Members.Add(user);
group.Save();
isUserAdded = user.IsMemberOf(group);
}
}
if (isUserAdded)
{
//Do Code
}
3) Remove User From Group
GroupPrincipal group = GroupPrincipal.FindByIdentity(principalContext, "groupName");
UserPrincipal user = UserPrincipal.FindByIdentity(principalContext, "userName");
bool isUserRemoved = false;
if (user != null & group != null)
{
if (user.IsMemberOf(group))
{
group.Members.Remove(user);
group.Save();
isUserRemoved = user.IsMemberOf(group);
}
else
{
//Do Code
}
}
if (!isUserRemoved)
{
//Do Code
}
4) Add or Remove AccessRule(Permission) to Group
From my side, I have no clear idea about what actually your logic or implementation,
But Here I tried to give a solution for adding or remving access rule to group
//DirectoryEntry for OU Level
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://OU=MYOU,DC=MYDC,DC=COM");
NTAccount account = new NTAccount("MYDC", "groupName");
ActiveDirectoryAccessRule ruleRead = new ActiveDirectoryAccessRule(
account,
ActiveDirectoryRights.ReadProperty,
AccessControlType.Allow,
ActiveDirectorySecurityInheritance.None);
ActiveDirectoryAccessRule ruleWrite = new ActiveDirectoryAccessRule(
account,
ActiveDirectoryRights.WriteProperty,
AccessControlType.Deny,
ActiveDirectorySecurityInheritance.None);
if (Permission == "User shall be able to export private key from an RSA keypair")
{
directoryEntry.ObjectSecurity.AddAccessRule(ruleRead);
directoryEntry.ObjectSecurity.AddAccessRule(ruleWrite);
directoryEntry.Options.SecurityMasks = SecurityMasks.Dacl;
directoryEntry.CommitChanges();
Console.WriteLine("Added Deny Access to Read & Write.");
}
if (Permission == "User is able to decrypt imported data")
{
directoryEntry.ObjectSecurity.RemoveAccessRule(ruleRead);
directoryEntry.ObjectSecurity.RemoveAccessRule(ruleWrite);
directoryEntry.Options.SecurityMasks = SecurityMasks.Dacl;
directoryEntry.CommitChanges();
Console.WriteLine("Removed Deny Access to Read & Write.");
}
directoryEntry.Close();
directoryEntry.Dispose();
Note: Please test all above code in your test environment first.
You're trying to use AD as both your AuthZ store and your AuthZ engine if I follow the question correctly. The former (using it as a data store) makes perfect sense, but, I don't think it's the right tool to evaluate access for your app.
What I would do is layer your groups in two levels:
Level 1 - permission groups (e.g. can encrypt, can decrypt, etc.)
Level 2 - roles - these are members of various permission groups, and in turn users are added to these groups to grant them the roles. They will inherit the permissions the roles have when their logon token is built by Windows.
Assuming your app uses Windows Authentication, the WindowsTokenRoleProvider (https://msdn.microsoft.com/en-us/library/system.web.security.windowstokenroleprovider(v=vs.110).aspx) will surface all of the group memberships up into your app and you can then check if someone is in a permission group and let them do something (or not)...
Below is the code which is being used to reset the password. I want to stop this behavior. Only new password should work. user should not be able to log in with old password.
using (var search= new DirectorySearcher(dir))
{
search.Asynchronous = false;
search.CacheResults = false;
dirSearch.Filter = "(&(objectCategory=User)(objectClass=person)(name=" + UserName.Trim() + "))";
SearchResult result = dirSearch.FindOne();
if (result != null)
{
using (var entryUpdate = result.GetDirectoryEntry())
{
entryUpdate.Invoke("setpassword", new object[] { NewPassword });
entryUpdate.CommitChanges();
//entryUpdate.RefreshCache();
}
}
result = null;
}
It's only possible to have two different passwords at the same time when Active Directory replication is broken. This is not actually a code issue. The way to fix it is to determine where the AD replication is broken. You can quickly check AD Health at a glance by running the command repadmin /showrepl. If you see errors, then run dcdiag /v on any domain controllers showing errors in the output. A new favorite tool of mine now to determine AD Health also is to run the PowerShell utility ADHealthCheck.
Technology used: asp.net(C#), MVC, LINQ, Entity
I use the Active Directory for our company website. This system works for all of our employees on site and when we take our laptop offsite and use VPN.
However we recently hired some developers(offsite) who we have given VPN access to. But they are unable to run our code and I am unsure why. We have active directory users set up for them and they can connect to us through VPN.
They are running into two errors.
A username/password error.
The Server is not Operational error.
Does the VPN username/password have to match the active directory account?
If they log into their development machine does that username/password have to match the active directory account?
Is there some restriction on Active Directory and offsite I am unaware of as to why this would work for us but not our off site developers?
Below is the section of code that is giving the error: It errors on the line SearchResult rs = ds.FindOne();
public AD_CurrentUserProfile GetUserProfile(string userName)
{
using (DirectoryEntry de = new DirectoryEntry("LDAP://server.com"))
using (DirectorySearcher ds = new DirectorySearcher(de))
{
ds.SearchScope = SearchScope.Subtree;
ds.Filter = ("(&(objectCategory=person)(objectClass=User)(samaccountname=" + userName + "))");
ds.PropertiesToLoad.Add("distinguishedName");
ds.PropertiesToLoad.Add("manager");
ds.PropertiesToLoad.Add("directreports");
SearchResult rs = ds.FindOne();
AD_CurrentUserProfile userProfile = new AD_CurrentUserProfile();
userProfile.currentUser = GetProfileFromDN(rs.Properties["distinguishedName"][0].ToString());
userProfile.managerProfile = GetProfileFromDN(rs.Properties["manager"][0].ToString(), true);
int departmentID = db.IPACS_Department.First(v => (v.name == userProfile.currentUser.department)).departmentID;
userProfile.ipacs_department = db.IPACS_Department.Find(departmentID);
if (userProfile.currentUser.userName == userProfile.ipacs_department.owner)
{
userProfile.currentUser.isManager = true;
}
// Override for Provana and upper management
if (userProfile.currentUser.department == "Provana" || userProfile.currentUser.department == "JM")
{
userProfile.currentUser.isManager = true;
}
if (rs.Properties["DirectReports"].Count > 0)
{
if (!userProfile.currentUser.isManager)
{
userProfile.currentUser.isSupervisor = true;
}
userProfile.directReports = new HashSet<AD_User>();
foreach (string value in rs.Properties["DirectReports"])
{
userProfile.directReports.Add(GetProfileFromDN(value));
}
}
return userProfile;
}
}
I never pass 'password' anywhere in any of my code, so is this something Active Directory does by default?
A connection to AD will always require windows credentials. Your code, as posted, does not supply any credentials to AD. (You pass in a user name that you are looking up, but that is not the same as supplying credentials for the connection). This will work for users whose machines are attached to the domain...because your network credentials are passed in implicitly. For the external devs, when they VPN in, they supply credentials to the VPN protocol, which allows their machines to access your network, but that doesn't mean their machines are 'joined' to the domain...so AD will still require explicit credentials from them, including a personal password or a service account password that has permissions to access AD.
This line:
using (DirectoryEntry de = new DirectoryEntry("LDAP://server.com"))
essentially needs to allow a user name and password:
using (DirectoryEntry de = new DirectoryEntry("LDAP://server.com"), userName, pwd)
{
de.AuthenticationType = AuthenticationTypes.None | AuthenticationTypes.ReadonlyServer;
}
...you'll have to experiment with the AuthenticationTypes flags depending on how your network admins have it set up.
I am using a WCF service to expose certain Active Directory management functions to our help desk staff without giving them the group membership required to manipulate AD directly. Adding users to and removing users from groups is working like a champ with existing users, but every time I create a new user it throws back this fun code:
The server is unwilling to process the request. (Exception from HRESULT: 0x80072035)
The code I use to add the user to the group is
public bool AddGroupToUser(string userDn, string groupDn)
{
try
{
DirectoryEntry groupEntry = LdapTools.GetDirectoryEntry(groupDn);
groupEntry.Properties["member"].Add(userDn);
groupEntry.CommitChanges();
groupEntry.Close();
return true;
}
catch (DirectoryServicesCOMException)
{
return false;
}
}
Everything I've read on the subject is rather vague and I can't seem to find out WHY the exception is being triggered. Any ideas?
UPDATE
This is the code I use to create the user in AD:
try
{
DirectoryEntry container = GetDirectoryEntry(storageOu);
DirectoryEntry newUser = container.Children.Add("CN=" + employee.FullName, "user");
newUser.Properties["sAMAccountName"].Value = employee.Username;
newUser.Properties["displayName"].Value = employee.FullName;
newUser.Properties["givenName"].Value = employee.FirstName;
newUser.Properties["sn"].Value = employee.LastName;
newUser.Properties["department"].Value = departmentName;
newUser.Properties["userPrincipalName"].Value = employee.Username + "#APEX.Local";
newUser.CommitChanges();
newUser.Invoke("SetPassword", new object[] { employee.Password });
newUser.CommitChanges();
AdsUserFlags userSettings = AdsUserFlags.NormalAccount;
newUser.Properties["userAccountControl"].Value = userSettings;
newUser.CommitChanges();
ldapPath = newUser.Path;
newUser.Close();
container.Close();
}
catch (DirectoryServicesCOMException e)
{
// Something went wrong... what???
}
catch (Exception e)
{
// Something else went wrong
}
The new user can login, and can be manipulated using the standard MS tools.
Apparently, unless I'm missing an important step here, the issue is time. When forcing the system to sleep for 8 seconds before attempting to add groups to the new user the process works. If I do it any sooner than the 8 second mark it fails.
I'm marking this answer as correct unless anybody can provide me a better solution.
Try:
public bool AddUserToGroup(string userName, string groupName)
{
bool done = false;
GroupPrincipal group = GroupPrincipal.FindByIdentity(context, groupName);
if (group == null)
{
group = new GroupPrincipal(context, groupName);
}
UserPrincipal user = UserPrincipal.FindByIdentity(context, userName);
if (user != null & group != null)
{
group.Members.Add(user);
group.Save();
done = (user.IsMemberOf(group));
}
return done;
}
Reference:
http://www.c-sharpcorner.com/UploadFile/dhananjaycoder/activedirectoryoperations11132009113015AM/activedirectoryoperations.aspx
As it mentioned in here, can you tell us that you set the password of the newly created users? In the reference, it says that you should SetPassword of the user before doing anything with it.
Time issue could occur from a Active Directory replication issue.
Once, I gave a product to my customer which creates user over Active Directory, which has over 10.000 records in it, with the given informations on a SharePoint form and then program adds the user to a SharePoint group. The problem was, SharePoint throws an error about the newly created user, it says user does not exist in AD.
So, one of out system engineer told us about the replication opearation on the Active Directory that it could be the source of the problem and it was true.
For the solution, program tries to do the job for 10 times with 1 second sleeps. No problem occurred so far. So as a solution, I would suggest you to check out the AD for the existence of replication method.
P.S: When I asked questions to my customers system engineers about the replication operation, they all rejected the existence of AD replication operation and told me that the program has the problem. They believed me when we created a new user in AD from a computer, we couldn't see the user for 5 seconds on an another computer.
I'm looking for a way to create Active Directory users and set their password, preferably without giving my application/service Domain Admin privileges.
I've tried the following:
DirectoryEntry newUser = _directoryEntry.Children.Add("CN=" + fullname, USER);
newUser.Properties["samAccountName"].Value = username;
newUser.Properties["userPassword"].Value = password;
newUser.Properties["mail"].Value = email;
newUser.CommitChanges();
The user is created, but it seems the password is never set on the user.
Does anyone have an idea on how to set the user's password initially when creating the user? I know about
.Invoke("SetPassword", new object[] { password })
But that requires my code to be run with Domain Admin privileges. As I don't really see the point to grant my code Domain Admin privileges, just to set the initial password (I also allow user password resets, but those run in the context of that particular user), I am hoping someone has a clever solution that doesn't require me to do so.
Thanks in advance!
You can do this whole process much easier now with System.DirectoryServices.AccountManagement (long as you're on .Net 3.5):
See here for a full rundown
Here's a quick example of your specific case:
using(var pc = new PrincipalContext(ContextType.Domain))
{
using(var up = new UserPrincipal(pc))
{
up.SamAccountName = username;
up.EmailAddress = email;
up.SetPassword(password);
up.Enabled = true;
up.ExpirePasswordNow();
up.Save();
}
}
I'd use #Nick's code (wrapped in using statements so the context and principal are disposed properly). As for privileges, you'll need to at least have enough privileges on the OU where you are creating the user to create and manage objects. I'd create a specific user under which your program will run and give it just enough privileges to do the tasks that it needs in that specific OU and no more.
Yes can also use below code to create bulk of users
DirectoryEntry ouEntry = new DirectoryEntry("LDAP://OU=TestOU,DC=TestDomain,DC=local");
for (int i = 0; i < 10; i++)
{
try
{
DirectoryEntry childEntry = ouEntry.Children.Add("CN=TestUser" + i, "user");
childEntry.CommitChanges();
ouEntry.CommitChanges();
childEntry.Invoke("SetPassword", new object[] { "password" });
childEntry.CommitChanges();
}
catch (Exception ex)
{
}
}
The actual attribute for the password is unicodePwd, which requires a specific format that is described in the documentation. But this is how you can do it:
newUser.Properties["unicodePwd"].Value = Encoding.Unicode.GetBytes("\"NewPassword\"");
Doing it this way, you can create the user with a password in one step.