I'm gonna build a webpart for creating user in active directory .
For creating user account i use method like this :
public string CreateUserAccount(string ldapPath, string userName,
string userPassword)
{
try
{
string oGUID = string.Empty;
string connectionPrefix = "LDAP://" + ldapPath;
DirectoryEntry dirEntry = new DirectoryEntry(connectionPrefix);
DirectoryEntry newUser = dirEntry.Children.Add
("CN=" + userName, "user");
newUser.Properties["samAccountName"].Value = userName;
newUser.CommitChanges();
oGUID = newUser.Guid.ToString();
newUser.Invoke("SetPassword", new object[] { userPassword });
newUser.CommitChanges();
dirEntry.Close();
newUser.Close();
}
catch (System.DirectoryServices.DirectoryServicesCOMException E)
{
//DoSomethingwith --> E.Message.ToString();
}
return oGUID;
}
When executing this method the following error occurred:
"The server is not operational"
say we have active directory installed with domain TestDomain.com and you have a OU ( Organization Unit ) called USERS and you have a user in it called TestUser
so we can saye the following
ldapDomain: the fully qualified domain as TestDomain.com or dc=contoso,dc=com
objectPath: the fully qualified path to the object: CN=TestUser, OU=USERS, DC=TestDomain, DC=com
userDn: the distinguishedName of the user: CN=TestUser, OU=USERS, DC=TestDomain, DC=com
in creating user you should determine where you want to create by determining its path ( ldap path )
In our sample we can consider it as below :
string ldapPath = "LDAP://OU=USERS, DC=TestDomain, DC=com"
For more information check the following links :
http://www.selfadsi.org/ldap-path.htm
http://www.informit.com/articles/article.aspx?p=101405&seqNum=7
http://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.path.aspx
Using System.DirectoryServices
To use this namespace you need to add reference System.DirectoryServices.dll
DirectoryEntry ouEntry = new DirectoryEntry("LDAP://OU=TestOU,DC=TestDomain,DC=local");
for (int i = 3; i < 6; 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)
{
}
}
Using System.DirectoryServices.AccountManagement
To use this namespace you need to add reference System.DirectoryServices.AccountManagement.dll
PrincipalContext ouContex = new PrincipalContext(ContextType.Domain, "TestDomain.local", "OU=TestOU,DC=TestDomain,DC=local");
for (int i = 0; i < 3; i++)
{
try
{
UserPrincipal up = new UserPrincipal(ouContex);
up.SamAccountName = "TestUser" + i;
up.SetPassword("password");
up.Enabled = true;
up.ExpirePasswordNow();
up.Save();
}
catch (Exception ex)
{
}
}
Related
I have this action method inside my ASP.NET MVC-5 .net 4.6:-
public ActionResult UsersInfo2()
{
List<DomainContext> results = new List<DomainContext>();
try
{
// create LDAP connection object
DirectoryEntry myLdapConnection = createDirectoryEntry();
string ADServerName = System.Web.Configuration.WebConfigurationManager.AppSettings["ADServerName"];
string ADusername = System.Web.Configuration.WebConfigurationManager.AppSettings["ADUserName"];
string ADpassword = System.Web.Configuration.WebConfigurationManager.AppSettings["ADPassword"];
using (var context = new DirectoryEntry("LDAP://mydomain.com:389/DC=mydomain,DC=com", ADusername, ADpassword))
using (var search = new DirectorySearcher(context))
{
SearchResult r = search.FindOne();
ResultPropertyCollection fields = r.Properties;
foreach (String ldapField in fields.PropertyNames)
string temp;
foreach (Object myCollection in fields[ldapField])
temp = String.Format("{0,-20} : {1}",
ldapField, myCollection.ToString());
}
}
using (var context = new PrincipalContext(ContextType.Domain, "mydomain.com", ADusername, ADpassword))
{
bool isvalid = context.ValidateCredentials("*******", "****************");
}
}
catch (Exception e)
{
Console.WriteLine("Exception caught:\n\n" + e.ToString());
}
return View(results);
}
so after around one day of testing i realize that for the DirectoryEntry I need to pass the server/ldap as follow ("LDAP://mydomain.com:389/DC=mydomain,DC=com", ADusername, ADpassword)) , while for the PrincipalContext we need to pass it as follow:- (ContextType.Domain, "mydomain.com", ADusername, ADpassword)).. so i can not pass the ldap inside the PrincipalContext nor the servrname only inside the DirectoryEntry .. so is this the case? or i am doing things wrongly ?
Thanks
You are correct.
The System.DirectoryServices.AccountManagement namespace (PrincipalContext, UserPrincipal, etc.) was created to simplify things. However, it just uses the System.DirectoryServices namespace (DirectoryEntry, etc.) in the background. (except for ValidateCredentials, which uses System.DirectoryServices.Protocols.LdapConnection).
I prefer to always use DirectoryEntry and friends because it gives me more control over performance. That's something I wrote an article about: Active Directory: Better performance
further to this question, i have the same problem. PubFolder on Prem , users in O365
I have fetched and added the routing headers from Glen's post but still get the error
GetToken works...
https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth
GetX headers works...
https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/public-folder-access-with-ews-in-exchange
--->> ewsClient.FindFolders(WellKnownFolderName.PublicFoldersRoot, new FolderView(10))
Microsoft.Exchange.WebServices.Data.ServiceResponseException: 'There are no public folder servers available.'
static async System.Threading.Tasks.Task Test3()
{
string ClientId = ConfigurationManager.AppSettings["appId"];
string TenantId = ConfigurationManager.AppSettings["tenantId"];
string secret = ConfigurationManager.AppSettings["clientSecret"];
string uMbox = ConfigurationManager.AppSettings["userId"];
string uPwd = ConfigurationManager.AppSettings["userPWD"];
// Using Microsoft.Identity.Client 4.22.0
//https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth//
var cca = ConfidentialClientApplicationBuilder
.Create(ClientId)
.WithClientSecret(secret)
.WithTenantId(TenantId)
.Build();
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
try
{
var authResult = await cca.AcquireTokenForClient(ewsScopes)
.ExecuteAsync();
// Configure the ExchangeService with the access token
var ewsClient = new ExchangeService();
ewsClient.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
ewsClient.ImpersonatedUserId =
new ImpersonatedUserId(ConnectingIdType.SmtpAddress, uMbox);
AutodiscoverService autodiscoverService = GetAutodiscoverService(uMbox, uPwd);
GetUserSettingsResponse userResponse = GetUserSettings(autodiscoverService, uMbox, 3, UserSettingName.PublicFolderInformation, UserSettingName.InternalRpcClientServer);
string pfAnchorHeader= userResponse.Settings[UserSettingName.PublicFolderInformation].ToString();
string pfMailboxHeader = userResponse.Settings[UserSettingName.InternalRpcClientServer].ToString(); ;
// Make an EWS call
var folders = ewsClient.FindFolders(WellKnownFolderName.MsgFolderRoot, new FolderView(10));
foreach (var folder in folders)
{
Console.WriteLine($"Folder: {folder.DisplayName}");
}
//get Public folder root
//Include x-anchormailbox header
Console.WriteLine("X-AnchorMailbox value for public folder hierarchy requests: {0}", pfAnchorHeader);
Console.WriteLine("X-PublicFolderMailbox value for public folder hierarchy requests: {0}", pfMailboxHeader);
//var test3 = GetMailboxGuidAddress(ewsClient, pfAnchorHeader, pfMailboxHeader, uMbox);
///https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-route-public-folder-content-requests <summary>
ewsClient.HttpHeaders.Add("X-AnchorMailbox", userResponse.Settings[UserSettingName.PublicFolderInformation].ToString());
//ewsClient.HttpHeaders.Add("X-AnchorMailbox", "SharedPublicFolder#contoso.com");
ewsClient.HttpHeaders.Add("X-PublicFolderMailbox", userResponse.Settings[UserSettingName.InternalRpcClientServer].ToString());
try
{
var pubfolders = ewsClient.FindFolders(WellKnownFolderName.PublicFoldersRoot, new FolderView(10));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
foreach (var folder in folders)
{
Console.WriteLine($"Folder: {folder.DisplayName}");
}
}
catch (MsalException ex)
{
Console.WriteLine($"Error acquiring access token: {ex}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex}");
}
if (System.Diagnostics.Debugger.IsAttached)
{
Console.WriteLine("Hit any key to exit...");
Console.ReadKey();
}
}
public static AutodiscoverService GetAutodiscoverService(string username, string pwd)
{
AutodiscoverService adAutoDiscoverService = new AutodiscoverService();
adAutoDiscoverService.Credentials = new WebCredentials(username, pwd);
adAutoDiscoverService.EnableScpLookup = true;
adAutoDiscoverService.RedirectionUrlValidationCallback = RedirectionUrlValidationCallback;
adAutoDiscoverService.PreAuthenticate = true;
adAutoDiscoverService.TraceEnabled = true;
adAutoDiscoverService.KeepAlive = false;
return adAutoDiscoverService;
}
public static GetUserSettingsResponse GetUserSettings(
AutodiscoverService service,
string emailAddress,
int maxHops,
params UserSettingName[] settings)
{
Uri url = null;
GetUserSettingsResponse response = null;
for (int attempt = 0; attempt < maxHops; attempt++)
{
service.Url = url;
service.EnableScpLookup = (attempt < 2);
response = service.GetUserSettings(emailAddress, settings);
if (response.ErrorCode == AutodiscoverErrorCode.RedirectAddress)
{
url = new Uri(response.RedirectTarget);
}
else if (response.ErrorCode == AutodiscoverErrorCode.RedirectUrl)
{
url = new Uri(response.RedirectTarget);
}
else
{
return response;
}
}
throw new Exception("No suitable Autodiscover endpoint was found.");
}
Your code won't work against an OnPrem Public folder tree as EWS in Office365 won't proxy to an OnPrem Exchange Org (even if hybrid is setup). (Outlook MAPI is a little different and allows this via versa setup but in that case it never proxies either it just makes a different connection to that store and its all the Outlook client doing this).
Because your trying to use the client credentials oauth flow for that to work onPrem you must have setup hybrid modern authentication https://learn.microsoft.com/en-us/microsoft-365/enterprise/hybrid-modern-auth-overview?view=o365-worldwide. Then you need to acquire a token with an audience set to the local OnPrem endpoint. (this is usually just your onPrem ews endpoint's host name but it should be one of the service principal names configured in your hybrid auth setup Get-MsolServicePrincipal). So in your code you would change
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
to
var ewsScopes = new string[] { "https://OnPrem.whatever.com/.default" };
which will then give you a token with an audience set for the onprem server then you need to send the EWS request to that endpoint so change that eg
ewsClient.Url = new Uri("https://OnPrem.whatever.com/EWS/Exchange.asmx");
if Hybird Modern Auth is setup then you need to default back to use Integrated or Basic Authenticaiton.
A system need single user login at a time. If tried for multiple login simultaneously the user get blocked. I have used Cookie Authentication which will manage from client browser.
Login Code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel oLoginViewModel)
{
try
{
bool Result = new UserBL().ValidateUser(oLoginViewModel.UserName, oLoginViewModel.Password);
if (Result == true)
{
FormsService.SignIn(oLoginViewModel.UserName, oLoginViewModel.RememberMe);
CreateAuthenticationTicket(oLoginViewModel.UserName);
return RedirectToLocal(Request.Form["returnUrl"]);
}
else
ViewBag.Error = "Invalid Username or Password / Due to simultaneous login you get blocked.";
return View();
}
catch (Exception ex)
{
throw ex;
}
}
public void CreateAuthenticationTicket(string username)
{
Users oUsers = new Users();
oUsers.Email = username;
oUsers.Role = "User";
int sessionid = new UserBL().GetByUserName(username).UserId;
string userData = JsonConvert.SerializeObject(oUsers);
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1,
username,
DateTime.Now,
DateTime.Now.AddYears(1), // value of time out property
false, //pass here true, if you want to implement remember me functionality
userData);
string encTicket = FormsAuthentication.Encrypt(authTicket);
var isSsl = Request.IsSecureConnection; // if we are running in SSL mode then make the cookie secure only
HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket)
{
HttpOnly = false,
Secure = isSsl,
};
faCookie.Expires = DateTime.Now.AddYears(1);
Response.Cookies.Add(faCookie);
//Login Repository Entry
LoginsRepository oLogin = new LoginsRepository();
oLogin.UserName = username;
oLogin.SessionId = sessionid.ToString();
oLogin.LoggedIn = true;
oLogin.CreatedOn = Utility.CommonFunction.DateTime_Now();
oLogin.IPAddress = HttpContext.Request.RequestContext.HttpContext.Request.ServerVariables["REMOTE_ADDR"];
oLogin.Status = En_LoginStatus.SingleUser.ToString();
new LoginRepositoryBL().Add(oLogin);
}
I'm saving every user login with their IP Address to check the user multiple login.
After login it redirects to home controller and their I checked the multiple logins logic from database table Loginsrepository which is mentioned above :
public class HomeController : CustomerBaseController
{
public ActionResult Index()
{
Users oUser = new Users();
oUser = new UserBL().getActiveUser();
// check to see if your ID in the Logins table has
// LoggedIn = true - if so, continue, otherwise, redirect to Login page.
if (new LoginRepositoryBL().IsYourLoginStillTrue(System.Web.HttpContext.Current.User.Identity.Name, oUser.UserId.ToString()))
{
// check to see if your user ID is being used elsewhere under a different session ID
if (!new LoginRepositoryBL().IsUserLoggedOnElsewhere(System.Web.HttpContext.Current.User.Identity.Name, oUser.UserId.ToString()))
{
Answers oAnswer = new Answers();
return View(oAnswer);
}
else
{
// if it is being used elsewhere, update all their
// Logins records to LoggedIn = false, except for your session ID
new LoginRepositoryBL().LogEveryoneElseOut(System.Web.HttpContext.Current.User.Identity.Name, oUser.UserId.ToString());
Answers oAnswer = new Answers();
return View(oAnswer);
}
}
else
{
oUser = new UserBL().GetByUserName(System.Web.HttpContext.Current.User.Identity.Name);
oUser.Status = En_Status.Inactive.ToString();
new UserBL().update(oUser);
FormsService.SignOut();
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Account");
}
}
}
Above methods :
public bool IsYourLoginStillTrue(string userId, string sid)
{
try
{
using (var ctx = new CnSiteEntities())
{
IEnumerable<LoginsRepository> logins = (from i in ctx.LoginsRepository
where i.LoggedIn == true &&
i.UserName == userId && i.SessionId == sid
select i).AsEnumerable();
return logins.Any();
}
}
catch (Exception)
{
throw;
}
}
public bool IsUserLoggedOnElsewhere(string userId, string sid)
{
try
{
using (var ctx = new CnSiteEntities())
{
IEnumerable<LoginsRepository> logins = (from i in ctx.LoginsRepository
where i.LoggedIn == true &&
i.UserName == userId && i.SessionId != sid
select i).AsEnumerable();
return logins.Any();
}
}
catch (Exception)
{
throw;
}
}
public void LogEveryoneElseOut(string userId, string sid)
{
try
{
using (var ctx = new CnSiteEntities())
{
IEnumerable<LoginsRepository> logins = (from i in ctx.LoginsRepository
where i.LoggedIn == true &&
i.UserName == userId &&
i.SessionId != sid // need to filter by user ID
select i).AsEnumerable();
foreach (LoginsRepository item in logins)
{
item.LoggedIn = false;
}
ctx.SaveChanges();
}
}
catch (Exception)
{
throw;
}
}
It's not working properly. It keeps it true after login even if multiple simultaneous logins. I have googled it and tried it much but I didn't get any solution.
I'm using Alfresco 5.1 Community, and i'm trying to get a property value of a current person logged for example, in the user I have:
"{http://www.someco.org/model/people/1.0}customProperty"
How can I obtain this in java?
Is a custom property, so, in http://localhost:8080/alfresco/service/api/people it does not appear. How can I do this?
I try this to obtain at least nodeRef:
protected ServiceRegistry getServiceRegistry() {
ProcessEngineConfigurationImpl config = Context.getProcessEngineConfiguration();
if (config != null) {
// Fetch the registry that is injected in the activiti spring-configuration
ServiceRegistry registry = (ServiceRegistry) config.getBeans().get(ActivitiConstants.SERVICE_REGISTRY_BEAN_KEY);
if (registry == null) {
throw new RuntimeException("Service-registry not present in ProcessEngineConfiguration beans, expected ServiceRegistry with key" + ActivitiConstants.SERVICE_REGISTRY_BEAN_KEY);
}
return registry;
}
throw new IllegalStateException("No ProcessEngineConfiguration found in active context");
}
public void writeToCatalina() {
PersonService personService = getServiceRegistry().getPersonService();
System.out.println("test");
String name = AuthenticationUtil.getFullyAuthenticatedUser();
System.out.println(name);
NodeRef personRef = personService.getPerson(name);
System.out.println(personRef);
}
But I got:
No ProcessEngineConfiguration found in active context
Help me !
You can query Alfresco using CMIS and call the API:
GET /alfresco/service/api/people/{userName}.
For first you can define the method to create the session CmisSession:
public Session getCmisSession() {
logger.debug("Starting: getCmisSession()");
// default factory implementation
SessionFactory factory = SessionFactoryImpl.newInstance();
Map<String, String> parameter = new HashMap<String, String>();
// connection settings
parameter.put(SessionParameter.ATOMPUB_URL, url + ATOMPUB_URL);
parameter.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value());
parameter.put(SessionParameter.AUTH_HTTP_BASIC, "true");
parameter.put(SessionParameter.USER, username);
parameter.put(SessionParameter.PASSWORD, password);
parameter.put(SessionParameter.OBJECT_FACTORY_CLASS, "org.alfresco.cmis.client.impl.AlfrescoObjectFactoryImpl");
List<Repository> repositories = factory.getRepositories(parameter);
return repositories.get(0).createSession();
}
Then execute the query (this method returns more than one result, you probably need to change it):
public void doQuery(String cql, int maxItems) {
Session cmisSession = getCmisSession();
OperationContext oc = new OperationContextImpl();
oc.setMaxItemsPerPage(maxItems);
ItemIterable<QueryResult> results = cmisSession.query(cql, false, oc);
for (QueryResult result : results) {
for (PropertyData<?> prop : result.getProperties()) {
logger.debug(prop.getQueryName() + ": " + prop.getFirstValue());
}
}
}
If you need to get the token, use this:
public String getAuthenticationTicket() {
try {
logger.info("ALFRESCO: Starting connection...");
RestTemplate restTemplate = new RestTemplate();
Map<String, String> params = new HashMap<String, String>();
params.put("user", username);
params.put("password", password);
Source result = restTemplate.getForObject(url + AFMConstants.URL_LOGIN_PARAM, Source.class, params);
logger.info("ALFRESCO: CONNECTED!");
XPathOperations xpath = new Jaxp13XPathTemplate();
return xpath.evaluateAsString("//ticket", result);
}
catch (RestClientException ex) {
logger.error("FATAL ERROR - Alfresco Authentication failed - getAuthenticationTicket() - " + ex );
return null;
}
catch (Exception ex) {
logger.error("FATAL ERROR - Alfresco Authentication failed - getAuthenticationTicket() - " + ex );
return null;
}
}
You need to obtain your user noderef using this API then access its properties this way!
Edit : You need to inject service registry on your bean!
String name = AuthenticationUtil.getFullyAuthenticatedUser()
You can use this. Let me know if it works.
This will give you current logged in user name and detail.
String name = AuthenticationUtil.getFullyAuthenticatedUser();
System.out.println("Current user:"+name);
PersonService personService=serviceRegistry.getPersonService();
NodeRef node=personService.getPerson(name);
NodeService nodeService=serviceRegistry.getNodeService();
Map<QName, Serializable> props=nodeService.getProperties(node);
for (Entry<QName, Serializable> entry : props.entrySet())
{
System.out.println(entry.getKey() + "/" + entry.getValue());
}
I have an intranet that gets the current logged in user through active directory. When a user is locked out they get a windows prompt to enter their username and password. Is there a way for me to catch this and redirect them to a page where they are asked to enter their credentials again or tell them that their account might be locked out and to contact the help desk?
On your application once you grab the user that is logged in do the IsAccountLocked method below
public bool IsAccountLocked(string sUserName)
{
UserPrincipal oUserPrincipal = GetUser(sUserName);
return oUserPrincipal.IsAccountLockedOut();
}
public UserPrincipal GetUser(string sUserName)
{
PrincipalContext oPrincipalContext = GetPrincipalContext();
UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(oPrincipalContext, sUserName);
return oUserPrincipal;
}
public PrincipalContext GetPrincipalContext()
{
PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain, sDomain, sDefaultOU, ContextOptions.SimpleBind, sServiceUser, sServicePassword);
return oPrincipalContext;
}
This is using System.DirectoryServices.AccountManagement for using only System.DirectoryServices you can do this
public bool IsAccountLocked(DirectoryEntry oDE)
{
return Convert.ToBoolean(oDE.InvokeGet("IsAccountLocked"));
}
public DirectoryEntry GetUser(string sUserName)
{
//Create an Instance of the DirectoryEntry
oDE = GetDirectoryObject();
//Create Instance fo the Direcory Searcher
oDS = new DirectorySearcher();
oDS.SearchRoot = oDE;
//Set the Search Filter
oDS.Filter = "(&(objectClass=user)(sAMAccountName=" + sUserName + "))";
oDS.SearchScope = SearchScope.Subtree;
oDS.PageSize = 10000;
//Find the First Instance
SearchResult oResults = oDS.FindOne();
//If found then Return Directory Object, otherwise return Null
if (oResults != null)
{
oDE = new DirectoryEntry(oResults.Path, sADUser, sADPassword, AuthenticationTypes.Secure);
return oDE;
}
else
{
return null;
}
}
private DirectoryEntry GetDirectoryObject()
{
oDE = new DirectoryEntry(sADPath, sADUser, sADPassword, AuthenticationTypes.Secure);
return oDE;
}
for a full implementation you can go to
http://anyrest.wordpress.com/2010/06/28/active-directory-c/
or
http://anyrest.wordpress.com/2010/02/01/active-directory-objects-and-c/