From 6153963f8729def1116c31bf92f9dcba0738f3d4 Mon Sep 17 00:00:00 2001 From: Maksym Sadovnychyy Date: Sat, 23 Nov 2024 22:38:43 +0100 Subject: [PATCH] (refactor): not null when attribute implementation --- src/MaksIT.Core/Culture.cs | 6 +-- src/MaksIT.Core/EnvVar.cs | 9 ++-- src/MaksIT.Core/FileSystem.cs | 10 +++-- src/MaksIT.Core/MaksIT.Core.csproj | 2 +- src/MaksIT.Core/Networking/PingPort.cs | 14 ++++++- .../Networking/Windows/NetworkConnection.cs | 18 ++++---- src/MaksIT.Core/Processes.cs | 6 ++- src/MaksIT.Core/Security/AESGCMUtility.cs | 16 +++++++- src/MaksIT.Core/Security/Base32Encoder.cs | 19 ++++++--- src/MaksIT.Core/Security/ChecksumUtility.cs | 18 ++++++-- src/MaksIT.Core/Security/Crc32.cs | 24 +++++++++-- src/MaksIT.Core/Security/JwtGenerator.cs | 21 +++++++++- src/MaksIT.Core/Security/PasswordHasher.cs | 17 ++++++-- src/MaksIT.Core/Security/TotpGenerator.cs | 41 ++++++++++++++++--- 14 files changed, 171 insertions(+), 50 deletions(-) diff --git a/src/MaksIT.Core/Culture.cs b/src/MaksIT.Core/Culture.cs index c67ff46..e00ccc1 100644 --- a/src/MaksIT.Core/Culture.cs +++ b/src/MaksIT.Core/Culture.cs @@ -1,6 +1,6 @@ -using System; +using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Threading; + namespace MaksIT.Core; @@ -15,7 +15,7 @@ public static class Culture { /// The culture to set. If null or empty, the invariant culture is used. /// The error message if the operation fails. /// True if the operation was successful; otherwise, false. - public static bool TrySet(string? culture, out string? errorMessage) { + public static bool TrySet(string? culture, [NotNullWhen(false)] out string? errorMessage) { try { var threadCulture = CultureInfo.InvariantCulture; diff --git a/src/MaksIT.Core/EnvVar.cs b/src/MaksIT.Core/EnvVar.cs index eb07235..7c12016 100644 --- a/src/MaksIT.Core/EnvVar.cs +++ b/src/MaksIT.Core/EnvVar.cs @@ -1,6 +1,7 @@ -using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; + namespace MaksIT.Core; /// @@ -15,7 +16,7 @@ public static class EnvVar { /// The new path to add. /// The error message if the operation fails. /// True if the operation was successful; otherwise, false. - public static bool TryAddToPath(string newPath, out string? errorMessage) { + public static bool TryAddToPath(string newPath, [NotNullWhen(false)] out string? errorMessage) { try { var pathEnvVar = Environment.GetEnvironmentVariable("PATH") ?? string.Empty; char separator = IsWindows ? ';' : ':'; @@ -42,7 +43,7 @@ public static class EnvVar { /// The target of the environment variable (machine, user, process). /// The error message if the operation fails. /// True if the operation was successful; otherwise, false. - public static bool TrySet(string envName, string envValue, string envTarget, out string? errorMessage) { + public static bool TrySet(string envName, string envValue, string envTarget, [NotNullWhen(false)] out string? errorMessage) { try { EnvironmentVariableTarget target = GetEnvironmentVariableTarget(envTarget); if (target == EnvironmentVariableTarget.Machine && !IsWindows) { @@ -66,7 +67,7 @@ public static class EnvVar { /// The target of the environment variable (machine, user, process). /// The error message if the operation fails. /// True if the operation was successful; otherwise, false. - public static bool TryUnSet(string envName, string envTarget, out string? errorMessage) { + public static bool TryUnSet(string envName, string envTarget, [NotNullWhen(false)] out string? errorMessage) { try { EnvironmentVariableTarget target = GetEnvironmentVariableTarget(envTarget); if (target == EnvironmentVariableTarget.Machine && !IsWindows) { diff --git a/src/MaksIT.Core/FileSystem.cs b/src/MaksIT.Core/FileSystem.cs index 4d23fd5..661c433 100644 --- a/src/MaksIT.Core/FileSystem.cs +++ b/src/MaksIT.Core/FileSystem.cs @@ -1,6 +1,8 @@ -using System.Runtime.InteropServices; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; using MaksIT.Core.Extensions; + namespace MaksIT.Core; /// @@ -18,7 +20,7 @@ public static class FileSystem { /// Whether to overwrite existing files. /// The error message if the operation fails. /// True if the copy operation was successful; otherwise, false. - public static bool TryCopyToFolder(string sourcePath, string destDirPath, bool overwrite, out string? errorMessage) { + public static bool TryCopyToFolder(string sourcePath, string destDirPath, bool overwrite, [NotNullWhen(false)] out string? errorMessage) { try { if (!Directory.Exists(destDirPath)) { Directory.CreateDirectory(destDirPath); @@ -58,7 +60,7 @@ public static class FileSystem { /// File or directory path. /// The error message if the operation fails. /// True if the delete operation was successful; otherwise, false. - public static bool TryDeleteFileOrDirectory(string itemPath, out string? errorMessage) { + public static bool TryDeleteFileOrDirectory(string itemPath, [NotNullWhen(false)] out string? errorMessage) { try { if (File.Exists(itemPath)) { File.Delete(itemPath); @@ -169,7 +171,7 @@ public static class FileSystem { var count = 1; while (File.Exists(newFullPath)) { var tempFileName = $"{fileNameOnly}({count++})"; - newFullPath = Path.Combine(path, tempFileName + extension); + newFullPath = Path.Combine(path, $"{tempFileName}{extension}"); } return newFullPath; diff --git a/src/MaksIT.Core/MaksIT.Core.csproj b/src/MaksIT.Core/MaksIT.Core.csproj index e2cc200..ab9b39a 100644 --- a/src/MaksIT.Core/MaksIT.Core.csproj +++ b/src/MaksIT.Core/MaksIT.Core.csproj @@ -8,7 +8,7 @@ MaksIT.Core - 1.1.7 + 1.1.8 Maksym Sadovnychyy MAKS-IT MaksIT.Core diff --git a/src/MaksIT.Core/Networking/PingPort.cs b/src/MaksIT.Core/Networking/PingPort.cs index dfe2369..0691c7a 100644 --- a/src/MaksIT.Core/Networking/PingPort.cs +++ b/src/MaksIT.Core/Networking/PingPort.cs @@ -1,6 +1,8 @@ using System.Net; using System.Net.Sockets; using System.Text; +using System.Diagnostics.CodeAnalysis; + namespace MaksIT.Core.Networking; @@ -15,7 +17,11 @@ public static class PingPort { /// The port number. /// The error message if the operation fails. /// True if the host is reachable on the specified port; otherwise, false. - public static bool TryHostPort(string hostUri, int portNumber, out string? errorMessage) { + public static bool TryHostPort( + string hostUri, + int portNumber, + [NotNullWhen(false)] out string? errorMessage + ) { if (string.IsNullOrEmpty(hostUri)) { errorMessage = "Host URI cannot be null or empty."; return false; @@ -53,7 +59,11 @@ public static class PingPort { /// The port number. /// The error message if the operation fails. /// True if the host is reachable on the specified port; otherwise, false. - public static bool TryUDPPort(string hostUri, int portNumber, out string? errorMessage) { + public static bool TryUDPPort( + string hostUri, + int portNumber, + [NotNullWhen(false)] out string? errorMessage + ) { if (string.IsNullOrEmpty(hostUri)) { errorMessage = "Host URI cannot be null or empty."; return false; diff --git a/src/MaksIT.Core/Networking/Windows/NetworkConnection.cs b/src/MaksIT.Core/Networking/Windows/NetworkConnection.cs index 67799cb..14995d3 100644 --- a/src/MaksIT.Core/Networking/Windows/NetworkConnection.cs +++ b/src/MaksIT.Core/Networking/Windows/NetworkConnection.cs @@ -1,7 +1,8 @@ -using Microsoft.Extensions.Logging; -using System; +using System.Diagnostics.CodeAnalysis; using System.Net; using System.Runtime.InteropServices; +using Microsoft.Extensions.Logging; + namespace MaksIT.Core.Networking.Windows; @@ -15,11 +16,12 @@ public class NetworkConnection : IDisposable { } public static bool TryCreate( - ILogger logger, - string networkName, - NetworkCredential credentials, - out NetworkConnection? networkConnection, - out string? errorMessage) { + ILogger logger, + string networkName, + NetworkCredential credentials, + [NotNullWhen(true)] out NetworkConnection? networkConnection, + [NotNullWhen(false)] out string? errorMessage + ) { try { if (!OperatingSystem.IsWindows()) { throw new PlatformNotSupportedException("NetworkConnection is only supported on Windows."); @@ -81,7 +83,7 @@ public class NetworkConnection : IDisposable { public ResourceDisplayType DisplayType; public int Usage; public string? LocalName; - public string RemoteName; + public string? RemoteName; public string? Comment; public string? Provider; } diff --git a/src/MaksIT.Core/Processes.cs b/src/MaksIT.Core/Processes.cs index 938165b..2b22556 100644 --- a/src/MaksIT.Core/Processes.cs +++ b/src/MaksIT.Core/Processes.cs @@ -1,5 +1,7 @@ using MaksIT.Core.Extensions; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + namespace MaksIT.Core; @@ -27,7 +29,7 @@ public static class Processes { /// If true, the process will be started without creating a window. /// The error message if the operation fails. /// True if the process started successfully; otherwise, false. - public static bool TryStart(string fileName, string arguments, int timeout, bool silent, out string? errorMessage) { + public static bool TryStart(string fileName, string arguments, int timeout, bool silent, [NotNullWhen(false)] out string? errorMessage) { try { var processInfo = new ProcessStartInfo(fileName) { Arguments = arguments, @@ -60,7 +62,7 @@ public static class Processes { /// Process name. Accepts wildcards '*' or '?' /// The error message if the operation fails. /// True if at least one process was killed successfully; otherwise, false. - public static bool TryKill(string process, out string? errorMessage) { + public static bool TryKill(string process, [NotNullWhen(false)] out string? errorMessage) { bool success = false; errorMessage = null; foreach (var proc in System.Diagnostics.Process.GetProcesses()) { diff --git a/src/MaksIT.Core/Security/AESGCMUtility.cs b/src/MaksIT.Core/Security/AESGCMUtility.cs index 05d4987..bd1e3a8 100644 --- a/src/MaksIT.Core/Security/AESGCMUtility.cs +++ b/src/MaksIT.Core/Security/AESGCMUtility.cs @@ -1,4 +1,6 @@ using System.Security.Cryptography; +using System.Diagnostics.CodeAnalysis; + namespace MaksIT.Core.Security; @@ -9,7 +11,12 @@ public static class AESGCMUtility { private const int IvLength = 12; // 12 bytes for AES-GCM IV private const int TagLength = 16; // 16 bytes for AES-GCM Tag - public static bool TryEncryptData(byte[] data, string base64Key, out byte[]? result, out string? errorMessage) { + public static bool TryEncryptData( + byte[] data, + string base64Key, + [NotNullWhen(true)] out byte[]? result, + [NotNullWhen(false)] out string? errorMessage + ) { try { var key = Convert.FromBase64String(base64Key); using (AesGcm aesGcm = new AesGcm(key, AesGcm.TagByteSizes.MaxSize)) { @@ -38,7 +45,12 @@ public static class AESGCMUtility { } } - public static bool TryDecryptData(byte[] data, string base64Key, out byte[]? decryptedData, out string? errorMessage) { + public static bool TryDecryptData( + byte[] data, + string base64Key, + [NotNullWhen(true)] out byte[]? decryptedData, + [NotNullWhen(false)] out string? errorMessage + ) { try { var key = Convert.FromBase64String(base64Key); diff --git a/src/MaksIT.Core/Security/Base32Encoder.cs b/src/MaksIT.Core/Security/Base32Encoder.cs index 3356d2f..26e5036 100644 --- a/src/MaksIT.Core/Security/Base32Encoder.cs +++ b/src/MaksIT.Core/Security/Base32Encoder.cs @@ -1,7 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Text; +using System.Diagnostics.CodeAnalysis; + namespace MaksIT.Core.Security; @@ -9,7 +8,11 @@ public static class Base32Encoder { private static readonly char[] Base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".ToCharArray(); private const string PaddingChar = "="; - public static bool TryEncode(byte[] data, out string? encoded, out string? errorMessage) { + public static bool TryEncode( + byte[] data, + [NotNullWhen(true)] out string? encoded, + [NotNullWhen(false)] out string? errorMessage + ) { try { if (data == null || data.Length == 0) { throw new ArgumentNullException(nameof(data)); @@ -57,7 +60,11 @@ public static class Base32Encoder { } } - public static bool TryDecode(string base32, out byte[]? decoded, out string? errorMessage) { + public static bool TryDecode( + string base32, + [NotNullWhen(true)] out byte[]? decoded, + [NotNullWhen(false)] out string? errorMessage + ) { try { if (string.IsNullOrEmpty(base32)) { throw new ArgumentNullException(nameof(base32)); diff --git a/src/MaksIT.Core/Security/ChecksumUtility.cs b/src/MaksIT.Core/Security/ChecksumUtility.cs index af5c180..41b0818 100644 --- a/src/MaksIT.Core/Security/ChecksumUtility.cs +++ b/src/MaksIT.Core/Security/ChecksumUtility.cs @@ -1,7 +1,14 @@ -namespace MaksIT.Core.Security; +using System.Diagnostics.CodeAnalysis; + +namespace MaksIT.Core.Security; + public static class ChecksumUtility { - public static bool TryCalculateCRC32Checksum(byte[] data, out string? checksum, out string? errorMessage) { + public static bool TryCalculateCRC32Checksum( + byte[] data, + [NotNullWhen(true)] out string? checksum, + [NotNullWhen(false)] out string? errorMessage + ) { if (Crc32.TryCompute(data, out var result, out errorMessage)) { checksum = BitConverter.ToString(BitConverter.GetBytes(result)).Replace("-", "").ToLower(); return true; @@ -26,7 +33,12 @@ public static class ChecksumUtility { } } - public static bool TryCalculateCRC32ChecksumFromFileInChunks(string filePath, out string? checksum, out string? errorMessage, int chunkSize = 8192) { + public static bool TryCalculateCRC32ChecksumFromFileInChunks( + string filePath, + [NotNullWhen(true)] out string? checksum, + [NotNullWhen(false)] out string? errorMessage, + int chunkSize = 8192 + ) { try { using var crc32 = new Crc32(); using var stream = File.OpenRead(filePath); diff --git a/src/MaksIT.Core/Security/Crc32.cs b/src/MaksIT.Core/Security/Crc32.cs index 9d959a3..9bceca9 100644 --- a/src/MaksIT.Core/Security/Crc32.cs +++ b/src/MaksIT.Core/Security/Crc32.cs @@ -1,4 +1,5 @@ -using System.Security.Cryptography; +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; namespace MaksIT.Core.Security; @@ -37,7 +38,11 @@ public class Crc32 : HashAlgorithm { public override int HashSize => 32; - public static bool TryCompute(byte[] buffer, out uint result, out string? errorMessage) { + public static bool TryCompute( + byte[] buffer, + out uint result, + [NotNullWhen(false)] out string? errorMessage + ) { try { result = Compute(DefaultPolynomial, DefaultSeed, buffer); errorMessage = null; @@ -50,7 +55,12 @@ public class Crc32 : HashAlgorithm { } } - public static bool TryCompute(uint seed, byte[] buffer, out uint result, out string? errorMessage) { + public static bool TryCompute( + uint seed, + byte[] buffer, + out uint result, + [NotNullWhen(false)] out string? errorMessage + ) { try { result = Compute(DefaultPolynomial, seed, buffer); errorMessage = null; @@ -63,7 +73,13 @@ public class Crc32 : HashAlgorithm { } } - public static bool TryCompute(uint polynomial, uint seed, byte[] buffer, out uint result, out string? errorMessage) { + public static bool TryCompute( + uint polynomial, + uint seed, + byte[] buffer, + out uint result, + [NotNullWhen(false)] out string? errorMessage + ) { try { result = Compute(polynomial, seed, buffer); errorMessage = null; diff --git a/src/MaksIT.Core/Security/JwtGenerator.cs b/src/MaksIT.Core/Security/JwtGenerator.cs index 1eba54b..e79a532 100644 --- a/src/MaksIT.Core/Security/JwtGenerator.cs +++ b/src/MaksIT.Core/Security/JwtGenerator.cs @@ -3,6 +3,7 @@ using System.Security.Claims; using System.Security.Cryptography; using System.IdentityModel.Tokens.Jwt; using Microsoft.IdentityModel.Tokens; +using System.Diagnostics.CodeAnalysis; namespace MaksIT.Core.Security; @@ -14,7 +15,16 @@ public class JWTTokenClaims { } public static class JwtGenerator { - public static bool TryGenerateToken(string secret, string issuer, string audience, double expiration, string username, List roles, out (string, JWTTokenClaims)? tokenData, out string? errorMessage) { + public static bool TryGenerateToken( + string secret, + string issuer, + string audience, + double expiration, + string username, + List roles, + [NotNullWhen(true)] out (string, JWTTokenClaims)? tokenData, + [NotNullWhen(false)] out string? errorMessage + ) { try { var secretKey = GetSymmetricSecurityKey(secret); var credentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256); @@ -62,7 +72,14 @@ public static class JwtGenerator { public static string GenerateSecret(int keySize = 32) => Convert.ToBase64String(GetRandomBytes(keySize)); - public static bool TryValidateToken(string secret, string issuer, string audience, string token, out JWTTokenClaims? tokenClaims, out string? errorMessage) { + public static bool TryValidateToken( + string secret, + string issuer, + string audience, + string token, + out JWTTokenClaims? tokenClaims, + [NotNullWhen(false)] out string? errorMessage + ) { try { var key = Encoding.UTF8.GetBytes(secret); var tokenHandler = new JwtSecurityTokenHandler(); diff --git a/src/MaksIT.Core/Security/PasswordHasher.cs b/src/MaksIT.Core/Security/PasswordHasher.cs index d714039..ae858c1 100644 --- a/src/MaksIT.Core/Security/PasswordHasher.cs +++ b/src/MaksIT.Core/Security/PasswordHasher.cs @@ -1,4 +1,5 @@ -using System.Security.Cryptography; +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; using Microsoft.AspNetCore.Cryptography.KeyDerivation; namespace MaksIT.Core.Security; @@ -23,7 +24,11 @@ public static class PasswordHasher { return Convert.ToBase64String(valueBytes); } - public static bool TryCreateSaltedHash(string value, out (string Salt, string Hash)? saltedHash, out string? errorMessage) { + public static bool TryCreateSaltedHash( + string value, + [NotNullWhen(true)] out (string Salt, string Hash)? saltedHash, + [NotNullWhen(false)] out string? errorMessage + ) { try { var saltBytes = CreateSaltBytes(); var hash = CreateHash(value, saltBytes); @@ -40,7 +45,13 @@ public static class PasswordHasher { } } - public static bool TryValidateHash(string value, string salt, string hash, out bool isValid, out string? errorMessage) { + public static bool TryValidateHash( + string value, + string salt, + string hash, + [NotNullWhen(true)] out bool isValid, + [NotNullWhen(false)] out string? errorMessage + ) { try { var saltBytes = Convert.FromBase64String(salt); var hashToCompare = CreateHash(value, saltBytes); diff --git a/src/MaksIT.Core/Security/TotpGenerator.cs b/src/MaksIT.Core/Security/TotpGenerator.cs index f1690d4..be23f89 100644 --- a/src/MaksIT.Core/Security/TotpGenerator.cs +++ b/src/MaksIT.Core/Security/TotpGenerator.cs @@ -1,4 +1,5 @@ -using System.Security.Cryptography; +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; namespace MaksIT.Core.Security; @@ -6,7 +7,13 @@ public static class TotpGenerator { private const int Timestep = 30; // Time step in seconds (standard is 30 seconds) private const int TotpDigits = 6; // Standard TOTP length is 6 digits - public static bool TryValidate(string totpCode, string base32Secret, int timeTolerance, out bool isValid, out string? errorMessage) { + public static bool TryValidate( + string totpCode, + string base32Secret, + int timeTolerance, + out bool isValid, + [NotNullWhen(false)] out string? errorMessage + ) { try { // Convert the Base32 encoded secret to a byte array if (!Base32Encoder.TryDecode(base32Secret, out byte[]? secretBytes, out errorMessage)) { @@ -38,7 +45,12 @@ public static class TotpGenerator { } } - public static bool TryGenerate(string base32Secret, long timestep, out string? totpCode, out string? errorMessage) { + public static bool TryGenerate( + string base32Secret, + long timestep, + [NotNullWhen(true)] out string? totpCode, + [NotNullWhen(false)] out string? errorMessage + ) { try { // Convert the Base32 encoded secret to a byte array if (!Base32Encoder.TryDecode(base32Secret, out byte[]? secretBytes, out errorMessage)) { @@ -86,7 +98,10 @@ public static class TotpGenerator { return unixTimestamp / Timestep; } - public static bool TryGenerateSecret(out string? secret, out string? errorMessage) { + public static bool TryGenerateSecret( + [NotNullWhen(true)] out string? secret, + [NotNullWhen(false)] out string? errorMessage + ) { try { // Example of generating a 32-character base32 secret for TOTP var random = new byte[20]; @@ -108,7 +123,11 @@ public static class TotpGenerator { } } - public static bool TryGenerateRecoveryCodes(int defaultCodeCount, out List? recoveryCodes, out string? errorMessage) { + public static bool TryGenerateRecoveryCodes( + int defaultCodeCount, + [NotNullWhen(true)] out List? recoveryCodes, + [NotNullWhen(false)] out string? errorMessage + ) { try { recoveryCodes = new List(); @@ -128,7 +147,17 @@ public static class TotpGenerator { } } - public static bool TryGenerateTotpAuthLink(string label, string username, string twoFactoSharedKey, string issuer, string? algorithm, int? digits, int? period, out string? authLink, out string? errorMessage) { + public static bool TryGenerateTotpAuthLink( + string label, + string username, + string twoFactoSharedKey, + string issuer, + string? algorithm, + int? digits, + int? period, + [NotNullWhen(true)] out string? authLink, + [NotNullWhen(false)] out string? errorMessage + ) { try { var queryParams = new List { $"secret={Uri.EscapeDataString(twoFactoSharedKey)}",