namespace LaaProductionWeb.Services { using LaaProductionWeb.Data.Interfaces; using LaaProductionWeb.Services.Interfaces; using LaaProductionWeb.Services.Models.Software; using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text.RegularExpressions; public class SoftwareService : ISoftwareService { private readonly ISqlClient sqlClient; public SoftwareService(ISqlClient sqlClient) => this.sqlClient = sqlClient; public void AddFunction(short appId, string name) => this.sqlClient .CreateCommand($@" INSERT INTO [SoftwareFunctions] ( [AppId] , [Function]) VALUES (@{nameof(appId)} , @{nameof(name)})") .AddParameter(nameof(appId), appId) .AddParameter(nameof(name), name) .ExecuteNonQuery(); public int ApplicationId(string name) => this.sqlClient .CreateCommand($@" SELECT TOP 1 [AppId] FROM [VersionSoftwareAccess] WHERE [SoftwareAccess_Program] = @{nameof(name)}") .AddParameter(nameof(name), name) .FirstOrDefault(x => x.GetSmallint()); public void DeleteFunction(int funcId) { this.sqlClient .CreateCommand($@" DELETE FROM [SoftwareFunctions] WHERE [Id] = @{nameof(funcId)}") .AddParameter(nameof(funcId), funcId) .ExecuteNonQuery(); this.sqlClient .CreateCommand($@" DELETE FROM [UsersFunctions] WHERE [SoftwareFunctionId] = @{nameof(funcId)}") .AddParameter(nameof(funcId), funcId) .ExecuteNonQuery(); } public short FindAndUpdateSoftware(string name, IEnumerable functions) { var appId = this.sqlClient .CreateCommand($@" SELECT TOP 1 [AppId] , MAX([SoftwareAccess_VaildTo]) OVER (PARTITION BY [AppId]) FROM [VersionSoftwareAccess] WHERE [SoftwareAccess_Program] = @{nameof(name)} AND [SoftwareAccess_VaildTo] > GETDATE()") .AddParameter(nameof(name), name) .FirstOrDefault(x => x.GetSmallint()); if (appId > 0 && functions != null && functions.Any()) { foreach (var function in functions) { if (Regex.IsMatch(function, "[a-zA-Z_]+")) { this.AddFunction(appId, function); } } } return appId; } public IEnumerable GetAllSoftwareFunctions() => this.sqlClient .CreateCommand($@" SELECT [Id] , [AppId] , [Function] FROM [SoftwareFunctions]") .ExecuteReader(f => new SoftwareFunction { Id = f.GetInt(), AppId = f.GetSmallint(), Function = f.GetString() }); public IEnumerable GetSoftwareFunctions(short appId) => this.sqlClient .CreateCommand($@" SELECT [Id] , [AppId] , [Function] FROM [SoftwareFunctions] WHERE [AppId] = @{nameof(appId)}") .AddParameter(nameof(appId), appId) .ExecuteReader(f => new SoftwareFunction { Id = f.GetInt(), AppId = f.GetSmallint(), Function = f.GetString() }); public IEnumerable GetSoftwareFunctions(short appId, short userId) => this.sqlClient .CreateCommand($@" SELECT [SF].[Function] AS [Function] FROM [UsersFunctions] AS [UF] JOIN [SoftwareFunctions] AS [SF] ON [SF].[Id] = [UF].[SoftwareFunctionId] WHERE [UF].[UserId] = @{nameof(userId)} AND [SF].[AppId] = @{nameof(appId)}") .AddParameter(nameof(appId), appId) .AddParameter(nameof(userId), userId) .ExecuteReader(x => x.GetString()); public SoftwareOverview GetSoftwareOverview(short appId) => this.sqlClient .CreateCommand($@" SELECT [vsa].[AppID] , [vsa].[SoftwareAccess_Program] , COUNT([vsa].[AppID]) , (SELECT COUNT(1) FROM [SoftwareFunctions] WHERE [AppId] = [vsa].[AppID]) AS [CountF] FROM [VersionSoftwareAccess] AS [vsa] WHERE [vsa].[AppID] = @{nameof(appId)} GROUP BY [vsa].[AppID] , [vsa].[SoftwareAccess_Program]") .AddParameter(nameof(appId), appId) .FirstOrDefault(x => new SoftwareOverview { AppId = x.GetSmallint(), Name = x.GetString(), Versions = x.GetInt(), Functions = x.GetInt() }); public IEnumerable GetSoftwareVersions(short appId) => this.sqlClient .CreateCommand($@" SELECT [SoftwareAccess_ID] , [AppID] , [SoftwareAccess_Program] , [SoftwareAccess_MajorVersion] , [SoftwareAccess_MinorVersion] , [SoftwareAccess_BuildVersion] , [SoftwareAccess_VaildTo] , [SoftwareAccess_Desc] FROM [VersionSoftwareAccess] WHERE [AppId] = @{nameof(appId)} ORDER BY [SoftwareAccess_VaildTo] DESC , [SoftwareAccess_MajorVersion] DESC , [SoftwareAccess_MinorVersion] DESC , [SoftwareAccess_BuildVersion] DESC") .AddParameter(nameof(appId), appId) .ExecuteReader(x => new Software { Id = x.GetInt(), AppId = x.GetSmallint(), FullName = x.GetString(), MajorV = x.GetInt(), MinorV = x.GetInt(), BuildV = x.GetInt(), ValidTo = x.GetDate(), Description = x.GetString() }); public IEnumerable GetSoftwares() => this.sqlClient .CreateCommand(@" SELECT [VSA].[AppId] AS [AppId] , [VSA].[SoftwareAccess_Program] AS [Program] , COUNT([VSA].[AppId]) AS [Versions] , (SELECT COUNT(1) FROM [SoftwareFunctions] WHERE [AppId] = [VSA].[AppID]) AS [Functions] FROM [VersionSoftwareAccess] AS [VSA] GROUP BY [VSA].[AppId] , [VSA].[SoftwareAccess_Program]") .ExecuteReader(x => new SoftwareOverview { AppId = x.GetSmallint(), Name = x.GetString(), Versions = x.GetInt(), Functions = x.GetInt(), }); public IEnumerable GetUsers(string username = null) => this.sqlClient .CreateCommand($@" SELECT [U].[MitarbeiterNr] , [U].[Vorname] , [U].[Name] , [U].[Benutzername] , COUNT([UF].[UserId]) , CAST(CASE WHEN [U].[PasswordHash] IS NULL THEN 0 ELSE 1 END AS BIT) FROM [Mitarbeiter] AS [U] LEFT JOIN [UsersFunctions] AS [UF] ON [UF].[UserId] = [U].[MitarbeiterNr] WHERE [U].[Vorname] LIKE @{nameof(username)} OR [U].[Name] LIKE @{nameof(username)} OR [U].[Benutzername] LIKE @{nameof(username)} GROUP BY [U].[MitarbeiterNr] , [U].[Vorname] , [U].[Name] , [U].[Benutzername] , [U].[PasswordHash] ORDER BY [U].[Vorname] , [U].[Name] , [U].[Benutzername]") .AddParameter(nameof(username), $"%{username}%") .ExecuteReader(x => new SoftwareUser { Id = x.GetSmallint(), FirstName = x.GetString(), LastName = x.GetString(), Username = x.GetString(), FunctionsCount = x.GetInt(), HasPassword = x.GetBool() }); public IEnumerable GetUsersWithFunctions(int userId = -1, string username = null) => this.sqlClient .CreateCommand($@" SELECT [U].[MitarbeiterNr] , [U].[Vorname] , [U].[Name] , [U].[Benutzername] , COUNT([UF].[UserId]) , CAST(CASE WHEN [U].[PasswordHash] IS NULL THEN 0 ELSE 1 END AS BIT) , (SELECT CAST([SoftwareFunctionId] * -1 AS VARCHAR(MAX)) FROM [UsersFunctions] WHERE [UserId] = [UF].[UserId] ORDER BY [SoftwareFunctionId] FOR XML PATH ('')) FROM [Mitarbeiter] AS [U] LEFT JOIN [UsersFunctions] AS [UF] ON [UF].[UserId] = [U].[MitarbeiterNr] WHERE [U].[Vorname] LIKE @{nameof(username)} OR [U].[Name] LIKE @{nameof(username)} OR [U].[Benutzername] LIKE @{nameof(username)} OR [U].[MitarbeiterNr] = @{nameof(userId)} GROUP BY [U].[MitarbeiterNr] , [U].[Vorname] , [U].[Name] , [U].[Benutzername] , [U].[PasswordHash] , [UF].[UserId] ORDER BY [U].[Vorname] , [U].[Name] , [U].[Benutzername]") .AddParameter(nameof(username), $"%{username}%") .AddParameter(nameof(userId), userId) .ExecuteReader(x => new SoftwareUser { Id = x.GetSmallint(), FirstName = x.GetString(), LastName = x.GetString(), Username = x.GetString(), FunctionsCount = x.GetInt(), HasPassword = x.GetBool(), Functions = x.GetString() }); public void UpdateFunction(int funcId, string name) => this.sqlClient .CreateCommand($@" UPDATE [SoftwareFunctions] SET [Function] = @{nameof(name)} WHERE [Id] = @{nameof(funcId)}") .AddParameter(nameof(funcId), funcId) .AddParameter(nameof(name), name) .ExecuteNonQuery(); public void UpdatePasswordHashes() { var pwdUsers = this.sqlClient .CreateCommand($@" SELECT [MitarbeiterNr] , [Kennwort] FROM [Mitarbeiter] WHERE [Kennwort] IS NOT NULL AND [PasswordHash] IS NULL") .ExecuteReader(x => new KeyValuePair( key: x.GetSmallint(), value: x.GetString())); var sha256 = SHA256.Create(); foreach (var kvp in pwdUsers) { var userId = kvp.Key; var password = sha256.ComputeHash(kvp.Value); this.sqlClient .CreateCommand($@" UPDATE [Mitarbeiter] SET [PasswordHash] = @{nameof(password)} WHERE [MitarbeiterNr] = @{nameof(userId)}") .AddParameter(nameof(userId), userId) .AddParameter(nameof(password), password) .ExecuteNonQuery(); } } public int ResetPassword(short userId) => this.sqlClient .CreateCommand($@" UPDATE [Mitarbeiter] SET [PasswordHash] = NULL WHERE [MitarbeiterNr] = @{nameof(userId)}") .AddParameter(nameof(userId), userId) .ExecuteNonQuery(); public int ResetPassword(short userId, string password) { password = SHA256.Create().ComputeHash(password); return this.sqlClient .CreateCommand($@" UPDATE [Mitarbeiter] SET [PasswordHash] = @{nameof(password)} WHERE [MitarbeiterNr] = @{nameof(userId)}") .AddParameter(nameof(userId), userId) .AddParameter(nameof(password), password) .ExecuteNonQuery(); } public void UpdateUsersFunctions(Action mapper) { var model = new UserFunctions(); mapper(model); var userId = model.UserId; this.sqlClient .CreateCommand($@"DELETE FROM [UsersFunctions] WHERE [UserId] = @{nameof(userId)}") .AddParameter(nameof(userId), userId) .ExecuteNonQuery(); foreach (var funcId in model.Functions ?? Array.Empty()) { this.sqlClient .CreateCommand($@" INSERT INTO [UsersFunctions] ([UserId] ,[SoftwareFunctionId]) VALUES (@{nameof(userId)}, @{nameof(funcId)})") .AddParameter(nameof(userId), userId) .AddParameter(nameof(funcId), funcId) .ExecuteNonQuery(); } } public void UpdateUsersFunctions(Action mapper) { var model = new FunctionUsers(); mapper(model); var funcId = model.FunctionId; this.sqlClient .CreateCommand($@"DELETE FROM [UsersFunctions] WHERE [SoftwareFunctionId] = @{nameof(funcId)}") .AddParameter(nameof(funcId), funcId) .ExecuteNonQuery(); foreach (var userId in model.Users ?? Array.Empty()) { this.sqlClient .CreateCommand($@" INSERT INTO [UsersFunctions] ([UserId] ,[SoftwareFunctionId]) VALUES (@{nameof(userId)}, @{nameof(funcId)})") .AddParameter(nameof(userId), userId) .AddParameter(nameof(funcId), funcId) .ExecuteNonQuery(); } } } }