Add project files.

This commit is contained in:
Marcio
2023-09-20 00:18:58 -03:00
parent 3e9c6dcb61
commit 61bca1bcba
20 changed files with 614 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
+31
View File
@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.34024.191
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActiveDirectoryToRestApi", "src\ActiveDirectoryToRestApi\ActiveDirectoryToRestApi.csproj", "{2A1D2EF2-8050-48ED-AD6A-0049D800DE39}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{69C1A7CC-CE13-44EC-924A-104C576C2D34}"
ProjectSection(SolutionItems) = preProject
LICENSE = LICENSE
README.md = README.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2A1D2EF2-8050-48ED-AD6A-0049D800DE39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2A1D2EF2-8050-48ED-AD6A-0049D800DE39}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A1D2EF2-8050-48ED-AD6A-0049D800DE39}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A1D2EF2-8050-48ED-AD6A-0049D800DE39}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D76E9DAF-9C86-445C-B53D-A96BC45CD2C9}
EndGlobalSection
EndGlobal
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 zimbres
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+3
View File
@@ -0,0 +1,3 @@
# ActiveDirectoryToRestApi
Rest Api to get some data from Active Directory by LDAP
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>disable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>..\..</DockerfileContext>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.11" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="System.DirectoryServices.Protocols" Version="7.0.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,10 @@
namespace ActiveDirectoryToRestApi.Configurations;
public class LdapConfigs
{
public string Server { get; set; }
public string DomainName { get; set; }
public string User { get; set; }
public string Pass { get; set; }
public string DistinguishedName { get; set; }
}
@@ -0,0 +1,26 @@
namespace ActiveDirectoryToRestApi.Controllers;
[ApiController]
[Route("[controller]")]
public class GroupController : ControllerBase
{
private readonly ILogger<GroupController> _logger;
private readonly LdapService _ldapService;
public GroupController(ILogger<GroupController> logger, LdapService ldapService)
{
_logger = logger;
_ldapService = ldapService;
}
[HttpPost("/group")]
public ActionResult<Group> Group(string group = "mygroup")
{
var members = _ldapService.GetGroupMembers(group);
if (members == null)
{
return NotFound();
}
return members;
}
}
@@ -0,0 +1,50 @@
namespace ActiveDirectoryToRestApi.Controllers;
[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase
{
private readonly ILogger<UserController> _logger;
private readonly LdapService _ldapService;
private readonly AuthUserService _authUserService;
public UserController(ILogger<UserController> logger, LdapService ldapService, AuthUserService authUserService)
{
_logger = logger;
_ldapService = ldapService;
_authUserService = authUserService;
}
[HttpPost("/email")]
public ActionResult<User> Email(string email = "test.user@zimbres.com")
{
var user = _ldapService.GetUserByEmail(email);
if (user == null)
{
return NotFound();
}
return user;
}
[HttpPost("/username")]
public ActionResult<User> Username(string username = "testUser")
{
var user = _ldapService.GetUserByUserName(username);
if (user == null)
{
return NotFound();
}
return user;
}
[HttpPost("/authUser")]
public IActionResult AuthUser(UserLogin userLogin)
{
var status = _authUserService.AuthUser(userLogin.UserName, userLogin.Password);
if (status)
{
return Ok();
}
return Unauthorized();
}
}
+20
View File
@@ -0,0 +1,20 @@
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
RUN apt update && apt install -y --no-install-recommends libldap-2.4-2 && rm -rf /var/lib/apt/lists/*
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["src/ActiveDirectoryToRestApi/ActiveDirectoryToRestApi.csproj", "src/ActiveDirectoryToRestApi/"]
RUN dotnet restore "src/ActiveDirectoryToRestApi/ActiveDirectoryToRestApi.csproj"
COPY . .
WORKDIR "/src/src/ActiveDirectoryToRestApi"
RUN dotnet build "ActiveDirectoryToRestApi.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "ActiveDirectoryToRestApi.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ActiveDirectoryToRestApi.dll"]
@@ -0,0 +1,56 @@
namespace ActiveDirectoryToRestApi.Extensions;
public static class LdapAttrExtensions
{
public static string ConvertToDateTime(this string ldapTime)
{
if (ldapTime == "0" || ldapTime == "9223372036854775807") return "Never";
var date = long.Parse(ldapTime);
return DateTime.FromFileTime(date).ToString();
}
public static bool IsExpired(this string ldapTime)
{
if (ldapTime == "0" || ldapTime == "9223372036854775807") return false;
var expiration = DateTime.FromFileTime(long.Parse(ldapTime));
if (expiration < DateTime.Now) return true;
return false;
}
public static bool IsPasswordExpired(this string userAccountControl)
{
if (userAccountControl == "8388608") return true;
return false;
}
public static bool IsEnabled(this string userAccountControl)
{
if (userAccountControl == "512" || userAccountControl == "66048" || userAccountControl == "262656") return true;
return false;
}
public static bool IsPasswordNeverExpire(this string userAccountControl)
{
if (userAccountControl == "66048" || userAccountControl == "66050") return true;
return false;
}
public static string GroupType(this string groupType)
{
return groupType switch
{
"2" => "Global distribution group",
"4" => "Global distribution group",
"8" => "Universal distribution group",
"-2147483646" => "Global security group",
"-2147483644" => "Domain local security group",
"-2147483640" => "Universal security group",
_ => null,
};
}
}
@@ -0,0 +1,9 @@
global using ActiveDirectoryToRestApi.Configurations;
global using ActiveDirectoryToRestApi.Extensions;
global using ActiveDirectoryToRestApi.Models;
global using ActiveDirectoryToRestApi.Services;
global using Microsoft.AspNetCore.Mvc;
global using System.ComponentModel;
global using System.ComponentModel.DataAnnotations;
global using System.DirectoryServices.Protocols;
global using System.Net;
@@ -0,0 +1,9 @@
namespace ActiveDirectoryToRestApi.Models;
public class Group
{
public string Name { get; set; }
public string Type { get; set; }
public string DistinguishedName { get; set; }
public List<string> Members { get; set; } = new();
}
@@ -0,0 +1,20 @@
namespace ActiveDirectoryToRestApi.Models;
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string DisplayName { get; set; }
public string Email { get; set; }
public string LogonName { get; set; }
public string SamAccountName { get; set; }
public bool Enabled { get; set; }
public bool PasswordNeverExpire { get; set; }
public bool PasswordExpired { get; set; }
public string PasswordLastSet { get; set; }
public string PasswordExpiration { get; set; }
public bool Expired { get; set; }
public string Expiration { get; set; }
public string DistinguishedName { get; set; }
public List<string> Groups { get; set; }
}
@@ -0,0 +1,12 @@
namespace ActiveDirectoryToRestApi.Models;
public class UserLogin
{
[Required]
[DefaultValue("testUser")]
public string UserName { get; set; }
[Required]
[DefaultValue("Thi$1sMyPassw0rd")]
public string Password { get; set; }
}
+31
View File
@@ -0,0 +1,31 @@
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton(provider =>
{
var configs = builder.Configuration.GetSection(nameof(LdapConfigs)).Get<LdapConfigs>();
var endpoint = new LdapDirectoryIdentifier(configs.Server, false, false);
var ldapConnection = new LdapConnection(endpoint,
new NetworkCredential($"{configs.User}@{configs.DomainName}", configs.Pass))
{
AuthType = AuthType.Basic
};
ldapConnection.SessionOptions.ReferralChasing = ReferralChasingOptions.None;
ldapConnection.Bind();
return ldapConnection;
});
builder.Services.AddSingleton<LdapService>();
builder.Services.AddTransient<AuthUserService>();
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();
app.UseAuthorization();
app.MapControllers();
app.Run();
@@ -0,0 +1,40 @@
{
"profiles": {
"http": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:5220"
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"environmentVariables": {
"ASPNETCORE_URLS": "http://+:80"
},
"publishAllPorts": true
}
},
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:20943",
"sslPort": 0
}
}
}
@@ -0,0 +1,32 @@
namespace ActiveDirectoryToRestApi.Services;
public class AuthUserService
{
private readonly IConfiguration _configuration;
public AuthUserService(IConfiguration configuration)
{
_configuration = configuration;
}
public bool AuthUser(string username, string password)
{
var configs = _configuration.GetSection(nameof(LdapConfigs)).Get<LdapConfigs>();
try
{
var endpoint = new LdapDirectoryIdentifier(configs.Server, false, false);
using var connection = new LdapConnection(endpoint,
new NetworkCredential($"{configs.DomainName}\\{username}", password))
{
AuthType = AuthType.Basic
};
connection.SessionOptions.ReferralChasing = ReferralChasingOptions.None;
connection.Bind();
return true;
}
catch (LdapException)
{
return false;
}
}
}
@@ -0,0 +1,177 @@
namespace ActiveDirectoryToRestApi.Services;
public class LdapService
{
private readonly LdapConfigs _ldapConfigs;
private readonly LdapConnection _connection;
private readonly IConfiguration _configuration;
public LdapService(LdapConnection connection, IConfiguration configuration)
{
_connection = connection;
_configuration = configuration;
_ldapConfigs = _configuration.GetSection(nameof(LdapConfigs)).Get<LdapConfigs>();
}
public User GetUserByUserName(string username)
{
string filter = $"(&(objectClass=user)(objectCategory=person)(sAMAccountName={username}))";
return GetUser(filter);
}
public User GetUserByEmail(string email)
{
string filter = $"(&(objectClass=user)(objectCategory=person)(mail={email}))";
return GetUser(filter);
}
private User GetUser(string filter)
{
string[] attributesToReturn = { "givenName", "sn", "mail", "userPrincipalName", "displayName", "sAMAccountName",
"userAccountControl", "accountExpires", "memberOf", "distinguishedName", "pwdLastSet" };
SearchRequest searchRequest = new(_ldapConfigs.DistinguishedName, filter, SearchScope.Subtree, attributesToReturn);
SearchResponse searchResponse = (SearchResponse)_connection.SendRequest(searchRequest);
if (searchResponse.Entries.Count > 0)
{
SearchResultEntry entry = searchResponse.Entries[0];
string userAccountControl = entry.Attributes["userAccountControl"][0].ToString();
string accountExpires = entry.Attributes["accountExpires"][0].ToString();
string expiration = accountExpires.ConvertToDateTime();
string pwdLastSet = entry.Attributes["pwdLastSet"][0].ToString().ConvertToDateTime();
string passwordExpiration = GetPasswordExpiration(pwdLastSet);
bool expired = accountExpires.IsExpired();
string mail = entry.Attributes["mail"]?[0].ToString() ?? "";
string givenName = entry.Attributes["givenName"]?[0].ToString() ?? "";
string sn = entry.Attributes["sn"]?[0].ToString() ?? "";
string displayName = entry.Attributes["displayName"]?[0].ToString() ?? "";
string sAMAccountName = entry.Attributes["sAMAccountName"][0].ToString();
string userPrincipalName = entry.Attributes["userPrincipalName"]?[0].ToString() ?? "";
string distinguishedName = entry.DistinguishedName;
bool enabled = userAccountControl.IsEnabled();
bool passwordNeverExpire = userAccountControl.IsPasswordNeverExpire();
bool passwordExpired = userAccountControl.IsPasswordExpired();
var groups = new List<string>();
var memberOfAttribute = entry.Attributes["memberOf"];
if (memberOfAttribute != null)
{
foreach (var groupDn in memberOfAttribute.GetValues(typeof(string)))
{
groups.Add(ParseGroupName(groupDn.ToString()));
}
}
var user = new User
{
DisplayName = displayName,
Email = mail,
SamAccountName = sAMAccountName,
Enabled = enabled,
LogonName = userPrincipalName,
FirstName = givenName,
LastName = sn,
PasswordNeverExpire = passwordNeverExpire,
PasswordExpired = passwordExpired,
PasswordLastSet = pwdLastSet,
PasswordExpiration = passwordExpiration,
Expiration = expiration,
Expired = expired,
DistinguishedName = distinguishedName,
Groups = groups
};
return user;
}
else
{
return null;
}
}
private static string ParseGroupName(string groupDn)
{
int startIndex = groupDn.IndexOf('=') + 1;
int endIndex = groupDn.IndexOf(',');
if (startIndex > 0 && endIndex > 0)
{
return groupDn[startIndex..endIndex];
}
else
{
return groupDn;
}
}
public Group GetGroupMembers(string groupName)
{
Group group = GetGroup(groupName);
if (group is null) return null;
string filter = $"(&(objectCategory=user)(memberOf={group.DistinguishedName}))";
string[] attributesToReturn = { "sAMAccountName" };
SearchRequest searchRequest = new(_ldapConfigs.DistinguishedName, filter, SearchScope.Subtree, attributesToReturn);
SearchResponse searchResponse = (SearchResponse)_connection.SendRequest(searchRequest);
foreach (SearchResultEntry entry in searchResponse.Entries)
{
group.Members.Add(entry.Attributes["sAMAccountName"][0].ToString());
}
return group;
}
private Group GetGroup(string groupName)
{
string filter = $"(&(objectClass=group)(cn={groupName}))";
string[] attributesToReturn = { "distinguishedName", "cn", "groupType" };
SearchRequest searchRequest = new(_ldapConfigs.DistinguishedName, filter, SearchScope.Subtree, attributesToReturn);
SearchResponse searchResponse = (SearchResponse)_connection.SendRequest(searchRequest);
if (searchResponse.Entries.Count > 0)
{
SearchResultEntry entry = searchResponse.Entries[0];
Group group = new()
{
Name = entry.Attributes["cn"][0].ToString(),
Type = entry.Attributes["groupType"][0].ToString().GroupType(),
DistinguishedName = searchResponse.Entries[0].DistinguishedName
};
return group;
}
return null;
}
private string GetPasswordExpiration(string pwdLastSet)
{
var pwdLastSetDate = DateTime.Parse(pwdLastSet);
var passwordExpiration = pwdLastSetDate.AddDays(GetMaxPwdAge());
return passwordExpiration.ToString();
}
private double GetMaxPwdAge()
{
string filter = "(objectClass=domain)";
string[] attributesToRetrieve = { "maxPwdAge" };
SearchRequest searchRequest = new(_ldapConfigs.DistinguishedName, filter, SearchScope.Subtree, attributesToRetrieve);
SearchResponse searchResponse = (SearchResponse)_connection.SendRequest(searchRequest);
if (searchResponse.Entries.Count == 1)
{
SearchResultEntry entry = searchResponse.Entries[0];
long value = long.Parse(entry.Attributes["maxPwdAge"][0].ToString());
TimeSpan maxPwdAge = TimeSpan.FromTicks(value);
double days = maxPwdAge.TotalDays;
return Math.Abs(days);
}
else
{
return 42;
}
}
}
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
@@ -0,0 +1,16 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"LdapConfigs": {
"Server": "192.168.100.25",
"DomainName": "AD", // (pre-Windows 2000)
"User": "ldapAdmin",
"Pass": "Thi$1sMyPassw0rd",
"DistinguishedName": "DC=ad,DC=zimbres,DC=com"
}
}