I am having some difficulty with doing an automated login for users in my desktop Active Directory application. I may be trying to do an SSO, but I am under the impression that is only for web applications.
The code I have, is this:
PrincipalContext theContext = new PrincipalContext(ContextType.Domain);
if (theContext.ValidateCredentials(null, null))
Console.WriteLine("Credentials have been validated. Tell your friends.");
else
Console.WriteLine("Invalid credentials");
UserPrincipal user = new UserPrincipal(theContext, "uatu", "Passw0rd", true);
user.Save();
The PrincipalContext is being created without error, and I am validating the credentials. I assumed this would validate me as the user that logged in to the computer, which is under the Active Directory domain. And I can find users and groups. But as soon as I call user.Save() I get the error "Access is denied." Am I actually getting into Active Directory as a guest user?
If I set the user name and password in ValidateCredentials, it doesn't help.
PrincipalContext theContext = new PrincipalContext(ContextType.Domain);
if (theContext.ValidateCredentials("<username>", "<password", ContextOptions.Negotiate | ContextOptions.Signing | ContextOptions.Sealing))
Console.WriteLine("Credentials have been validated. Tell your friends.");
else
Console.WriteLine("Invalid credentials");
UserPrincipal user = new UserPrincipal(theContext, "uatu", "Passw0rd", true);
user.Save();
That code still fails on user.Save().
If I explicitly set the username and password to match myself as the logged in user in the PrincipalContext constructor, then I get success.
PrinicipalContext theContext = new PrincipalContext(ContextType.Domain,"<address>", "<domain context>", "<username>", "<password>");
UserPrincipal user = new UserPrincipal(theContext, "uatu", "Passw0rd", true);
user.Save();
That code succeeds. But I would rather not have the user log in to my application after they have logged into their computer with the exact same credentials.
I have been hearing a bit about "Affiliate Application", so I'm wondering if I have to let Active Directory know that it can trust my application. I am still hazy on the details through, and don't know if that is the wrong direction.
Does anyone have an idea as to what I should be doing?
If you are trying to modify UserPrincipals, you have a couple options:
User is already authenticated to windows as a user with permission to edit active directory:
Use the Constructor for PrincipalContext which doesn't take username/password
This will run the context as the currently logged in user
Run query, var usr = UserPrincipal.FindByIdentity(ctx, "bob#domain.local");
Perform manipulations on usr object
Call usr.Save(); on the returned user from the query.
User is authenticated to windows, but you must "impersonate" a user with AD permission
Use the Constructor for PrincipalContext which takes username/password
This will run the context as the credentials you passed in
Run query, var usr = UserPrincipal.FindByIdentity(ctx, "bob#domain.local");
Perform manipulations on usr object
Call usr.Save(); on the returned user from the query.
Based on your explanation above, I'm presuming you need option #1. ValidateCredentials(); is only used to validate credentials, it returns a true/false if the credentials you've given it are valid. Calling it has no lasting affect, it only validates. If you need to impersonate a user, you need to use the PrincipalContext constructor which takes credentials.
Related
I need help to protect a very private tool that accepts followers on Instagram, I want to make the exe openable only if the user has logins credentials to prevent people from leaking it. I only have the .exe file and it's CMD line based, I wonder if we could like make an auth form in C# and then bind it to the .exe file
Thanks
Hard coded info:
The most simple scenario is just asking about credentials when starting the tool -> i.e. hard-coded username and password.
Active directory (or local PC) data:
other more professional option is to read the data from active directory (I do not know the environment you are working in), for example check if this user belongs to a specific group, the code will look like this:
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "DOMAINNAME");
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");
// find the group in question
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, "YourGroupNameHere");
if(user != null)
{
// check if user is member of that group
if (user.IsMemberOf(group))
{
// do something.....
}
}
The code above is taken from this answer.
I have a SharePoint solution with a custom application where a user should be able to change some properties in his own Active Directory object.
I am doing the following:
PrincipalContext ctx = ActiveDirectory.GetPrincipalContext("lab");
UserPrincipal user = ActiveDirectory.GetUserPrincipal(ctx, "Administrator");
user.DisplayName = user.DisplayName + DateTime.Now.ToString("ddMMyyyyHHmmss");
user.Save();
I am logged in to SharePoint as the domain administrator and i am trying to change my own DisplayName.
What is wrong with my code?
Update 20.04.2016
I have built a small Console Application with the following code:
static void Main(string[] args)
{
try
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "lab");
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "administrator");
DirectoryEntry de = (user.GetUnderlyingObject() as DirectoryEntry);
user.DisplayName = user.DisplayName + DateTime.Now.ToString("ddMMyyyyHHmmss");
user.Save();
Console.WriteLine("OK");
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
This works fine! That means normally, that the Authentication goes wrong or the user which is logged into SharePoint is not getting used to connect to AD and do the changes. If this could be the case, how could i find out with which user i am doing the operation?
Update 20.04.2016 - 2
I have now tried to put the username and password in the PrincipalContext contructor as below:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "lab", "administrator", "pass");
This works, too! That means now definitely, that the user which is logged in to SharePoint is not used to create the PrincipalContext. But why? Normally code is always executed in the context of the current user!?
How can i find out which user is used to create the PrincipalContext and how can i change it that the logged in user is getting used?
I had the same problem, I resolved it like this:
In IIS, in the advanced settings of my app -> In Identity I add my service account (example: domain\service-account)
In my active directory, I give to this service account full control on folder and sub-folder where I want to create/update user (you need to modify view to see security tab -> right click -> view -> advanced features)
If you already have that, check if your service account is not disabled
And now everything is working :)
I try to authenticate users belonging to remote ActiveDirectory from my machine, which is not the same domain as the current machine or user domain. There will be no trust between my machine and remote ActiveDirectory machine.
Initial Try
I tried to authenticate a user(Input: sAMAccountName, machine's ipaddress, machine's domain username("Administrator") and machine's password(***). Able to get result that the user with 'sAMAccountName' do exist in ActiveDirectory.
My Requirement:
Imagine that already a user("qwerty") is created in ActiveDirectory
From my local machine, I will have the following information,
a. Remote ActiveDirectory ipaddress
b. Remote ActiveDirectory machine's username and password.
c. Username and password of User "qwerty"
I need to check whether User "qwerty" is present in remote ActiveDirectory's users list and validate whether the password entered is same in ActiveDirectory's Users list
Code I tried:
DirectoryEntry entry = new DirectoryEntry("LDAP://ipaddress/DC=dinesh,DC=com", name, password);
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "(sAMAccountName=" + name + ")";
try
{
SearchResult adsSearchResult = adsSearcher.FindOne();
isValid = true;
adsEntry.Close();
}
catch (Exception ex)
{
adsEntry.Close();
}
Do I need to create a trust between local machine and remote ActiveDirectory machine before validating Users in a remote ActiveDirectory? If yes please tell how it can be done;
After creating trust, how can I validate Users?
===========================================================================
I am able to use the solution suggested by Rainer, but with a new problem. When I create a new user via C# code from a different machine, then some properties do not set properly.
Does this need to be set compulsorily while creating user?
First some basics (independent of this question)
Authentication
The system checks if Bob is really Bob. In an Active Directory environment, this is usually done with a domain login from the workstation, Bob enters his username and password, and he gets a Kerberos ticket. Later, if he wants to access e.g. a file share on a remote fileserver, he does not need to login anymore, and can access the files without entering username/password.
Authorization
The system checks which resources Bob is allowed to access. Usually Bob is in domain groups, and a group is in the ACL (access control list) of the resource.
If there are multiple trusting domains, Bob needs to login in one domain, and can access resources in all other domains.
This is one of the main reasons using Active Directory: single sign on
Checking if user / password is valid
If you have a username and password and want to check if the password is valid, you have to do a login to the domain. There is no way of just “checking if the password is correct”.
Login means: if there is a security policy “lock account if more than 3 invalid logins”, the account will be locked out checking with wrong password, even if you “only want to check the user+password”.
Using .NET Directory Service functions
I assume here that the process is either run by a human account as a normal program, or the program is a Windows service or a scheduled task which runs under a domain “technical user” account. In this case, you do not need to provide credentials for using the AD functions. If accessing other trusting AD domains, this is also true.
If you want to login to a “foreign domain”, and there is no trust, you need to provide a username+password (as in your code).
"Manually" authenticating a user
Normally, this should not be needed. Example: ASP.NET intranet usage. The user access a web application on the current domain or trusting domain, the authentication is done “in the background” by browser and IIS (if integrated Windows authentication is on). So you never need to handle user passwords in the application.
I don’t see many use cases where a password is handled by code.
One may that your program is a helper tool for storing emergency user accounts/passwords. And you want to check periodically if these accounts are valid.
This is a simple way to check:
using System.DirectoryServices.AccountManagement;
...
PrincipalContext principalContext =
new PrincipalContext(ContextType.Domain, "192.168.1.1");
bool userValid = principalContext.ValidateCredentials(name, password);
One can also use the older, raw ADSI functions:
using System.DirectoryServices;
....
bool userOk = false;
string realName = string.Empty;
using (DirectoryEntry directoryEntry =
new DirectoryEntry"LDAP://192.168.1.1/DC=ad,DC=local", name, password))
{
using (DirectorySearcher searcher = new DirectorySearcher(directoryEntry))
{
searcher.Filter = "(samaccountname=" + name + ")";
searcher.PropertiesToLoad.Add("displayname");
SearchResult adsSearchResult = searcher.FindOne();
if (adsSearchResult != null)
{
if (adsSearchResult.Properties["displayname"].Count == 1)
{
realName = (string)adsSearchResult.Properties["displayname"][0];
}
userOk = true;
}
}
}
If your real requirement is actually a validity check of user+password, you can do it in one of these ways.
However, if it is a "normal application", which just wants to check if the entered credentials are valid, you should rethink your logic. In this case, you better should rely on the single sign on capabilities of AD.
If there are further questions, please comment.
b. Remote ActiveDirectory machine's username and password.
This sounds a bit unclear. I assume you mean "a username and corresponding password in the remote domain".
There is also the concept of a machine account, which is the hostname appended with $. But that's another topic.
Creating new user
Option 1
using (DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://192.168.1.1/CN=Users,DC=ad,DC=local",
name, password))
{
using (DirectoryEntry newUser = directoryEntry.Children.Add("CN=CharlesBarker", "user"))
{
newUser.Properties["sAMAccountName"].Value = "CharlesBarker";
newUser.Properties["givenName"].Value = "Charles";
newUser.Properties["sn"].Value = "Barker";
newUser.Properties["displayName"].Value = "CharlesBarker";
newUser.Properties["userPrincipalName"].Value = "CharlesBarker";
newUser.CommitChanges();
}
}
Option 2
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, "192.168.1.1",
"CN=Users,DC=ad,DC=local", name, password))
{
using (UserPrincipal userPrincipal = new UserPrincipal(principalContext))
{
userPrincipal.Name = "CharlesBarker";
userPrincipal.SamAccountName = "CharlesBarker";
userPrincipal.GivenName = "Charles";
userPrincipal.Surname = "Barker";
userPrincipal.DisplayName = "CharlesBarker";
userPrincipal.UserPrincipalName = "CharlesBarker";
userPrincipal.Save();
}
}
I leave as an exercise to you to find out which attribute goes into which User dialog entry field :-)
I'm trying to query a domain to determine if:
User is a valid user (and has the correct password)
User is enabled
User belongs to group x
My development machine does not belong to this domain.
I want to specify the username and password via my application
I'm using the System.DirectoryServices.AccountManagement namespace as this seems to be the most efficient way doing this, however I've struggling to get even the most basic of information out of my domain controller.
I can explore LDAP via another tool.
First test is to collect user information, the code below returns null on user.
The user however is valid.
What am I doing wrong?
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "server","CN=Users,DC=doom,DC=home", "ldapuser","password");
// get user contect
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.Name, username);
//is user locked?
var locked = user.Enabled;
Update:
Having defined the bind method as below, I now receive error
"Information about the domain could not be retrieved (1355)."
var ctx = new PrincipalContext(ContextType.Domain, "server", "DC=doom,DC=home", ContextOptions.SimpleBind, "ldapuser", "password");
Sorted.
This answer resolves the two issues I came across when attempting to connect to a domain controller that I am not a member of.
This article get me the final answer:
http://elegantcode.com/2009/03/21/one-scenario-where-the-systemdirectoryservices-accountmanagement-api-falls-down/
you need to define the Bind in the context (i.e. ContextOptions.SimpleBind)
You must set up the domain server in your Network adaptors DNS settings as the first DNS server to use.
I can now connect to my AD and collect data.
I've been searching for solutions on this for a while now, but each thread a dead-end unfortunately.
I'm working on a C#/ASP.net web app that will only be used internally by our company. Anonymous access has been switched off both in IIS and my web.config file forcing IIS to use the windows authenticated user (Active Dir user).
My problem is that the following code works perfectly to get the required (or any) AD user:
using System.DirectoryServices.AccountManagement;
... other code ...
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "mydomain", "someADuser", "someADuserpassword");
UserPrincipal winuser = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, "samaccountname");
"someADuser" used in the PrincipalContext above is the current logged in user through windows, thus authenticated and a valid AD user. Using the following code (with the exact same user still logged in) gives me a "Logon failure: unknown user name or bad password" error:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "mydomain");
UserPrincipal winuser = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, "samaccountname");
It seems UserPrincipal.FindByIdentity doesn't use the logged in user's validated credentials for some reason if it's not specified in the PrincipalContext object - something I don't want to do.
Is it possible that the ctx aren't picking up the logged in Windows users for some reason, even if the necessary settings (i hope) is added to web.config :
<authentication mode="Windows"/>
<authorization>
<deny users="?"/>
</authorization>
And Anonymous access is completely disabled in IIS?
It seems UserPrincipal.FindByIdentity doesn't use the logged in user's validated credentials for some reason if it's not specified in the PrincipalContext object - something I don't want to do.
UserPrincipal.FindByIdentity doesn't care about the user's credentials at all. You're just performing a look up to see if the account exists. The reason you're getting an error is because the default user credentials (i.e. the identity that your web application is running as) doesn't have access to the directory, so it can't perform the look up. When you pass in the client's credentials to the PrincipalContext then the problem goes away because your client has a valid AD account with access to the directory.
You should investigate which identity is being used to run the application pool and make sure it has access to the directory.
Quite annoying as I though if anonymous access was turned off, the current principal would default to the user logged in to windows. It turns out it's not as indicated by #RogerN.
Using the following statement as mentioned by #TheKingDave, it basically impersonates the user logged in to windows and makes the current thread run on it's principal rather than the "ASP" (in my case) account.
Because all the users on our domain has query/read access to Active Directory, this shouldn't be a problem to get more detail on them, which is what I wanted in the first place.
The end code (was testing):
System.Web.HttpContext.Current.Request.LogonUserIdentity.Impersonate();
ctx = new PrincipalContext(ContextType.Domain, System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName);
UserPrincipal winuser = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, "whicheveraccount");
Hope it helps some1 in future! thx! ;-)
I did a lot of search/research to resolve the issue, but nothing worked, at last, all what I did was to add # to the servername, container, username & password like below:
app.CreatePerOwinContext(() => new PrincipalContext(ContextType.Domain, #"abc.net", #"OU=Customers,DC=abc,DC=net", ContextOptions.SimpleBind, #"abcnet\authuser", #"!$%MyPassword"));
And it worked. doh!
So I found out how to pass the impersonated identity. I had a situation where, as an admin, I wanted to unlock a user account automatically. I had to get the user names into variables and pass them in to the function like this:
string accountname = #"domain\username";
string admin = "adminusername";
string domain = Environment.UserDomainName;
string password = "password";
string dc = "WIN2K8DC1"; // example host name of domain controller, could use IP
// This determines the domain automatically, no need to specify
// Use the constructor that takes the domain controller name or IP,
// admin user name, and password
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, dc, admin, password);
UserPrincipal winuser = UserPrincipal.FindByIdentity(ctx, accountname)
if (winuser != null)
{
WindowsImpersonationContext wic = Impersonation.doImpersonation(admin, domain, password); //class/function that does the logon of the user and returns the WIC
if (wic != null)
{
winuser.UnlockAccount();
}
}
Impersonation class functionality that can be used to return a WIC can be found on MSDN:
https://msdn.microsoft.com/en-us/library/w070t6ka(v=vs.110).aspx