Skip to content

Commit

Permalink
Merge pull request #14 from bitai-cs/fix/create-ms-ad-user-account
Browse files Browse the repository at this point in the history
Fix create Ms AD user account.
  • Loading branch information
bitai-cs authored Jan 27, 2023
2 parents af4b74f + 9a8e5d1 commit 3f322cb
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 41 deletions.
87 changes: 85 additions & 2 deletions client/LDAPWebApi.Client.Demo/Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Net.Http.Headers;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Bitai.LDAPHelper.DTO;
using Bitai.LDAPWebApi.DTO;
using Bitai.WebApi.Client;
using Serilog;
Expand All @@ -11,8 +13,8 @@ namespace Bitai.LDAPWebApi.Clients.Demo
{
public class Program
{
//static string WebApiBaseUrl = "http://10.100.54.40:8077/Visiva.LDAPWebApi";
static string WebApiBaseUrl = "https://localhost:5101";
static string WebApiBaseUrl = "http://10.100.54.40:8077/Visiva.LDAPWebApi";
//static string WebApiBaseUrl = "https://localhost:5101";

static bool WebApiRequiresAccessToken = false;

Expand Down Expand Up @@ -46,6 +48,10 @@ static async Task Main(string[] args)

await CatalogTypesClient_GetAllAsync();

await DirectoryClient_CreateMsADUserAccountAsync();

await DirectoryClient_SetMsADUserAccountPassword();

await AuthenticationsClient_AccountAuthenticationAsync();

await DirectoryClient_SearchByIdentifierAsync();
Expand Down Expand Up @@ -99,6 +105,83 @@ static async Task CatalogTypesClient_GetAllAsync()
}
}

static async Task DirectoryClient_CreateMsADUserAccountAsync()
{
try
{
var client = new LDAPDirectoryWebApiClient(WebApiBaseUrl, Selected_LDAPServerProfile, false, ClientCredentials);

LogInfoOfType(client.GetType());
NewBlankLines(1);

var newUserAccount = new LDAPMsADUserAccount
{
DistinguishedNameOfContainer = "OU=TEST_RPA_MDA,OU=ADM,OU=CERTUS,DC=certus,DC=edu,DC=pe",
Cn = "Victor German Bastidas Gonzales",
DisplayName = "Victor German Bastidas Gonzales (SDN)",
ObjectClass = new string[] { "user" },
SAMAccountName = "vbastidas01",
UserAccountControl = "NORMAL_ACCOUNT,DONT_EXPIRE_PASSWORD",
Password = "rpa2023@@"
};
LogInfo(newUserAccount);
NewBlankLines(1);

LogInfo($"{nameof(client.CreateMsADUserAccountAsync)}...");
var httpResponse = await client.CreateMsADUserAccountAsync(newUserAccount, RequestLabel, WebApiRequiresAccessToken);
if (!httpResponse.IsSuccessResponse)
{
client.ThrowClientRequestException("Error al realizar la solicitud", httpResponse);
}
else
{
var result = await client.GetDTOFromResponseAsync<LDAPHelper.DTO.LDAPCreateMsADUserAccountResult>(httpResponse);

LogInfo(result);
}
}
catch (WebApiRequestException ex)
{
LogWebApiRequestError(ex);
}
}

static async Task DirectoryClient_SetMsADUserAccountPassword()
{
try
{
var client = new LDAPDirectoryWebApiClient(WebApiBaseUrl, Selected_LDAPServerProfile, false, ClientCredentials);

LogInfoOfType(client.GetType());
NewBlankLines(1);

var credential = new LDAPCredential
{
UserAccount = "CERTUS\\vbastidas01",
Password = "17viko@@"
};
LogInfo(credential);
NewBlankLines(1);

LogInfo($"{nameof(client.SetMsADUserAccountPassword)}...");
var httpResponse = await client.SetMsADUserAccountPassword("vbastidas01", credential, EntryAttribute.sAMAccountName, RequestLabel, WebApiRequiresAccessToken);
if (!httpResponse.IsSuccessResponse)
{
client.ThrowClientRequestException("Error al realizar la solicitud", httpResponse);
}
else
{
var result = await client.GetDTOFromResponseAsync<LDAPHelper.DTO.LDAPPasswordUpdateResult>(httpResponse);

LogInfo(result);
}
}
catch (WebApiRequestException ex)
{
LogWebApiRequestError(ex);
}
}

static async Task AuthenticationsClient_AccountAuthenticationAsync()
{
try
Expand Down
10 changes: 5 additions & 5 deletions client/LDAPWebApi.Client/Bitai.LDAPWebApi.Clients.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Bitai.LDAPWebApi.Clients</RootNamespace>
<Version>6.1.2</Version>
<Version>6.1.3</Version>
<Authors>Viko Bastidas (BITAI)</Authors>
<Company>BITAI</Company>
<Product>LDAP Web API</Product>
<Description>Library to make client requests to Bitai LDAP Web API.</Description>
<Copyright></Copyright>
<PackageIcon>api1_64.png</PackageIcon>
<AssemblyVersion>6.1.2.7</AssemblyVersion>
<FileVersion>6.1.2.7</FileVersion>
<AssemblyVersion>6.1.3.7</AssemblyVersion>
<FileVersion>6.1.3.7</FileVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageProjectUrl>https://github.com/bitai-cs/LDAPWebApi</PackageProjectUrl>
<RepositoryUrl>https://github.com/bitai-cs/LDAPWebApi</RepositoryUrl>
Expand All @@ -31,9 +31,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Bitai.LDAPHelper.DTO" Version="6.1.9" />
<PackageReference Include="Bitai.LDAPHelper.DTO" Version="6.1.10" />
<PackageReference Include="Bitai.LDAPWebApi.DTO" Version="6.1.1" />
<PackageReference Include="Bitai.WebApi.Helpers" Version="6.0.4" />
<PackageReference Include="Bitai.WebApi.Helpers" Version="6.0.5" />
<PackageReference Include="IdentityModel" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
</ItemGroup>
Expand Down
61 changes: 31 additions & 30 deletions client/LDAPWebApi.Client/LDAPAuthenticationsWebApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@

namespace Bitai.LDAPWebApi.Clients
{
/// <summary>
/// Client that makes requests to the authentication controller
/// of the LDAP Web Api.
/// </summary>
public class LDAPAuthenticationsWebApiClient : LDAPWebApiBaseClient
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="ldapWebApiBaseUrl">LDAP Web Api base URL.</param>
/// <param name="ldapServerProfile">LDAP Server Profile Id.</param>
/// <param name="useLdapServerGlobalCatalog">Whether or not the global catalog of the LDAP server will be used; otherwise the local catalog of the LDAP server will be used.</param>
/// <param name="clientCredential">Client credentials to request an access token from the Identity Server. This access token will be sent in the HTTP authorization header as Bearer Token.</param>
public LDAPAuthenticationsWebApiClient(string ldapWebApiBaseUrl, string ldapServerProfile, bool useLdapServerGlobalCatalog, WebApiClientCredential? clientCredential = null) : base(ldapWebApiBaseUrl, ldapServerProfile, useLdapServerGlobalCatalog, clientCredential)
{
}
/// <summary>
/// Client that makes requests to the authentication controller
/// of the LDAP Web Api.
/// </summary>
public class LDAPAuthenticationsWebApiClient : LDAPWebApiBaseClient
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="ldapWebApiBaseUrl">LDAP Web Api base URL.</param>
/// <param name="ldapServerProfile">LDAP Server Profile Id.</param>
/// <param name="useLdapServerGlobalCatalog">Whether or not the global catalog of the LDAP server will be used; otherwise the local catalog of the LDAP server will be used.</param>
/// <param name="clientCredential">Client credentials to request an access token from the Identity Server. This access token will be sent in the HTTP authorization header as Bearer Token.</param>
public LDAPAuthenticationsWebApiClient(string ldapWebApiBaseUrl, string ldapServerProfile, bool useLdapServerGlobalCatalog, WebApiClientCredential? clientCredential = null) : base(ldapWebApiBaseUrl, ldapServerProfile, useLdapServerGlobalCatalog, clientCredential)
{
}



Expand All @@ -34,19 +34,20 @@ public LDAPAuthenticationsWebApiClient(string ldapWebApiBaseUrl, string ldapServ
/// <param name="cancellationToken">See <see cref="CancellationToken"/>.</param>
/// <returns><see cref="IHttpResponse"/></returns>
public async Task<IHttpResponse> AccountAuthenticationAsync(Bitai.LDAPHelper.DTO.LDAPDomainAccountCredential ldapCredential, string? requestLabel = null, bool setBearerToken = true, CancellationToken cancellationToken = default)
{
var uri = $"{WebApiBaseUrl}/api/{LDAPServerProfile}/{LDAPServerCatalogTypes.GetCatalogTypeName(UseLDAPServerGlobalCatalog)}/{ControllerNames.AuthenticationsController}?requestLabel={requestLabel}";

using (var httpClient = await CreateHttpClient(setBearerToken))
{
var content = new ObjectContent<LDAPHelper.DTO.LDAPDomainAccountCredential>(ldapCredential, new JsonMediaTypeFormatter());
{
var uri = $"{WebApiBaseUrl}/api/{LDAPServerProfile}/{LDAPServerCatalogTypes.GetCatalogTypeName(UseLDAPServerGlobalCatalog)}/{ControllerNames.AuthenticationsController}?requestLabel={requestLabel}";

var responseMessage = await httpClient.PostAsync(uri, content, cancellationToken);
if (!responseMessage.IsSuccessStatusCode)
return await responseMessage.ToUnsuccessfulHttpResponseAsync();
else
return await responseMessage.ToSuccessfulHttpResponseAsync<LDAPHelper.DTO.LDAPDomainAccountAuthenticationResult>();
}
}
}
using (var httpClient = await CreateHttpClient(setBearerToken))
{
using (var content = new ObjectContent<LDAPHelper.DTO.LDAPDomainAccountCredential>(ldapCredential, new JsonMediaTypeFormatter()))
{
var responseMessage = await httpClient.PostAsync(uri, content, cancellationToken);
if (!responseMessage.IsSuccessStatusCode)
return await responseMessage.ToUnsuccessfulHttpResponseAsync();
else
return await responseMessage.ToSuccessfulHttpResponseAsync<LDAPHelper.DTO.LDAPDomainAccountAuthenticationResult>();
}
}
}
}
}
62 changes: 62 additions & 0 deletions client/LDAPWebApi.Client/LDAPDirectoryWebApiClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Bitai.LDAPHelper.DTO;
using Bitai.LDAPWebApi.DTO;
using Bitai.WebApi.Client;
using System.Net.Http.Formatting;

namespace Bitai.LDAPWebApi.Clients
{
Expand All @@ -21,6 +23,7 @@ public LDAPDirectoryWebApiClient(string ldapWebApiBaseUrl, string ldapServerProf




#region GET /API/{SERVERPROFILE}/{CATALOGTYPE}/DIRECTORY/{identifier}
/// <summary>
/// Send a GET request to the route {serverProfile}/{catalogType}/[controller]/{identifier} of LDAP Web Api Directory controller.
Expand Down Expand Up @@ -72,6 +75,7 @@ public async Task<IHttpResponse> SearchByIdentifierAsync(string identifier, Entr
}
#endregion //GET /API/{SERVERPROFILE}/{CATALOGTYPE}/DIRECTORY/{identifier}


#region GET /API/{SERVERPROFILE}/{CATALOGTYPE}/DIRECTORY/filterBy
/// <summary>
/// Send a GET request to the route {serverProfile}/{catalogType}/[controller]/[action] of LDAP Web Api Directory controller.
Expand Down Expand Up @@ -145,5 +149,63 @@ public async Task<IHttpResponse> SearchByFiltersAsync(EntryAttribute filterAttri
}
}
#endregion GET /API/{SERVERPROFILE}/{CATALOGTYPE}/DIRECTORY/filterBy


#region POST /API/{SERVERPROFILE}/{CATALOGTYPE}/DIRECTORY/MsADUsers
/// <summary>
/// Create a Ms AS user account
/// </summary>
/// <param name="newUSerAccount">Data of the new user account. See <see cref="LDAPMsADUserAccount"/>.</param>
/// <param name="requestLabel">Custom tag to identify the request and mark the data returned in the response.</param>
/// <param name="setBearerToken">Whether or not to request and / or assign the access token in the authorization HTTP header.</param>
/// <param name="cancellationToken">See <see cref="CancellationToken"/>.</param>
/// <returns>An <see cref="IHttpResponse{TContent}"/> that encapsulates an <see cref="IHttpResponse"/>.</returns>
public async Task<IHttpResponse> CreateMsADUserAccountAsync(LDAPMsADUserAccount newUSerAccount, string? requestLabel = null, bool setBearerToken = true, CancellationToken cancellationToken = default)
{
var uri = $"{WebApiBaseUrl}/api/{LDAPServerProfile}/{LDAPServerCatalogTypes.GetCatalogTypeName(UseLDAPServerGlobalCatalog)}/{ControllerNames.DirectoryController}/MsADUsers?requestLabel={requestLabel}";

using (var httpClient = await CreateHttpClient(setBearerToken))
{
using (var content = new ObjectContent<LDAPMsADUserAccount>(newUSerAccount, new JsonMediaTypeFormatter()))
{
var responseMessage = await httpClient.PostAsync(uri, content, cancellationToken);
if (!responseMessage.IsSuccessStatusCode)
return await responseMessage.ToUnsuccessfulHttpResponseAsync();
else
return await responseMessage.ToSuccessfulHttpResponseAsync<LDAPCreateMsADUserAccountResult>();
}
}
}
#endregion


#region POST /API/{SERVERPROFILE}/{CATALOGTYPE}/DIRECTORY/USERS/{IDENTIFIER}/CREDENTIAL
/// <summary>
/// Set password to a Ms AD user account
/// </summary>
/// <param name="identifier">User account identifier. Can be the value of a SAMAccountName or DistinguishedName attribute</param>
/// <param name="credential">The <see cref="LDAPCredential"/> that contains the password to be assigned to the user account. It must be coherent with the value of the <paramref name="identifier"/> (route) parameter.</param>
/// <param name="identifierAttribute">The LDAP <see cref="EntryAttribute"/> type to which the <paramref name="identifier"/> (route) parameter relates.</param>
/// <param name="requestLabel">Custom tag to identify the request and mark the data returned in the response.</param>
/// <param name="setBearerToken">Whether or not to request and / or assign the access token in the authorization HTTP header.</param>
/// <param name="cancellationToken">See <see cref="CancellationToken"/>.</param>
/// <returns>An <see cref="IHttpResponse{TContent}"/> that encapsulates an <see cref="IHttpResponse"/>.</returns>
public async Task<IHttpResponse> SetMsADUserAccountPassword(string identifier, LDAPCredential credential, EntryAttribute? identifierAttribute = EntryAttribute.sAMAccountName, string? requestLabel = null, bool setBearerToken = true, CancellationToken cancellationToken = default)
{
var uri = $"{WebApiBaseUrl}/api/{LDAPServerProfile}/{LDAPServerCatalogTypes.GetCatalogTypeName(UseLDAPServerGlobalCatalog)}/{ControllerNames.DirectoryController}/Users/{identifier}/Credential?identifierAttribute={GetOptionalEntryAttributeName(identifierAttribute)}&requestLabel={requestLabel}";

using (var httpClient = await CreateHttpClient(setBearerToken))
{
using (var content = new ObjectContent<LDAPCredential>(credential, new JsonMediaTypeFormatter()))
{
var responseMessage = await httpClient.PostAsync(uri, content, cancellationToken);
if (!responseMessage.IsSuccessStatusCode)
return await responseMessage.ToUnsuccessfulHttpResponseAsync();
else
return await responseMessage.ToSuccessfulHttpResponseAsync<LDAPPasswordUpdateResult>();
}
}
}
#endregion
}
}
4 changes: 2 additions & 2 deletions src/LDAPWebApi/Bitai.LDAPWebApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="6.0.5" />
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="6.0.5" />
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="6.0.3" />
<PackageReference Include="Bitai.LDAPHelper" Version="6.1.11" />
<PackageReference Include="Bitai.WebApi.Helpers" Version="6.0.4" />
<PackageReference Include="Bitai.LDAPHelper" Version="6.1.13" />
<PackageReference Include="Bitai.WebApi.Helpers" Version="6.0.5" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.0" />
<PackageReference Include="Serilog.AspNetCore" Version="6.1.0" />
Expand Down
45 changes: 43 additions & 2 deletions src/LDAPWebApi/Controllers/DirectoryController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,48 @@ public async Task<ActionResult<LDAPCreateMsADUserAccountResult>> CreateUserAccou
throw new BadRequestException("Cannot create user accounts in the global catalog of the LDAP server.");

var clientConfig = GetLdapClientConfiguration(serverProfile, IsGlobalCatalog(catalogType));

var accountManager = new LDAPHelper.AccountManager(clientConfig);
accountManager.InitializeMissingMsADUserAccountDN(newUserAccount);

#region Check if DN already exists
var onlyUsersFilterCombiner = LDAPHelper.QueryFilters.AttributeFilterCombiner.CreateOnlyUsersFilterCombiner();
var attributeFilter = new LDAPHelper.QueryFilters.AttributeFilter(EntryAttribute.distinguishedName, new LDAPHelper.QueryFilters.FilterValue(newUserAccount.DistinguishedName));
var searchFilterCombiner = new LDAPHelper.QueryFilters.AttributeFilterCombiner(false, true, new List<LDAPHelper.QueryFilters.ICombinableFilter> { onlyUsersFilterCombiner, attributeFilter });

var searcher = new LDAPHelper.Searcher(clientConfig);
var searchResult = await searcher.SearchEntriesAsync(searchFilterCombiner, RequiredEntryAttributes.Minimun, requestLabel);
if (!searchResult.IsSuccessfulOperation)
{
if (searchResult.HasErrorObject)
throw searchResult.ErrorObject;
else
throw new Exception(searchResult.OperationMessage);
}
if (searchResult.Entries.Count() != 0)
{
throw new ConflictException($"There is already an entry in the AD with the {EntryAttribute.distinguishedName} equal to {newUserAccount.DistinguishedName}");
}
#endregion

#region Check if samAccountName already exists
attributeFilter = new LDAPHelper.QueryFilters.AttributeFilter(EntryAttribute.sAMAccountName, new LDAPHelper.QueryFilters.FilterValue(newUserAccount.SAMAccountName));
searchFilterCombiner = new LDAPHelper.QueryFilters.AttributeFilterCombiner(false, true, new List<LDAPHelper.QueryFilters.ICombinableFilter> { onlyUsersFilterCombiner, attributeFilter });

searchResult = await searcher.SearchEntriesAsync(searchFilterCombiner, RequiredEntryAttributes.Minimun, requestLabel);
if (!searchResult.IsSuccessfulOperation)
{
if (searchResult.HasErrorObject)
throw searchResult.ErrorObject;
else
throw new Exception(searchResult.OperationMessage);
}
if (searchResult.Entries.Count() != 0)
{
throw new ConflictException($"There is already an entry in the AD with the {EntryAttribute.sAMAccountName } equal to {newUserAccount.SAMAccountName}");
}
#endregion

var createUserAccountResult = await accountManager.CreateUserAccountForMsAD(newUserAccount, requestLabel);
if (!createUserAccountResult.IsSuccessfulOperation)
{
Expand Down Expand Up @@ -205,7 +246,7 @@ public async Task<ActionResult<LDAPCreateMsADUserAccountResult>> CreateUserAccou
/// <returns><see cref="LDAPPasswordUpdateResult"/></returns>
[HttpPost]
[Route("{serverProfile:ldapSvrPf}/{catalogType:ldapCatType}/[controller]/Users/{identifier}/Credential")]
public async Task<ActionResult<LDAPPasswordUpdateResult>> SetUserCredential(
public async Task<ActionResult<LDAPPasswordUpdateResult>> SetUserAccountCredentialForMsAD(
[FromRoute] string serverProfile,
[FromRoute] string catalogType,
[FromRoute] string identifier,
Expand Down Expand Up @@ -268,7 +309,7 @@ public async Task<ActionResult<LDAPPasswordUpdateResult>> SetUserCredential(

var dnCredential = new LDAPDistinguishedNameCredential(entry.distinguishedName, credential.Password);
var accountManager = new LDAPHelper.AccountManager(GetLdapClientConfiguration(serverProfile, IsGlobalCatalog(catalogType)));
var pwdUpdateResult = await accountManager.SetAccountPassword(dnCredential, requestLabel);
var pwdUpdateResult = await accountManager.SetUserAccountPasswordForMsAD(dnCredential, requestLabel);

if (!pwdUpdateResult.IsSuccessfulOperation)
{
Expand Down

0 comments on commit 3f322cb

Please sign in to comment.