I am doing a C# application targeting WinXP, Vista, and 7 Operating Systems.
One feature is, I can Add, Remove, Modify the Group set to a user programmatically.
Can I ask for help how to make this happen?
Will it be possible to do this in WMI? My codes mainly using WMI to get the users..
Currently am using Windows7
I am trying to test this code
DirectoryEntry localMachine = new DirectoryEntry("WinNT://" + Environment.MachineName + ",Computer");
localMachine.Properties["member"].Add("Chevi");
localMachine.CommitChanges();
localMachine.Close();
and it's spitting this error
The directory property cannot be found in the cache.
I tried enumerating the Property collection and I got this
OperatingSystem
OperatingSystemVersion
Owner
Division
ProcessorCount
Processor
Name
If you're using local groups, you can do this by calling the system net command. For example, to add a user to a group, you'd call:
net localgroup MyGroup /add SomeUser
Type net help localgroup at a command prompt for more info.
You can also do this using WMI. This is VBScript but can be adapted to .NET or your preferred programming toolkit:
Dim oComputer
Computer = "computername"
Groupname = "Administrators"
ObjectToAdd = "Administrator"
' Bind to the computer.
Set oComputer = GetObject("WinNT://" & Computer & ",computer")
' get group object
Dim oGroup
Set oGroup = oComputer.GetObject("group", GroupName)
' Add the user object to the group.
oGroup.Add "WinNT://" & Computer & "/" & ObjectToAdd
Credit: Matt Hickman, http://www.tech-archive.net/Archive/WinXP/microsoft.public.windowsxp.wmi/2004-04/0007.html
I have also developed one windows application on Visual Studio 2010, using C#. This is a working version of the program, which will add an existing user to a particular group.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.DirectoryServices;
namespace Utility_Add_User_To_Group {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void btn_Add_Click(object sender, EventArgs e) {
string usr, grp;
usr = txt_UserName.Text;
grp = txt_GroupName.Text;
add(usr, grp);
groupBox2.Visible=true;
}
private void add(string usr, string grp) {
bool flagUsr, flagGrp;
try {
DirectoryEntry AD = new DirectoryEntry("WinNT://" +Environment.MachineName + ",computer");
DirectoryEntry group, user;
group = AD.Children.Find(grp, "group");
user = AD.Children.Find(usr, "user");
if (user != null) {
label3.Text += "User Name Exists!!!";
flagUsr = true;
} else {
label3.Text += "Sorry, No Such User Name Found!!!";
flagUsr = false;
}
if (group != null) {
label4.Text += "Group Exists!!!";
flagGrp = true;
} else {
label4.Text += "Sorry, Group Does Not Exists!!!";
flagGrp= false;
}
if(flagGrp == true && flagUsr == true) {
group.Invoke("Add", new object[] { user.Path.ToString() });
label5.Text += "Congratulations!!! User has been added to the group";
} else {
label5.Text += "Error Happened!!! User could not be added to the group!!!";
}
} catch (Exception e) {
label6.Text +=e.Message.ToString();
label6.Visible= true;
}
}
private void btn_Clear_Click(object sender, EventArgs e) {
normal();
}
private void normal() {
txt_GroupName.Text="";
txt_UserName.Text="";
txt_UserName.Focus();
groupBox2.Visible=false;
}
}
}
NetUserAdd to create a user
NetGroupAdd to create a group
NetGroupAddUser to add a user to a group
NetGroupDelUser to remove an user form a group
NetLocalGroupAdd to create a local group
NetLocalGroupAddMembers to add users to a local group
etc etc
Related
I need to implement authentication using Active Directory for my Winforms application.
I know that it is possible to get current Windows user credential like this:
AppDomain currentDomain = AppDomain.CurrentDomain;
// Set the principal policy to WindowsPrincipal.
currentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
But I need that system asks user to either:
select logged in credentials => this would be possible with PrincipalPolicy.WindowsPrincipal I guess
choose to enter different credentials in Windows login form (not in my application) => how can I get this working?
P.S. I know it is possible to send username/password like it is described here:
Validate a username and password against Active Directory?
but I don't want to have user credentials going through my application because off security risks
I found out also this project in CodeProject.com on how to authenticate against Active Directory using LDAP, but this also requires entering user credentials in my application...
I know that there is also Active Directory Federated Services, but as far as I know it is for Web based authentication...
Any solutions for desktop authentication against Active Directory?
Here is a method I wrote in VB.NET in the past, and converted to C# for you.
use System.DirectoryServices namespace (you need to make a reference) in order to check credentials of user against DC(LDAP) server in your network (windows network of course).
clarification: this method was requested by our company security department for circumstances that user need to log in to company software from a colleague computer. i recommend (for security reasons) to require from your IT department to add all users to a specific active directory group and check also if the user is a member of this AD group, also record every local ip address that connects to the software.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.DirectoryServices;
namespace WindowsFormsApplication15
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
if (IsAuthenticated("YOUR DOMAIN", "USERNAME", "PASSWORD") == false)
{
// not exist in active directory!
}
else
{
// user is exist in active directory
}
}
private string _path { get; set; }
private string _filterAttribute { get; set; }
public bool IsAuthenticated(string domain, string username, string pwd)
{
string domainAndUsername = domain + "\\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
try
{
object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if ((result == null))
{
return false;
}
_path = result.Path;
_filterAttribute = result.Properties["cn"][0].ToString();
}
catch (Exception ex)
{
return false;
}
return true;
}
}
}
Found a documentation (here) in an answer thread on this site but i can´t get an connection to an AD. When i use a program like Active Directory Explorer i can connect. I think, because i am trying to connect to a LDAPS i need a different approach?
I have the server IP, a domain, username/pwd and the port 636.
I tried various combinations # new DirectoryEntry but couldn´t get it to connect. Always get a COMException Domain is not existing .
static DirectoryEntry createDirectoryEntry()
{
DirectoryEntry ldapConnection = new DirectoryEntry("LDAP://192.168.2.59", USER, PWD);
ldapConnection.AuthenticationType = AuthenticationTypes.SecureSocketsLayer;
return ldapConnection;
}
Background Infos:
User places his card to a Card Reader Unit. Porgram gets ID from card and searches the DB for this ID and returns the eMail address belonging to the ID/User
.
And here the working solution:
private string getEmail(string userID)
{
try
{
string ldapfilter = "(&(otherPager=" + userID + "))";
DirectoryEntry myLdapConnection = new DirectoryEntry("LDAP://" + SERVER, USER, PWD);
DirectorySearcher search = new DirectorySearcher(myLdapConnection);
search.Filter = ldapfilter;
/*search.PropertiesToLoad.Add("mail");
SearchResult result = search.FindOne();*/
string[] requiredValue = new String[] { "mail" };
foreach (String value in requiredValue)
search.PropertiesToLoad.Add(value);
SearchResult result = search.FindOne();
if (result != null)
{
foreach (String value in requiredValue)
foreach (Object myCollection in result.Properties[value])
{
return myCollection.ToString();
}
}
else
{
return "No Entry fround";
}
}
catch (Exception e)
{
Console.WriteLine("Exception Problem: " + e.ToString());
return null;
}
return null;
}
private void cmdClose_Click(object sender, EventArgs e)
{
Close();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
label1.Text = getEmail(textBox1.Text);
}
You need to specify the port, since 636 is the default LDAPS port.
new DirectoryEntry("LDAP://192.168.2.59:636", USER, PWD)
I do this in some of my code, and using "LDAP://" (not "LDAPS://") is what works.
If that doesn't work, then there may be a certificate error. You can test this with a browser. If you use Chrome, open Chrome with this (so it lets you use port 636):
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --explicitly-allowed-ports=636
Then go to https://192.168.2.59:636. If you get a big fancy certificate error, then the problem is that the certificate is not trusted. View the certificate from Chrome and see what the problem is. It could be issued by an authority that is not in the Windows cert store.
I am using System.DirectoryServices.DirectoryEntry to create AD user and everything work fine except for some Remote Desktop specifics properties.
Exemple :
newUser.Properties["msTSConnectClientDrives"].Value = false;
newUser.Properties["msTSConnectPrinterDrives"].Value = false;
newUser.Properties["msTSDefaultToMainPrinter"].Value = false;
This doesn't throw any exception, so I guess the properties are found in the object but they don't have any effect. When I go into the property window of that user, under "Environment" tab, these 3 checkbox are still checked on.
Am I missing something particular for these properties ?
Thank for your help.
EDIT :
Sorry I have been really busy, here is a code sample :
private string CreateNewADAccount(string accountName, string accountPassword)
{
try
{
PrincipalContext context = new PrincipalContext(ContextType.Domain, "SV-LITE", #"LITE\xxxxxxxx", "yyyyyyyy");
UserPrincipal newUser = new UserPrincipal(context);
newUser.SamAccountName = accountName;
newUser.UserPrincipalName = accountName;
newUser.Name = "LiteUser2015 - " + accountName;
newUser.DisplayName = "LiteUser2015 - " + accountName;
newUser.SetPassword(accountPassword);
newUser.PasswordNeverExpires = true;
newUser.UserCannotChangePassword = true;
newUser.Save();
// Set advanced properties
if (newUser.GetUnderlyingObjectType() == typeof(DirectoryEntry))
{
DirectoryEntry entry = (DirectoryEntry)newUser.GetUnderlyingObject();
entry.Properties["msTSConnectClientDrives"].Value = false;
entry.Properties["msTSConnectPrinterDrives"].Value = false;
entry.Properties["msTSDefaultToMainPrinter"].Value = false;
entry.Properties["msTSInitialProgram"].Value = "test";
entry.CommitChanges();
}
return newUser.Guid.ToString();
}
catch (Exception e)
{
MessageBox.Show("Failed to create PrincipalContext. Exception: " + e);
}
return null;
}
After making the changes, you have to call CommitChanges - newUser.CommitChanges();
See https://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.commitchanges%28v=vs.110%29.aspx
By default, changes to properties are made locally to a cache..
It might have something to do with the Server OS version you're using. I found this answer on another site that talks about Windows 2000 and 2003. It should work for Windows2008 and above:
For 2000 / 2003 you have to access them using the Terminal Services
ADSI extension. The reference for that is here:
http://msdn.microsoft.com/en-us/library/aa380823(VS.85).aspx
I want to retrieve the login name of a user from Active Directory.
For example the name is 'Jan Van der Linden'
After giving this name as parameter I must get his login name in return for example jvdlinden
Since you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
Basically, you can define a domain context and easily find users and/or groups in AD:
public string GetLoginName(string userName)
{
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find user by name
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, userName);
if(user != null)
return user.SamAccountName;
else
return string.Empty;
}
The new S.DS.AM makes it really easy to play around with users and groups in AD:
this actually does almost the opposite but can be a starting point to check and modify as needed:
Finding a User in Active Directory with the Login Name
using .net library you can use the following code to get username or any info from active directory
using System.Management;
using System.Management.Instrumentation;
using System.Runtime.InteropServices;
using System.DirectoryServices;
ManagementObjectSearcher Usersearcher = new ManagementObjectSearcher("Select * From Win32_ComputerSystem Where (Name LIKE 'ws%' or Name LIKE 'it%')");
ManagementObjectCollection Usercollection = Usersearcher.Get();
string[] sep = { "\\" };
string[] UserNameDomain = Usercollection.Cast<ManagementBaseObject>().First()["UserName"].ToString().Split(sep, StringSplitOptions.None);
i add "Select * From Win32_ComputerSystem Where (Name LIKE 'ws%' or Name LIKE 'it%')"
this will get the user name by the full name
hope this could help you
Check this link has needed code snipple
Validate AD-LDAP USer
using (DirectoryEntry entry = new DirectoryEntry())
{
entry.Username = "DOMAIN\\LOGINNAME";
entry.Password = "PASSWORD";
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "(objectclass=user)";
try
{
searcher.FindOne();
{
//Add Your Code if user Found..
}
}
catch (COMException ex)
{
if (ex.ErrorCode == -2147023570)
{
ex.Message.ToString();
// Login or password is incorrect
}
}
}
Without Identity:
private string GetLogonFromDisplayName(string displayName)
{
var search = new DirectorySearcher(string.Format("(&(displayname={0})(objectCategory=user))", displayName));
search.PropertiesToLoad.Add("sAMAccountName");
SearchResult result = search.FindOne();
if (result != null)
{
var logonNameResults = result.Properties["sAMAccountName"];
if (logonNameResults == null || logonNameResults.Count == 0)
{
return null;
}
return logonNameResults[0].ToString();
}
return null;
}
First off, I don't have any C# skills or experience. A friend of mine took a couple classes in college and was able to give me what I've got so far in this C# program.
I asked my friend to create a program that would look at WMI for the current logged on user's full name, then look at the RegisteredOwner value. If the full name is the same as the RegisteredOwner then the program quits (all silent), if the full name is different than the RegisteredOwner then the program would launch a form with some text and a yes/no option. If the user clicks yes, then the program sets the RegisteredOwner value to the logged on users full name, and if they click no, the program quits.
He delivered exactly what I asked for; however, it only runs if ran by a user with local admin rights and unfortunately, in my environment, no user is a local admin on their machine. When I presented the issue to him, he wasn't sure what he could do to resolve the problem, and after looking into this all day, I'm afraid there isn't much that can be done to resolve the issue and allow the program to be launched using the local users permissions.
So my question for you is do you know of a different way we could go with this program that would allow it to be run by a user without local admin rights? I would like to have the executable stored somewhere locally on the PC and then it in the startup items list of having something in the startup items list launch it. Maybe there's a way I can use a executable that works with non-local admin rights and then have it work with a windows service that's running under the System account?
When ran by a non local admin, nothing happens when you launch the script.
Below is the code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Management;
using System.Security.Principal;
using Microsoft.Win32;
using System.Threading;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
bool compare;
public Form1()
{
InitializeComponent();
if (PreLoad())
compare = true;
else
{
this.Text = GetUser();
compare = false;
}
}
private bool PreLoad()
{
string temp = GetCaption(GetUser());
RegistryKey regKey1 = Registry.LocalMachine.CreateSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
string keyString = regKey1.GetValue("RegisteredOwner").ToString();
if (temp == keyString)
return true;
else
return false;
}
private void btnYes_Click(object sender, EventArgs e)
{
MessageBox.Show("Are you sure?", "Confirmation", MessageBoxButtons.OKCancel);
string temp = GetCaption(GetUser());
DoRegistryEdit(temp);
lblShowAll.Text = "-Successfully registered the machine to: " + temp + " -";
//Refreshes the screen so that the status message displays
this.Refresh();
Thread.Sleep(5000);
this.Close();
}
private void btnNo_Click(object sender, EventArgs e)
{
//MessageBox.Show("Better change computers then!");
this.Close();
}
private void Form1_Load(object sender, EventArgs e)
{
if (compare)
this.Close();
}
public string GetCaption(string userName)
{
String QueryStringTemp = "Select * from Win32_NetworkLoginProfile where Caption = '" + userName + "'";
System.Management.ObjectQuery oQuery = new ObjectQuery(QueryStringTemp);
ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oQuery);
ManagementObjectCollection oReturnCollection = oSearcher.Get();
string capturedResults = "";
foreach (ManagementObject oReturn in oReturnCollection)
{
capturedResults += oReturn["FullName"].ToString();
}
return capturedResults;
}
public string GetUser()
{
System.Management.ObjectQuery oQuery = new ObjectQuery("Select * from Win32_ComputerSystem");
ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oQuery);
ManagementObjectCollection oReturnCollection = oSearcher.Get();
string capturedResults = "";
foreach (ManagementObject oReturn in oReturnCollection)
{
capturedResults += oReturn["UserName"].ToString();
}
int hold = capturedResults.IndexOf("\\");
capturedResults = capturedResults.Substring(hold + 1);
return capturedResults;
}
public void DoRegistryEdit(string name)
{
RegistryKey masterKey = Registry.LocalMachine.CreateSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
if (masterKey == null)
MessageBox.Show("Null Master Key!");
else
{
try
{
masterKey.SetValue("RegisteredOwner", name);
}
catch (Exception ex)
{
MessageBox.Show("Uh OH!" + ex);
}
finally
{
masterKey.Close();
}
}
}
}
}
Any advice and suggestions would be appreciated!
WMI is the killer here. I suppose the whole "Management" part of WMI forces it to run in the admin space.
I found this resource on the Web:
http://skysigal.xact-solutions.com/Blog/tabid/427/EntryId/417/C-Compact-Framework-Getting-the-Registered-Owner.aspx
I tested it out to see that it worked respectably well on my Win7 X86 box. Judging from other sources on the web, this should be good for most recent versions of Windows, including several mobile editions.
Good luck!