24 Jun 2024
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
var service = new DirectoryService();
service.GetUserByPrincipal("user").Dump();
#pragma warning disable CA1416
public class DirectoryService
{
private readonly ContextType _contextType = ContextType.Domain;
public User? GetUserByPrincipal(string userName)
{
using var context = new PrincipalContext(_contextType);
return Parse(context, userName);
}
public User? GetUserByPrincipal(string userName, string password)
{
using var context = new PrincipalContext(_contextType, null, null, userName, password);
try
{
if (string.IsNullOrWhiteSpace(context.ConnectedServer))
{
throw new Exception("ConnectedServer is null or empty.");
}
}
catch (DirectoryServicesCOMException)
{
// Invalid password
return null;
}
return Parse(context, userName);
}
private static User? Parse(PrincipalContext context, string userName)
{
using var userPrincipal = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, userName);
if (userPrincipal?.Enabled ?? false)
{
return new User(userPrincipal);
}
return null;
}
public IEnumerable<(string Key, object Value)>? GetUserByDirectoryEntry(string userName)
{
using var dcEntry = new DirectoryEntry();
return Parse(dcEntry, userName);
}
public IEnumerable<(string Key, object Value)>? GetUserByDirectoryEntry(string userName, string password)
{
using var dcEntry = new DirectoryEntry();
using var userEntry = new DirectoryEntry(dcEntry.Path, userName, password);
return Parse(userEntry, userName);
}
private static IEnumerable<(string Key, object Value)>? Parse(DirectoryEntry dcEntry, string userName)
{
var search = new DirectorySearcher(dcEntry)
{
Filter = $"(&(objectClass=user)(sAMAccountName={userName}))"
};
SearchResult? searchResult;
try
{
searchResult = search.FindOne();
}
catch (DirectoryServicesCOMException)
{
// Invalid password
return null;
}
if (searchResult == null)
{
return null;
}
var user = new List<(string Key, object Value)>();
foreach (var p in searchResult.Properties)
{
if (p is not System.Collections.DictionaryEntry { Key: string key, Value: ResultPropertyValueCollection propertyValue })
{
continue;
}
var value = GetResultPropertyValue(propertyValue);
user.Add((key, value));
}
return user;
}
private static object GetResultPropertyValue(ResultPropertyValueCollection propertyValue)
{
var value = propertyValue[0];
if (value is byte[] b)
{
value = ByteArrayToGuid(b);
}
return value;
static object ByteArrayToGuid(byte[] binaryData)
{
var strHex = BitConverter.ToString(binaryData);
if (Guid.TryParse(strHex.Replace("-", string.Empty), out var guid))
{
return guid;
}
return strHex;
}
}
public class User(UserPrincipal principal)
{
public Guid? Id { get; } = principal.Guid;
public string? EmployeeId { get; } = principal.EmployeeId;
public string? Account { get; } = principal.SamAccountName;
public string? UserName { get; } = principal.Name;
public string? Surname { get; } = principal.Surname;
public string? GivenName { get; } = principal.GivenName;
public string? EmailAddress { get; } = principal.EmailAddress;
public Group[]? Groups { get; } = GetGroups(principal);
private static Group[]? GetGroups(Principal user)
{
var groups = user.GetGroups().ToArray().Where(x => x is GroupPrincipal).Select(x => new Group((GroupPrincipal)x)).ToArray();
return groups;
}
}
public class Group(GroupPrincipal principal)
{
public Guid? Id { get; } = principal.Guid;
public string? Account { get; } = principal.SamAccountName;
public string? Name { get; } = principal.Name;
public bool? IsSecurityGroup { get; } = principal.IsSecurityGroup;
}
}
#pragma warning restore CA1416
26 Jun 2020
至從升級到 Windows 10 20H1 後 WSL2 一直都沒辦法用 vscode 連上, 今天弄了一個乾淨的環境重頭開始也順利安裝完成.
安裝
-
首先確認 Windows 10 是否為 Windows 10 20H1
以後的版本.
-
安裝 vscode 並安裝套件 Remote - WSL
-
使用 PowerShell 安裝 WSL.
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
-
安裝VirtualMachinePlatform
並 重新啟動 Windows
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
-
安裝 WSL 2 Linux kernel
.
-
將 WSL 2 設定為預設
wsl --set-default-version 2
-
安裝 Ubuntu 並設定帳號密碼.
-
輸入 vscode .
後會安裝 WSL Server 並啟動 vscode
ISSUE
如果啟動 Ubuntu 後出現以下錯誤代碼 0xc03a001a
Installing, this may take a few minutes...
WslRegisterDistribution failed with error: 0xc03a001a
Error: 0xc03a001a The requested operation could not be completed due to a virtual disk system limitation. Virtual hard disk files must be uncompressed and unencrypted and must not be sparse.
Press any key to continue...
請在資料夾 %LOCALAPPDATA%/packages/
找到 Ubuntu 的資料夾, 例如 CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc
並在進階裡把 壓縮內容以節省磁碟空間
取消勾選即可
參考資料
Windows Subsystem for Linux Installation Guide for Windows 10
25 May 2020
直接上 code
public void ConfigureServices(IServiceCollection services)
{
services.AddOpenApiDocument(config =>
{
config.TypeMappers.Add(new PrimitiveTypeMapper(typeof(Geometry), schema =>
{
schema.Title = "GeoJson";
schema.Properties.Add("type", new JsonSchemaProperty
{
IsRequired = true,
Title = "GeoJson type",
Type = JsonObjectType.String
});
schema.Properties.Add("coordinates", new JsonSchemaProperty
{
IsRequired = true,
Title = "Coordinates"
});
schema.Example = new
{
type = "Point",
coordinates = new[] { 121.517283, 25.047732 }
}; //在這邊寫輸入範例
}));
});
}
22 May 2020
直接上 code
dotnet add package Microsoft.Extensions.DependencyInjection --version 3.1.4
// 取得config
var config = new ConfigurationBuilder()
// ...
.Build();
var services = new ServiceCollection();
// 注入 ILogger<T> 元件 (Microsoft.Extensions.Logging)
// 如果有安裝 Microsoft.Extensions.Logging.Console 就可以像 ASP.NET Core 的 console 顯示訊息
services.AddLogging(configure => LoggingHelper.SetLoggingBuilder(configure, config));
// 跟 ASP.NET Core 一樣註冊 Service
services.AddTransient<Service>();
var provider = services.BuildServiceProvider();
var service = provider.GetService<Service>(); // 使用 provider 取得所需要的 service
21 May 2020
加入參考
dotnet add package Microsoft.Extensions.Logging.Configuration
建立 LoggerConfiguration
LoggerConfiguration 的作用在於傳入自訂的 Log 元件設定檔, 比如說 LogPath
與 Log 記錄等級
.
public class CustomLoggerConfiguration
{
public string LogPath { get; set; }
private readonly IList<LogLevel> _logLevels;
public CustomLoggerConfiguration()
{
_logLevels = new List<LogLevel>
{
LogLevel.Error,
LogLevel.Critical
};
}
public void AddLogLevel(params LogLevel[] levels)
{
foreach (var level in levels)
{
if (IsLogLevelEnable(level))
{
return;
}
_logLevels.Add(level);
}
}
public bool IsLogLevelEnable(LogLevel level)
{
return _logLevels.Contains(level);
}
}
建立 CustomLogger
繼承介面 ILogger
建立我們自訂的 Logger 元件, 將訊息寫入原本就建立好的 MyLogger
元件.
public class CustomLogger : ILogger
{
private readonly string _name;
private readonly Func<CustomLoggerConfiguration> _getCurrentConfig;
private readonly MyLogger _loggerService;
public CustomLogger(string name, Func<CustomLoggerConfiguration> getCurrentConfig)
{
_name = name;
_getCurrentConfig = getCurrentConfig;
_loggerService = new MyLogger(_getCurrentConfig().LogPath);
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
}
try
{
var logLevel = MapLevel(logLevel);
var message = formatter(state, exception);
_loggerService.Log(_name, logLevel, message, exception);
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}
private static MyLogLevel MapLevel(LogLevel level)
{
return level switch
{
LogLevel.Trace => MyLogLevel.Trace,
LogLevel.Debug => MyLogLevel.Debug,
LogLevel.Information => MyLogLevel.Info,
LogLevel.Warning => MyLogLevel.Warn,
LogLevel.Error => MyLogLevel.Error,
LogLevel.Critical => MyLogLevel.Fatal,
LogLevel.None => MyLogLevel.Trace,
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null)
};
}
public bool IsEnabled(LogLevel logLevel) => _getCurrentConfig().IsLogLevelEnable(logLevel);
public IDisposable BeginScope<TState>(TState state) => default;
}
建立 CustomLoggerProvider
繼承介面 ILoggerProvider
, 建立 CustomLoggerProvider
.
public class CustomLoggerProvider : ILoggerProvider
{
private readonly IDisposable _onChangeToken;
private CustomLoggerConfiguration _currentConfig;
public CustomLoggerProvider(IOptionsMonitor<CustomLoggerConfiguration> config)
{
_currentConfig = config.CurrentValue;
_onChangeToken = config.OnChange(updatedConfig => _currentConfig = updatedConfig);
}
public ILogger CreateLogger(string categoryName) => new CustomLogger(categoryName, GetCurrentConfig);
private CustomLoggerConfiguration GetCurrentConfig() => _currentConfig;
public void Dispose()
{
_onChangeToken.Dispose();
}
}
註冊 CustomLoggerProvider
最後我們將剛的建立的 CustomLoggerProvider
註冊到 Microsoft.Extensions.DependencyInjection
的 IServiceCollection
, 讓 ASP.NET Core 可以建立我們自訂的 CustomLogger
.
in Startup.cs
services.AddLogging(builder =>
{
Action<CustomLoggerConfiguration> configure = config =>
{
var logPath = Configuration.GetSection("Log:LogPath").Value;
config.LogPath = logPath;
config.AddLogLevel(LogLevel.Warning);
}
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, CustomLoggerProvider>());
LoggerProviderOptions.RegisterProviderOptions<CustomLoggerConfiguration, CustomLoggerProvider>(builder.Services);
builder.Services.Configure(configure);
});
這樣, 使用注入的 ILogger 時就會將資訊一併寫入我們自訂的 Log 元件了.