<commit_before>﻿using Microsoft.Extensions.Logging;
using Microsoft.Win32.SafeHandles;
using System;
using System.DirectoryServices.AccountManagement;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Host.Models;

namespace Tgstation.Server.Host.Security
{
	/// <summary>
	/// <see cref="ISystemIdentityFactory"/> for windows systems. Uses long running tasks due to potential networked domains
	/// </summary>
	sealed class WindowsSystemIdentityFactory : ISystemIdentityFactory
	{
		/// <summary>
		/// The <see cref="ILogger"/> for the <see cref="WindowsSystemIdentityFactory"/>
		/// </summary>
		readonly ILogger<WindowsSystemIdentityFactory> logger;

		/// <summary>
		/// Extract the username and domain name from a <see cref="string"/> in the format "username\\domainname"
		/// </summary>
		/// <param name="input">The input <see cref="string"/></param>
		/// <param name="username">The output username</param>
		/// <param name="domainName">The output domain name. May be <see langword="null"/></param>
		static void GetUserAndDomainName(string input, out string username, out string domainName)
		{
			var splits = input.Split('\\');
			username = splits.Length > 1 ? splits[1] : splits[0];
			domainName = splits.Length > 1 ? splits[0] : null;
		}

		/// <summary>
		/// Construct a <see cref="WindowsSystemIdentityFactory"/>
		/// </summary>
		/// <param name="logger">The value of logger</param>
		public WindowsSystemIdentityFactory(ILogger<WindowsSystemIdentityFactory> logger)
		{
			this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
		}

		/// <inheritdoc />
		public Task<ISystemIdentity> CreateSystemIdentity(User user, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
		{
			if (user == null)
				throw new ArgumentNullException(nameof(user));

			if (user.SystemIdentifier == null)
				throw new InvalidOperationException("User's SystemIdentifier must not be null!");

			PrincipalContext pc = null;
			UserPrincipal principal = null;

			bool TryGetPrincipalFromContextType(ContextType contextType)
			{
				try
				{
					pc = new PrincipalContext(contextType);
					cancellationToken.ThrowIfCancellationRequested();
					principal = UserPrincipal.FindByIdentity(pc, user.SystemIdentifier);
				}
				catch (OperationCanceledException)
				{
					throw;
				}
				catch (Exception e)
				{
					logger.LogWarning("Error loading user for context type {0}! Exception: {1}", contextType, e);
				}
				finally
				{
					if (principal == null)
					{
						pc?.Dispose();
						cancellationToken.ThrowIfCancellationRequested();
					}
				}

				return principal != null;
			}

			if (!TryGetPrincipalFromContextType(ContextType.Machine) && !TryGetPrincipalFromContextType(ContextType.Domain))
				return null;
			return (ISystemIdentity)new WindowsSystemIdentity(principal);
		},
			cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);

		/// <inheritdoc />
		public Task<ISystemIdentity> CreateSystemIdentity(string username, string password, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
		{
			if (username == null)
				throw new ArgumentNullException(nameof(username));
			if (password == null)
				throw new ArgumentNullException(nameof(password));

			var originalUsername = username;
			GetUserAndDomainName(originalUsername, out username, out var domainName);

			var res = NativeMethods.LogonUser(username, domainName, password, 3 /*LOGON32_LOGON_NETWORK*/, 0 /*LOGON32_PROVIDER_DEFAULT*/, out var token);
			if (!res)
			{
				logger.LogTrace("Failed to log in username {0}!", originalUsername);
				return null;
			}

			logger.LogTrace("Successfully logged in username {0}!", originalUsername);
<commit_msg>Rewrite some log messages<commit_after>﻿using Microsoft.Extensions.Logging;
using Microsoft.Win32.SafeHandles;
using System;
using System.DirectoryServices.AccountManagement;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Host.Models;

namespace Tgstation.Server.Host.Security
{
	/// <summary>
	/// <see cref="ISystemIdentityFactory"/> for windows systems. Uses long running tasks due to potential networked domains
	/// </summary>
	sealed class WindowsSystemIdentityFactory : ISystemIdentityFactory
	{
		/// <summary>
		/// The <see cref="ILogger"/> for the <see cref="WindowsSystemIdentityFactory"/>
		/// </summary>
		readonly ILogger<WindowsSystemIdentityFactory> logger;

		/// <summary>
		/// Extract the username and domain name from a <see cref="string"/> in the format "username\\domainname"
		/// </summary>
		/// <param name="input">The input <see cref="string"/></param>
		/// <param name="username">The output username</param>
		/// <param name="domainName">The output domain name. May be <see langword="null"/></param>
		static void GetUserAndDomainName(string input, out string username, out string domainName)
		{
			var splits = input.Split('\\');
			username = splits.Length > 1 ? splits[1] : splits[0];
			domainName = splits.Length > 1 ? splits[0] : null;
		}

		/// <summary>
		/// Construct a <see cref="WindowsSystemIdentityFactory"/>
		/// </summary>
		/// <param name="logger">The value of logger</param>
		public WindowsSystemIdentityFactory(ILogger<WindowsSystemIdentityFactory> logger)
		{
			this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
		}

		/// <inheritdoc />
		public Task<ISystemIdentity> CreateSystemIdentity(User user, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
		{
			if (user == null)
				throw new ArgumentNullException(nameof(user));

			if (user.SystemIdentifier == null)
				throw new InvalidOperationException("User's SystemIdentifier must not be null!");

			PrincipalContext pc = null;
			UserPrincipal principal = null;

			bool TryGetPrincipalFromContextType(ContextType contextType)
			{
				try
				{
					pc = new PrincipalContext(contextType);
					cancellationToken.ThrowIfCancellationRequested();
					principal = UserPrincipal.FindByIdentity(pc, user.SystemIdentifier);
				}
				catch (OperationCanceledException)
				{
					throw;
				}
				catch (Exception e)
				{
					logger.LogWarning("Error loading user for context type {0}! Exception: {1}", contextType, e);
				}
				finally
				{
					if (principal == null)
					{
						pc?.Dispose();
						cancellationToken.ThrowIfCancellationRequested();
					}
				}

				return principal != null;
			}

			if (!TryGetPrincipalFromContextType(ContextType.Machine) && !TryGetPrincipalFromContextType(ContextType.Domain))
				return null;
			return (ISystemIdentity)new WindowsSystemIdentity(principal);
		},
			cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);

		/// <inheritdoc />
		public Task<ISystemIdentity> CreateSystemIdentity(string username, string password, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
		{
			if (username == null)
				throw new ArgumentNullException(nameof(username));
			if (password == null)
				throw new ArgumentNullException(nameof(password));

			var originalUsername = username;
			GetUserAndDomainName(originalUsername, out username, out var domainName);

			var res = NativeMethods.LogonUser(username, domainName, password, 3 /*LOGON32_LOGON_NETWORK*/, 0 /*LOGON32_PROVIDER_DEFAULT*/, out var token);
			if (!res)
			{
				logger.LogTrace("Invalid system identity/password combo for username {0}!", originalUsername);
				return null;
			}

			logger.LogTrace("Authenticated username {0} using system identity!", originalUsername);

{'fraction_non_alphanumeric': 0.12026229508196722, 'fraction_numerical': 0.004459016393442623, 'mean_word_length': 4.198364008179959, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4251230', 'n_tokens_mistral': 2506, 'n_tokens_neox': 2104, 'n_words': 535}