Skip to content

Commit

Permalink
Merge pull request #9 from MultifactorLab/netbios_name_resolving
Browse files Browse the repository at this point in the history
Netbios name resolving
  • Loading branch information
Gimemor committed Apr 3, 2024
2 parents 99f8044 + 8131a02 commit f53eb49
Show file tree
Hide file tree
Showing 20 changed files with 482 additions and 15 deletions.
6 changes: 3 additions & 3 deletions MultiFactor.Ldap.Adapter/App.config
Expand Up @@ -53,10 +53,10 @@
<!--Windows Service settings -->
<add key="service-unit-name" value="MFLdapAdapter" />
<add key="service-display-name" value="MultiFactor Ldap Adapter" />

<!--certificate password leave empty or null for certificate without password-->
<!--<add key="certificate-password" value="XXXXXX"/>-->

<!--certificate password leave empty or null for certificate without password-->
<!--<add key="certificate-password" value="XXXXXX"/>-->
<!--<add key="transform-ldap-identity" value="upn"/>-->
</appSettings>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
Expand Down
5 changes: 3 additions & 2 deletions MultiFactor.Ldap.Adapter/Configuration/ClientConfiguration.cs
Expand Up @@ -2,6 +2,7 @@
//Please see licence at
//https://github.com/MultifactorLab/MultiFactor.Ldap.Adapter/blob/main/LICENSE.md

using MultiFactor.Ldap.Adapter.Core.NameResolving;
using System.Collections.Generic;
using System.Linq;

Expand All @@ -20,6 +21,7 @@ public ClientConfiguration()

LoadActiveDirectoryNestedGroups = true;
UserNameTransformRules = new List<UserNameTransformRulesElement>();
TransformLdapIdentity = LdapIdentityFormat.None;
}

/// <summary>
Expand Down Expand Up @@ -79,10 +81,9 @@ public ClientConfiguration()
/// API Secret
/// </summary>
public string MultifactorApiSecret { get; set; }

public LdapIdentityFormat TransformLdapIdentity { get; set; }
public AuthenticatedClientCacheConfig AuthenticationCacheLifetime { get; internal set; }


public bool CheckUserGroups()
{
return
Expand Down
Expand Up @@ -3,6 +3,7 @@
//https://github.com/MultifactorLab/MultiFactor.Ldap.Adapter/blob/main/LICENSE.md

using MultiFactor.Ldap.Adapter.Core;
using MultiFactor.Ldap.Adapter.Core.NameResolving;
using NetTools;
using Serilog;
using System;
Expand Down Expand Up @@ -104,7 +105,6 @@ public static ServiceConfiguration Load(ILogger logger)
var apiTimeoutSetting = appSettings.Settings["multifactor-api-timeout"]?.Value;
var logLevelSetting = appSettings.Settings["logging-level"]?.Value;
var certificatePassword = appSettings.Settings["certificate-password"]?.Value;

if (string.IsNullOrEmpty(apiUrlSetting))
{
throw new Exception("Configuration error: 'multifactor-api-url' element not found");
Expand Down Expand Up @@ -209,7 +209,7 @@ private static ClientConfiguration Load(string name, AppSettingsSection appSetti
var activeDirectory2FaBypassGroupSetting = appSettings.Settings["active-directory-2fa-bypass-group"]?.Value;
var bypassSecondFactorWhenApiUnreachableSetting = appSettings.Settings["bypass-second-factor-when-api-unreachable"]?.Value;
var loadActiveDirectoryNestedGroupsSettings = appSettings.Settings["load-active-directory-nested-groups"]?.Value;

var transformLdapIdentity = appSettings.Settings["transform-ldap-identity"]?.Value;

if (string.IsNullOrEmpty(ldapServerSetting))
{
Expand All @@ -230,6 +230,9 @@ private static ClientConfiguration Load(string name, AppSettingsSection appSetti
LdapServer = ldapServerSetting,
MultifactorApiKey = multifactorApiKeySetting,
MultifactorApiSecret = multifactorApiSecretSetting,
TransformLdapIdentity = string.IsNullOrEmpty(transformLdapIdentity)
? LdapIdentityFormat.None
: (LdapIdentityFormat)Enum.Parse(typeof(LdapIdentityFormat), transformLdapIdentity, true)
};

if (!string.IsNullOrEmpty(serviceAccountsSetting))
Expand Down
12 changes: 12 additions & 0 deletions MultiFactor.Ldap.Adapter/Core/NameResolving/LdapIdentityFormat.cs
@@ -0,0 +1,12 @@
namespace MultiFactor.Ldap.Adapter.Core.NameResolving
{
public enum LdapIdentityFormat
{
None = 0,
Upn = 1,
UidAndNetbios = 2, // uid@netbios
SamAccountName = 3,
NetBIOSAndUid = 4, // NETBIOS\uid
DistinguishedName = 5
}
}
10 changes: 10 additions & 0 deletions MultiFactor.Ldap.Adapter/Core/NameResolving/NameResolverContext.cs
@@ -0,0 +1,10 @@
using MultiFactor.Ldap.Adapter.Services;

namespace MultiFactor.Ldap.Adapter.Core.NameResolving
{
public class NameResolverContext
{
public NetbiosDomainName[] Domains;
public LdapProfile Profile;
}
}
61 changes: 61 additions & 0 deletions MultiFactor.Ldap.Adapter/Core/NameResolving/NameResolverService.cs
@@ -0,0 +1,61 @@
using MultiFactor.Ldap.Adapter.Core.NameResolving;
using MultiFactor.Ldap.Adapter.Core.NameResolving.NameTranslators;
using Serilog;

namespace MultiFactor.Ldap.Adapter.Core.NameResolving
{
public class NameResolverService
{
private ILogger _logger;

public NameResolverService(ILogger logger)
{
_logger = logger;
}

public string Resolve(NameResolverContext context, string name, LdapIdentityFormat to)
{
var from = NameTypeDetector.GetType(name);
if(from == null)
{
return name;
}

var resolver = GetTranslator(context, (LdapIdentityFormat)from, to);
if(resolver == null)
{
return name;
}
return resolver.Translate(context, name);
}


public INameTranslator GetTranslator(NameResolverContext context, LdapIdentityFormat from, LdapIdentityFormat to)
{
if (from == LdapIdentityFormat.UidAndNetbios && to == LdapIdentityFormat.Upn)
{
return new sAMAccountNameAndNetbiosToUpnNameTranslator();
}
else if (from == LdapIdentityFormat.NetBIOSAndUid && to == LdapIdentityFormat.Upn)
{
return new NetbiosToUpnNameTranslator();
}
else if (from == LdapIdentityFormat.DistinguishedName && to == LdapIdentityFormat.Upn)
{
return new DistinguishedNameToUpnTranslator();
}
// There are a case when sAMAccountName@domain.local looks exactly like UPN
// Let's try an UPN we got from the profile
if (from == LdapIdentityFormat.Upn && to == LdapIdentityFormat.Upn && context.Profile != null)
{
return new UpnFromProfileNameTranslator();
}
if(from == LdapIdentityFormat.SamAccountName && to == LdapIdentityFormat.Upn)
{
return new sAMAccountNameToUpnNameTranslator();
}
_logger.Error($"Suitable username format was not found");
return null;
}
}
}
@@ -0,0 +1,16 @@
using System.Text.RegularExpressions;

namespace MultiFactor.Ldap.Adapter.Core.NameResolving.NameTranslators
{
public class DistinguishedNameToUpnTranslator : INameTranslator
{
public string Translate(NameResolverContext nameTranslatorContext, string from)
{
if (nameTranslatorContext.Profile != null)
{
return nameTranslatorContext.Profile.Upn;
}
return from;
}
}
}
@@ -0,0 +1,7 @@
namespace MultiFactor.Ldap.Adapter.Core.NameResolving.NameTranslators
{
public interface INameTranslator
{
string Translate(NameResolverContext context, string from);
}
}
@@ -0,0 +1,26 @@
using System.Text.RegularExpressions;

namespace MultiFactor.Ldap.Adapter.Core.NameResolving.NameTranslators
{
public class NetbiosToUpnNameTranslator : INameTranslator
{
public string Translate(NameResolverContext nameTranslatorContext, string from)
{
if(nameTranslatorContext.Profile != null)
{
return nameTranslatorContext.Profile.Upn;
}

foreach (var domain in nameTranslatorContext.Domains)
{
var regex = new Regex("^" + domain.NetbiosName.ToLower() + "\\\\", RegexOptions.IgnoreCase);
if (regex.IsMatch(from))
{
var result = regex.Replace(from + "@" + domain.Domain.ToLower(), "");
return result;
}
}
return from;
}
}
}
@@ -0,0 +1,15 @@

namespace MultiFactor.Ldap.Adapter.Core.NameResolving.NameTranslators
{
public class UpnFromProfileNameTranslator : INameTranslator
{
public string Translate(NameResolverContext nameResolverContext, string from)
{
if (nameResolverContext.Profile != null)
{
return nameResolverContext.Profile.Upn;
}
return from;
}
}
}
@@ -0,0 +1,25 @@
using System.Text.RegularExpressions;

namespace MultiFactor.Ldap.Adapter.Core.NameResolving.NameTranslators
{
public class sAMAccountNameAndNetbiosToUpnNameTranslator : INameTranslator
{
public string Translate(NameResolverContext nameTranslatorContext, string from)
{
if (nameTranslatorContext.Profile != null)
{
return nameTranslatorContext.Profile.Upn;
}
foreach(var domain in nameTranslatorContext.Domains)
{
var regex = new Regex("@" + domain.NetbiosName.ToLower() + "$", RegexOptions.IgnoreCase);
if(regex.IsMatch(from))
{
var result = regex.Replace(from, "@" + domain.Domain.ToLower());
return result;
}
}
return from;
}
}
}
@@ -0,0 +1,16 @@
using System.Text.RegularExpressions;

namespace MultiFactor.Ldap.Adapter.Core.NameResolving.NameTranslators
{
public class sAMAccountNameToUpnNameTranslator : INameTranslator
{
public string Translate(NameResolverContext nameTranslatorContext, string from)
{
if (nameTranslatorContext.Profile != null)
{
return nameTranslatorContext.Profile.Upn;
}
return from;
}
}
}
30 changes: 30 additions & 0 deletions MultiFactor.Ldap.Adapter/Core/NameResolving/NameTypeDetector.cs
@@ -0,0 +1,30 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;

namespace MultiFactor.Ldap.Adapter.Core.NameResolving
{
public class NameTypeDetector
{
public static LdapIdentityFormat? GetType(string name)
{
if (name.Contains('\\'))
{
return LdapIdentityFormat.NetBIOSAndUid;
}
if (name.IndexOf("CN=", StringComparison.OrdinalIgnoreCase) >= 0)
{
return LdapIdentityFormat.DistinguishedName;
}
var domainRegex = new Regex("^[^@]+@(.+)$");
var domainMatch = domainRegex.Match(name);
if (!domainMatch.Success || domainMatch.Groups.Count < 2)
{
return LdapIdentityFormat.SamAccountName;
}
return domainMatch.Groups[1].Value.Count(x => x == '.') == 0
? LdapIdentityFormat.UidAndNetbios
: LdapIdentityFormat.Upn;
}
}
}
@@ -0,0 +1,8 @@
namespace MultiFactor.Ldap.Adapter.Core.NameResolving
{
public class NetbiosDomainName
{
public string Domain;
public string NetbiosName;
}
}
@@ -1,5 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using MultiFactor.Ldap.Adapter.Configuration;
using MultiFactor.Ldap.Adapter.Core.NameResolving;
using MultiFactor.Ldap.Adapter.Server;
using MultiFactor.Ldap.Adapter.Services;
using MultiFactor.Ldap.Adapter.Services.Caching;
Expand Down Expand Up @@ -73,7 +74,7 @@ public static void ConfigureApplicationServices(this IServiceCollection services
services.AddSingleton<AuthenticatedClientCache>();
services.AddSingleton<MultiFactorApiClient>();
services.AddHttpClientWithProxy();

services.AddTransient<NameResolverService>();
services.AddSingleton<AdapterService>();
}

Expand Down
11 changes: 11 additions & 0 deletions MultiFactor.Ldap.Adapter/MultiFactor.Ldap.Adapter.csproj
Expand Up @@ -206,6 +206,17 @@
<Compile Include="Core\LdapPartialAttribute.cs" />
<Compile Include="Core\LdapResult.cs" />
<Compile Include="Core\LdapResultAttribute.cs" />
<Compile Include="Core\NameResolving\LdapIdentityFormat.cs" />
<Compile Include="Core\NameResolving\NameResolverContext.cs" />
<Compile Include="Core\NameResolving\NameResolverService.cs" />
<Compile Include="Core\NameResolving\NameTranslators\DistinguishedNameToUpnTranslator.cs" />
<Compile Include="Core\NameResolving\NameTranslators\INameTranslator.cs" />
<Compile Include="Core\NameResolving\NameTranslators\NetbiosToUpnNameTranslator.cs" />
<Compile Include="Core\NameResolving\NameTranslators\sAMAccountNameAndNetbiosToUpnNameTranslator.cs" />
<Compile Include="Core\NameResolving\NameTranslators\sAMAccountNameToUpnNameTranslator.cs" />
<Compile Include="Core\NameResolving\NameTranslators\UpnFromProfileNameTranslator.cs" />
<Compile Include="Core\NameResolving\NameTypeDetector.cs" />
<Compile Include="Core\NameResolving\NetbiosDomainName.cs" />
<Compile Include="Core\Tag.cs" />
<Compile Include="Core\TagClass.cs" />
<Compile Include="Core\UniversalDataType.cs" />
Expand Down

0 comments on commit f53eb49

Please sign in to comment.