diff --git a/BLAZAM.sln b/BLAZAM.sln index 680a6618d..29f985c30 100644 --- a/BLAZAM.sln +++ b/BLAZAM.sln @@ -46,8 +46,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BLAZAMJobs", "BLAZAMJobs\BL EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlaywrightTests", "PlaywrightTests\PlaywrightTests.csproj", "{7E4AD00B-CA62-4994-A80E-A3900FF98E8E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BLAZAMNav", "BLAZAMNav\BLAZAMNav.csproj", "{EB80E5D9-D0A9-4808-AC66-F0094615EAE1}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -286,18 +284,6 @@ Global {7E4AD00B-CA62-4994-A80E-A3900FF98E8E}.Release|x64.Build.0 = Release|Any CPU {7E4AD00B-CA62-4994-A80E-A3900FF98E8E}.Release|x86.ActiveCfg = Release|Any CPU {7E4AD00B-CA62-4994-A80E-A3900FF98E8E}.Release|x86.Build.0 = Release|Any CPU - {EB80E5D9-D0A9-4808-AC66-F0094615EAE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EB80E5D9-D0A9-4808-AC66-F0094615EAE1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EB80E5D9-D0A9-4808-AC66-F0094615EAE1}.Debug|x64.ActiveCfg = Debug|Any CPU - {EB80E5D9-D0A9-4808-AC66-F0094615EAE1}.Debug|x64.Build.0 = Debug|Any CPU - {EB80E5D9-D0A9-4808-AC66-F0094615EAE1}.Debug|x86.ActiveCfg = Debug|Any CPU - {EB80E5D9-D0A9-4808-AC66-F0094615EAE1}.Debug|x86.Build.0 = Debug|Any CPU - {EB80E5D9-D0A9-4808-AC66-F0094615EAE1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EB80E5D9-D0A9-4808-AC66-F0094615EAE1}.Release|Any CPU.Build.0 = Release|Any CPU - {EB80E5D9-D0A9-4808-AC66-F0094615EAE1}.Release|x64.ActiveCfg = Release|Any CPU - {EB80E5D9-D0A9-4808-AC66-F0094615EAE1}.Release|x64.Build.0 = Release|Any CPU - {EB80E5D9-D0A9-4808-AC66-F0094615EAE1}.Release|x86.ActiveCfg = Release|Any CPU - {EB80E5D9-D0A9-4808-AC66-F0094615EAE1}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/BLAZAM/App.razor b/BLAZAM/App.razor index 29a700290..b65821195 100644 --- a/BLAZAM/App.razor +++ b/BLAZAM/App.razor @@ -1,13 +1,16 @@ @inject IHttpContextAccessor context @inject IApplicationUserStateService userStateService @inject ICurrentUserStateService currentUser - - +@{ + var dsad = 3; +} + + @@ -17,7 +20,6 @@ @if (context.User.Identity?.IsAuthenticated != true) { - Login @@ -31,7 +33,6 @@

You are not authorized to access this resource.

}
-
@@ -42,6 +43,8 @@
+ +
@@ -55,13 +58,13 @@ -
- - - - - + + + + + +
@code { bool darkMode = false; @@ -84,7 +87,7 @@ activeTheme = new BlueTheme(); } - + } darkMode = currentUser.State?.Preferences?.DarkMode == true; } diff --git a/BLAZAM/BLAZAM - Backup.csproj b/BLAZAM/BLAZAM - Backup.csproj deleted file mode 100644 index d3f8fdfae..000000000 --- a/BLAZAM/BLAZAM - Backup.csproj +++ /dev/null @@ -1,142 +0,0 @@ - - - - net8.0 - enable - enable - false - 1.0.7 - 2024.11.02.0114 - false - BLAZAM - False - - - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - - - - True - True - Resources.resx - - - - - - Never - - - PreserveNewest - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - PreserveNewest - - - PreserveNewest - - - - - - - diff --git a/BLAZAM/BLAZAM.csproj b/BLAZAM/BLAZAM.csproj index b335af3c2..4b94afb7a 100644 --- a/BLAZAM/BLAZAM.csproj +++ b/BLAZAM/BLAZAM.csproj @@ -5,11 +5,11 @@ enable enable false - 1.1.0 - 2024.11.13.2234 + 1.2.0 + 2024.11.25.0412 false BLAZAM - False + True @@ -57,8 +57,9 @@ - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -66,14 +67,18 @@ - + + - - - + + + + + + @@ -103,6 +108,10 @@ + + + + True diff --git a/BLAZAM/Helpers/Helpers.cs b/BLAZAM/Helpers/Helpers.cs new file mode 100644 index 000000000..a7225dc3e --- /dev/null +++ b/BLAZAM/Helpers/Helpers.cs @@ -0,0 +1,47 @@ +using BLAZAM.ActiveDirectory.Interfaces; +using BLAZAM.Common.Data; +using BLAZAM.Database.Models.Templates; +using BLAZAM.EmailMessage.Email.Notifications; +using MudBlazor; +using System.Security; + +namespace BLAZAM.Helpers +{ + public static class Helpers + { + public static IADUser GenerateTemplateUser(this DirectoryTemplate template, NewUserName newUserName, IActiveDirectoryContext directory) + { + IADUser? newUser; + var ou = directory.OUs.FindOuByString(template.EffectiveParentOU).FirstOrDefault(); + if (ou == null) throw new ApplicationException("OU could not be found for new user"); + var displayName = template.GenerateDisplayName(newUserName); + newUser = ou.CreateUser(displayName); + + newUser.SamAccountName = template.GenerateUsername(newUserName); + newUser.DisplayName = displayName; + //newUser.SetPassword(template.GeneratePassword().ToSecureString(),false); + //newUser.CanonicalName = template.GenerateDisplayName(newUserName); + newUser.StagePasswordChange(template.GeneratePassword(newUserName).ToSecureString()); + if (template.EffectiveRequirePasswordChange == true) + newUser.StageRequirePasswordChange(true); + if (!newUserName.GivenName.IsNullOrEmpty()) + newUser.GivenName = newUserName.GivenName; + if (!newUserName.MiddleName.IsNullOrEmpty()) + newUser.MiddleName = newUserName.MiddleName; + if (!newUserName.Surname.IsNullOrEmpty()) + newUser.Surname = newUserName.Surname; + + + + template.EffectiveAssignedGroupSids.ForEach(sid => + { + var group = directory.Groups.FindGroupBySID(sid.GroupSid); + if (group != null) + newUser.AssignTo(group); + + }); + return newUser; + } + + } +} diff --git a/BLAZAM/Pages/API/Data/NewUserDetails.cs b/BLAZAM/Pages/API/Data/NewUserDetails.cs new file mode 100644 index 000000000..3bf3d9816 --- /dev/null +++ b/BLAZAM/Pages/API/Data/NewUserDetails.cs @@ -0,0 +1,72 @@ +using BLAZAM.Database.Models.Templates; +using System.Text.Json; + +namespace BLAZAM.Pages.API.Data +{ + /// + /// Request package for creation of a templated user + /// + public class NewUserDetails + { + /// + /// The given name for this user + /// + public string FirstName { get; set; } + /// + /// The middle name for this user + /// + public string? MiddleName { get; set; } + /// + /// The surname for this user + /// + public string? LastName { get; set; } + /// + /// If set, overrides the template generated username + /// + public string? Username { get; set; } + /// + /// The Distinguished Name of the Organizational Unit to + /// create the new user under + /// + /// + /// Only used for custom user creation, ignored for API template execution + /// + public string? OU { get; set; } + + /// + /// The fields to set for this user. Template field with values will also + /// be applied. + /// + public List? Fields { get; set; } + /// + /// A list of group SID's to assign for this user. Template groups will also + /// be applied. + /// + public List? Groups { get; set; } + + /// + /// If the template is set to send a welcome email, and requests a destination, will be sent + /// to this email address. + /// + public string? SendWelcomeEmailTo { get; set; } + } + public class NewUserDetailsExample + { + public object GetExamples() + { + return new NewUserDetails + { + FirstName = "John", + LastName = "Doe", + Username = "johndoe", + OU = "OU=Users,DC=example,DC=com", + Fields = new List + { + new NewUserField { FieldName = "Department", FieldValue = "Sales" }, + new NewUserField { FieldName = "Title", FieldValue = "Sales Representative" } + }, + Groups = new List { "S-1-5-21-1004336348-1177238915-682003330-512", "S-1-5-21-1004336348-1148567915-615476330-495" } + }; + } + } +} diff --git a/BLAZAM/Pages/API/Data/NewUserField.cs b/BLAZAM/Pages/API/Data/NewUserField.cs new file mode 100644 index 000000000..2e3ea1e5b --- /dev/null +++ b/BLAZAM/Pages/API/Data/NewUserField.cs @@ -0,0 +1,19 @@ +using System.Text.Json; + +namespace BLAZAM.Pages.API.Data +{ + /// + /// Represents an Active Directory attribute field to set for thee new user + /// + public class NewUserField + { + /// + /// The attribute name as set in Active Directory + /// + public string FieldName { get; set; } + /// + /// The value to set for this attribute field + /// + public object? FieldValue { get; set; } + } +} \ No newline at end of file diff --git a/BLAZAM/Pages/API/Token.cshtml b/BLAZAM/Pages/API/Token.cshtml deleted file mode 100644 index bea56a89a..000000000 --- a/BLAZAM/Pages/API/Token.cshtml +++ /dev/null @@ -1,4 +0,0 @@ -@page -@model BLAZAM.Server.Pages.API.TokenModel -@{ -} diff --git a/BLAZAM/Pages/API/Token.cshtml.cs b/BLAZAM/Pages/API/Token.cshtml.cs deleted file mode 100644 index b8a5f63be..000000000 --- a/BLAZAM/Pages/API/Token.cshtml.cs +++ /dev/null @@ -1,50 +0,0 @@ -using BLAZAM.Common.Data; -using BLAZAM.Common.Data.Database; -using BLAZAM.Database.Context; -using BLAZAM.Database.Models.User; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.IdentityModel.Tokens; -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; - -namespace BLAZAM.Server.Pages.API -{ - [Obsolete("Not using any local REST API")] - public class TokenModel : PageModel - { - public JwtSecurityTokenHandler JwtTokenHandler { get; private set; } - public string Token { get; private set; } - public IDatabaseContext Context { get; private set; } - - - public TokenModel(IDatabaseContext context) - { - Context = context; - } - - public JsonResult OnGet() - { - JwtTokenHandler = new JwtSecurityTokenHandler(); - var user = this.User.Identity?.Name; - if (string.IsNullOrEmpty(user)) - { - throw new InvalidOperationException("Name is not specified."); - } - - var claims = new[] { new Claim(ClaimTypes.Name, user) }; - var credentials = new SigningCredentials(ApplicationInfo.tokenKey, SecurityAlgorithms.HmacSha256); - var token = new JwtSecurityToken("ExampleServer", "ExampleClients", claims, expires: DateTime.Now.AddSeconds(60), signingCredentials: credentials); - Token = JwtTokenHandler.WriteToken(token); - var userSettings = Context.UserSettings.Where(u => u.UserGUID == this.User.Identity.Name).FirstOrDefault(); - if (userSettings != null) - { - userSettings.APIToken = Token; - } - - Context.SaveChanges(); - return new JsonResult(userSettings); - } - } -} diff --git a/BLAZAM/Pages/API/ValidateUpdateToken.cshtml b/BLAZAM/Pages/API/ValidateUpdateToken.cshtml deleted file mode 100644 index 96d98e309..000000000 --- a/BLAZAM/Pages/API/ValidateUpdateToken.cshtml +++ /dev/null @@ -1,4 +0,0 @@ -@page -@model BLAZAM.Server.Pages.API.ValidateUpdateTokenModel -@{ -} diff --git a/BLAZAM/Pages/API/ValidateUpdateToken.cshtml.cs b/BLAZAM/Pages/API/ValidateUpdateToken.cshtml.cs deleted file mode 100644 index 62200d55b..000000000 --- a/BLAZAM/Pages/API/ValidateUpdateToken.cshtml.cs +++ /dev/null @@ -1,19 +0,0 @@ -using BLAZAM.Server.Data.Services; -using BLAZAM.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace BLAZAM.Server.Pages.API -{ - public class ValidateUpdateTokenModel : PageModel - { - - public IActionResult OnGet(string updateToken) - { - if (updateToken.Equals(AdminTokenService.Token.Guid.ToString())) - return new OkResult(); - - return new UnauthorizedResult(); - } - } -} diff --git a/BLAZAM/Pages/API/v1/ApiController.cs b/BLAZAM/Pages/API/v1/ApiController.cs new file mode 100644 index 000000000..cc4d9b8e6 --- /dev/null +++ b/BLAZAM/Pages/API/v1/ApiController.cs @@ -0,0 +1,63 @@ +using BLAZAM.ActiveDirectory.Interfaces; +using BLAZAM.Common.Data; +using BLAZAM.Database.Context; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Security.Claims; +using Microsoft.EntityFrameworkCore; +using BLAZAM.Services.Audit; +using BLAZAM.Session.Interfaces; + +namespace BLAZAM.Pages.API.v1 +{ + [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = UserRoles.Login)] + [ApiController] + [Produces("application/json")] + [Route("api/v1/[controller]")] + public class ApiController : Controller + { + private DateTime _startTime = DateTime.Now; + protected Dictionary ResponseData = new(); + protected readonly IAppDatabaseFactory DbFactory; + protected readonly AuditLogger AuditLogger; + protected readonly IApplicationUserStateService UserStateService; + + protected IApplicationUserState? CurrentUserState { get; } + + public ApiController(IApplicationUserStateService applicationUserStateService, AuditLogger audit, IAppDatabaseFactory appDatabaseFactory, IHttpContextAccessor httpContextAccessor, IActiveDirectoryContextFactory adFactory) + { + //User = httpContextAccessor.HttpContext.User; + AuditLogger = audit; + UserStateService = applicationUserStateService; + CurrentUserState = UserStateService.CurrentUserState; + + Directory = adFactory.CreateActiveDirectoryContext(); + DbFactory = appDatabaseFactory; + RequestId = Guid.NewGuid(); + ResponseData.Add("Request Id", RequestId); + ResponseData.Add("Version", "1.0"); + ResponseData.Add("Received Time", _startTime); + ResponseData.Add("User", httpContextAccessor?.HttpContext?.User?.Identity?.Name); + ResponseData.Add("User Id", httpContextAccessor?.HttpContext?.User?.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Sid)?.Value); + ResponseData.Add("IP Address", httpContextAccessor?.HttpContext?.Connection?.RemoteIpAddress?.ToString()); + + } + //[HttpGet("badrequest")] // Add a route attribute + //public IActionResult BadRequest() + //{ + // return new BadRequestResult(); + //} + protected IActiveDirectoryContext Directory { get; } + protected Guid RequestId { get; } + + protected IActionResult FormatData(dynamic data) + { + ResponseData.Add("Data", data); + ResponseData.Add("Finish Time", DateTime.Now.ToString()); + ResponseData.Add("Runtime", (DateTime.Now - _startTime).TotalMilliseconds + "ms"); + + return new JsonResult(ResponseData); + } + } +} diff --git a/BLAZAM/Pages/API/v1/Search.cs b/BLAZAM/Pages/API/v1/Search.cs new file mode 100644 index 000000000..fec7e303b --- /dev/null +++ b/BLAZAM/Pages/API/v1/Search.cs @@ -0,0 +1,46 @@ +using BLAZAM.ActiveDirectory.Interfaces; +using BLAZAM.ActiveDirectory.Searchers; +using BLAZAM.Database.Context; +using BLAZAM.Services.Audit; +using BLAZAM.Session.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace BLAZAM.Pages.API.v1 +{ + /// + /// Searches Active Directory. + /// + [Produces("application/json")] + public class Search : ApiController + { + public Search(IApplicationUserStateService applicationUserStateService, AuditLogger audit, IAppDatabaseFactory appDatabaseFactory, IHttpContextAccessor httpContextAccessor, IActiveDirectoryContextFactory adFactory) : base(applicationUserStateService, audit, appDatabaseFactory, httpContextAccessor, adFactory) + { + } + + + + /// + /// Run a general search term query against all AD object types. + /// + /// + /// Sample request: + /// + /// GET /api/v1/search?query=fragment + /// + /// + /// The string fragment to search for + /// Returns a list of matching Active Directory objects. + /// Unauthorized - The user is not authenticated. + /// Forbidden - The user does not have the required role. + [HttpGet] + public IActionResult OnGet([FromQuery] string query) + { + ADSearch search = new ADSearch(Directory); + search.GeneralSearchTerm = query; + var data = search.Search(); + var data2 = data.Where(de => de.CanRead).ToList(); + var data3 = data2.Select(de => de.CanonicalName).ToList(); + return FormatData(data3); + } + } +} diff --git a/BLAZAM/Pages/API/v1/Templates.cs b/BLAZAM/Pages/API/v1/Templates.cs new file mode 100644 index 000000000..5dd9d4e42 --- /dev/null +++ b/BLAZAM/Pages/API/v1/Templates.cs @@ -0,0 +1,268 @@ +using BLAZAM.ActiveDirectory.Interfaces; +using BLAZAM.Database.Context; +using BLAZAM.Pages.API.Data; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using System.Text.Json.Serialization; +using Microsoft.EntityFrameworkCore; +using AngleSharp.Html.Construction; +using BLAZAM.Common.Data; +using Octokit; +using BLAZAM.EmailMessage.Email.Notifications; +using MudBlazor; +using System.Security; +using Microsoft.Extensions.Localization; +using BLAZAM.Localization; +using BLAZAM.Database.Models.Notifications; +using BLAZAM.Database.Models.Templates; +using BLAZAM.Jobs; +using BLAZAM.Services.Audit; +using BLAZAM.Session.Interfaces; +using System.Text.Json; + +namespace BLAZAM.Pages.API.v1 +{ + public class Templates : ApiController + { + private IAppDatabaseFactory _appDatabaseFactory; + private IStringLocalizer AppLocalization; + private EmailService EmailService; + private NotificationGenerationService OUNotificationService; + + public Templates(NotificationGenerationService ouNotificationService, EmailService email, IApplicationUserStateService applicationUserStateService, IStringLocalizer localizer, AuditLogger audit, IAppDatabaseFactory appDatabaseFactory, IHttpContextAccessor httpContextAccessor, IActiveDirectoryContextFactory adFactory) : base(applicationUserStateService, audit, appDatabaseFactory, httpContextAccessor, adFactory) + { + AppLocalization = localizer; + EmailService = email; + OUNotificationService = ouNotificationService; + } + + + + /// + /// Executes a user creation template. Any required fields will need to be provided in form data. + /// + /// /// + /// Sample request: + /// + /// POST /api/v1/templates/execute/2 + /// { + /// "firstName": "Test", + /// "lastName": "User", + /// "fields": [ + /// { + /// "FieldName": "l", + /// "FieldValue": "Boston" + /// } + /// ] + /// "groups": [ + /// { + /// "S-1-5-21-1004336348-1177238915-682003330-512" + /// } + /// ] + /// } + /// + /// + /// The ID of the template to execute. + /// A complete NewUserDetails request schema + /// Returns the DN of the created user. + /// Unauthorized - The user is not authenticated. + /// Forbidden - The user does not have the required role. + /// Unprocessable - The creation request cannot be processed due to an internal error. + [HttpPost] + [Route("/api/v1/templates/execute/{templateId}")] + + public async Task Execute(int templateId, [FromBody] NewUserDetails newUserDetails) + { + //newUserDetails.Fields.Add(new() { FieldName = "test", FieldValue = "val" }); + //var test = JsonConvert.SerializeObject(newUserDetails); + + var context = await DbFactory.CreateDbContextAsync(); + var template = await context.DirectoryTemplates.Include(t => t.ParentTemplate).FirstOrDefaultAsync(t => t.Id == templateId); + + if (template != null) + { + if (template.HasRequiredFields()) + { + var requiredFields = template.EffectiveFieldValues.Where(fv => fv.Required).ToList(); + foreach (var field in requiredFields) + { + if (!newUserDetails.Fields.Any(f => f.FieldName.Equals(field.FieldName, StringComparison.InvariantCultureIgnoreCase))) + { + return new BadRequestObjectResult(field.FieldName + " is a required field"); + } + + } + } + + var newUserName = new NewUserName() + { + GivenName = newUserDetails.FirstName, + MiddleName = newUserDetails.MiddleName, + Surname = newUserDetails.LastName + }; + + var newUser = template.GenerateTemplateUser(newUserName, Directory); + if (!newUserDetails.Username.IsNullOrEmpty()) + { + newUser.SamAccountName = newUserDetails.Username; + } + var password = newUser.NewPassword.ToPlainText().ToSecureString(); + foreach (var fieldValue in template.EffectiveFieldValues) + { + try + { + if (fieldValue.Field != null && fieldValue.Value != null) + if (fieldValue.Field.FieldName.ToLower() == "homedirectory") + newUser.HomeDirectory = template.ReplaceVariables(fieldValue.Value, newUserName, newUser.SamAccountName); + else + newUser.NewEntryProperties[fieldValue.Field.FieldName] = template.ReplaceVariables(fieldValue.Value, newUserName, newUser.SamAccountName); + else if (fieldValue.CustomField != null && fieldValue.Value != null) + newUser.NewEntryProperties[fieldValue.CustomField.FieldName] = template.ReplaceVariables(fieldValue.Value, newUserName, newUser.SamAccountName); + } + catch (Exception ex) + { + Loggers.ActiveDirectoryLogger.Error("Could not set value for " + fieldValue.Field?.FieldName + ": " + fieldValue.Value?.ToString() + " {@Error}", ex); + } + + } + if (newUserDetails.Fields != null) + { + foreach (var field in newUserDetails.Fields) + { + var json = field.FieldValue as JsonElement?; + var kind = json.Value.ValueKind; + object? value=null; + switch (kind) + { + case JsonValueKind.String: + value = json.Value.GetString(); break; + case JsonValueKind.Number: + value = json.Value.GetDouble(); break; + case JsonValueKind.False: + case JsonValueKind.True: + value = json.Value.GetBoolean(); break; + + } + newUser.SetCustomProperty(field.FieldName,value) ; + } + } + if (newUserDetails.Groups != null) + { + foreach (var groupSid in newUserDetails.Groups) + { + var group = (IADGroup)Directory.GetDirectoryEntryByDN(groupSid); + if (group != null) + { + newUser.AssignTo(group); + + } + else + { + + } + } + } + + + IJob createUserJob = new Job(AppLocalization["Create User"]); + createUserJob.StopOnFailedStep = true; + //createUserJob.ShowJobDetailsDialog(MessageService); + //_username = User.SamAccountName; + //_userPassword = User.NewPassword; + var result = await newUser.CommitChangesAsync(createUserJob); + if (result.FailedSteps.Count == 0) + { + newUser = (IADUser)Directory.GetDirectoryEntryByDN(newUser.DN); + await AuditLogger.User.Created(newUser); + + _ = OUNotificationService.PostAsync(newUser, NotificationType.Create, CurrentUserState); + + try + { + if (template?.EffectiveSendWelcomeEmail == true) + { + if (template.EffectiveAskForAlternateEmail == true || newUser.Email.IsNullOrEmpty()) + { + + await SendWelcomeEmail(newUser, newUserDetails.SendWelcomeEmailTo, password); + + } + else + { + await SendWelcomeEmail(newUser, newUser.Email, password); + } + } + } + catch + { + + } + return new CreatedResult(newUser.OU, newUser.DN); + } + else + { + return new UnprocessableEntityObjectResult(result.FailedSteps.Select(s => s.Exception.InnerException != null ? s.Exception.InnerException.Message : s.Exception.Message)); + } + } + else + { + return new NotFoundObjectResult(templateId); + } + return new BadRequestResult(); + } + + + /// + /// Returns all user creation templates the user has access to. + /// + /// Returns a list of user creation templates. + /// Unauthorized - The user is not authenticated. + /// Forbidden - The user does not have the required role. + [HttpGet] + [Route("/api/v1/templates/list/")] + public IActionResult List() + { + using var context = DbFactory.CreateDbContext(); + var list = context.DirectoryTemplates.Where(t => t.DeletedAt == null && t.Visible).ToList(); + return FormatData(list); + + } + + private void Add(dynamic data, string title, string key) + { + var raw = User.Claims.FirstOrDefault(x => x.Type == key)?.Value; + + var str = raw?.ToString(); + data.Add(title, str); + } + private void AddDateTime(dynamic data, string title, string key) + { + var raw = User.Claims.FirstOrDefault(x => x.Type == key)?.Value; + var lng = long.Parse(raw); + + + var dt = DateTime.UnixEpoch.AddSeconds(lng); ; + var str = dt.ToString(); + data.Add(title, str); + } + + async Task SendWelcomeEmail(IADUser user, string to, SecureString password) + { + try + { + NewUserWelcomeEmailMessage message = new NewUserWelcomeEmailMessage(); + message.Domain = user.Directory.ConnectionSettings?.FQDN; + message.Username = user.SamAccountName; + message.Password = password; + var html = message.Render(); + await EmailService.SendMessage(AppLocalization["New Account Details"], message, to); + + } + catch (Exception ex) + { + Loggers.SystemLogger.Error("Error sending welcome email {@Error}", ex); + } + //emailPreview = EmailService.PrepareHTMLForEmail(html); + } + } +} diff --git a/BLAZAM/Pages/API/v1/Test.cs b/BLAZAM/Pages/API/v1/Test.cs new file mode 100644 index 000000000..6841ee53b --- /dev/null +++ b/BLAZAM/Pages/API/v1/Test.cs @@ -0,0 +1,66 @@ +using BLAZAM.ActiveDirectory.Interfaces; +using BLAZAM.Common.Data; +using BLAZAM.Database.Context; +using BLAZAM.Services.Audit; +using BLAZAM.Session.Interfaces; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace BLAZAM.Pages.API.v1 +{ + public class Test : ApiController + { + public Test(IApplicationUserStateService applicationUserStateService, AuditLogger audit, IAppDatabaseFactory appDatabaseFactory, IHttpContextAccessor httpContextAccessor, IActiveDirectoryContextFactory adFactory) : base(applicationUserStateService, audit, appDatabaseFactory, httpContextAccessor, adFactory) + { + } + + + + /// + /// API connection check to test the configuration. + /// + /// + /// Returns details about the user performing the test. + /// Unauthorized - The user is not authenticated. + /// Forbidden - The user does not have the required role. + [HttpGet] + public IActionResult OnGet() + { + dynamic data = new Dictionary(); + Add(data, "Issuer", "iss"); + + data.Add("Username", User.Identity?.Name); + AddDateTime(data, "Not Before", "nbf"); + AddDateTime(data, "Issued At", "iat"); + AddDateTime(data, "Expires", "exp"); + var claims = new List>(); + foreach (var claim in User.Claims) + { + claims.Add(new(claim.Type, claim.Value)); + } + data.Add("Claims", claims); + return FormatData(data); + } + + + + private void Add(dynamic data, string title, string key) + { + var raw = User.Claims.FirstOrDefault(x => x.Type == key)?.Value; + + var str = raw?.ToString(); + data.Add(title, str); + } + private void AddDateTime(dynamic data, string title, string key) + { + var raw = User.Claims.FirstOrDefault(x => x.Type == key)?.Value; + var lng = long.Parse(raw); + + + var dt = DateTime.UnixEpoch.AddSeconds(lng); ; + var str = dt.ToString(); + data.Add(title, str); + } + } +} diff --git a/BLAZAM/Pages/Configure/ApiTokens.razor b/BLAZAM/Pages/Configure/ApiTokens.razor new file mode 100644 index 000000000..9722a0725 --- /dev/null +++ b/BLAZAM/Pages/Configure/ApiTokens.razor @@ -0,0 +1,70 @@ +@page "/api-tokens" +@attribute [Authorize(Roles = UserRoles.SuperAdmin)] + +@inherits DatabaseComponentBase +@AppLocalization["API Tokens"] + + + + + + + + @AppLocalization["API Tokens"] + + + + + + +@code { + List _tokens; + bool _showDeleted; + + bool ShowDeleted + { + get => _showDeleted; set + { + if (_showDeleted == value) return; + _showDeleted = value; + _=GetTokens(); + + } + } + protected override void OnInitialized() + { + base.OnInitialized(); + _=GetTokens(); + } + public async Task GetTokens() + { + LoadingData = true; + if (_showDeleted) + _tokens = await Context.ApiTokens.Include(t=>t.User).ToListAsync(); + else + _tokens = await Context.ApiTokens.Where(t => t.DeletedAt == null).Include(t => t.User).ToListAsync(); + LoadingData = false; + + } + public async Task Save() + { + await Context.SaveChangesAsync(); + SnackBarService.Success(AppLocalization["Changes have been saved"]); + } + + private async Task Delete(ApiToken? tokenToDelete) + { + if (await MessageService.Confirm(AppLocalization["Are you sure you want to delete this API token?"], AppLocalization["Confirm delete"])) + { + if (tokenToDelete != null) + { + tokenToDelete.DeletedAt = DateTime.UtcNow; + Context.SaveChangesAsync(); + } + } + + } +} diff --git a/BLAZAM/Pages/Configure/Audit.razor b/BLAZAM/Pages/Configure/Audit.razor index e62dcb98a..8b10ccfde 100644 --- a/BLAZAM/Pages/Configure/Audit.razor +++ b/BLAZAM/Pages/Configure/Audit.razor @@ -177,6 +177,16 @@ + + + + + + + @AppLocalization["Webhooks"] + + + diff --git a/BLAZAM/Pages/Configure/Fields.razor b/BLAZAM/Pages/Configure/Fields.razor index ee92a9053..fc1705c51 100644 --- a/BLAZAM/Pages/Configure/Fields.razor +++ b/BLAZAM/Pages/Configure/Fields.razor @@ -88,15 +88,14 @@ async Task> RefreshData(GridState currentState) { LoadingData = true; - await InvokeAsync(StateHasChanged); if (Context != null) { ADFields = await Context.CustomActiveDirectoryFields.Where(x => x.DeletedAt == null).ToListAsync(); } - LoadingData = false; var data = new GridData(); data.Items = ADFields; data.TotalItems = ADFields.Count; + LoadingData = false; return data; } diff --git a/BLAZAM/Pages/Configure/Permissions.razor b/BLAZAM/Pages/Configure/Permissions.razor index bfb88e993..d4b6dec4c 100644 --- a/BLAZAM/Pages/Configure/Permissions.razor +++ b/BLAZAM/Pages/Configure/Permissions.razor @@ -8,7 +8,7 @@ - + @AppLocalization["Permissions"] diff --git a/BLAZAM/Pages/Configure/Settings.razor b/BLAZAM/Pages/Configure/Settings.razor index 8d31f69ed..8cb5c5a0a 100644 --- a/BLAZAM/Pages/Configure/Settings.razor +++ b/BLAZAM/Pages/Configure/Settings.razor @@ -7,6 +7,7 @@ @AppLocalization["Settings"] + @AppLocalization["Settings"] @{ diff --git a/BLAZAM/Pages/Configure/WebHookAuditContent.razor b/BLAZAM/Pages/Configure/WebHookAuditContent.razor new file mode 100644 index 000000000..4358f7c55 --- /dev/null +++ b/BLAZAM/Pages/Configure/WebHookAuditContent.razor @@ -0,0 +1,77 @@ + @inherits DatabaseComponentBase + + + + + + @context.Item.MessageGuid.ToString() + + + + + + + @context.Item.LastAttemptTimestamp.ToLocalTime() + + + + + + + + + + + + + @context.Item.EventTimestamp.ToLocalTime() + + + + + + + + + + + +@code { + private List _attempts = new(); + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + _ = GetAttempts(); + } + private async Task GetAttempts() + { + _attempts = await Context.WebHookAttempts.ToListAsync(); + await InvokeAsync(StateHasChanged); + } +} + diff --git a/BLAZAM/Pages/Configure/WebHooks.razor b/BLAZAM/Pages/Configure/WebHooks.razor new file mode 100644 index 000000000..12ab54bb1 --- /dev/null +++ b/BLAZAM/Pages/Configure/WebHooks.razor @@ -0,0 +1,27 @@ +@page "/webhooks" +@using BLAZAM.Gui.UI.Settings.WebHooks +@attribute [Authorize(Roles = UserRoles.SuperAdmin)] + +@inherits TabbedAppComponentBase + +@AppLocalization["Webhooks"] + + + + + + + + + + + + + + + + + +@code { + +} diff --git a/BLAZAM/Pages/Groups/ConfirmNewGroup.razor b/BLAZAM/Pages/Groups/ConfirmNewGroup.razor index cb1db960f..6ab8b18e7 100644 --- a/BLAZAM/Pages/Groups/ConfirmNewGroup.razor +++ b/BLAZAM/Pages/Groups/ConfirmNewGroup.razor @@ -33,20 +33,20 @@ createGroupJob.StopOnFailedStep = true; createGroupJob.ShowJobDetailsDialog(MessageService); var result = await Group.CommitChangesAsync(createGroupJob); - + disableCreateGroupButton = false; await InvokeAsync(StateHasChanged); if (result.FailedSteps.Count == 0) { + Group = (IADGroup)Directory.GetDirectoryEntryByDN(Group.DN); SnackBarService.Success("Group created"); + await AuditLogger.Group.Created(Group); + _ = OUNotificationService.PostAsync(Group, NotificationType.Create, CurrentUser.State); + await Confirmed.InvokeAsync(Group); } - await AuditLogger.Group.Created(Group); - _ = OUNotificationService.PostAsync(Group, NotificationType.Create,CurrentUser.State); - - var commitJob = Group.CommitChanges(); - await Confirmed.InvokeAsync(Group); + } } } \ No newline at end of file diff --git a/BLAZAM/Pages/Groups/CreateGroup.razor b/BLAZAM/Pages/Groups/CreateGroup.razor index c5e822137..e02cb7496 100644 --- a/BLAZAM/Pages/Groups/CreateGroup.razor +++ b/BLAZAM/Pages/Groups/CreateGroup.razor @@ -1,4 +1,4 @@ -@page "/groups/create" +@page "/create/group" @inherits TabbedAppComponentBase @attribute [Authorize] Create Group @@ -106,7 +106,6 @@ async void CreateNewGroup() { LoadingData = true; - await InvokeAsync(StateHasChanged); newGroup = parentOU?.CreateGroup(newGroupName); @@ -116,6 +115,5 @@ LoadingData = true; - await InvokeAsync(StateHasChanged); } } diff --git a/BLAZAM/Pages/Login.razor b/BLAZAM/Pages/Login.razor index 6c38d2271..1afcfcb5e 100644 --- a/BLAZAM/Pages/Login.razor +++ b/BLAZAM/Pages/Login.razor @@ -8,6 +8,7 @@ { } +@AppLocalization["Login"] diff --git a/BLAZAM/Pages/OU/ConfirmNewOU.razor b/BLAZAM/Pages/OU/ConfirmNewOU.razor index e004b666f..169c6e057 100644 --- a/BLAZAM/Pages/OU/ConfirmNewOU.razor +++ b/BLAZAM/Pages/OU/ConfirmNewOU.razor @@ -13,9 +13,9 @@ } @code { -#nullable disable warnings + #nullable disable warnings + - [Parameter] public EventCallback Confirmed { get; set; } @@ -27,12 +27,16 @@ { if (await MessageService.Confirm("Are you sure you want to create this OU?", "Create OU")) { - await OU.CommitChangesAsync(); - SnackBarService.Success("OU created"); - await AuditLogger.OU.Created(OU); - _ = OUNotificationService.PostAsync(OU, NotificationType.Create, CurrentUser.State); - await Confirmed.InvokeAsync(OU); - Nav.NavigateTo("/ou/create",true); + var results = await OU.CommitChangesAsync(); + if (results.FailedSteps.Count == 0) + { + OU = (IADOrganizationalUnit)Directory.GetDirectoryEntryByDN(OU.DN); + SnackBarService.Success("OU created"); + await AuditLogger.OU.Created(OU); + _ = OUNotificationService.PostAsync(OU, NotificationType.Create, CurrentUser.State); + await Confirmed.InvokeAsync(OU); + Nav.NavigateTo("/ou/create", true); + } } } } \ No newline at end of file diff --git a/BLAZAM/Pages/OU/CreateOU.razor b/BLAZAM/Pages/OU/CreateOU.razor index 623bacf15..a4cb89237 100644 --- a/BLAZAM/Pages/OU/CreateOU.razor +++ b/BLAZAM/Pages/OU/CreateOU.razor @@ -1,4 +1,4 @@ -@page "/ou/create" +@page "/create/ou" @inherits TabbedAppComponentBase @attribute [Authorize] @AppLocalization["Create OU"] @@ -94,7 +94,6 @@ LoadingData = false; - await InvokeAsync(StateHasChanged); } } diff --git a/BLAZAM/Pages/Search.razor b/BLAZAM/Pages/Search.razor index 2826247f0..01409c7f5 100644 --- a/BLAZAM/Pages/Search.razor +++ b/BLAZAM/Pages/Search.razor @@ -197,7 +197,6 @@ Searcher.Results.Clear(); LoadingData = false; - await InvokeAsync(StateHasChanged); diff --git a/BLAZAM/Pages/Users/ConfirmNewUser.razor b/BLAZAM/Pages/Users/ConfirmNewUser.razor index f4e980a89..9679c155a 100644 --- a/BLAZAM/Pages/Users/ConfirmNewUser.razor +++ b/BLAZAM/Pages/Users/ConfirmNewUser.razor @@ -86,12 +86,12 @@ InDirectoryTemplate(ActiveDirectoryFields.PhysicalDeliveryOffice) } @if (InDirectoryTemplate(ActiveDirectoryFields.HomePhone) || - InDirectoryTemplate(ActiveDirectoryFields.StreetAddress) || - InDirectoryTemplate(ActiveDirectoryFields.POBox) || - InDirectoryTemplate(ActiveDirectoryFields.City) || - InDirectoryTemplate(ActiveDirectoryFields.State) || - InDirectoryTemplate(ActiveDirectoryFields.PostalCode) - ) + InDirectoryTemplate(ActiveDirectoryFields.StreetAddress) || + InDirectoryTemplate(ActiveDirectoryFields.POBox) || + InDirectoryTemplate(ActiveDirectoryFields.City) || + InDirectoryTemplate(ActiveDirectoryFields.State) || + InDirectoryTemplate(ActiveDirectoryFields.PostalCode) + ) {
@@ -146,10 +146,10 @@ InDirectoryTemplate(ActiveDirectoryFields.PhysicalDeliveryOffice) Disabled=true /> } @if (InDirectoryTemplate(ActiveDirectoryFields.HomeDirectory) || - InDirectoryTemplate(ActiveDirectoryFields.HomeDrive) || - InDirectoryTemplate(ActiveDirectoryFields.ScriptPath) || - InDirectoryTemplate(ActiveDirectoryFields.ProfilePath) - ) + InDirectoryTemplate(ActiveDirectoryFields.HomeDrive) || + InDirectoryTemplate(ActiveDirectoryFields.ScriptPath) || + InDirectoryTemplate(ActiveDirectoryFields.ProfilePath) + ) {
@if (InDirectoryTemplate(ActiveDirectoryFields.HomeDirectory)) @@ -276,11 +276,12 @@ else _username = User.SamAccountName; _userPassword = User.NewPassword; var result = await User.CommitChangesAsync(createUserJob); - disableCreateUserButton = false; await InvokeAsync(StateHasChanged); if (result.FailedSteps.Count == 0) { + User = (IADUser)Directory.GetDirectoryEntryByDN(User.DN); + SnackBarService.Success("User has been created"); confirmed = true; await Confirmed.InvokeAsync(); diff --git a/BLAZAM/Pages/Users/CreateUser.razor b/BLAZAM/Pages/Users/CreateUser.razor index e7e962ec4..438e3420d 100644 --- a/BLAZAM/Pages/Users/CreateUser.razor +++ b/BLAZAM/Pages/Users/CreateUser.razor @@ -1,4 +1,4 @@ -@page "/users/create" +@page "/create/user" @inherits TemplateComponent @attribute [Authorize(Roles = UserRoles.CreateUsers)] @@ -356,34 +356,8 @@ newUser?.StagePasswordChange(customConfirmPassword.ToSecureString()); { LoadingData = true; await InvokeAsync(StateHasChanged); - var ou = Directory.OUs.FindOuByString(SelectedTemplate.EffectiveParentOU).FirstOrDefault(); - if (ou == null) throw new ApplicationException("OU could not be found for new user"); - newUser = ou.CreateUser(SelectedTemplate.GenerateDisplayName(newUserName)); - - newUser.SamAccountName = SelectedTemplate.GenerateUsername(newUserName); - newUser.DisplayName = SelectedTemplate.GenerateDisplayName(newUserName); - //newUser.SetPassword(SelectedTemplate.GeneratePassword().ToSecureString(),false); - //newUser.CanonicalName = SelectedTemplate.GenerateDisplayName(newUserName); - newUser.StagePasswordChange(SelectedTemplate.GeneratePassword(newUserName).ToSecureString()); - if (SelectedTemplate.EffectiveRequirePasswordChange == true) - newUser.StageRequirePasswordChange(true); - if (!newUserName.GivenName.IsNullOrEmpty()) - newUser.GivenName = newUserName.GivenName; - if (!newUserName.MiddleName.IsNullOrEmpty()) - newUser.MiddleName = newUserName.MiddleName; - if (!newUserName.Surname.IsNullOrEmpty()) - newUser.Surname = newUserName.Surname; - - - - SelectedTemplate.EffectiveAssignedGroupSids.ForEach(sid => - { - var group = Directory.Groups.FindGroupBySID(sid.GroupSid); - if (group != null) - newUser.AssignTo(group); - - }); - if (IsAdmin || SelectedTemplate.HasEmptyFields()) + newUser = SelectedTemplate.GenerateTemplateUser(newUserName,Directory); + if (IsAdmin || SelectedTemplate.HasEditableFields()) { SelectedStep = 2; } @@ -405,7 +379,7 @@ newUser?.StagePasswordChange(customConfirmPassword.ToSecureString()); await InvokeAsync(StateHasChanged); } - + async Task SetTemplate(DirectoryTemplate selectedTemplate) { SelectedTemplate = selectedTemplate; diff --git a/BLAZAM/Pages/View.razor b/BLAZAM/Pages/View.razor index 869d92b52..c0ceaf837 100644 --- a/BLAZAM/Pages/View.razor +++ b/BLAZAM/Pages/View.razor @@ -174,14 +174,12 @@ results.Clear(); LoadingData = true; - await InvokeAsync(StateHasChanged); if (!SearchTermParameter.IsNullOrEmpty() && SearchTermParameter?.Length > 0) await InvokeSearch(); else Searcher.Results.Clear(); LoadingData = false; - await InvokeAsync(StateHasChanged); diff --git a/BLAZAM/Pages/_Layout.cshtml b/BLAZAM/Pages/_Layout.cshtml index 4ad942ecf..64304cfaa 100644 --- a/BLAZAM/Pages/_Layout.cshtml +++ b/BLAZAM/Pages/_Layout.cshtml @@ -41,8 +41,9 @@ } else { - An unhandled exception has occurred.See browser dev tools for details. + An unhandled exception has occurred. See browser dev tools for details. + } Reload 🗙 @@ -112,4 +113,4 @@ - + \ No newline at end of file diff --git a/BLAZAM/Program.cs b/BLAZAM/Program.cs index 3a1b15b8c..537bbb427 100644 --- a/BLAZAM/Program.cs +++ b/BLAZAM/Program.cs @@ -4,13 +4,18 @@ using BLAZAM.Database.Models; using BLAZAM.Server; using BLAZAM.Server.Middleware; +using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.Extensions.Hosting.WindowsServices; +using Microsoft.IdentityModel.Tokens; using Serilog; +using System; using System.Diagnostics; using System.Net; +using System.Security; using System.Security.Cryptography.X509Certificates; +using System.Text; namespace BLAZAM { @@ -51,8 +56,9 @@ public class Program /// core application configuration /// public static ConfigurationManager? Configuration { get; set; } + /// - /// Indicates whether the Account running the website can wrrite to the writable path + /// Indicates whether the Account running the website can write to the writable path /// public static bool Writable { get; private set; } @@ -156,6 +162,8 @@ public static void Main(string[] args) AppInstance.UseMiddleware(); AppInstance.UseStaticFiles(); AppInstance.UseRouting(); + AppInstance.MapControllers(); + //AppInstance.UseCors(builder => // builder.AllowAnyOrigin() // .SetIsOriginAllowed((host) => true) @@ -166,10 +174,30 @@ public static void Main(string[] args) AppInstance.UseAuthentication(); AppInstance.UseAuthorization(); AppInstance.UseSession(); + AppInstance.UseSwagger(); + AppInstance.UseSwaggerUI(c => + { + c.SwaggerEndpoint("v1/swagger.json", "Blazam API V1"); + }); + AppInstance.MapSwagger().RequireAuthorization(); //AppInstance.MapControllers(); AppInstance.MapBlazorHub(); AppInstance.MapFallbackToPage("/_Host"); + //AppInstance.UseExceptionHandler(errorApp => + //{ + // errorApp.Run(async context => + // { + // var exceptionHandlerPathFeature = context.Features.Get(); + + // var exception = exceptionHandlerPathFeature?.Error; + + // // Log the exception (you can use a logging framework like Serilog or NLog) + // Console.WriteLine($"Unhandled exception: {exception}"); + + // // ... optional: Customize the response (e.g., return a custom error page) + // }); + //}); AppInstance.Start(); GetRunningWebServerConfiguration(); //ScheduleAutoLoad(); diff --git a/BLAZAM/ProgramHelpers.cs b/BLAZAM/ProgramHelpers.cs index 78face01b..2235ad68d 100644 --- a/BLAZAM/ProgramHelpers.cs +++ b/BLAZAM/ProgramHelpers.cs @@ -1,9 +1,9 @@  +using BLAZAM.ActiveDirectory.Searchers; using BLAZAM.Common.Data; using BLAZAM.Common.Data.Services; using BLAZAM.Database.Context; using BLAZAM.Gui.Services; -using BLAZAM.Nav; using BLAZAM.Notifications.Services; using BLAZAM.Services; using BLAZAM.Services.Audit; @@ -14,17 +14,29 @@ using BLAZAM.Update.Services; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authentication.Negotiate; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; +using MimeKit; using MudBlazor; using MudBlazor.Services; +using Polly.Extensions.Http; +using Polly; +using Serilog; using System.Diagnostics; using System.Globalization; using System.Management; using System.Reflection; +using System.Text; +using Polly.Contrib.WaitAndRetry; namespace BLAZAM.Server { public static class ProgramHelpers { + /// /// Sets up the core configuration like debug, installation id, and running process and version /// @@ -141,46 +153,49 @@ public static WebApplicationBuilder InjectServices(this WebApplicationBuilder bu builder.Services.AddSingleton(); - - //Set up authentication and api token authentication + // Set up authentication and API token authentication builder.Services.Configure(options => { options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); - builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) - .AddCookie(AppAuthenticationStateProvider.ApplyAuthenticationCookieOptions()); - builder.Services.Configure(options => + builder.Services.AddAuthentication(options => { - options.RequireAuthenticatedSignIn = false; - }); - /* - Keeping this here for a possible API in the future - It's some original test code from before AppAuthenticatinProvider was - completed so it may not be usable as is + options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; - builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) - .AddNegotiate().AddJwtBearer(options => + }).AddCookie(AppAuthenticationStateProvider.ApplyAuthenticationCookieOptions()) + .AddJwtBearer(options => // Configure JWT Bearer here { - options.TokenValidationParameters = - new TokenValidationParameters - { - ValidateAudience = false, - ValidateIssuer = false, - ValidateActor = false, - ValidateLifetime = true, - IssuerSigningKey = TokenKey - }; + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, // Important: Validate the signing key + IssuerSigningKey = ApplicationInfo.tokenKey, + ValidateIssuer = false, + ValidateAudience = false, + ValidateActor = false, + ValidateLifetime = true + }; + options.Events = new JwtAuthenticationEventsHandler( + builder.Services.BuildServiceProvider().GetRequiredService(), + builder.Services.BuildServiceProvider().GetRequiredService(), + builder.Services.BuildServiceProvider().GetRequiredService(), + builder.Services.BuildServiceProvider().GetRequiredService() + ); }); - - - builder.Services.AddAuthorization(options => + builder.Services.Configure(options => { - // By default, all incoming requests will be authorized according to the default policy. - options.FallbackPolicy = options.DefaultPolicy; + options.RequireAuthenticatedSignIn = false; }); - */ + /* + builder.Services.AddAuthorization(options => + { + // By default, all incoming requests will be authorized according to the default policy. + options.FallbackPolicy = options.DefaultPolicy; + }); + */ builder.Services.AddDistributedMemoryCache(); builder.Services.AddSession(options => @@ -211,6 +226,17 @@ completed so it may not be usable as is //Provide an Http client as a service with custom construction via api service class builder.Services.AddHttpClient(); + builder.Services.AddHttpClient(HttpClientNames.WebHookHttpClientName) + .SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Set lifetime to five minutes + .AddPolicyHandler(GetWebhookRetryPolicy()); + + builder.Services.AddHttpClient(HttpClientNames.WebHookHttpClientNoSSLCheckName) + .SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Set lifetime to five minutes + .AddPolicyHandler(GetWebhookRetryPolicy()).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (m, c, ch, e) => true + }); + //Also keeping this here for a possible future API, though this would be for internal use //builder.Services.AddTransient(); //builder.Services.AddTransient(provider => provider.GetService().HttpContext.User); @@ -245,6 +271,12 @@ completed so it may not be usable as is //Provide a AuditLogger as a service builder.Services.AddScoped(); + //Provide a JwtTokens as a service + builder.Services.AddScoped(); + + //Provide a JwtTokens as a service + builder.Services.AddSingleton(); + @@ -255,10 +287,6 @@ completed so it may not be usable as is builder.Services.AddScoped(); - //A substitute Navigation Manager for the app to enable navigation warning on unsaved - //changes - builder.Services.AddScoped(); - //A service to provide the appropriate widgets to users, based on permissions builder.Services.AddScoped(); @@ -307,9 +335,48 @@ completed so it may not be usable as is builder.Services.AddSingleton(); + builder.Services.AddControllers(options => + { + + options.Conventions.Add(new LowercaseControllerRouteConvention()); + }); + + builder.Services.AddMvc(); + + + + builder.Services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "Blazam API", + Version = "v1" , + Description="The official Blazam API documentation. Authorization is required for API access.", + License=new OpenApiLicense() { Name="MIT License", Url= new Uri("https://github.com/Blazam-App/BLAZAM/blob/v1-Dev/LICENSE") }, + Contact=new() { Email = "support@blazam.org", + Name="Blazam Support", + Url=new("https://blazam.org/support") }, + TermsOfService=new Uri("https://blazam.org/tos") + }); + + // Add descriptions using XML comments + var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); + + // Configure Swagger to use JWT Bearer authorization + //c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + //{ + // Description = "JWT Authorization header using the Bearer scheme.Example: \"Authorization: Bearer {token}\"", + // Name = "Authorization", + // In = ParameterLocation.Header, + // Type = SecuritySchemeType.Http, + // Scheme = "bearer", + // BearerFormat = "JWT" + //}); + }); builder.Host.UseWindowsService(); + + return builder; } @@ -333,7 +400,15 @@ public static void PreRun(this WebApplication application) PreloadServices(); } + static IAsyncPolicy GetWebhookRetryPolicy() + { + var delay = Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay: TimeSpan.FromSeconds(1), retryCount: 5); + return HttpPolicyExtensions + .HandleTransientHttpError() + .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound) + .WaitAndRetryAsync(delay); + } private static void PreloadServices() { try @@ -364,6 +439,19 @@ private static void PreloadServices() context.Initialize(); } + } + catch (Exception ex) + { + Loggers.SystemLogger.Error(ex.Message + " {@Error}", ex); + } + try + { + if (ApplicationInfo.installationCompleted) + { + var context = Program.AppInstance.Services.GetRequiredService(); + + } + } catch (Exception ex) { diff --git a/BLAZAM/_Imports.razor b/BLAZAM/_Imports.razor index b367d5f8d..da211f2a1 100644 --- a/BLAZAM/_Imports.razor +++ b/BLAZAM/_Imports.razor @@ -32,7 +32,6 @@ @using BLAZAM @using BLAZAM.Jobs -@using BLAZAM.Nav @using BLAZAM.ActiveDirectory; @using BLAZAM.ActiveDirectory.Interfaces; diff --git a/BLAZAMActiveDirectory/ActiveDirectoryContext.cs b/BLAZAMActiveDirectory/ActiveDirectoryContext.cs index 7ddd66159..42d82cf63 100644 --- a/BLAZAMActiveDirectory/ActiveDirectoryContext.cs +++ b/BLAZAMActiveDirectory/ActiveDirectoryContext.cs @@ -26,6 +26,7 @@ public IApplicationUserState? CurrentUser get { if (currentUser != null) return currentUser; + if (UserStateService.CurrentUserState != null) return UserStateService.CurrentUserState; //throw new ApplicationException("Current User State was not provided to this directory entry"); return null; } diff --git a/BLAZAMActiveDirectory/Adapters/ADOrganizationalUnit.cs b/BLAZAMActiveDirectory/Adapters/ADOrganizationalUnit.cs index 9082141ac..aa30363ab 100644 --- a/BLAZAMActiveDirectory/Adapters/ADOrganizationalUnit.cs +++ b/BLAZAMActiveDirectory/Adapters/ADOrganizationalUnit.cs @@ -241,7 +241,7 @@ public IADOrganizationalUnit CreateOU(string containerName) /// Directory. /// /// The container name of the new user - /// An uncommited user + /// An uncommitted user public IADUser CreateUser(string containerName) { var fullContainerName = "CN=" + containerName.Trim().Replace(",", "\\,"); diff --git a/BLAZAMActiveDirectory/Adapters/DirectoryEntryAdapter.cs b/BLAZAMActiveDirectory/Adapters/DirectoryEntryAdapter.cs index ed8b7a3ad..d78db9de6 100644 --- a/BLAZAMActiveDirectory/Adapters/DirectoryEntryAdapter.cs +++ b/BLAZAMActiveDirectory/Adapters/DirectoryEntryAdapter.cs @@ -19,6 +19,7 @@ using System.DirectoryServices.ActiveDirectory; using System.Linq; using System.Reflection; +using System.Text.RegularExpressions; using System.Web; using static MudBlazor.Colors; @@ -305,6 +306,23 @@ public virtual string? DN { get { + if (NewEntry) + { + // Regular expression to match everything after the last slash + string pattern = @"[^/]+$"; + + Match match = Regex.Match(ADSPath, pattern); + + if (match.Success) + { + return match.Value; + } + else + { + // Handle the case where no match is found + return null; + } + } return GetStringProperty("distinguishedName"); } set @@ -643,7 +661,6 @@ public virtual IEnumerable Children { List directoryEntries = new List(); var children = DirectoryEntry.Children; - DirectoryEntryAdapter? thisObject = null; var list = new List(); foreach (DirectoryEntry child in children) { @@ -651,6 +668,8 @@ public virtual IEnumerable Children } Parallel.ForEach(list, child => { + DirectoryEntryAdapter? thisObject = null; + if (child.Properties["objectClass"].Contains("top")) { var objectClass = child.Properties["objectClass"]; @@ -690,7 +709,6 @@ public virtual IEnumerable Children } } - thisObject = null; }); //foreach (DirectoryEntry child in children) //{ diff --git a/BLAZAMCommon.Tests/BLAZAMCommon.Tests.csproj b/BLAZAMCommon.Tests/BLAZAMCommon.Tests.csproj index 8a422ad12..546f0ac15 100644 --- a/BLAZAMCommon.Tests/BLAZAMCommon.Tests.csproj +++ b/BLAZAMCommon.Tests/BLAZAMCommon.Tests.csproj @@ -9,10 +9,10 @@ - - - - + + + + diff --git a/BLAZAMCommon/BLAZAMCommon.csproj b/BLAZAMCommon/BLAZAMCommon.csproj index 2d21df84e..37874a031 100644 --- a/BLAZAMCommon/BLAZAMCommon.csproj +++ b/BLAZAMCommon/BLAZAMCommon.csproj @@ -20,12 +20,12 @@ - - - - - - + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/BLAZAMCommon/Data/ApplicationInfo.cs b/BLAZAMCommon/Data/ApplicationInfo.cs index 5b74d500d..13a4f7e3a 100644 --- a/BLAZAMCommon/Data/ApplicationInfo.cs +++ b/BLAZAMCommon/Data/ApplicationInfo.cs @@ -1,8 +1,12 @@ using BLAZAM.FileSystem; using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; +using System.Configuration; using System.Diagnostics; +using System.Security.Cryptography.X509Certificates; +using System.Text; namespace BLAZAM.Common.Data @@ -73,9 +77,13 @@ public class ApplicationInfo /// A local store of the .Net web application Services /// public static IServiceProvider services; + public static SymmetricSecurityKey tokenKey { get { + + var keyString = configuration.GetValue("EncryptionKey"); + var keyBytes = Encoding.ASCII.GetBytes(keyString); + return new SymmetricSecurityKey(keyBytes); + } } - [Obsolete("Not using any local REST API")] - public static SecurityKey tokenKey; /// @@ -104,6 +112,8 @@ public class ApplicationInfo /// eg: C:\Users\user\appdata\temp\ /// public SystemDirectory TempDirectory { get => tempDirectory; set => tempDirectory = value; } + public Microsoft.Extensions.Configuration.ConfigurationManager Configuration { get => configuration; } + public static Microsoft.Extensions.Configuration.ConfigurationManager configuration; /// /// A collection of active listening address's with port @@ -147,6 +157,7 @@ public static bool installationCompleted get; set; } public Guid InstallationId { get => installationId; set => installationId = value; } + public SymmetricSecurityKey TokenKey { get => tokenKey; } /// /// Use only for UnitTests @@ -165,6 +176,7 @@ public ApplicationInfo(WebApplicationBuilder builder) RunningProcess = Process.GetCurrentProcess(); ApplicationRoot = new SystemDirectory(builder.Environment.ContentRootPath); TempDirectory = new SystemDirectory(Path.GetTempPath() + "Blazam\\"); + configuration = builder.Configuration; //AppDataDirectory = new SystemDirectory(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "Blazam\\"); } } diff --git a/BLAZAMCommon/Data/Encryption.cs b/BLAZAMCommon/Data/Encryption.cs index 168efd335..5021f46ab 100644 --- a/BLAZAMCommon/Data/Encryption.cs +++ b/BLAZAMCommon/Data/Encryption.cs @@ -1,4 +1,6 @@ using BLAZAM.Helpers; +using Microsoft.Extensions.Configuration; +using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json; using System.Security.Cryptography; using System.Text; @@ -93,12 +95,32 @@ private byte[] GenerateOldKeyFromSeedString() /// The key based on the private byte[] GenerateKeyFromSeedString(byte[] salt) { - // Use a key derivation function to generate a repeatable key - var keyGenerator = new Rfc2898DeriveBytes(KeySeedString, salt, 1000); + + // Use a key derivation function to generate a repeatable key + var keyGenerator = new Rfc2898DeriveBytes(KeySeedString, salt, 1000); + + return keyGenerator.GetBytes(KeySize / 8); ; + - return keyGenerator.GetBytes(KeySize / 8); ; } + /// + /// Generates a key of the configured key size, seeding the + /// key from the appsettings configuration value "EncryptionKey" + /// + /// + /// Sets the local value to the newly generated key + /// + /// The key based on the + private static byte[] GenerateKeyFromSeedString(byte[] salt, string seedString,int keySize) + { + // Use a key derivation function to generate a repeatable key + var keyGenerator = new Rfc2898DeriveBytes(seedString, salt, 1000); + + return keyGenerator.GetBytes(keySize / 8); ; + + + } /// /// Decrypts cipher-text /// @@ -263,6 +285,7 @@ public string EncryptObject(object obj) } } + } diff --git a/BLAZAMCommon/Data/HttpClientNames.cs b/BLAZAMCommon/Data/HttpClientNames.cs new file mode 100644 index 000000000..05753e4ba --- /dev/null +++ b/BLAZAMCommon/Data/HttpClientNames.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BLAZAM.Common.Data +{ + public class HttpClientNames + { + public const string WebHookHttpClientName = "WebHookClient"; + public const string WebHookHttpClientNoSSLCheckName = "WebHookClientIgnoreSSL"; + + } +} diff --git a/BLAZAMCommon/Data/LowercaseControllerRouteConvention.cs b/BLAZAMCommon/Data/LowercaseControllerRouteConvention.cs new file mode 100644 index 000000000..34955c95f --- /dev/null +++ b/BLAZAMCommon/Data/LowercaseControllerRouteConvention.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.AspNetCore.Routing; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BLAZAM.Common.Data +{ + public class LowercaseControllerRouteConvention : IApplicationModelConvention + { + public void Apply(ApplicationModel application) + { + foreach (var controller in application.Controllers) + + { + foreach (var selector in controller.Selectors) + + { + foreach (var template in selector.AttributeRouteModel.Template.Split('/')) + { + if (template == "[controller]") + { + // Replace "[controller]" with lowercase controller name + selector.AttributeRouteModel.Template = + selector.AttributeRouteModel.Template.Replace( + "[controller]", + controller.ControllerName.ToLower()); + } + } + } + } + } + } +} diff --git a/BLAZAMCommon/Data/NewUserName.cs b/BLAZAMCommon/Data/NewUserName.cs index 8e1d9ede8..37bdfadd1 100644 --- a/BLAZAMCommon/Data/NewUserName.cs +++ b/BLAZAMCommon/Data/NewUserName.cs @@ -12,7 +12,7 @@ public class NewUserName public string? MiddleName { get => middleName; set => middleName = value; } - public string Surname { get => surname; set => surname = value; } + public string? Surname { get => surname; set => surname = value; } } diff --git a/BLAZAMCommon/Data/Services/IEncryptionService.cs b/BLAZAMCommon/Data/Services/IEncryptionService.cs index 0daefaa29..d2d7189ef 100644 --- a/BLAZAMCommon/Data/Services/IEncryptionService.cs +++ b/BLAZAMCommon/Data/Services/IEncryptionService.cs @@ -5,6 +5,7 @@ namespace BLAZAM.Common.Data.Services public interface IEncryptionService { ServiceConnectionState Status { get; } + byte[] Key { get; } T? DecryptObject(string? cipherText); string EncryptObject(object obj); diff --git a/BLAZAMCommon/Data/SubdirectoryAwareRouteConvention.cs b/BLAZAMCommon/Data/SubdirectoryAwareRouteConvention.cs new file mode 100644 index 000000000..872b55ea4 --- /dev/null +++ b/BLAZAMCommon/Data/SubdirectoryAwareRouteConvention.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BLAZAM.Common.Data +{ + public class SubdirectoryAwareRouteConvention : IApplicationModelConvention + { + public void Apply(ApplicationModel application) + { + foreach (var controller in application.Controllers) + + { + var namespaceParts = controller.ControllerType.Namespace?.Split('.'); + + if (namespaceParts?.Length > 3) // Check if the namespace has at least 3 levels (BLAZAM.Pages.API.v1...) + { + var subdirectoryPath = string.Join('/', namespaceParts.Skip(3)); // Skip the first 3 parts (BLAZAM.Pages.API.v1) + + foreach (var selector in controller.Selectors) + { + // Prepend "api/v1" and the subdirectory path to the route template + selector.AttributeRouteModel.Template = $"api/v1/{subdirectoryPath}/{selector.AttributeRouteModel.Template}"; + } + } + else + { + // For controllers in shallower namespaces, still prepend "api/v1" + foreach (var selector in controller.Selectors) + { + selector.AttributeRouteModel.Template = $"api/v1/{selector.AttributeRouteModel.Template}"; + } + } + } + } + } +} diff --git a/BLAZAMDatabase/BLAZAMDatabase.csproj b/BLAZAMDatabase/BLAZAMDatabase.csproj index 50954a83f..14118982a 100644 --- a/BLAZAMDatabase/BLAZAMDatabase.csproj +++ b/BLAZAMDatabase/BLAZAMDatabase.csproj @@ -8,12 +8,12 @@ - - - - - - + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/BLAZAMDatabase/Context/DatabaseContextBase.cs b/BLAZAMDatabase/Context/DatabaseContextBase.cs index 5e0487973..ce9fe1653 100644 --- a/BLAZAMDatabase/Context/DatabaseContextBase.cs +++ b/BLAZAMDatabase/Context/DatabaseContextBase.cs @@ -101,6 +101,10 @@ public DatabaseContextBase(DbContextOptions options) : base(options) public virtual DbSet AuthenticationSettings { get; set; } public virtual DbSet EmailSettings { get; set; } public virtual DbSet EmailTemplates { get; set; } + public virtual DbSet WebHookSubscriptions { get; set; } + public virtual DbSet WebHookAttempts{ get; set; } + + //User Tables public virtual DbSet UserSettings { get; set; } @@ -110,11 +114,13 @@ public DatabaseContextBase(DbContextOptions options) : base(options) public virtual DbSet UserDashboardWidgets { get; set; } public virtual DbSet NotificationMessages { get; set; } public virtual DbSet NotificationSubscriptions { get; set; } + public virtual DbSet ApiTokens { get; set; } //Audit Logs public virtual DbSet SystemAuditLog { get; set; } public virtual DbSet LogonAuditLog { get; set; } + //public virtual DbSet EmailAuditLog { get; set; } public virtual DbSet DirectoryEntryAuditLogs { get; set; } //public virtual DbSet UserAuditLog { get; set; } //public virtual DbSet GroupAuditLog { get; set; } @@ -548,6 +554,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { + //entity.Navigation(e => e.ParentTemplate).AutoInclude(); entity.Navigation(e => e.AssignedGroupSids).AutoInclude(); }); diff --git a/BLAZAMDatabase/Context/IDatabaseContext.cs b/BLAZAMDatabase/Context/IDatabaseContext.cs index 1ad358357..d27dfd0ba 100644 --- a/BLAZAMDatabase/Context/IDatabaseContext.cs +++ b/BLAZAMDatabase/Context/IDatabaseContext.cs @@ -27,6 +27,7 @@ public interface IDatabaseContext : IEFCoreDbContext DbSet DirectoryTemplates { get; set; } DbSet EmailSettings { get; set; } DbSet EmailTemplates { get; set; } + //DbSet EmailAuditLog { get; set; } DbSet FieldAccessLevel { get; set; } //DbSet GroupAuditLog { get; set; } DbSet LogonAuditLog { get; set; } @@ -66,6 +67,9 @@ public interface IDatabaseContext : IEFCoreDbContext DbSet NotificationSubscriptions { get; set; } DbSet GlobalPermissionSettings { get; set; } DbSet GlobalPermissionRequestActions { get; set; } + DbSet ApiTokens { get; set; } + DbSet WebHookSubscriptions { get; set; } + DbSet WebHookAttempts { get; set; } void Export(string directory); } diff --git a/BLAZAMDatabase/Migrations/MySql/20241123221439_Add_Webhooks_MySql.Designer.cs b/BLAZAMDatabase/Migrations/MySql/20241123221439_Add_Webhooks_MySql.Designer.cs new file mode 100644 index 000000000..78f7020b7 --- /dev/null +++ b/BLAZAMDatabase/Migrations/MySql/20241123221439_Add_Webhooks_MySql.Designer.cs @@ -0,0 +1,2243 @@ +// +using System; +using BLAZAM.Database.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BLAZAM.Database.Migrations.MySql +{ + [DbContext(typeof(MySqlDatabaseContext))] + [Migration("20241123221439_Add_Webhooks_MySql")] + partial class Add_Webhooks_MySql + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("AccessLevelFieldAccessMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("int"); + + b.Property("FieldMapId") + .HasColumnType("int"); + + b.HasKey("AccessLevelsId", "FieldMapId"); + + b.HasIndex("FieldMapId"); + + b.ToTable("AccessLevelFieldAccessMapping"); + }); + + modelBuilder.Entity("AccessLevelObjectAccessMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("int"); + + b.Property("ObjectMapId") + .HasColumnType("int"); + + b.HasKey("AccessLevelsId", "ObjectMapId"); + + b.HasIndex("ObjectMapId"); + + b.ToTable("AccessLevelObjectAccessMapping"); + }); + + modelBuilder.Entity("AccessLevelPermissionMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("int"); + + b.Property("PermissionMapsId") + .HasColumnType("int"); + + b.HasKey("AccessLevelsId", "PermissionMapsId"); + + b.HasIndex("PermissionMapsId"); + + b.ToTable("AccessLevelPermissionMapping"); + }); + + modelBuilder.Entity("AppUserChatRoom", b => + { + b.Property("ChatRoomId") + .HasColumnType("int"); + + b.Property("MembersId") + .HasColumnType("int"); + + b.HasKey("ChatRoomId", "MembersId"); + + b.HasIndex("MembersId"); + + b.ToTable("AppUserChatRoom"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ADSettings", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("ApplicationBaseDN") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FQDN") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerPort") + .HasColumnType("int"); + + b.Property("UseTLS") + .HasColumnType("tinyint(1)"); + + b.Property("Username") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ActiveDirectorySettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "Id = 1"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryField", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FieldName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FieldType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("ActiveDirectoryFields"); + + b.HasData( + new + { + Id = 1, + DisplayName = "Last Name", + FieldName = "sn", + FieldType = 0 + }, + new + { + Id = 2, + DisplayName = "First Name", + FieldName = "givenname", + FieldType = 0 + }, + new + { + Id = 3, + DisplayName = "Office", + FieldName = "physicalDeliveryOfficeName", + FieldType = 0 + }, + new + { + Id = 4, + DisplayName = "Employee ID", + FieldName = "employeeId", + FieldType = 0 + }, + new + { + Id = 5, + DisplayName = "Home Directory", + FieldName = "homeDirectory", + FieldType = 0 + }, + new + { + Id = 6, + DisplayName = "Logon Script Path", + FieldName = "scriptPath", + FieldType = 0 + }, + new + { + Id = 7, + DisplayName = "Profile Path", + FieldName = "profilePath", + FieldType = 0 + }, + new + { + Id = 8, + DisplayName = "Home Phone Number", + FieldName = "homePhone", + FieldType = 0 + }, + new + { + Id = 9, + DisplayName = "Street Address", + FieldName = "streetAddress", + FieldType = 0 + }, + new + { + Id = 10, + DisplayName = "City", + FieldName = "l", + FieldType = 0 + }, + new + { + Id = 11, + DisplayName = "State", + FieldName = "st", + FieldType = 0 + }, + new + { + Id = 12, + DisplayName = "Zip Code", + FieldName = "postalCode", + FieldType = 0 + }, + new + { + Id = 13, + DisplayName = "Site", + FieldName = "site", + FieldType = 0 + }, + new + { + Id = 14, + DisplayName = "Name", + FieldName = "name", + FieldType = 0 + }, + new + { + Id = 15, + DisplayName = "Username", + FieldName = "samaccountname", + FieldType = 0 + }, + new + { + Id = 16, + DisplayName = "SID", + FieldName = "objectSID", + FieldType = 2 + }, + new + { + Id = 17, + DisplayName = "E-Mail Address", + FieldName = "mail", + FieldType = 0 + }, + new + { + Id = 18, + DisplayName = "Description", + FieldName = "description", + FieldType = 0 + }, + new + { + Id = 19, + DisplayName = "Display Name", + FieldName = "displayName", + FieldType = 0 + }, + new + { + Id = 20, + DisplayName = "Distinguished Name", + FieldName = "distinguishedName", + FieldType = 0 + }, + new + { + Id = 21, + DisplayName = "Member Of", + FieldName = "memberOf", + FieldType = 4 + }, + new + { + Id = 22, + DisplayName = "Company", + FieldName = "company", + FieldType = 0 + }, + new + { + Id = 23, + DisplayName = "Title", + FieldName = "title", + FieldType = 0 + }, + new + { + Id = 24, + DisplayName = "User Principal Name", + FieldName = "userPrincipalName", + FieldType = 0 + }, + new + { + Id = 25, + DisplayName = "Telephone Number", + FieldName = "telephoneNumber", + FieldType = 0 + }, + new + { + Id = 26, + DisplayName = "PO Box", + FieldName = "postOfficeBox", + FieldType = 0 + }, + new + { + Id = 27, + DisplayName = "Canonical Name", + FieldName = "cn", + FieldType = 0 + }, + new + { + Id = 28, + DisplayName = "Home Drive", + FieldName = "homeDrive", + FieldType = 3 + }, + new + { + Id = 29, + DisplayName = "Department", + FieldName = "department", + FieldType = 0 + }, + new + { + Id = 30, + DisplayName = "Middle Name", + FieldName = "middleName", + FieldType = 0 + }, + new + { + Id = 31, + DisplayName = "Pager", + FieldName = "pager", + FieldType = 0 + }, + new + { + Id = 32, + DisplayName = "OS", + FieldName = "operatingSystemVersion", + FieldType = 0 + }, + new + { + Id = 33, + DisplayName = "Account Expiration", + FieldName = "accountExpires", + FieldType = 1 + }, + new + { + Id = 34, + DisplayName = "Manager", + FieldName = "manager", + FieldType = 0 + }, + new + { + Id = 35, + DisplayName = "Photo", + FieldName = "thumbnail", + FieldType = 2 + }, + new + { + Id = 36, + DisplayName = "Log On To", + FieldName = "userWorkstations", + FieldType = 0 + }, + new + { + Id = 37, + DisplayName = "Logon Hours", + FieldName = "logonHours", + FieldType = 2 + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryFieldObjectType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ActiveDirectoryFieldId") + .HasColumnType("int"); + + b.Property("CustomActiveDirectoryFieldId") + .HasColumnType("int"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CustomActiveDirectoryFieldId"); + + b.ToTable("ActiveDirectoryFieldObjectMappings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.AppSettings", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("AnalyticsId") + .HasColumnType("longtext"); + + b.Property("AppFQDN") + .HasColumnType("longtext"); + + b.Property("AppIcon") + .HasColumnType("longblob"); + + b.Property("AppName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AutoUpdate") + .HasColumnType("tinyint(1)"); + + b.Property("AutoUpdateTime") + .HasColumnType("time(6)"); + + b.Property("ForceHTTPS") + .HasColumnType("tinyint(1)"); + + b.Property("InstallationCompleted") + .HasColumnType("tinyint(1)"); + + b.Property("LastUpdateCheck") + .HasColumnType("datetime(6)"); + + b.Property("MOTD") + .HasColumnType("longtext"); + + b.Property("MyrtilleURL") + .HasColumnType("longtext"); + + b.Property("SSLCertificateCipher") + .HasColumnType("longtext"); + + b.Property("SendDeveloperAnalytics") + .HasColumnType("tinyint(1)"); + + b.Property("SendLogsToDeveloper") + .HasColumnType("tinyint(1)"); + + b.Property("UpdateBranch") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdateDomain") + .HasColumnType("longtext"); + + b.Property("UpdatePassword") + .HasColumnType("longtext"); + + b.Property("UpdateUsername") + .HasColumnType("longtext"); + + b.Property("UseUpdateCredentials") + .HasColumnType("tinyint(1)"); + + b.Property("UserHelpdeskURL") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("AppSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "Id = 1") + .HasName("CK_Table_Column1"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.DirectoryEntryAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AfterAction") + .HasColumnType("longtext"); + + b.Property("BeforeAction") + .HasColumnType("longtext"); + + b.Property("IpAddress") + .HasColumnType("longtext"); + + b.Property("Sid") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Target") + .HasColumnType("longtext"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.Property("Username") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("DirectoryEntryAuditLogs"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.LogonAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AfterAction") + .HasColumnType("longtext"); + + b.Property("BeforeAction") + .HasColumnType("longtext"); + + b.Property("IpAddress") + .HasColumnType("longtext"); + + b.Property("Target") + .HasColumnType("longtext"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.Property("Username") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("LogonAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.PermissionsAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AfterAction") + .HasColumnType("longtext"); + + b.Property("BeforeAction") + .HasColumnType("longtext"); + + b.Property("IpAddress") + .HasColumnType("longtext"); + + b.Property("Target") + .HasColumnType("longtext"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.Property("Username") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PermissionsAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.RequestAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AfterAction") + .HasColumnType("longtext"); + + b.Property("BeforeAction") + .HasColumnType("longtext"); + + b.Property("IpAddress") + .HasColumnType("longtext"); + + b.Property("Target") + .HasColumnType("longtext"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.Property("Username") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("RequestAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.SettingsAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AfterAction") + .HasColumnType("longtext"); + + b.Property("BeforeAction") + .HasColumnType("longtext"); + + b.Property("IpAddress") + .HasColumnType("longtext"); + + b.Property("Target") + .HasColumnType("longtext"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.Property("Username") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("SettingsAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.SystemAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AfterAction") + .HasColumnType("longtext"); + + b.Property("BeforeAction") + .HasColumnType("longtext"); + + b.Property("IpAddress") + .HasColumnType("longtext"); + + b.Property("Target") + .HasColumnType("longtext"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.Property("Username") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("SystemAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.AuthenticationSettings", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("AdminPassword") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DuoApiHost") + .HasColumnType("longtext"); + + b.Property("DuoClientId") + .HasColumnType("longtext"); + + b.Property("DuoClientSecret") + .HasColumnType("longtext"); + + b.Property("DuoEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("DuoUnreachableBehavior") + .HasColumnType("int"); + + b.Property("SessionTimeout") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AuthenticationSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "Id = 1") + .HasName("CK_Table_Column2"); + }); + + b.HasData( + new + { + Id = 1, + AdminPassword = "password", + DuoEnabled = false, + DuoUnreachableBehavior = 0, + SessionTimeout = 15 + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.ChatRoom", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsPublic") + .HasColumnType("tinyint(1)"); + + b.Property("MembersHash") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ChatRooms"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.UnreadChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ChatMessageId") + .HasColumnType("int"); + + b.Property("ChatRoomId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ChatMessageId"); + + b.ToTable("UnreadChatMessages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.CustomActiveDirectoryField", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FieldName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FieldType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("CustomActiveDirectoryFields"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.EmailSettings", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("AdminBcc") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("FromAddress") + .HasColumnType("longtext"); + + b.Property("FromName") + .HasColumnType("longtext"); + + b.Property("ReplyToAddress") + .HasColumnType("longtext"); + + b.Property("ReplyToName") + .HasColumnType("longtext"); + + b.Property("SMTPPassword") + .HasColumnType("longtext"); + + b.Property("SMTPPort") + .HasColumnType("int"); + + b.Property("SMTPServer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SMTPUsername") + .HasColumnType("longtext"); + + b.Property("UseSMTPAuth") + .HasColumnType("tinyint(1)"); + + b.Property("UseTLS") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("EmailSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "Id = 1") + .HasName("CK_Table_Column3"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.EmailTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Body") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TemplateType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("EmailTemplates"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Block") + .HasColumnType("tinyint(1)"); + + b.Property("ByEmail") + .HasColumnType("tinyint(1)"); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("InApp") + .HasColumnType("tinyint(1)"); + + b.Property("OU") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationSubscriptions"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionNotificationType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("NotificationSubscriptionId") + .HasColumnType("int"); + + b.Property("NotificationType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NotificationSubscriptionId"); + + b.ToTable("SubscriptionNotificationType"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionWebHookType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("NotificationType") + .HasColumnType("int"); + + b.Property("WebHookSubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WebHookSubscriptionId"); + + b.ToTable("SubscriptionWebHookType"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookAttempt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Body") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Delivered") + .HasColumnType("tinyint(1)"); + + b.Property("EventTimestamp") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastAttemptTimestamp") + .HasColumnType("datetime(6)"); + + b.Property("MessageGuid") + .HasColumnType("char(36)"); + + b.Property("ResponseCode") + .HasColumnType("int"); + + b.Property("ResponseHeaders") + .HasColumnType("longtext"); + + b.Property("ResponseMessage") + .HasColumnType("longtext"); + + b.Property("RetryCount") + .HasColumnType("int"); + + b.Property("Signature") + .HasColumnType("longtext"); + + b.Property("Uri") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WebHookSubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WebHookSubscriptionId"); + + b.ToTable("WebHookAttempts"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AuthorizationToken") + .HasColumnType("longtext"); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("HmacKey") + .HasColumnType("longtext"); + + b.Property("IgnoreSSLVerification") + .HasColumnType("tinyint(1)"); + + b.Property("OU") + .HasColumnType("longtext"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("URL") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WebHookAuthorization") + .HasColumnType("int"); + + b.Property("WebHookMethod") + .HasColumnType("int"); + + b.Property("WebHookSignature") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("WebHookSubscriptions"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("AccessLevels"); + + b.HasData( + new + { + Id = 1, + Name = "Deny All" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ActionAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AccessLevelId") + .HasColumnType("int"); + + b.Property("AllowOrDeny") + .HasColumnType("tinyint(1)"); + + b.Property("ObjectActionId") + .HasColumnType("int"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AccessLevelId"); + + b.HasIndex("ObjectActionId"); + + b.ToTable("ActionAccessMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("FieldAccessLevel"); + + b.HasData( + new + { + Id = 1, + Level = 10, + Name = "Deny" + }, + new + { + Id = 2, + Level = 100, + Name = "Read" + }, + new + { + Id = 3, + Level = 1000, + Name = "Edit" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CustomFieldId") + .HasColumnType("int"); + + b.Property("FieldAccessLevelId") + .HasColumnType("int"); + + b.Property("FieldId") + .HasColumnType("int"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CustomFieldId"); + + b.HasIndex("FieldAccessLevelId"); + + b.HasIndex("FieldId"); + + b.ToTable("AccessLevelFieldMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.GlobalPermissionRequestActions", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ObjectAction") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("GlobalPermissionRequestActions"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.GlobalPermissionSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AllowAccessRequest") + .HasColumnType("tinyint(1)"); + + b.Property("AllowSelfModification") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("GlobalPermissionSettings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ObjectAccessLevel"); + + b.HasData( + new + { + Id = 1, + Level = 10, + Name = "Deny" + }, + new + { + Id = 2, + Level = 1000, + Name = "Read" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AllowDisabled") + .HasColumnType("tinyint(1)"); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("ObjectAccessLevelId") + .HasColumnType("int"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ObjectAccessLevelId"); + + b.ToTable("AccessLevelObjectMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ObjectActionFlag"); + + b.HasData( + new + { + Id = 1, + Action = 4, + Name = "Assign" + }, + new + { + Id = 2, + Action = 3, + Name = "UnAssign" + }, + new + { + Id = 3, + Action = 8, + Name = "Unlock" + }, + new + { + Id = 4, + Action = 5, + Name = "Enable" + }, + new + { + Id = 5, + Action = 6, + Name = "Disable" + }, + new + { + Id = 6, + Action = 7, + Name = "Rename" + }, + new + { + Id = 7, + Action = 0, + Name = "Move" + }, + new + { + Id = 8, + Action = 2, + Name = "Create" + }, + new + { + Id = 9, + Action = 1, + Name = "Delete" + }, + new + { + Id = 10, + Action = 9, + Name = "Set Password" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.PermissionDelegate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("DelegateName") + .HasColumnType("longtext"); + + b.Property("DelegateSid") + .IsRequired() + .HasColumnType("varbinary(3072)"); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsSuperAdmin") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("DelegateSid") + .IsUnique(); + + b.ToTable("PermissionDelegate"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.PermissionMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("OU") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PermissionMap"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AllowCustomGroups") + .HasColumnType("tinyint(1)"); + + b.Property("AllowUsernameOverride") + .HasColumnType("tinyint(1)"); + + b.Property("AskForAlternateEmail") + .HasColumnType("tinyint(1)"); + + b.Property("Category") + .HasColumnType("longtext"); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("DisplayNameFormula") + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.Property("ParentOU") + .HasColumnType("longtext"); + + b.Property("ParentTemplateId") + .HasColumnType("int"); + + b.Property("PasswordFormula") + .HasColumnType("longtext"); + + b.Property("RequirePasswordChange") + .HasColumnType("tinyint(1)"); + + b.Property("SendWelcomeEmail") + .HasColumnType("tinyint(1)"); + + b.Property("UsernameFormula") + .HasColumnType("longtext"); + + b.Property("Visible") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("ParentTemplateId"); + + b.ToTable("DirectoryTemplates"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateFieldValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CustomFieldId") + .HasColumnType("int"); + + b.Property("DirectoryTemplateId") + .HasColumnType("int"); + + b.Property("Editable") + .HasColumnType("tinyint(1)"); + + b.Property("FieldId") + .HasColumnType("int"); + + b.Property("Required") + .HasColumnType("tinyint(1)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CustomFieldId"); + + b.HasIndex("DirectoryTemplateId"); + + b.HasIndex("FieldId"); + + b.ToTable("DirectoryTemplateFieldValues"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("DirectoryTemplateId") + .HasColumnType("int"); + + b.Property("GroupSid") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryTemplateId"); + + b.ToTable("DirectoryTemplateGroups"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("ExpiresAt") + .HasColumnType("datetime(6)"); + + b.Property("IsRevoked") + .HasColumnType("tinyint(1)"); + + b.Property("LastUsedAt") + .HasColumnType("datetime(6)"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TokenHash") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ApiTokens"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("DarkMode") + .HasColumnType("tinyint(1)"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("ProfilePicture") + .HasColumnType("longblob"); + + b.Property("SearchDisabledComputers") + .HasColumnType("tinyint(1)"); + + b.Property("SearchDisabledUsers") + .HasColumnType("tinyint(1)"); + + b.Property("Theme") + .HasColumnType("longtext"); + + b.Property("UserGUID") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("UserGUID") + .IsUnique(); + + b.ToTable("UserSettings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.NotificationMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatorId") + .HasColumnType("int"); + + b.Property("Dismissable") + .HasColumnType("tinyint(1)"); + + b.Property("Expires") + .HasColumnType("datetime(6)"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Link") + .HasColumnType("longtext"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("MessageType") + .HasColumnType("int"); + + b.Property("TargetDN") + .HasColumnType("longtext"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.ToTable("NotificationMessages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ReadNewsItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("NewsItemId") + .HasColumnType("double"); + + b.Property("NewsItemUpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ReadNewsItems"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserDashboardWidget", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ItemsPerPage") + .HasColumnType("int"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("Slot") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WidgetType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserDashboardWidgets"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserFavoriteEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("DN") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserFavoriteEntries"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("IsRead") + .HasColumnType("tinyint(1)"); + + b.Property("NotificationId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NotificationId"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotifications"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ChatRoomId") + .HasColumnType("int"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ChatRoomId"); + + b.HasIndex("UserId"); + + b.ToTable("ChatMessages"); + }); + + modelBuilder.Entity("PermissionDelegatePermissionMapping", b => + { + b.Property("PermissionDelegatesId") + .HasColumnType("int"); + + b.Property("PermissionsMapsId") + .HasColumnType("int"); + + b.HasKey("PermissionDelegatesId", "PermissionsMapsId"); + + b.HasIndex("PermissionsMapsId"); + + b.ToTable("PermissionDelegatePermissionMapping"); + }); + + modelBuilder.Entity("AccessLevelFieldAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.FieldAccessMapping", null) + .WithMany() + .HasForeignKey("FieldMapId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AccessLevelObjectAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", null) + .WithMany() + .HasForeignKey("ObjectMapId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AccessLevelPermissionMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionMapping", null) + .WithMany() + .HasForeignKey("PermissionMapsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AppUserChatRoom", b => + { + b.HasOne("BLAZAM.Database.Models.Chat.ChatRoom", null) + .WithMany() + .HasForeignKey("ChatRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", null) + .WithMany() + .HasForeignKey("MembersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryFieldObjectType", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", null) + .WithMany("ObjectTypes") + .HasForeignKey("CustomActiveDirectoryFieldId"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.UnreadChatMessage", b => + { + b.HasOne("BLAZAM.Server.Data.ChatMessage", "ChatMessage") + .WithMany("NotReadBy") + .HasForeignKey("ChatMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatMessage"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("NotificationSubscriptions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionNotificationType", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.NotificationSubscription", "NotificationSubscription") + .WithMany("NotificationTypes") + .HasForeignKey("NotificationSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationSubscription"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionWebHookType", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.WebHookSubscription", "WebHookSubscription") + .WithMany("NotificationTypes") + .HasForeignKey("WebHookSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebHookSubscription"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookAttempt", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.WebHookSubscription", "WebHookSubscription") + .WithMany("WebHookAttempts") + .HasForeignKey("WebHookSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebHookSubscription"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ActionAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany("ActionMap") + .HasForeignKey("AccessLevelId"); + + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAction", "ObjectAction") + .WithMany() + .HasForeignKey("ObjectActionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ObjectAction"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", "CustomField") + .WithMany() + .HasForeignKey("CustomFieldId"); + + b.HasOne("BLAZAM.Database.Models.Permissions.FieldAccessLevel", "FieldAccessLevel") + .WithMany() + .HasForeignKey("FieldAccessLevelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.ActiveDirectoryField", "Field") + .WithMany() + .HasForeignKey("FieldId"); + + b.Navigation("CustomField"); + + b.Navigation("Field"); + + b.Navigation("FieldAccessLevel"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", "ObjectAccessLevel") + .WithMany("ObjectAccessMappings") + .HasForeignKey("ObjectAccessLevelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ObjectAccessLevel"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", "ParentTemplate") + .WithMany() + .HasForeignKey("ParentTemplateId"); + + b.Navigation("ParentTemplate"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateFieldValue", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", "CustomField") + .WithMany() + .HasForeignKey("CustomFieldId"); + + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", null) + .WithMany("FieldValues") + .HasForeignKey("DirectoryTemplateId"); + + b.HasOne("BLAZAM.Database.Models.ActiveDirectoryField", "Field") + .WithMany() + .HasForeignKey("FieldId"); + + b.Navigation("CustomField"); + + b.Navigation("Field"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateGroup", b => + { + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", null) + .WithMany("AssignedGroupSids") + .HasForeignKey("DirectoryTemplateId"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("APITokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.NotificationMessage", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "Creator") + .WithMany() + .HasForeignKey("CreatorId"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ReadNewsItem", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("ReadNewsItems") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserDashboardWidget", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("DashboardWidgets") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserFavoriteEntry", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("FavoriteEntries") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserNotification", b => + { + b.HasOne("BLAZAM.Database.Models.User.NotificationMessage", "Notification") + .WithMany() + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("Notifications") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.HasOne("BLAZAM.Database.Models.Chat.ChatRoom", "ChatRoom") + .WithMany("Messages") + .HasForeignKey("ChatRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatRoom"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("PermissionDelegatePermissionMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionDelegate", null) + .WithMany() + .HasForeignKey("PermissionDelegatesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionMapping", null) + .WithMany() + .HasForeignKey("PermissionsMapsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.ChatRoom", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.CustomActiveDirectoryField", b => + { + b.Navigation("ObjectTypes"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.Navigation("NotificationTypes"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookSubscription", b => + { + b.Navigation("NotificationTypes"); + + b.Navigation("WebHookAttempts"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => + { + b.Navigation("ActionMap"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", b => + { + b.Navigation("ObjectAccessMappings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.Navigation("AssignedGroupSids"); + + b.Navigation("FieldValues"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + { + b.Navigation("APITokens"); + + b.Navigation("DashboardWidgets"); + + b.Navigation("FavoriteEntries"); + + b.Navigation("NotificationSubscriptions"); + + b.Navigation("Notifications"); + + b.Navigation("ReadNewsItems"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.Navigation("NotReadBy"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BLAZAMDatabase/Migrations/MySql/20241123221439_Add_Webhooks_MySql.cs b/BLAZAMDatabase/Migrations/MySql/20241123221439_Add_Webhooks_MySql.cs new file mode 100644 index 000000000..47cdfce08 --- /dev/null +++ b/BLAZAMDatabase/Migrations/MySql/20241123221439_Add_Webhooks_MySql.cs @@ -0,0 +1,177 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BLAZAM.Database.Migrations.MySql +{ + /// + public partial class Add_Webhooks_MySql : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "APIToken", + table: "UserSettings"); + + migrationBuilder.CreateTable( + name: "ApiTokens", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Token = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + TokenHash = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + ExpiresAt = table.Column(type: "datetime(6)", nullable: false), + LastUsedAt = table.Column(type: "datetime(6)", nullable: true), + UserId = table.Column(type: "int", nullable: false), + Description = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsRevoked = table.Column(type: "tinyint(1)", nullable: false), + DeletedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ApiTokens", x => x.Id); + table.ForeignKey( + name: "FK_ApiTokens_UserSettings_UserId", + column: x => x.UserId, + principalTable: "UserSettings", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WebHookSubscriptions", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + OU = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IgnoreSSLVerification = table.Column(type: "tinyint(1)", nullable: false), + URL = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + WebHookMethod = table.Column(type: "int", nullable: false), + WebHookSignature = table.Column(type: "int", nullable: false), + WebHookAuthorization = table.Column(type: "int", nullable: false), + AuthorizationToken = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + HmacKey = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PrivateKey = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PublicKey = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + DeletedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_WebHookSubscriptions", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "SubscriptionWebHookType", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + WebHookSubscriptionId = table.Column(type: "int", nullable: false), + NotificationType = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SubscriptionWebHookType", x => x.Id); + table.ForeignKey( + name: "FK_SubscriptionWebHookType_WebHookSubscriptions_WebHookSubscrip~", + column: x => x.WebHookSubscriptionId, + principalTable: "WebHookSubscriptions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WebHookAttempts", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + MessageGuid = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LastAttemptTimestamp = table.Column(type: "datetime(6)", nullable: false), + EventTimestamp = table.Column(type: "datetime(6)", nullable: false), + EventType = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + WebHookSubscriptionId = table.Column(type: "int", nullable: false), + Uri = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Delivered = table.Column(type: "tinyint(1)", nullable: false), + Body = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Signature = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + RetryCount = table.Column(type: "int", nullable: false), + ResponseCode = table.Column(type: "int", nullable: false), + ResponseHeaders = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ResponseMessage = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_WebHookAttempts", x => x.Id); + table.ForeignKey( + name: "FK_WebHookAttempts_WebHookSubscriptions_WebHookSubscriptionId", + column: x => x.WebHookSubscriptionId, + principalTable: "WebHookSubscriptions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_ApiTokens_UserId", + table: "ApiTokens", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_SubscriptionWebHookType_WebHookSubscriptionId", + table: "SubscriptionWebHookType", + column: "WebHookSubscriptionId"); + + migrationBuilder.CreateIndex( + name: "IX_WebHookAttempts_WebHookSubscriptionId", + table: "WebHookAttempts", + column: "WebHookSubscriptionId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ApiTokens"); + + migrationBuilder.DropTable( + name: "SubscriptionWebHookType"); + + migrationBuilder.DropTable( + name: "WebHookAttempts"); + + migrationBuilder.DropTable( + name: "WebHookSubscriptions"); + + migrationBuilder.AddColumn( + name: "APIToken", + table: "UserSettings", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + } + } +} diff --git a/BLAZAMDatabase/Migrations/MySql/MySqlDatabaseContextModelSnapshot.cs b/BLAZAMDatabase/Migrations/MySql/MySqlDatabaseContextModelSnapshot.cs index fdd7f360d..d159b0a54 100644 --- a/BLAZAMDatabase/Migrations/MySql/MySqlDatabaseContextModelSnapshot.cs +++ b/BLAZAMDatabase/Migrations/MySql/MySqlDatabaseContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.10") + .HasAnnotation("ProductVersion", "8.0.11") .HasAnnotation("Relational:MaxIdentifierLength", 64); MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); @@ -982,6 +982,131 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("SubscriptionNotificationType"); }); + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionWebHookType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("NotificationType") + .HasColumnType("int"); + + b.Property("WebHookSubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WebHookSubscriptionId"); + + b.ToTable("SubscriptionWebHookType"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookAttempt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Body") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Delivered") + .HasColumnType("tinyint(1)"); + + b.Property("EventTimestamp") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastAttemptTimestamp") + .HasColumnType("datetime(6)"); + + b.Property("MessageGuid") + .HasColumnType("char(36)"); + + b.Property("ResponseCode") + .HasColumnType("int"); + + b.Property("ResponseHeaders") + .HasColumnType("longtext"); + + b.Property("ResponseMessage") + .HasColumnType("longtext"); + + b.Property("RetryCount") + .HasColumnType("int"); + + b.Property("Signature") + .HasColumnType("longtext"); + + b.Property("Uri") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WebHookSubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WebHookSubscriptionId"); + + b.ToTable("WebHookAttempts"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AuthorizationToken") + .HasColumnType("longtext"); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("HmacKey") + .HasColumnType("longtext"); + + b.Property("IgnoreSSLVerification") + .HasColumnType("tinyint(1)"); + + b.Property("OU") + .HasColumnType("longtext"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("URL") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WebHookAuthorization") + .HasColumnType("int"); + + b.Property("WebHookMethod") + .HasColumnType("int"); + + b.Property("WebHookSignature") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("WebHookSubscriptions"); + }); + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => { b.Property("Id") @@ -1459,7 +1584,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("DirectoryTemplateGroups"); }); - modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -1467,9 +1592,49 @@ protected override void BuildModel(ModelBuilder modelBuilder) MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - b.Property("APIToken") + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") .HasColumnType("longtext"); + b.Property("ExpiresAt") + .HasColumnType("datetime(6)"); + + b.Property("IsRevoked") + .HasColumnType("tinyint(1)"); + + b.Property("LastUsedAt") + .HasColumnType("datetime(6)"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TokenHash") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ApiTokens"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("DarkMode") .HasColumnType("tinyint(1)"); @@ -1799,6 +1964,28 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("NotificationSubscription"); }); + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionWebHookType", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.WebHookSubscription", "WebHookSubscription") + .WithMany("NotificationTypes") + .HasForeignKey("WebHookSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebHookSubscription"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookAttempt", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.WebHookSubscription", "WebHookSubscription") + .WithMany("WebHookAttempts") + .HasForeignKey("WebHookSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebHookSubscription"); + }); + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ActionAccessMapping", b => { b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) @@ -1883,6 +2070,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("DirectoryTemplateId"); }); + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("APITokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + modelBuilder.Entity("BLAZAM.Database.Models.User.NotificationMessage", b => { b.HasOne("BLAZAM.Database.Models.User.AppUser", "Creator") @@ -1993,6 +2191,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("NotificationTypes"); }); + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookSubscription", b => + { + b.Navigation("NotificationTypes"); + + b.Navigation("WebHookAttempts"); + }); + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => { b.Navigation("ActionMap"); @@ -2012,6 +2217,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => { + b.Navigation("APITokens"); + b.Navigation("DashboardWidgets"); b.Navigation("FavoriteEntries"); diff --git a/BLAZAMDatabase/Migrations/Sql/20241118154703_Add_Api_Tokens_Sql.Designer.cs b/BLAZAMDatabase/Migrations/Sql/20241118154703_Add_Api_Tokens_Sql.Designer.cs new file mode 100644 index 000000000..91d09af25 --- /dev/null +++ b/BLAZAMDatabase/Migrations/Sql/20241118154703_Add_Api_Tokens_Sql.Designer.cs @@ -0,0 +1,2089 @@ +// +using System; +using BLAZAM.Database.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BLAZAM.Database.Migrations.Sql +{ + [DbContext(typeof(SqlDatabaseContext))] + [Migration("20241118154703_Add_Api_Tokens_Sql")] + partial class Add_Api_Tokens_Sql + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("AccessLevelFieldAccessMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("int"); + + b.Property("FieldMapId") + .HasColumnType("int"); + + b.HasKey("AccessLevelsId", "FieldMapId"); + + b.HasIndex("FieldMapId"); + + b.ToTable("AccessLevelFieldAccessMapping"); + }); + + modelBuilder.Entity("AccessLevelObjectAccessMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("int"); + + b.Property("ObjectMapId") + .HasColumnType("int"); + + b.HasKey("AccessLevelsId", "ObjectMapId"); + + b.HasIndex("ObjectMapId"); + + b.ToTable("AccessLevelObjectAccessMapping"); + }); + + modelBuilder.Entity("AccessLevelPermissionMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("int"); + + b.Property("PermissionMapsId") + .HasColumnType("int"); + + b.HasKey("AccessLevelsId", "PermissionMapsId"); + + b.HasIndex("PermissionMapsId"); + + b.ToTable("AccessLevelPermissionMapping"); + }); + + modelBuilder.Entity("AppUserChatRoom", b => + { + b.Property("ChatRoomId") + .HasColumnType("int"); + + b.Property("MembersId") + .HasColumnType("int"); + + b.HasKey("ChatRoomId", "MembersId"); + + b.HasIndex("MembersId"); + + b.ToTable("AppUserChatRoom"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ADSettings", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("ApplicationBaseDN") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FQDN") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ServerAddress") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ServerPort") + .HasColumnType("int"); + + b.Property("UseTLS") + .HasColumnType("bit"); + + b.Property("Username") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ActiveDirectorySettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryField", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FieldName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FieldType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("ActiveDirectoryFields"); + + b.HasData( + new + { + Id = 1, + DisplayName = "Last Name", + FieldName = "sn", + FieldType = 0 + }, + new + { + Id = 2, + DisplayName = "First Name", + FieldName = "givenname", + FieldType = 0 + }, + new + { + Id = 3, + DisplayName = "Office", + FieldName = "physicalDeliveryOfficeName", + FieldType = 0 + }, + new + { + Id = 4, + DisplayName = "Employee ID", + FieldName = "employeeId", + FieldType = 0 + }, + new + { + Id = 5, + DisplayName = "Home Directory", + FieldName = "homeDirectory", + FieldType = 0 + }, + new + { + Id = 6, + DisplayName = "Logon Script Path", + FieldName = "scriptPath", + FieldType = 0 + }, + new + { + Id = 7, + DisplayName = "Profile Path", + FieldName = "profilePath", + FieldType = 0 + }, + new + { + Id = 8, + DisplayName = "Home Phone Number", + FieldName = "homePhone", + FieldType = 0 + }, + new + { + Id = 9, + DisplayName = "Street Address", + FieldName = "streetAddress", + FieldType = 0 + }, + new + { + Id = 10, + DisplayName = "City", + FieldName = "l", + FieldType = 0 + }, + new + { + Id = 11, + DisplayName = "State", + FieldName = "st", + FieldType = 0 + }, + new + { + Id = 12, + DisplayName = "Zip Code", + FieldName = "postalCode", + FieldType = 0 + }, + new + { + Id = 13, + DisplayName = "Site", + FieldName = "site", + FieldType = 0 + }, + new + { + Id = 14, + DisplayName = "Name", + FieldName = "name", + FieldType = 0 + }, + new + { + Id = 15, + DisplayName = "Username", + FieldName = "samaccountname", + FieldType = 0 + }, + new + { + Id = 16, + DisplayName = "SID", + FieldName = "objectSID", + FieldType = 2 + }, + new + { + Id = 17, + DisplayName = "E-Mail Address", + FieldName = "mail", + FieldType = 0 + }, + new + { + Id = 18, + DisplayName = "Description", + FieldName = "description", + FieldType = 0 + }, + new + { + Id = 19, + DisplayName = "Display Name", + FieldName = "displayName", + FieldType = 0 + }, + new + { + Id = 20, + DisplayName = "Distinguished Name", + FieldName = "distinguishedName", + FieldType = 0 + }, + new + { + Id = 21, + DisplayName = "Member Of", + FieldName = "memberOf", + FieldType = 4 + }, + new + { + Id = 22, + DisplayName = "Company", + FieldName = "company", + FieldType = 0 + }, + new + { + Id = 23, + DisplayName = "Title", + FieldName = "title", + FieldType = 0 + }, + new + { + Id = 24, + DisplayName = "User Principal Name", + FieldName = "userPrincipalName", + FieldType = 0 + }, + new + { + Id = 25, + DisplayName = "Telephone Number", + FieldName = "telephoneNumber", + FieldType = 0 + }, + new + { + Id = 26, + DisplayName = "PO Box", + FieldName = "postOfficeBox", + FieldType = 0 + }, + new + { + Id = 27, + DisplayName = "Canonical Name", + FieldName = "cn", + FieldType = 0 + }, + new + { + Id = 28, + DisplayName = "Home Drive", + FieldName = "homeDrive", + FieldType = 3 + }, + new + { + Id = 29, + DisplayName = "Department", + FieldName = "department", + FieldType = 0 + }, + new + { + Id = 30, + DisplayName = "Middle Name", + FieldName = "middleName", + FieldType = 0 + }, + new + { + Id = 31, + DisplayName = "Pager", + FieldName = "pager", + FieldType = 0 + }, + new + { + Id = 32, + DisplayName = "OS", + FieldName = "operatingSystemVersion", + FieldType = 0 + }, + new + { + Id = 33, + DisplayName = "Account Expiration", + FieldName = "accountExpires", + FieldType = 1 + }, + new + { + Id = 34, + DisplayName = "Manager", + FieldName = "manager", + FieldType = 0 + }, + new + { + Id = 35, + DisplayName = "Photo", + FieldName = "thumbnail", + FieldType = 2 + }, + new + { + Id = 36, + DisplayName = "Log On To", + FieldName = "userWorkstations", + FieldType = 0 + }, + new + { + Id = 37, + DisplayName = "Logon Hours", + FieldName = "logonHours", + FieldType = 2 + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryFieldObjectType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ActiveDirectoryFieldId") + .HasColumnType("int"); + + b.Property("CustomActiveDirectoryFieldId") + .HasColumnType("int"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CustomActiveDirectoryFieldId"); + + b.ToTable("ActiveDirectoryFieldObjectMappings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.AppSettings", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("AnalyticsId") + .HasColumnType("nvarchar(max)"); + + b.Property("AppFQDN") + .HasColumnType("nvarchar(max)"); + + b.Property("AppIcon") + .HasColumnType("varbinary(max)"); + + b.Property("AppName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AutoUpdate") + .HasColumnType("bit"); + + b.Property("AutoUpdateTime") + .HasColumnType("time"); + + b.Property("ForceHTTPS") + .HasColumnType("bit"); + + b.Property("InstallationCompleted") + .HasColumnType("bit"); + + b.Property("LastUpdateCheck") + .HasColumnType("datetime2"); + + b.Property("MOTD") + .HasColumnType("nvarchar(max)"); + + b.Property("MyrtilleURL") + .HasColumnType("nvarchar(max)"); + + b.Property("SSLCertificateCipher") + .HasColumnType("nvarchar(max)"); + + b.Property("SendDeveloperAnalytics") + .HasColumnType("bit"); + + b.Property("SendLogsToDeveloper") + .HasColumnType("bit"); + + b.Property("UpdateBranch") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDomain") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatePassword") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateUsername") + .HasColumnType("nvarchar(max)"); + + b.Property("UseUpdateCredentials") + .HasColumnType("bit"); + + b.Property("UserHelpdeskURL") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AppSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1") + .HasName("CK_Table_Column1"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.DirectoryEntryAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AfterAction") + .HasColumnType("nvarchar(max)"); + + b.Property("BeforeAction") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Sid") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Target") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("DirectoryEntryAuditLogs"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.LogonAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AfterAction") + .HasColumnType("nvarchar(max)"); + + b.Property("BeforeAction") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Target") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("LogonAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.PermissionsAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AfterAction") + .HasColumnType("nvarchar(max)"); + + b.Property("BeforeAction") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Target") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("PermissionsAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.RequestAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AfterAction") + .HasColumnType("nvarchar(max)"); + + b.Property("BeforeAction") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Target") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("RequestAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.SettingsAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AfterAction") + .HasColumnType("nvarchar(max)"); + + b.Property("BeforeAction") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Target") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SettingsAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.SystemAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AfterAction") + .HasColumnType("nvarchar(max)"); + + b.Property("BeforeAction") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Target") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SystemAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.AuthenticationSettings", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("AdminPassword") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DuoApiHost") + .HasColumnType("nvarchar(max)"); + + b.Property("DuoClientId") + .HasColumnType("nvarchar(max)"); + + b.Property("DuoClientSecret") + .HasColumnType("nvarchar(max)"); + + b.Property("DuoEnabled") + .HasColumnType("bit"); + + b.Property("DuoUnreachableBehavior") + .HasColumnType("int"); + + b.Property("SessionTimeout") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AuthenticationSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1") + .HasName("CK_Table_Column2"); + }); + + b.HasData( + new + { + Id = 1, + AdminPassword = "password", + DuoEnabled = false, + DuoUnreachableBehavior = 0, + SessionTimeout = 15 + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.ChatRoom", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("IsPublic") + .HasColumnType("bit"); + + b.Property("MembersHash") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ChatRooms"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.UnreadChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChatMessageId") + .HasColumnType("int"); + + b.Property("ChatRoomId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ChatMessageId"); + + b.ToTable("UnreadChatMessages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.CustomActiveDirectoryField", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FieldName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FieldType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("CustomActiveDirectoryFields"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.EmailSettings", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("AdminBcc") + .HasColumnType("nvarchar(max)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("FromAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("FromName") + .HasColumnType("nvarchar(max)"); + + b.Property("ReplyToAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("ReplyToName") + .HasColumnType("nvarchar(max)"); + + b.Property("SMTPPassword") + .HasColumnType("nvarchar(max)"); + + b.Property("SMTPPort") + .HasColumnType("int"); + + b.Property("SMTPServer") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SMTPUsername") + .HasColumnType("nvarchar(max)"); + + b.Property("UseSMTPAuth") + .HasColumnType("bit"); + + b.Property("UseTLS") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.ToTable("EmailSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1") + .HasName("CK_Table_Column3"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.EmailTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Body") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TemplateType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("EmailTemplates"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Block") + .HasColumnType("bit"); + + b.Property("ByEmail") + .HasColumnType("bit"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("InApp") + .HasColumnType("bit"); + + b.Property("OU") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationSubscriptions"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionNotificationType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("NotificationSubscriptionId") + .HasColumnType("int"); + + b.Property("NotificationType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NotificationSubscriptionId"); + + b.ToTable("SubscriptionNotificationType"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AccessLevels"); + + b.HasData( + new + { + Id = 1, + Name = "Deny All" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ActionAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AccessLevelId") + .HasColumnType("int"); + + b.Property("AllowOrDeny") + .HasColumnType("bit"); + + b.Property("ObjectActionId") + .HasColumnType("int"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AccessLevelId"); + + b.HasIndex("ObjectActionId"); + + b.ToTable("ActionAccessMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("FieldAccessLevel"); + + b.HasData( + new + { + Id = 1, + Level = 10, + Name = "Deny" + }, + new + { + Id = 2, + Level = 100, + Name = "Read" + }, + new + { + Id = 3, + Level = 1000, + Name = "Edit" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CustomFieldId") + .HasColumnType("int"); + + b.Property("FieldAccessLevelId") + .HasColumnType("int"); + + b.Property("FieldId") + .HasColumnType("int"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CustomFieldId"); + + b.HasIndex("FieldAccessLevelId"); + + b.HasIndex("FieldId"); + + b.ToTable("AccessLevelFieldMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.GlobalPermissionRequestActions", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ObjectAction") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("GlobalPermissionRequestActions"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.GlobalPermissionSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AllowAccessRequest") + .HasColumnType("bit"); + + b.Property("AllowSelfModification") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.ToTable("GlobalPermissionSettings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ObjectAccessLevel"); + + b.HasData( + new + { + Id = 1, + Level = 10, + Name = "Deny" + }, + new + { + Id = 2, + Level = 1000, + Name = "Read" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AllowDisabled") + .HasColumnType("bit"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("ObjectAccessLevelId") + .HasColumnType("int"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ObjectAccessLevelId"); + + b.ToTable("AccessLevelObjectMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ObjectActionFlag"); + + b.HasData( + new + { + Id = 1, + Action = 4, + Name = "Assign" + }, + new + { + Id = 2, + Action = 3, + Name = "UnAssign" + }, + new + { + Id = 3, + Action = 8, + Name = "Unlock" + }, + new + { + Id = 4, + Action = 5, + Name = "Enable" + }, + new + { + Id = 5, + Action = 6, + Name = "Disable" + }, + new + { + Id = 6, + Action = 7, + Name = "Rename" + }, + new + { + Id = 7, + Action = 0, + Name = "Move" + }, + new + { + Id = 8, + Action = 2, + Name = "Create" + }, + new + { + Id = 9, + Action = 1, + Name = "Delete" + }, + new + { + Id = 10, + Action = 9, + Name = "Set Password" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.PermissionDelegate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DelegateName") + .HasColumnType("nvarchar(max)"); + + b.Property("DelegateSid") + .IsRequired() + .HasColumnType("varbinary(900)"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("IsSuperAdmin") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("DelegateSid") + .IsUnique(); + + b.ToTable("PermissionDelegate"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.PermissionMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("OU") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("PermissionMap"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AllowCustomGroups") + .HasColumnType("bit"); + + b.Property("AllowUsernameOverride") + .HasColumnType("bit"); + + b.Property("AskForAlternateEmail") + .HasColumnType("bit"); + + b.Property("Category") + .HasColumnType("nvarchar(max)"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("DisplayNameFormula") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.Property("ParentOU") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentTemplateId") + .HasColumnType("int"); + + b.Property("PasswordFormula") + .HasColumnType("nvarchar(max)"); + + b.Property("RequirePasswordChange") + .HasColumnType("bit"); + + b.Property("SendWelcomeEmail") + .HasColumnType("bit"); + + b.Property("UsernameFormula") + .HasColumnType("nvarchar(max)"); + + b.Property("Visible") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("ParentTemplateId"); + + b.ToTable("DirectoryTemplates"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateFieldValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CustomFieldId") + .HasColumnType("int"); + + b.Property("DirectoryTemplateId") + .HasColumnType("int"); + + b.Property("Editable") + .HasColumnType("bit"); + + b.Property("FieldId") + .HasColumnType("int"); + + b.Property("Required") + .HasColumnType("bit"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("CustomFieldId"); + + b.HasIndex("DirectoryTemplateId"); + + b.HasIndex("FieldId"); + + b.ToTable("DirectoryTemplateFieldValues"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DirectoryTemplateId") + .HasColumnType("int"); + + b.Property("GroupSid") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryTemplateId"); + + b.ToTable("DirectoryTemplateGroups"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("IsRevoked") + .HasColumnType("bit"); + + b.Property("LastUsedAt") + .HasColumnType("datetime2"); + + b.Property("Token") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TokenHash") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ApiTokens"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DarkMode") + .HasColumnType("bit"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("ProfilePicture") + .HasColumnType("varbinary(max)"); + + b.Property("SearchDisabledComputers") + .HasColumnType("bit"); + + b.Property("SearchDisabledUsers") + .HasColumnType("bit"); + + b.Property("Theme") + .HasColumnType("nvarchar(max)"); + + b.Property("UserGUID") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Username") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("UserGUID") + .IsUnique(); + + b.ToTable("UserSettings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.NotificationMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnType("int"); + + b.Property("Dismissable") + .HasColumnType("bit"); + + b.Property("Expires") + .HasColumnType("datetime2"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Link") + .HasColumnType("nvarchar(max)"); + + b.Property("Message") + .HasColumnType("nvarchar(max)"); + + b.Property("MessageType") + .HasColumnType("int"); + + b.Property("TargetDN") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.ToTable("NotificationMessages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ReadNewsItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("NewsItemId") + .HasColumnType("float"); + + b.Property("NewsItemUpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ReadNewsItems"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserDashboardWidget", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ItemsPerPage") + .HasColumnType("int"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("Slot") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WidgetType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserDashboardWidgets"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserFavoriteEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DN") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserFavoriteEntries"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("IsRead") + .HasColumnType("bit"); + + b.Property("NotificationId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NotificationId"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotifications"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChatRoomId") + .HasColumnType("int"); + + b.Property("Message") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ChatRoomId"); + + b.HasIndex("UserId"); + + b.ToTable("ChatMessages"); + }); + + modelBuilder.Entity("PermissionDelegatePermissionMapping", b => + { + b.Property("PermissionDelegatesId") + .HasColumnType("int"); + + b.Property("PermissionsMapsId") + .HasColumnType("int"); + + b.HasKey("PermissionDelegatesId", "PermissionsMapsId"); + + b.HasIndex("PermissionsMapsId"); + + b.ToTable("PermissionDelegatePermissionMapping"); + }); + + modelBuilder.Entity("AccessLevelFieldAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.FieldAccessMapping", null) + .WithMany() + .HasForeignKey("FieldMapId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AccessLevelObjectAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", null) + .WithMany() + .HasForeignKey("ObjectMapId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AccessLevelPermissionMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionMapping", null) + .WithMany() + .HasForeignKey("PermissionMapsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AppUserChatRoom", b => + { + b.HasOne("BLAZAM.Database.Models.Chat.ChatRoom", null) + .WithMany() + .HasForeignKey("ChatRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", null) + .WithMany() + .HasForeignKey("MembersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryFieldObjectType", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", null) + .WithMany("ObjectTypes") + .HasForeignKey("CustomActiveDirectoryFieldId"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.UnreadChatMessage", b => + { + b.HasOne("BLAZAM.Server.Data.ChatMessage", "ChatMessage") + .WithMany("NotReadBy") + .HasForeignKey("ChatMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatMessage"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("NotificationSubscriptions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionNotificationType", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.NotificationSubscription", "NotificationSubscription") + .WithMany("NotificationTypes") + .HasForeignKey("NotificationSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationSubscription"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ActionAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany("ActionMap") + .HasForeignKey("AccessLevelId"); + + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAction", "ObjectAction") + .WithMany() + .HasForeignKey("ObjectActionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ObjectAction"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", "CustomField") + .WithMany() + .HasForeignKey("CustomFieldId"); + + b.HasOne("BLAZAM.Database.Models.Permissions.FieldAccessLevel", "FieldAccessLevel") + .WithMany() + .HasForeignKey("FieldAccessLevelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.ActiveDirectoryField", "Field") + .WithMany() + .HasForeignKey("FieldId"); + + b.Navigation("CustomField"); + + b.Navigation("Field"); + + b.Navigation("FieldAccessLevel"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", "ObjectAccessLevel") + .WithMany("ObjectAccessMappings") + .HasForeignKey("ObjectAccessLevelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ObjectAccessLevel"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", "ParentTemplate") + .WithMany() + .HasForeignKey("ParentTemplateId"); + + b.Navigation("ParentTemplate"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateFieldValue", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", "CustomField") + .WithMany() + .HasForeignKey("CustomFieldId"); + + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", null) + .WithMany("FieldValues") + .HasForeignKey("DirectoryTemplateId"); + + b.HasOne("BLAZAM.Database.Models.ActiveDirectoryField", "Field") + .WithMany() + .HasForeignKey("FieldId"); + + b.Navigation("CustomField"); + + b.Navigation("Field"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateGroup", b => + { + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", null) + .WithMany("AssignedGroupSids") + .HasForeignKey("DirectoryTemplateId"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("APITokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.NotificationMessage", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "Creator") + .WithMany() + .HasForeignKey("CreatorId"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ReadNewsItem", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("ReadNewsItems") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserDashboardWidget", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("DashboardWidgets") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserFavoriteEntry", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("FavoriteEntries") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserNotification", b => + { + b.HasOne("BLAZAM.Database.Models.User.NotificationMessage", "Notification") + .WithMany() + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("Notifications") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.HasOne("BLAZAM.Database.Models.Chat.ChatRoom", "ChatRoom") + .WithMany("Messages") + .HasForeignKey("ChatRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatRoom"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("PermissionDelegatePermissionMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionDelegate", null) + .WithMany() + .HasForeignKey("PermissionDelegatesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionMapping", null) + .WithMany() + .HasForeignKey("PermissionsMapsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.ChatRoom", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.CustomActiveDirectoryField", b => + { + b.Navigation("ObjectTypes"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.Navigation("NotificationTypes"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => + { + b.Navigation("ActionMap"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", b => + { + b.Navigation("ObjectAccessMappings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.Navigation("AssignedGroupSids"); + + b.Navigation("FieldValues"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + { + b.Navigation("APITokens"); + + b.Navigation("DashboardWidgets"); + + b.Navigation("FavoriteEntries"); + + b.Navigation("NotificationSubscriptions"); + + b.Navigation("Notifications"); + + b.Navigation("ReadNewsItems"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.Navigation("NotReadBy"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BLAZAMDatabase/Migrations/Sql/20241118154703_Add_Api_Tokens_Sql.cs b/BLAZAMDatabase/Migrations/Sql/20241118154703_Add_Api_Tokens_Sql.cs new file mode 100644 index 000000000..2167534a5 --- /dev/null +++ b/BLAZAMDatabase/Migrations/Sql/20241118154703_Add_Api_Tokens_Sql.cs @@ -0,0 +1,64 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BLAZAM.Database.Migrations.Sql +{ + /// + public partial class Add_Api_Tokens_Sql : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "APIToken", + table: "UserSettings"); + + migrationBuilder.CreateTable( + name: "ApiTokens", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Token = table.Column(type: "nvarchar(max)", nullable: false), + TokenHash = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + ExpiresAt = table.Column(type: "datetime2", nullable: false), + LastUsedAt = table.Column(type: "datetime2", nullable: true), + UserId = table.Column(type: "int", nullable: false), + Description = table.Column(type: "nvarchar(max)", nullable: true), + IsRevoked = table.Column(type: "bit", nullable: false), + DeletedAt = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ApiTokens", x => x.Id); + table.ForeignKey( + name: "FK_ApiTokens_UserSettings_UserId", + column: x => x.UserId, + principalTable: "UserSettings", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_ApiTokens_UserId", + table: "ApiTokens", + column: "UserId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ApiTokens"); + + migrationBuilder.AddColumn( + name: "APIToken", + table: "UserSettings", + type: "nvarchar(max)", + nullable: true); + } + } +} diff --git a/BLAZAMDatabase/Migrations/Sql/20241123221415_Add_Webhooks_Sql.Designer.cs b/BLAZAMDatabase/Migrations/Sql/20241123221415_Add_Webhooks_Sql.Designer.cs new file mode 100644 index 000000000..68a84e118 --- /dev/null +++ b/BLAZAMDatabase/Migrations/Sql/20241123221415_Add_Webhooks_Sql.Designer.cs @@ -0,0 +1,2243 @@ +// +using System; +using BLAZAM.Database.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BLAZAM.Database.Migrations.Sql +{ + [DbContext(typeof(SqlDatabaseContext))] + [Migration("20241123221415_Add_Webhooks_Sql")] + partial class Add_Webhooks_Sql + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("AccessLevelFieldAccessMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("int"); + + b.Property("FieldMapId") + .HasColumnType("int"); + + b.HasKey("AccessLevelsId", "FieldMapId"); + + b.HasIndex("FieldMapId"); + + b.ToTable("AccessLevelFieldAccessMapping"); + }); + + modelBuilder.Entity("AccessLevelObjectAccessMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("int"); + + b.Property("ObjectMapId") + .HasColumnType("int"); + + b.HasKey("AccessLevelsId", "ObjectMapId"); + + b.HasIndex("ObjectMapId"); + + b.ToTable("AccessLevelObjectAccessMapping"); + }); + + modelBuilder.Entity("AccessLevelPermissionMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("int"); + + b.Property("PermissionMapsId") + .HasColumnType("int"); + + b.HasKey("AccessLevelsId", "PermissionMapsId"); + + b.HasIndex("PermissionMapsId"); + + b.ToTable("AccessLevelPermissionMapping"); + }); + + modelBuilder.Entity("AppUserChatRoom", b => + { + b.Property("ChatRoomId") + .HasColumnType("int"); + + b.Property("MembersId") + .HasColumnType("int"); + + b.HasKey("ChatRoomId", "MembersId"); + + b.HasIndex("MembersId"); + + b.ToTable("AppUserChatRoom"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ADSettings", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("ApplicationBaseDN") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FQDN") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ServerAddress") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ServerPort") + .HasColumnType("int"); + + b.Property("UseTLS") + .HasColumnType("bit"); + + b.Property("Username") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ActiveDirectorySettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryField", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FieldName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FieldType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("ActiveDirectoryFields"); + + b.HasData( + new + { + Id = 1, + DisplayName = "Last Name", + FieldName = "sn", + FieldType = 0 + }, + new + { + Id = 2, + DisplayName = "First Name", + FieldName = "givenname", + FieldType = 0 + }, + new + { + Id = 3, + DisplayName = "Office", + FieldName = "physicalDeliveryOfficeName", + FieldType = 0 + }, + new + { + Id = 4, + DisplayName = "Employee ID", + FieldName = "employeeId", + FieldType = 0 + }, + new + { + Id = 5, + DisplayName = "Home Directory", + FieldName = "homeDirectory", + FieldType = 0 + }, + new + { + Id = 6, + DisplayName = "Logon Script Path", + FieldName = "scriptPath", + FieldType = 0 + }, + new + { + Id = 7, + DisplayName = "Profile Path", + FieldName = "profilePath", + FieldType = 0 + }, + new + { + Id = 8, + DisplayName = "Home Phone Number", + FieldName = "homePhone", + FieldType = 0 + }, + new + { + Id = 9, + DisplayName = "Street Address", + FieldName = "streetAddress", + FieldType = 0 + }, + new + { + Id = 10, + DisplayName = "City", + FieldName = "l", + FieldType = 0 + }, + new + { + Id = 11, + DisplayName = "State", + FieldName = "st", + FieldType = 0 + }, + new + { + Id = 12, + DisplayName = "Zip Code", + FieldName = "postalCode", + FieldType = 0 + }, + new + { + Id = 13, + DisplayName = "Site", + FieldName = "site", + FieldType = 0 + }, + new + { + Id = 14, + DisplayName = "Name", + FieldName = "name", + FieldType = 0 + }, + new + { + Id = 15, + DisplayName = "Username", + FieldName = "samaccountname", + FieldType = 0 + }, + new + { + Id = 16, + DisplayName = "SID", + FieldName = "objectSID", + FieldType = 2 + }, + new + { + Id = 17, + DisplayName = "E-Mail Address", + FieldName = "mail", + FieldType = 0 + }, + new + { + Id = 18, + DisplayName = "Description", + FieldName = "description", + FieldType = 0 + }, + new + { + Id = 19, + DisplayName = "Display Name", + FieldName = "displayName", + FieldType = 0 + }, + new + { + Id = 20, + DisplayName = "Distinguished Name", + FieldName = "distinguishedName", + FieldType = 0 + }, + new + { + Id = 21, + DisplayName = "Member Of", + FieldName = "memberOf", + FieldType = 4 + }, + new + { + Id = 22, + DisplayName = "Company", + FieldName = "company", + FieldType = 0 + }, + new + { + Id = 23, + DisplayName = "Title", + FieldName = "title", + FieldType = 0 + }, + new + { + Id = 24, + DisplayName = "User Principal Name", + FieldName = "userPrincipalName", + FieldType = 0 + }, + new + { + Id = 25, + DisplayName = "Telephone Number", + FieldName = "telephoneNumber", + FieldType = 0 + }, + new + { + Id = 26, + DisplayName = "PO Box", + FieldName = "postOfficeBox", + FieldType = 0 + }, + new + { + Id = 27, + DisplayName = "Canonical Name", + FieldName = "cn", + FieldType = 0 + }, + new + { + Id = 28, + DisplayName = "Home Drive", + FieldName = "homeDrive", + FieldType = 3 + }, + new + { + Id = 29, + DisplayName = "Department", + FieldName = "department", + FieldType = 0 + }, + new + { + Id = 30, + DisplayName = "Middle Name", + FieldName = "middleName", + FieldType = 0 + }, + new + { + Id = 31, + DisplayName = "Pager", + FieldName = "pager", + FieldType = 0 + }, + new + { + Id = 32, + DisplayName = "OS", + FieldName = "operatingSystemVersion", + FieldType = 0 + }, + new + { + Id = 33, + DisplayName = "Account Expiration", + FieldName = "accountExpires", + FieldType = 1 + }, + new + { + Id = 34, + DisplayName = "Manager", + FieldName = "manager", + FieldType = 0 + }, + new + { + Id = 35, + DisplayName = "Photo", + FieldName = "thumbnail", + FieldType = 2 + }, + new + { + Id = 36, + DisplayName = "Log On To", + FieldName = "userWorkstations", + FieldType = 0 + }, + new + { + Id = 37, + DisplayName = "Logon Hours", + FieldName = "logonHours", + FieldType = 2 + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryFieldObjectType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ActiveDirectoryFieldId") + .HasColumnType("int"); + + b.Property("CustomActiveDirectoryFieldId") + .HasColumnType("int"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CustomActiveDirectoryFieldId"); + + b.ToTable("ActiveDirectoryFieldObjectMappings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.AppSettings", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("AnalyticsId") + .HasColumnType("nvarchar(max)"); + + b.Property("AppFQDN") + .HasColumnType("nvarchar(max)"); + + b.Property("AppIcon") + .HasColumnType("varbinary(max)"); + + b.Property("AppName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AutoUpdate") + .HasColumnType("bit"); + + b.Property("AutoUpdateTime") + .HasColumnType("time"); + + b.Property("ForceHTTPS") + .HasColumnType("bit"); + + b.Property("InstallationCompleted") + .HasColumnType("bit"); + + b.Property("LastUpdateCheck") + .HasColumnType("datetime2"); + + b.Property("MOTD") + .HasColumnType("nvarchar(max)"); + + b.Property("MyrtilleURL") + .HasColumnType("nvarchar(max)"); + + b.Property("SSLCertificateCipher") + .HasColumnType("nvarchar(max)"); + + b.Property("SendDeveloperAnalytics") + .HasColumnType("bit"); + + b.Property("SendLogsToDeveloper") + .HasColumnType("bit"); + + b.Property("UpdateBranch") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDomain") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatePassword") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateUsername") + .HasColumnType("nvarchar(max)"); + + b.Property("UseUpdateCredentials") + .HasColumnType("bit"); + + b.Property("UserHelpdeskURL") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AppSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1") + .HasName("CK_Table_Column1"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.DirectoryEntryAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AfterAction") + .HasColumnType("nvarchar(max)"); + + b.Property("BeforeAction") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Sid") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Target") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("DirectoryEntryAuditLogs"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.LogonAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AfterAction") + .HasColumnType("nvarchar(max)"); + + b.Property("BeforeAction") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Target") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("LogonAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.PermissionsAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AfterAction") + .HasColumnType("nvarchar(max)"); + + b.Property("BeforeAction") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Target") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("PermissionsAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.RequestAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AfterAction") + .HasColumnType("nvarchar(max)"); + + b.Property("BeforeAction") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Target") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("RequestAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.SettingsAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AfterAction") + .HasColumnType("nvarchar(max)"); + + b.Property("BeforeAction") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Target") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SettingsAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.SystemAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AfterAction") + .HasColumnType("nvarchar(max)"); + + b.Property("BeforeAction") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Target") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("Username") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SystemAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.AuthenticationSettings", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("AdminPassword") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DuoApiHost") + .HasColumnType("nvarchar(max)"); + + b.Property("DuoClientId") + .HasColumnType("nvarchar(max)"); + + b.Property("DuoClientSecret") + .HasColumnType("nvarchar(max)"); + + b.Property("DuoEnabled") + .HasColumnType("bit"); + + b.Property("DuoUnreachableBehavior") + .HasColumnType("int"); + + b.Property("SessionTimeout") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AuthenticationSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1") + .HasName("CK_Table_Column2"); + }); + + b.HasData( + new + { + Id = 1, + AdminPassword = "password", + DuoEnabled = false, + DuoUnreachableBehavior = 0, + SessionTimeout = 15 + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.ChatRoom", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("IsPublic") + .HasColumnType("bit"); + + b.Property("MembersHash") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ChatRooms"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.UnreadChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChatMessageId") + .HasColumnType("int"); + + b.Property("ChatRoomId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ChatMessageId"); + + b.ToTable("UnreadChatMessages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.CustomActiveDirectoryField", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FieldName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FieldType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("CustomActiveDirectoryFields"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.EmailSettings", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("AdminBcc") + .HasColumnType("nvarchar(max)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("FromAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("FromName") + .HasColumnType("nvarchar(max)"); + + b.Property("ReplyToAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("ReplyToName") + .HasColumnType("nvarchar(max)"); + + b.Property("SMTPPassword") + .HasColumnType("nvarchar(max)"); + + b.Property("SMTPPort") + .HasColumnType("int"); + + b.Property("SMTPServer") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SMTPUsername") + .HasColumnType("nvarchar(max)"); + + b.Property("UseSMTPAuth") + .HasColumnType("bit"); + + b.Property("UseTLS") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.ToTable("EmailSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1") + .HasName("CK_Table_Column3"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.EmailTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Body") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TemplateType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("EmailTemplates"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Block") + .HasColumnType("bit"); + + b.Property("ByEmail") + .HasColumnType("bit"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("InApp") + .HasColumnType("bit"); + + b.Property("OU") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationSubscriptions"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionNotificationType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("NotificationSubscriptionId") + .HasColumnType("int"); + + b.Property("NotificationType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NotificationSubscriptionId"); + + b.ToTable("SubscriptionNotificationType"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionWebHookType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("NotificationType") + .HasColumnType("int"); + + b.Property("WebHookSubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WebHookSubscriptionId"); + + b.ToTable("SubscriptionWebHookType"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookAttempt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Body") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Delivered") + .HasColumnType("bit"); + + b.Property("EventTimestamp") + .HasColumnType("datetime2"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastAttemptTimestamp") + .HasColumnType("datetime2"); + + b.Property("MessageGuid") + .HasColumnType("uniqueidentifier"); + + b.Property("ResponseCode") + .HasColumnType("int"); + + b.Property("ResponseHeaders") + .HasColumnType("nvarchar(max)"); + + b.Property("ResponseMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("RetryCount") + .HasColumnType("int"); + + b.Property("Signature") + .HasColumnType("nvarchar(max)"); + + b.Property("Uri") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("WebHookSubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WebHookSubscriptionId"); + + b.ToTable("WebHookAttempts"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AuthorizationToken") + .HasColumnType("nvarchar(max)"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("HmacKey") + .HasColumnType("nvarchar(max)"); + + b.Property("IgnoreSSLVerification") + .HasColumnType("bit"); + + b.Property("OU") + .HasColumnType("nvarchar(max)"); + + b.Property("PrivateKey") + .HasColumnType("nvarchar(max)"); + + b.Property("PublicKey") + .HasColumnType("nvarchar(max)"); + + b.Property("URL") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("WebHookAuthorization") + .HasColumnType("int"); + + b.Property("WebHookMethod") + .HasColumnType("int"); + + b.Property("WebHookSignature") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("WebHookSubscriptions"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AccessLevels"); + + b.HasData( + new + { + Id = 1, + Name = "Deny All" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ActionAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AccessLevelId") + .HasColumnType("int"); + + b.Property("AllowOrDeny") + .HasColumnType("bit"); + + b.Property("ObjectActionId") + .HasColumnType("int"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AccessLevelId"); + + b.HasIndex("ObjectActionId"); + + b.ToTable("ActionAccessMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("FieldAccessLevel"); + + b.HasData( + new + { + Id = 1, + Level = 10, + Name = "Deny" + }, + new + { + Id = 2, + Level = 100, + Name = "Read" + }, + new + { + Id = 3, + Level = 1000, + Name = "Edit" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CustomFieldId") + .HasColumnType("int"); + + b.Property("FieldAccessLevelId") + .HasColumnType("int"); + + b.Property("FieldId") + .HasColumnType("int"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CustomFieldId"); + + b.HasIndex("FieldAccessLevelId"); + + b.HasIndex("FieldId"); + + b.ToTable("AccessLevelFieldMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.GlobalPermissionRequestActions", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ObjectAction") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("GlobalPermissionRequestActions"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.GlobalPermissionSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AllowAccessRequest") + .HasColumnType("bit"); + + b.Property("AllowSelfModification") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.ToTable("GlobalPermissionSettings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ObjectAccessLevel"); + + b.HasData( + new + { + Id = 1, + Level = 10, + Name = "Deny" + }, + new + { + Id = 2, + Level = 1000, + Name = "Read" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AllowDisabled") + .HasColumnType("bit"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("ObjectAccessLevelId") + .HasColumnType("int"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ObjectAccessLevelId"); + + b.ToTable("AccessLevelObjectMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ObjectActionFlag"); + + b.HasData( + new + { + Id = 1, + Action = 4, + Name = "Assign" + }, + new + { + Id = 2, + Action = 3, + Name = "UnAssign" + }, + new + { + Id = 3, + Action = 8, + Name = "Unlock" + }, + new + { + Id = 4, + Action = 5, + Name = "Enable" + }, + new + { + Id = 5, + Action = 6, + Name = "Disable" + }, + new + { + Id = 6, + Action = 7, + Name = "Rename" + }, + new + { + Id = 7, + Action = 0, + Name = "Move" + }, + new + { + Id = 8, + Action = 2, + Name = "Create" + }, + new + { + Id = 9, + Action = 1, + Name = "Delete" + }, + new + { + Id = 10, + Action = 9, + Name = "Set Password" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.PermissionDelegate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DelegateName") + .HasColumnType("nvarchar(max)"); + + b.Property("DelegateSid") + .IsRequired() + .HasColumnType("varbinary(900)"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("IsSuperAdmin") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("DelegateSid") + .IsUnique(); + + b.ToTable("PermissionDelegate"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.PermissionMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("OU") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("PermissionMap"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AllowCustomGroups") + .HasColumnType("bit"); + + b.Property("AllowUsernameOverride") + .HasColumnType("bit"); + + b.Property("AskForAlternateEmail") + .HasColumnType("bit"); + + b.Property("Category") + .HasColumnType("nvarchar(max)"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("DisplayNameFormula") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ObjectType") + .HasColumnType("int"); + + b.Property("ParentOU") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentTemplateId") + .HasColumnType("int"); + + b.Property("PasswordFormula") + .HasColumnType("nvarchar(max)"); + + b.Property("RequirePasswordChange") + .HasColumnType("bit"); + + b.Property("SendWelcomeEmail") + .HasColumnType("bit"); + + b.Property("UsernameFormula") + .HasColumnType("nvarchar(max)"); + + b.Property("Visible") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("ParentTemplateId"); + + b.ToTable("DirectoryTemplates"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateFieldValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CustomFieldId") + .HasColumnType("int"); + + b.Property("DirectoryTemplateId") + .HasColumnType("int"); + + b.Property("Editable") + .HasColumnType("bit"); + + b.Property("FieldId") + .HasColumnType("int"); + + b.Property("Required") + .HasColumnType("bit"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("CustomFieldId"); + + b.HasIndex("DirectoryTemplateId"); + + b.HasIndex("FieldId"); + + b.ToTable("DirectoryTemplateFieldValues"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DirectoryTemplateId") + .HasColumnType("int"); + + b.Property("GroupSid") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryTemplateId"); + + b.ToTable("DirectoryTemplateGroups"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("IsRevoked") + .HasColumnType("bit"); + + b.Property("LastUsedAt") + .HasColumnType("datetime2"); + + b.Property("Token") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TokenHash") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ApiTokens"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DarkMode") + .HasColumnType("bit"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("ProfilePicture") + .HasColumnType("varbinary(max)"); + + b.Property("SearchDisabledComputers") + .HasColumnType("bit"); + + b.Property("SearchDisabledUsers") + .HasColumnType("bit"); + + b.Property("Theme") + .HasColumnType("nvarchar(max)"); + + b.Property("UserGUID") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Username") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("UserGUID") + .IsUnique(); + + b.ToTable("UserSettings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.NotificationMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnType("int"); + + b.Property("Dismissable") + .HasColumnType("bit"); + + b.Property("Expires") + .HasColumnType("datetime2"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Link") + .HasColumnType("nvarchar(max)"); + + b.Property("Message") + .HasColumnType("nvarchar(max)"); + + b.Property("MessageType") + .HasColumnType("int"); + + b.Property("TargetDN") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.ToTable("NotificationMessages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ReadNewsItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("NewsItemId") + .HasColumnType("float"); + + b.Property("NewsItemUpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ReadNewsItems"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserDashboardWidget", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ItemsPerPage") + .HasColumnType("int"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("Slot") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("WidgetType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserDashboardWidgets"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserFavoriteEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DN") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserFavoriteEntries"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("IsRead") + .HasColumnType("bit"); + + b.Property("NotificationId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NotificationId"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotifications"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChatRoomId") + .HasColumnType("int"); + + b.Property("Message") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ChatRoomId"); + + b.HasIndex("UserId"); + + b.ToTable("ChatMessages"); + }); + + modelBuilder.Entity("PermissionDelegatePermissionMapping", b => + { + b.Property("PermissionDelegatesId") + .HasColumnType("int"); + + b.Property("PermissionsMapsId") + .HasColumnType("int"); + + b.HasKey("PermissionDelegatesId", "PermissionsMapsId"); + + b.HasIndex("PermissionsMapsId"); + + b.ToTable("PermissionDelegatePermissionMapping"); + }); + + modelBuilder.Entity("AccessLevelFieldAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.FieldAccessMapping", null) + .WithMany() + .HasForeignKey("FieldMapId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AccessLevelObjectAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", null) + .WithMany() + .HasForeignKey("ObjectMapId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AccessLevelPermissionMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionMapping", null) + .WithMany() + .HasForeignKey("PermissionMapsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AppUserChatRoom", b => + { + b.HasOne("BLAZAM.Database.Models.Chat.ChatRoom", null) + .WithMany() + .HasForeignKey("ChatRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", null) + .WithMany() + .HasForeignKey("MembersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryFieldObjectType", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", null) + .WithMany("ObjectTypes") + .HasForeignKey("CustomActiveDirectoryFieldId"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.UnreadChatMessage", b => + { + b.HasOne("BLAZAM.Server.Data.ChatMessage", "ChatMessage") + .WithMany("NotReadBy") + .HasForeignKey("ChatMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatMessage"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("NotificationSubscriptions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionNotificationType", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.NotificationSubscription", "NotificationSubscription") + .WithMany("NotificationTypes") + .HasForeignKey("NotificationSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationSubscription"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionWebHookType", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.WebHookSubscription", "WebHookSubscription") + .WithMany("NotificationTypes") + .HasForeignKey("WebHookSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebHookSubscription"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookAttempt", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.WebHookSubscription", "WebHookSubscription") + .WithMany("WebHookAttempts") + .HasForeignKey("WebHookSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebHookSubscription"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ActionAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany("ActionMap") + .HasForeignKey("AccessLevelId"); + + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAction", "ObjectAction") + .WithMany() + .HasForeignKey("ObjectActionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ObjectAction"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", "CustomField") + .WithMany() + .HasForeignKey("CustomFieldId"); + + b.HasOne("BLAZAM.Database.Models.Permissions.FieldAccessLevel", "FieldAccessLevel") + .WithMany() + .HasForeignKey("FieldAccessLevelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.ActiveDirectoryField", "Field") + .WithMany() + .HasForeignKey("FieldId"); + + b.Navigation("CustomField"); + + b.Navigation("Field"); + + b.Navigation("FieldAccessLevel"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", "ObjectAccessLevel") + .WithMany("ObjectAccessMappings") + .HasForeignKey("ObjectAccessLevelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ObjectAccessLevel"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", "ParentTemplate") + .WithMany() + .HasForeignKey("ParentTemplateId"); + + b.Navigation("ParentTemplate"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateFieldValue", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", "CustomField") + .WithMany() + .HasForeignKey("CustomFieldId"); + + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", null) + .WithMany("FieldValues") + .HasForeignKey("DirectoryTemplateId"); + + b.HasOne("BLAZAM.Database.Models.ActiveDirectoryField", "Field") + .WithMany() + .HasForeignKey("FieldId"); + + b.Navigation("CustomField"); + + b.Navigation("Field"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateGroup", b => + { + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", null) + .WithMany("AssignedGroupSids") + .HasForeignKey("DirectoryTemplateId"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("APITokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.NotificationMessage", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "Creator") + .WithMany() + .HasForeignKey("CreatorId"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ReadNewsItem", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("ReadNewsItems") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserDashboardWidget", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("DashboardWidgets") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserFavoriteEntry", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("FavoriteEntries") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserNotification", b => + { + b.HasOne("BLAZAM.Database.Models.User.NotificationMessage", "Notification") + .WithMany() + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("Notifications") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.HasOne("BLAZAM.Database.Models.Chat.ChatRoom", "ChatRoom") + .WithMany("Messages") + .HasForeignKey("ChatRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatRoom"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("PermissionDelegatePermissionMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionDelegate", null) + .WithMany() + .HasForeignKey("PermissionDelegatesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionMapping", null) + .WithMany() + .HasForeignKey("PermissionsMapsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.ChatRoom", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.CustomActiveDirectoryField", b => + { + b.Navigation("ObjectTypes"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.Navigation("NotificationTypes"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookSubscription", b => + { + b.Navigation("NotificationTypes"); + + b.Navigation("WebHookAttempts"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => + { + b.Navigation("ActionMap"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", b => + { + b.Navigation("ObjectAccessMappings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.Navigation("AssignedGroupSids"); + + b.Navigation("FieldValues"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + { + b.Navigation("APITokens"); + + b.Navigation("DashboardWidgets"); + + b.Navigation("FavoriteEntries"); + + b.Navigation("NotificationSubscriptions"); + + b.Navigation("Notifications"); + + b.Navigation("ReadNewsItems"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.Navigation("NotReadBy"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BLAZAMDatabase/Migrations/Sql/20241123221415_Add_Webhooks_Sql.cs b/BLAZAMDatabase/Migrations/Sql/20241123221415_Add_Webhooks_Sql.cs new file mode 100644 index 000000000..cd6c9a02a --- /dev/null +++ b/BLAZAMDatabase/Migrations/Sql/20241123221415_Add_Webhooks_Sql.cs @@ -0,0 +1,112 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BLAZAM.Database.Migrations.Sql +{ + /// + public partial class Add_Webhooks_Sql : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "WebHookSubscriptions", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + OU = table.Column(type: "nvarchar(max)", nullable: true), + IgnoreSSLVerification = table.Column(type: "bit", nullable: false), + URL = table.Column(type: "nvarchar(max)", nullable: false), + WebHookMethod = table.Column(type: "int", nullable: false), + WebHookSignature = table.Column(type: "int", nullable: false), + WebHookAuthorization = table.Column(type: "int", nullable: false), + AuthorizationToken = table.Column(type: "nvarchar(max)", nullable: true), + HmacKey = table.Column(type: "nvarchar(max)", nullable: true), + PrivateKey = table.Column(type: "nvarchar(max)", nullable: true), + PublicKey = table.Column(type: "nvarchar(max)", nullable: true), + DeletedAt = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_WebHookSubscriptions", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "SubscriptionWebHookType", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + WebHookSubscriptionId = table.Column(type: "int", nullable: false), + NotificationType = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SubscriptionWebHookType", x => x.Id); + table.ForeignKey( + name: "FK_SubscriptionWebHookType_WebHookSubscriptions_WebHookSubscriptionId", + column: x => x.WebHookSubscriptionId, + principalTable: "WebHookSubscriptions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "WebHookAttempts", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + MessageGuid = table.Column(type: "uniqueidentifier", nullable: false), + LastAttemptTimestamp = table.Column(type: "datetime2", nullable: false), + EventTimestamp = table.Column(type: "datetime2", nullable: false), + EventType = table.Column(type: "nvarchar(max)", nullable: false), + WebHookSubscriptionId = table.Column(type: "int", nullable: false), + Uri = table.Column(type: "nvarchar(max)", nullable: false), + Delivered = table.Column(type: "bit", nullable: false), + Body = table.Column(type: "nvarchar(max)", nullable: false), + Signature = table.Column(type: "nvarchar(max)", nullable: true), + RetryCount = table.Column(type: "int", nullable: false), + ResponseCode = table.Column(type: "int", nullable: false), + ResponseHeaders = table.Column(type: "nvarchar(max)", nullable: true), + ResponseMessage = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_WebHookAttempts", x => x.Id); + table.ForeignKey( + name: "FK_WebHookAttempts_WebHookSubscriptions_WebHookSubscriptionId", + column: x => x.WebHookSubscriptionId, + principalTable: "WebHookSubscriptions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_SubscriptionWebHookType_WebHookSubscriptionId", + table: "SubscriptionWebHookType", + column: "WebHookSubscriptionId"); + + migrationBuilder.CreateIndex( + name: "IX_WebHookAttempts_WebHookSubscriptionId", + table: "WebHookAttempts", + column: "WebHookSubscriptionId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SubscriptionWebHookType"); + + migrationBuilder.DropTable( + name: "WebHookAttempts"); + + migrationBuilder.DropTable( + name: "WebHookSubscriptions"); + } + } +} diff --git a/BLAZAMDatabase/Migrations/Sql/SqlDatabaseContextModelSnapshot.cs b/BLAZAMDatabase/Migrations/Sql/SqlDatabaseContextModelSnapshot.cs index 2d2275f37..99aeaa020 100644 --- a/BLAZAMDatabase/Migrations/Sql/SqlDatabaseContextModelSnapshot.cs +++ b/BLAZAMDatabase/Migrations/Sql/SqlDatabaseContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.10") + .HasAnnotation("ProductVersion", "8.0.11") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -982,6 +982,131 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("SubscriptionNotificationType"); }); + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionWebHookType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("NotificationType") + .HasColumnType("int"); + + b.Property("WebHookSubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WebHookSubscriptionId"); + + b.ToTable("SubscriptionWebHookType"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookAttempt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Body") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Delivered") + .HasColumnType("bit"); + + b.Property("EventTimestamp") + .HasColumnType("datetime2"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastAttemptTimestamp") + .HasColumnType("datetime2"); + + b.Property("MessageGuid") + .HasColumnType("uniqueidentifier"); + + b.Property("ResponseCode") + .HasColumnType("int"); + + b.Property("ResponseHeaders") + .HasColumnType("nvarchar(max)"); + + b.Property("ResponseMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("RetryCount") + .HasColumnType("int"); + + b.Property("Signature") + .HasColumnType("nvarchar(max)"); + + b.Property("Uri") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("WebHookSubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WebHookSubscriptionId"); + + b.ToTable("WebHookAttempts"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AuthorizationToken") + .HasColumnType("nvarchar(max)"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("HmacKey") + .HasColumnType("nvarchar(max)"); + + b.Property("IgnoreSSLVerification") + .HasColumnType("bit"); + + b.Property("OU") + .HasColumnType("nvarchar(max)"); + + b.Property("PrivateKey") + .HasColumnType("nvarchar(max)"); + + b.Property("PublicKey") + .HasColumnType("nvarchar(max)"); + + b.Property("URL") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("WebHookAuthorization") + .HasColumnType("int"); + + b.Property("WebHookMethod") + .HasColumnType("int"); + + b.Property("WebHookSignature") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("WebHookSubscriptions"); + }); + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => { b.Property("Id") @@ -1459,7 +1584,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("DirectoryTemplateGroups"); }); - modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -1467,9 +1592,49 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - b.Property("APIToken") + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("Description") .HasColumnType("nvarchar(max)"); + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("IsRevoked") + .HasColumnType("bit"); + + b.Property("LastUsedAt") + .HasColumnType("datetime2"); + + b.Property("Token") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TokenHash") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ApiTokens"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + b.Property("DarkMode") .HasColumnType("bit"); @@ -1799,6 +1964,28 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("NotificationSubscription"); }); + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionWebHookType", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.WebHookSubscription", "WebHookSubscription") + .WithMany("NotificationTypes") + .HasForeignKey("WebHookSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebHookSubscription"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookAttempt", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.WebHookSubscription", "WebHookSubscription") + .WithMany("WebHookAttempts") + .HasForeignKey("WebHookSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebHookSubscription"); + }); + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ActionAccessMapping", b => { b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) @@ -1883,6 +2070,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("DirectoryTemplateId"); }); + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("APITokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + modelBuilder.Entity("BLAZAM.Database.Models.User.NotificationMessage", b => { b.HasOne("BLAZAM.Database.Models.User.AppUser", "Creator") @@ -1993,6 +2191,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("NotificationTypes"); }); + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookSubscription", b => + { + b.Navigation("NotificationTypes"); + + b.Navigation("WebHookAttempts"); + }); + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => { b.Navigation("ActionMap"); @@ -2012,6 +2217,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => { + b.Navigation("APITokens"); + b.Navigation("DashboardWidgets"); b.Navigation("FavoriteEntries"); diff --git a/BLAZAMDatabase/Migrations/Sqlite/20241118154642_Add_Api_Tokens_Sqlite.Designer.cs b/BLAZAMDatabase/Migrations/Sqlite/20241118154642_Add_Api_Tokens_Sqlite.Designer.cs new file mode 100644 index 000000000..4f60a7fd6 --- /dev/null +++ b/BLAZAMDatabase/Migrations/Sqlite/20241118154642_Add_Api_Tokens_Sqlite.Designer.cs @@ -0,0 +1,2009 @@ +// +using System; +using BLAZAM.Database.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BLAZAM.Database.Migrations.Sqlite +{ + [DbContext(typeof(SqliteDatabaseContext))] + [Migration("20241118154642_Add_Api_Tokens_Sqlite")] + partial class Add_Api_Tokens_Sqlite + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.11"); + + modelBuilder.Entity("AccessLevelFieldAccessMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("INTEGER"); + + b.Property("FieldMapId") + .HasColumnType("INTEGER"); + + b.HasKey("AccessLevelsId", "FieldMapId"); + + b.HasIndex("FieldMapId"); + + b.ToTable("AccessLevelFieldAccessMapping"); + }); + + modelBuilder.Entity("AccessLevelObjectAccessMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("INTEGER"); + + b.Property("ObjectMapId") + .HasColumnType("INTEGER"); + + b.HasKey("AccessLevelsId", "ObjectMapId"); + + b.HasIndex("ObjectMapId"); + + b.ToTable("AccessLevelObjectAccessMapping"); + }); + + modelBuilder.Entity("AccessLevelPermissionMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("INTEGER"); + + b.Property("PermissionMapsId") + .HasColumnType("INTEGER"); + + b.HasKey("AccessLevelsId", "PermissionMapsId"); + + b.HasIndex("PermissionMapsId"); + + b.ToTable("AccessLevelPermissionMapping"); + }); + + modelBuilder.Entity("AppUserChatRoom", b => + { + b.Property("ChatRoomId") + .HasColumnType("INTEGER"); + + b.Property("MembersId") + .HasColumnType("INTEGER"); + + b.HasKey("ChatRoomId", "MembersId"); + + b.HasIndex("MembersId"); + + b.ToTable("AppUserChatRoom"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ADSettings", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("ApplicationBaseDN") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FQDN") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ServerAddress") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ServerPort") + .HasColumnType("INTEGER"); + + b.Property("UseTLS") + .HasColumnType("INTEGER"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ActiveDirectorySettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryField", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FieldName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FieldType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ActiveDirectoryFields"); + + b.HasData( + new + { + Id = 1, + DisplayName = "Last Name", + FieldName = "sn", + FieldType = 0 + }, + new + { + Id = 2, + DisplayName = "First Name", + FieldName = "givenname", + FieldType = 0 + }, + new + { + Id = 3, + DisplayName = "Office", + FieldName = "physicalDeliveryOfficeName", + FieldType = 0 + }, + new + { + Id = 4, + DisplayName = "Employee ID", + FieldName = "employeeId", + FieldType = 0 + }, + new + { + Id = 5, + DisplayName = "Home Directory", + FieldName = "homeDirectory", + FieldType = 0 + }, + new + { + Id = 6, + DisplayName = "Logon Script Path", + FieldName = "scriptPath", + FieldType = 0 + }, + new + { + Id = 7, + DisplayName = "Profile Path", + FieldName = "profilePath", + FieldType = 0 + }, + new + { + Id = 8, + DisplayName = "Home Phone Number", + FieldName = "homePhone", + FieldType = 0 + }, + new + { + Id = 9, + DisplayName = "Street Address", + FieldName = "streetAddress", + FieldType = 0 + }, + new + { + Id = 10, + DisplayName = "City", + FieldName = "l", + FieldType = 0 + }, + new + { + Id = 11, + DisplayName = "State", + FieldName = "st", + FieldType = 0 + }, + new + { + Id = 12, + DisplayName = "Zip Code", + FieldName = "postalCode", + FieldType = 0 + }, + new + { + Id = 13, + DisplayName = "Site", + FieldName = "site", + FieldType = 0 + }, + new + { + Id = 14, + DisplayName = "Name", + FieldName = "name", + FieldType = 0 + }, + new + { + Id = 15, + DisplayName = "Username", + FieldName = "samaccountname", + FieldType = 0 + }, + new + { + Id = 16, + DisplayName = "SID", + FieldName = "objectSID", + FieldType = 2 + }, + new + { + Id = 17, + DisplayName = "E-Mail Address", + FieldName = "mail", + FieldType = 0 + }, + new + { + Id = 18, + DisplayName = "Description", + FieldName = "description", + FieldType = 0 + }, + new + { + Id = 19, + DisplayName = "Display Name", + FieldName = "displayName", + FieldType = 0 + }, + new + { + Id = 20, + DisplayName = "Distinguished Name", + FieldName = "distinguishedName", + FieldType = 0 + }, + new + { + Id = 21, + DisplayName = "Member Of", + FieldName = "memberOf", + FieldType = 4 + }, + new + { + Id = 22, + DisplayName = "Company", + FieldName = "company", + FieldType = 0 + }, + new + { + Id = 23, + DisplayName = "Title", + FieldName = "title", + FieldType = 0 + }, + new + { + Id = 24, + DisplayName = "User Principal Name", + FieldName = "userPrincipalName", + FieldType = 0 + }, + new + { + Id = 25, + DisplayName = "Telephone Number", + FieldName = "telephoneNumber", + FieldType = 0 + }, + new + { + Id = 26, + DisplayName = "PO Box", + FieldName = "postOfficeBox", + FieldType = 0 + }, + new + { + Id = 27, + DisplayName = "Canonical Name", + FieldName = "cn", + FieldType = 0 + }, + new + { + Id = 28, + DisplayName = "Home Drive", + FieldName = "homeDrive", + FieldType = 3 + }, + new + { + Id = 29, + DisplayName = "Department", + FieldName = "department", + FieldType = 0 + }, + new + { + Id = 30, + DisplayName = "Middle Name", + FieldName = "middleName", + FieldType = 0 + }, + new + { + Id = 31, + DisplayName = "Pager", + FieldName = "pager", + FieldType = 0 + }, + new + { + Id = 32, + DisplayName = "OS", + FieldName = "operatingSystemVersion", + FieldType = 0 + }, + new + { + Id = 33, + DisplayName = "Account Expiration", + FieldName = "accountExpires", + FieldType = 1 + }, + new + { + Id = 34, + DisplayName = "Manager", + FieldName = "manager", + FieldType = 0 + }, + new + { + Id = 35, + DisplayName = "Photo", + FieldName = "thumbnail", + FieldType = 2 + }, + new + { + Id = 36, + DisplayName = "Log On To", + FieldName = "userWorkstations", + FieldType = 0 + }, + new + { + Id = 37, + DisplayName = "Logon Hours", + FieldName = "logonHours", + FieldType = 2 + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryFieldObjectType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ActiveDirectoryFieldId") + .HasColumnType("INTEGER"); + + b.Property("CustomActiveDirectoryFieldId") + .HasColumnType("INTEGER"); + + b.Property("ObjectType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CustomActiveDirectoryFieldId"); + + b.ToTable("ActiveDirectoryFieldObjectMappings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.AppSettings", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("AnalyticsId") + .HasColumnType("TEXT"); + + b.Property("AppFQDN") + .HasColumnType("TEXT"); + + b.Property("AppIcon") + .HasColumnType("BLOB"); + + b.Property("AppName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AutoUpdate") + .HasColumnType("INTEGER"); + + b.Property("AutoUpdateTime") + .HasColumnType("TEXT"); + + b.Property("ForceHTTPS") + .HasColumnType("INTEGER"); + + b.Property("InstallationCompleted") + .HasColumnType("INTEGER"); + + b.Property("LastUpdateCheck") + .HasColumnType("TEXT"); + + b.Property("MOTD") + .HasColumnType("TEXT"); + + b.Property("MyrtilleURL") + .HasColumnType("TEXT"); + + b.Property("SSLCertificateCipher") + .HasColumnType("TEXT"); + + b.Property("SendDeveloperAnalytics") + .HasColumnType("INTEGER"); + + b.Property("SendLogsToDeveloper") + .HasColumnType("INTEGER"); + + b.Property("UpdateBranch") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdateDomain") + .HasColumnType("TEXT"); + + b.Property("UpdatePassword") + .HasColumnType("TEXT"); + + b.Property("UpdateUsername") + .HasColumnType("TEXT"); + + b.Property("UseUpdateCredentials") + .HasColumnType("INTEGER"); + + b.Property("UserHelpdeskURL") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AppSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.DirectoryEntryAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AfterAction") + .HasColumnType("TEXT"); + + b.Property("BeforeAction") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasColumnType("TEXT"); + + b.Property("Sid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Target") + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("DirectoryEntryAuditLogs"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.LogonAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AfterAction") + .HasColumnType("TEXT"); + + b.Property("BeforeAction") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasColumnType("TEXT"); + + b.Property("Target") + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("LogonAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.PermissionsAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AfterAction") + .HasColumnType("TEXT"); + + b.Property("BeforeAction") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasColumnType("TEXT"); + + b.Property("Target") + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("PermissionsAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.RequestAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AfterAction") + .HasColumnType("TEXT"); + + b.Property("BeforeAction") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasColumnType("TEXT"); + + b.Property("Target") + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RequestAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.SettingsAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AfterAction") + .HasColumnType("TEXT"); + + b.Property("BeforeAction") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasColumnType("TEXT"); + + b.Property("Target") + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SettingsAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.SystemAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AfterAction") + .HasColumnType("TEXT"); + + b.Property("BeforeAction") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasColumnType("TEXT"); + + b.Property("Target") + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SystemAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.AuthenticationSettings", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("AdminPassword") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DuoApiHost") + .HasColumnType("TEXT"); + + b.Property("DuoClientId") + .HasColumnType("TEXT"); + + b.Property("DuoClientSecret") + .HasColumnType("TEXT"); + + b.Property("DuoEnabled") + .HasColumnType("INTEGER"); + + b.Property("DuoUnreachableBehavior") + .HasColumnType("INTEGER"); + + b.Property("SessionTimeout") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("AuthenticationSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1"); + }); + + b.HasData( + new + { + Id = 1, + AdminPassword = "password", + DuoEnabled = false, + DuoUnreachableBehavior = 0, + SessionTimeout = 15 + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.ChatRoom", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("IsPublic") + .HasColumnType("INTEGER"); + + b.Property("MembersHash") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChatRooms"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.UnreadChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChatMessageId") + .HasColumnType("INTEGER"); + + b.Property("ChatRoomId") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChatMessageId"); + + b.ToTable("UnreadChatMessages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.CustomActiveDirectoryField", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FieldName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FieldType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("CustomActiveDirectoryFields"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.EmailSettings", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("AdminBcc") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("FromAddress") + .HasColumnType("TEXT"); + + b.Property("FromName") + .HasColumnType("TEXT"); + + b.Property("ReplyToAddress") + .HasColumnType("TEXT"); + + b.Property("ReplyToName") + .HasColumnType("TEXT"); + + b.Property("SMTPPassword") + .HasColumnType("TEXT"); + + b.Property("SMTPPort") + .HasColumnType("INTEGER"); + + b.Property("SMTPServer") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SMTPUsername") + .HasColumnType("TEXT"); + + b.Property("UseSMTPAuth") + .HasColumnType("INTEGER"); + + b.Property("UseTLS") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("EmailSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.EmailTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Body") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TemplateType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("EmailTemplates"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Block") + .HasColumnType("INTEGER"); + + b.Property("ByEmail") + .HasColumnType("INTEGER"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("InApp") + .HasColumnType("INTEGER"); + + b.Property("OU") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationSubscriptions"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionNotificationType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("NotificationSubscriptionId") + .HasColumnType("INTEGER"); + + b.Property("NotificationType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("NotificationSubscriptionId"); + + b.ToTable("SubscriptionNotificationType"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AccessLevels"); + + b.HasData( + new + { + Id = 1, + Name = "Deny All" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ActionAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessLevelId") + .HasColumnType("INTEGER"); + + b.Property("AllowOrDeny") + .HasColumnType("INTEGER"); + + b.Property("ObjectActionId") + .HasColumnType("INTEGER"); + + b.Property("ObjectType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("AccessLevelId"); + + b.HasIndex("ObjectActionId"); + + b.ToTable("ActionAccessMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Level") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("FieldAccessLevel"); + + b.HasData( + new + { + Id = 1, + Level = 10, + Name = "Deny" + }, + new + { + Id = 2, + Level = 100, + Name = "Read" + }, + new + { + Id = 3, + Level = 1000, + Name = "Edit" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CustomFieldId") + .HasColumnType("INTEGER"); + + b.Property("FieldAccessLevelId") + .HasColumnType("INTEGER"); + + b.Property("FieldId") + .HasColumnType("INTEGER"); + + b.Property("ObjectType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CustomFieldId"); + + b.HasIndex("FieldAccessLevelId"); + + b.HasIndex("FieldId"); + + b.ToTable("AccessLevelFieldMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.GlobalPermissionRequestActions", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ObjectAction") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("GlobalPermissionRequestActions"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.GlobalPermissionSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AllowAccessRequest") + .HasColumnType("INTEGER"); + + b.Property("AllowSelfModification") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("GlobalPermissionSettings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Level") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ObjectAccessLevel"); + + b.HasData( + new + { + Id = 1, + Level = 10, + Name = "Deny" + }, + new + { + Id = 2, + Level = 1000, + Name = "Read" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AllowDisabled") + .HasColumnType("INTEGER"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("ObjectAccessLevelId") + .HasColumnType("INTEGER"); + + b.Property("ObjectType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ObjectAccessLevelId"); + + b.ToTable("AccessLevelObjectMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ObjectActionFlag"); + + b.HasData( + new + { + Id = 1, + Action = 4, + Name = "Assign" + }, + new + { + Id = 2, + Action = 3, + Name = "UnAssign" + }, + new + { + Id = 3, + Action = 8, + Name = "Unlock" + }, + new + { + Id = 4, + Action = 5, + Name = "Enable" + }, + new + { + Id = 5, + Action = 6, + Name = "Disable" + }, + new + { + Id = 6, + Action = 7, + Name = "Rename" + }, + new + { + Id = 7, + Action = 0, + Name = "Move" + }, + new + { + Id = 8, + Action = 2, + Name = "Create" + }, + new + { + Id = 9, + Action = 1, + Name = "Delete" + }, + new + { + Id = 10, + Action = 9, + Name = "Set Password" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.PermissionDelegate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DelegateName") + .HasColumnType("TEXT"); + + b.Property("DelegateSid") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("IsSuperAdmin") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DelegateSid") + .IsUnique(); + + b.ToTable("PermissionDelegate"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.PermissionMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("OU") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("PermissionMap"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AllowCustomGroups") + .HasColumnType("INTEGER"); + + b.Property("AllowUsernameOverride") + .HasColumnType("INTEGER"); + + b.Property("AskForAlternateEmail") + .HasColumnType("INTEGER"); + + b.Property("Category") + .HasColumnType("TEXT"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("DisplayNameFormula") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ObjectType") + .HasColumnType("INTEGER"); + + b.Property("ParentOU") + .HasColumnType("TEXT"); + + b.Property("ParentTemplateId") + .HasColumnType("INTEGER"); + + b.Property("PasswordFormula") + .HasColumnType("TEXT"); + + b.Property("RequirePasswordChange") + .HasColumnType("INTEGER"); + + b.Property("SendWelcomeEmail") + .HasColumnType("INTEGER"); + + b.Property("UsernameFormula") + .HasColumnType("TEXT"); + + b.Property("Visible") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("ParentTemplateId"); + + b.ToTable("DirectoryTemplates"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateFieldValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CustomFieldId") + .HasColumnType("INTEGER"); + + b.Property("DirectoryTemplateId") + .HasColumnType("INTEGER"); + + b.Property("Editable") + .HasColumnType("INTEGER"); + + b.Property("FieldId") + .HasColumnType("INTEGER"); + + b.Property("Required") + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CustomFieldId"); + + b.HasIndex("DirectoryTemplateId"); + + b.HasIndex("FieldId"); + + b.ToTable("DirectoryTemplateFieldValues"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DirectoryTemplateId") + .HasColumnType("INTEGER"); + + b.Property("GroupSid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryTemplateId"); + + b.ToTable("DirectoryTemplateGroups"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("ExpiresAt") + .HasColumnType("TEXT"); + + b.Property("IsRevoked") + .HasColumnType("INTEGER"); + + b.Property("LastUsedAt") + .HasColumnType("TEXT"); + + b.Property("Token") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TokenHash") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ApiTokens"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DarkMode") + .HasColumnType("INTEGER"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("ProfilePicture") + .HasColumnType("BLOB"); + + b.Property("SearchDisabledComputers") + .HasColumnType("INTEGER"); + + b.Property("SearchDisabledUsers") + .HasColumnType("INTEGER"); + + b.Property("Theme") + .HasColumnType("TEXT"); + + b.Property("UserGUID") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserGUID") + .IsUnique(); + + b.ToTable("UserSettings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.NotificationMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .HasColumnType("INTEGER"); + + b.Property("Created") + .HasColumnType("TEXT"); + + b.Property("CreatorId") + .HasColumnType("INTEGER"); + + b.Property("Dismissable") + .HasColumnType("INTEGER"); + + b.Property("Expires") + .HasColumnType("TEXT"); + + b.Property("Level") + .HasColumnType("INTEGER"); + + b.Property("Link") + .HasColumnType("TEXT"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("MessageType") + .HasColumnType("INTEGER"); + + b.Property("TargetDN") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.ToTable("NotificationMessages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ReadNewsItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("NewsItemId") + .HasColumnType("REAL"); + + b.Property("NewsItemUpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ReadNewsItems"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserDashboardWidget", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ItemsPerPage") + .HasColumnType("INTEGER"); + + b.Property("Order") + .HasColumnType("INTEGER"); + + b.Property("Slot") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.Property("WidgetType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserDashboardWidgets"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserFavoriteEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DN") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserFavoriteEntries"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsRead") + .HasColumnType("INTEGER"); + + b.Property("NotificationId") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("NotificationId"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotifications"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChatRoomId") + .HasColumnType("INTEGER"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChatRoomId"); + + b.HasIndex("UserId"); + + b.ToTable("ChatMessages"); + }); + + modelBuilder.Entity("PermissionDelegatePermissionMapping", b => + { + b.Property("PermissionDelegatesId") + .HasColumnType("INTEGER"); + + b.Property("PermissionsMapsId") + .HasColumnType("INTEGER"); + + b.HasKey("PermissionDelegatesId", "PermissionsMapsId"); + + b.HasIndex("PermissionsMapsId"); + + b.ToTable("PermissionDelegatePermissionMapping"); + }); + + modelBuilder.Entity("AccessLevelFieldAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.FieldAccessMapping", null) + .WithMany() + .HasForeignKey("FieldMapId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AccessLevelObjectAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", null) + .WithMany() + .HasForeignKey("ObjectMapId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AccessLevelPermissionMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionMapping", null) + .WithMany() + .HasForeignKey("PermissionMapsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AppUserChatRoom", b => + { + b.HasOne("BLAZAM.Database.Models.Chat.ChatRoom", null) + .WithMany() + .HasForeignKey("ChatRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", null) + .WithMany() + .HasForeignKey("MembersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryFieldObjectType", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", null) + .WithMany("ObjectTypes") + .HasForeignKey("CustomActiveDirectoryFieldId"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.UnreadChatMessage", b => + { + b.HasOne("BLAZAM.Server.Data.ChatMessage", "ChatMessage") + .WithMany("NotReadBy") + .HasForeignKey("ChatMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatMessage"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("NotificationSubscriptions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionNotificationType", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.NotificationSubscription", "NotificationSubscription") + .WithMany("NotificationTypes") + .HasForeignKey("NotificationSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationSubscription"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ActionAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany("ActionMap") + .HasForeignKey("AccessLevelId"); + + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAction", "ObjectAction") + .WithMany() + .HasForeignKey("ObjectActionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ObjectAction"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", "CustomField") + .WithMany() + .HasForeignKey("CustomFieldId"); + + b.HasOne("BLAZAM.Database.Models.Permissions.FieldAccessLevel", "FieldAccessLevel") + .WithMany() + .HasForeignKey("FieldAccessLevelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.ActiveDirectoryField", "Field") + .WithMany() + .HasForeignKey("FieldId"); + + b.Navigation("CustomField"); + + b.Navigation("Field"); + + b.Navigation("FieldAccessLevel"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", "ObjectAccessLevel") + .WithMany("ObjectAccessMappings") + .HasForeignKey("ObjectAccessLevelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ObjectAccessLevel"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", "ParentTemplate") + .WithMany() + .HasForeignKey("ParentTemplateId"); + + b.Navigation("ParentTemplate"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateFieldValue", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", "CustomField") + .WithMany() + .HasForeignKey("CustomFieldId"); + + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", null) + .WithMany("FieldValues") + .HasForeignKey("DirectoryTemplateId"); + + b.HasOne("BLAZAM.Database.Models.ActiveDirectoryField", "Field") + .WithMany() + .HasForeignKey("FieldId"); + + b.Navigation("CustomField"); + + b.Navigation("Field"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateGroup", b => + { + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", null) + .WithMany("AssignedGroupSids") + .HasForeignKey("DirectoryTemplateId"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("APITokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.NotificationMessage", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "Creator") + .WithMany() + .HasForeignKey("CreatorId"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ReadNewsItem", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("ReadNewsItems") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserDashboardWidget", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("DashboardWidgets") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserFavoriteEntry", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("FavoriteEntries") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserNotification", b => + { + b.HasOne("BLAZAM.Database.Models.User.NotificationMessage", "Notification") + .WithMany() + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("Notifications") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.HasOne("BLAZAM.Database.Models.Chat.ChatRoom", "ChatRoom") + .WithMany("Messages") + .HasForeignKey("ChatRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatRoom"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("PermissionDelegatePermissionMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionDelegate", null) + .WithMany() + .HasForeignKey("PermissionDelegatesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionMapping", null) + .WithMany() + .HasForeignKey("PermissionsMapsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.ChatRoom", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.CustomActiveDirectoryField", b => + { + b.Navigation("ObjectTypes"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.Navigation("NotificationTypes"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => + { + b.Navigation("ActionMap"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", b => + { + b.Navigation("ObjectAccessMappings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.Navigation("AssignedGroupSids"); + + b.Navigation("FieldValues"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + { + b.Navigation("APITokens"); + + b.Navigation("DashboardWidgets"); + + b.Navigation("FavoriteEntries"); + + b.Navigation("NotificationSubscriptions"); + + b.Navigation("Notifications"); + + b.Navigation("ReadNewsItems"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.Navigation("NotReadBy"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BLAZAMDatabase/Migrations/Sqlite/20241118154642_Add_Api_Tokens_Sqlite.cs b/BLAZAMDatabase/Migrations/Sqlite/20241118154642_Add_Api_Tokens_Sqlite.cs new file mode 100644 index 000000000..f7d219e57 --- /dev/null +++ b/BLAZAMDatabase/Migrations/Sqlite/20241118154642_Add_Api_Tokens_Sqlite.cs @@ -0,0 +1,64 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BLAZAM.Database.Migrations.Sqlite +{ + /// + public partial class Add_Api_Tokens_Sqlite : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "APIToken", + table: "UserSettings"); + + migrationBuilder.CreateTable( + name: "ApiTokens", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Token = table.Column(type: "TEXT", nullable: false), + TokenHash = table.Column(type: "INTEGER", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: false), + ExpiresAt = table.Column(type: "TEXT", nullable: false), + LastUsedAt = table.Column(type: "TEXT", nullable: true), + UserId = table.Column(type: "INTEGER", nullable: false), + Description = table.Column(type: "TEXT", nullable: true), + IsRevoked = table.Column(type: "INTEGER", nullable: false), + DeletedAt = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ApiTokens", x => x.Id); + table.ForeignKey( + name: "FK_ApiTokens_UserSettings_UserId", + column: x => x.UserId, + principalTable: "UserSettings", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_ApiTokens_UserId", + table: "ApiTokens", + column: "UserId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ApiTokens"); + + migrationBuilder.AddColumn( + name: "APIToken", + table: "UserSettings", + type: "TEXT", + nullable: true); + } + } +} diff --git a/BLAZAMDatabase/Migrations/Sqlite/20241123221352_Add_Webhooks_Sqlite.Designer.cs b/BLAZAMDatabase/Migrations/Sqlite/20241123221352_Add_Webhooks_Sqlite.Designer.cs new file mode 100644 index 000000000..f2ffd98a5 --- /dev/null +++ b/BLAZAMDatabase/Migrations/Sqlite/20241123221352_Add_Webhooks_Sqlite.Designer.cs @@ -0,0 +1,2157 @@ +// +using System; +using BLAZAM.Database.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BLAZAM.Database.Migrations.Sqlite +{ + [DbContext(typeof(SqliteDatabaseContext))] + [Migration("20241123221352_Add_Webhooks_Sqlite")] + partial class Add_Webhooks_Sqlite + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.11"); + + modelBuilder.Entity("AccessLevelFieldAccessMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("INTEGER"); + + b.Property("FieldMapId") + .HasColumnType("INTEGER"); + + b.HasKey("AccessLevelsId", "FieldMapId"); + + b.HasIndex("FieldMapId"); + + b.ToTable("AccessLevelFieldAccessMapping"); + }); + + modelBuilder.Entity("AccessLevelObjectAccessMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("INTEGER"); + + b.Property("ObjectMapId") + .HasColumnType("INTEGER"); + + b.HasKey("AccessLevelsId", "ObjectMapId"); + + b.HasIndex("ObjectMapId"); + + b.ToTable("AccessLevelObjectAccessMapping"); + }); + + modelBuilder.Entity("AccessLevelPermissionMapping", b => + { + b.Property("AccessLevelsId") + .HasColumnType("INTEGER"); + + b.Property("PermissionMapsId") + .HasColumnType("INTEGER"); + + b.HasKey("AccessLevelsId", "PermissionMapsId"); + + b.HasIndex("PermissionMapsId"); + + b.ToTable("AccessLevelPermissionMapping"); + }); + + modelBuilder.Entity("AppUserChatRoom", b => + { + b.Property("ChatRoomId") + .HasColumnType("INTEGER"); + + b.Property("MembersId") + .HasColumnType("INTEGER"); + + b.HasKey("ChatRoomId", "MembersId"); + + b.HasIndex("MembersId"); + + b.ToTable("AppUserChatRoom"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ADSettings", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("ApplicationBaseDN") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FQDN") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ServerAddress") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ServerPort") + .HasColumnType("INTEGER"); + + b.Property("UseTLS") + .HasColumnType("INTEGER"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ActiveDirectorySettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryField", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FieldName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FieldType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ActiveDirectoryFields"); + + b.HasData( + new + { + Id = 1, + DisplayName = "Last Name", + FieldName = "sn", + FieldType = 0 + }, + new + { + Id = 2, + DisplayName = "First Name", + FieldName = "givenname", + FieldType = 0 + }, + new + { + Id = 3, + DisplayName = "Office", + FieldName = "physicalDeliveryOfficeName", + FieldType = 0 + }, + new + { + Id = 4, + DisplayName = "Employee ID", + FieldName = "employeeId", + FieldType = 0 + }, + new + { + Id = 5, + DisplayName = "Home Directory", + FieldName = "homeDirectory", + FieldType = 0 + }, + new + { + Id = 6, + DisplayName = "Logon Script Path", + FieldName = "scriptPath", + FieldType = 0 + }, + new + { + Id = 7, + DisplayName = "Profile Path", + FieldName = "profilePath", + FieldType = 0 + }, + new + { + Id = 8, + DisplayName = "Home Phone Number", + FieldName = "homePhone", + FieldType = 0 + }, + new + { + Id = 9, + DisplayName = "Street Address", + FieldName = "streetAddress", + FieldType = 0 + }, + new + { + Id = 10, + DisplayName = "City", + FieldName = "l", + FieldType = 0 + }, + new + { + Id = 11, + DisplayName = "State", + FieldName = "st", + FieldType = 0 + }, + new + { + Id = 12, + DisplayName = "Zip Code", + FieldName = "postalCode", + FieldType = 0 + }, + new + { + Id = 13, + DisplayName = "Site", + FieldName = "site", + FieldType = 0 + }, + new + { + Id = 14, + DisplayName = "Name", + FieldName = "name", + FieldType = 0 + }, + new + { + Id = 15, + DisplayName = "Username", + FieldName = "samaccountname", + FieldType = 0 + }, + new + { + Id = 16, + DisplayName = "SID", + FieldName = "objectSID", + FieldType = 2 + }, + new + { + Id = 17, + DisplayName = "E-Mail Address", + FieldName = "mail", + FieldType = 0 + }, + new + { + Id = 18, + DisplayName = "Description", + FieldName = "description", + FieldType = 0 + }, + new + { + Id = 19, + DisplayName = "Display Name", + FieldName = "displayName", + FieldType = 0 + }, + new + { + Id = 20, + DisplayName = "Distinguished Name", + FieldName = "distinguishedName", + FieldType = 0 + }, + new + { + Id = 21, + DisplayName = "Member Of", + FieldName = "memberOf", + FieldType = 4 + }, + new + { + Id = 22, + DisplayName = "Company", + FieldName = "company", + FieldType = 0 + }, + new + { + Id = 23, + DisplayName = "Title", + FieldName = "title", + FieldType = 0 + }, + new + { + Id = 24, + DisplayName = "User Principal Name", + FieldName = "userPrincipalName", + FieldType = 0 + }, + new + { + Id = 25, + DisplayName = "Telephone Number", + FieldName = "telephoneNumber", + FieldType = 0 + }, + new + { + Id = 26, + DisplayName = "PO Box", + FieldName = "postOfficeBox", + FieldType = 0 + }, + new + { + Id = 27, + DisplayName = "Canonical Name", + FieldName = "cn", + FieldType = 0 + }, + new + { + Id = 28, + DisplayName = "Home Drive", + FieldName = "homeDrive", + FieldType = 3 + }, + new + { + Id = 29, + DisplayName = "Department", + FieldName = "department", + FieldType = 0 + }, + new + { + Id = 30, + DisplayName = "Middle Name", + FieldName = "middleName", + FieldType = 0 + }, + new + { + Id = 31, + DisplayName = "Pager", + FieldName = "pager", + FieldType = 0 + }, + new + { + Id = 32, + DisplayName = "OS", + FieldName = "operatingSystemVersion", + FieldType = 0 + }, + new + { + Id = 33, + DisplayName = "Account Expiration", + FieldName = "accountExpires", + FieldType = 1 + }, + new + { + Id = 34, + DisplayName = "Manager", + FieldName = "manager", + FieldType = 0 + }, + new + { + Id = 35, + DisplayName = "Photo", + FieldName = "thumbnail", + FieldType = 2 + }, + new + { + Id = 36, + DisplayName = "Log On To", + FieldName = "userWorkstations", + FieldType = 0 + }, + new + { + Id = 37, + DisplayName = "Logon Hours", + FieldName = "logonHours", + FieldType = 2 + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryFieldObjectType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ActiveDirectoryFieldId") + .HasColumnType("INTEGER"); + + b.Property("CustomActiveDirectoryFieldId") + .HasColumnType("INTEGER"); + + b.Property("ObjectType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CustomActiveDirectoryFieldId"); + + b.ToTable("ActiveDirectoryFieldObjectMappings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.AppSettings", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("AnalyticsId") + .HasColumnType("TEXT"); + + b.Property("AppFQDN") + .HasColumnType("TEXT"); + + b.Property("AppIcon") + .HasColumnType("BLOB"); + + b.Property("AppName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AutoUpdate") + .HasColumnType("INTEGER"); + + b.Property("AutoUpdateTime") + .HasColumnType("TEXT"); + + b.Property("ForceHTTPS") + .HasColumnType("INTEGER"); + + b.Property("InstallationCompleted") + .HasColumnType("INTEGER"); + + b.Property("LastUpdateCheck") + .HasColumnType("TEXT"); + + b.Property("MOTD") + .HasColumnType("TEXT"); + + b.Property("MyrtilleURL") + .HasColumnType("TEXT"); + + b.Property("SSLCertificateCipher") + .HasColumnType("TEXT"); + + b.Property("SendDeveloperAnalytics") + .HasColumnType("INTEGER"); + + b.Property("SendLogsToDeveloper") + .HasColumnType("INTEGER"); + + b.Property("UpdateBranch") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdateDomain") + .HasColumnType("TEXT"); + + b.Property("UpdatePassword") + .HasColumnType("TEXT"); + + b.Property("UpdateUsername") + .HasColumnType("TEXT"); + + b.Property("UseUpdateCredentials") + .HasColumnType("INTEGER"); + + b.Property("UserHelpdeskURL") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AppSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.DirectoryEntryAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AfterAction") + .HasColumnType("TEXT"); + + b.Property("BeforeAction") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasColumnType("TEXT"); + + b.Property("Sid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Target") + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("DirectoryEntryAuditLogs"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.LogonAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AfterAction") + .HasColumnType("TEXT"); + + b.Property("BeforeAction") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasColumnType("TEXT"); + + b.Property("Target") + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("LogonAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.PermissionsAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AfterAction") + .HasColumnType("TEXT"); + + b.Property("BeforeAction") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasColumnType("TEXT"); + + b.Property("Target") + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("PermissionsAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.RequestAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AfterAction") + .HasColumnType("TEXT"); + + b.Property("BeforeAction") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasColumnType("TEXT"); + + b.Property("Target") + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RequestAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.SettingsAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AfterAction") + .HasColumnType("TEXT"); + + b.Property("BeforeAction") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasColumnType("TEXT"); + + b.Property("Target") + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SettingsAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Audit.SystemAuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AfterAction") + .HasColumnType("TEXT"); + + b.Property("BeforeAction") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasColumnType("TEXT"); + + b.Property("Target") + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SystemAuditLog"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.AuthenticationSettings", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("AdminPassword") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DuoApiHost") + .HasColumnType("TEXT"); + + b.Property("DuoClientId") + .HasColumnType("TEXT"); + + b.Property("DuoClientSecret") + .HasColumnType("TEXT"); + + b.Property("DuoEnabled") + .HasColumnType("INTEGER"); + + b.Property("DuoUnreachableBehavior") + .HasColumnType("INTEGER"); + + b.Property("SessionTimeout") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("AuthenticationSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1"); + }); + + b.HasData( + new + { + Id = 1, + AdminPassword = "password", + DuoEnabled = false, + DuoUnreachableBehavior = 0, + SessionTimeout = 15 + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.ChatRoom", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("IsPublic") + .HasColumnType("INTEGER"); + + b.Property("MembersHash") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChatRooms"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.UnreadChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChatMessageId") + .HasColumnType("INTEGER"); + + b.Property("ChatRoomId") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChatMessageId"); + + b.ToTable("UnreadChatMessages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.CustomActiveDirectoryField", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FieldName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FieldType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("CustomActiveDirectoryFields"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.EmailSettings", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("AdminBcc") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("FromAddress") + .HasColumnType("TEXT"); + + b.Property("FromName") + .HasColumnType("TEXT"); + + b.Property("ReplyToAddress") + .HasColumnType("TEXT"); + + b.Property("ReplyToName") + .HasColumnType("TEXT"); + + b.Property("SMTPPassword") + .HasColumnType("TEXT"); + + b.Property("SMTPPort") + .HasColumnType("INTEGER"); + + b.Property("SMTPServer") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SMTPUsername") + .HasColumnType("TEXT"); + + b.Property("UseSMTPAuth") + .HasColumnType("INTEGER"); + + b.Property("UseTLS") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("EmailSettings", t => + { + t.HasCheckConstraint("CK_Table_Column", "[Id] = 1"); + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.EmailTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Body") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TemplateType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("EmailTemplates"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Block") + .HasColumnType("INTEGER"); + + b.Property("ByEmail") + .HasColumnType("INTEGER"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("InApp") + .HasColumnType("INTEGER"); + + b.Property("OU") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationSubscriptions"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionNotificationType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("NotificationSubscriptionId") + .HasColumnType("INTEGER"); + + b.Property("NotificationType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("NotificationSubscriptionId"); + + b.ToTable("SubscriptionNotificationType"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionWebHookType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("NotificationType") + .HasColumnType("INTEGER"); + + b.Property("WebHookSubscriptionId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("WebHookSubscriptionId"); + + b.ToTable("SubscriptionWebHookType"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookAttempt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Body") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Delivered") + .HasColumnType("INTEGER"); + + b.Property("EventTimestamp") + .HasColumnType("TEXT"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastAttemptTimestamp") + .HasColumnType("TEXT"); + + b.Property("MessageGuid") + .HasColumnType("TEXT"); + + b.Property("ResponseCode") + .HasColumnType("INTEGER"); + + b.Property("ResponseHeaders") + .HasColumnType("TEXT"); + + b.Property("ResponseMessage") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("Signature") + .HasColumnType("TEXT"); + + b.Property("Uri") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WebHookSubscriptionId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("WebHookSubscriptionId"); + + b.ToTable("WebHookAttempts"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AuthorizationToken") + .HasColumnType("TEXT"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("HmacKey") + .HasColumnType("TEXT"); + + b.Property("IgnoreSSLVerification") + .HasColumnType("INTEGER"); + + b.Property("OU") + .HasColumnType("TEXT"); + + b.Property("PrivateKey") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("URL") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WebHookAuthorization") + .HasColumnType("INTEGER"); + + b.Property("WebHookMethod") + .HasColumnType("INTEGER"); + + b.Property("WebHookSignature") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("WebHookSubscriptions"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AccessLevels"); + + b.HasData( + new + { + Id = 1, + Name = "Deny All" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ActionAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessLevelId") + .HasColumnType("INTEGER"); + + b.Property("AllowOrDeny") + .HasColumnType("INTEGER"); + + b.Property("ObjectActionId") + .HasColumnType("INTEGER"); + + b.Property("ObjectType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("AccessLevelId"); + + b.HasIndex("ObjectActionId"); + + b.ToTable("ActionAccessMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Level") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("FieldAccessLevel"); + + b.HasData( + new + { + Id = 1, + Level = 10, + Name = "Deny" + }, + new + { + Id = 2, + Level = 100, + Name = "Read" + }, + new + { + Id = 3, + Level = 1000, + Name = "Edit" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CustomFieldId") + .HasColumnType("INTEGER"); + + b.Property("FieldAccessLevelId") + .HasColumnType("INTEGER"); + + b.Property("FieldId") + .HasColumnType("INTEGER"); + + b.Property("ObjectType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CustomFieldId"); + + b.HasIndex("FieldAccessLevelId"); + + b.HasIndex("FieldId"); + + b.ToTable("AccessLevelFieldMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.GlobalPermissionRequestActions", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ObjectAction") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("GlobalPermissionRequestActions"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.GlobalPermissionSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AllowAccessRequest") + .HasColumnType("INTEGER"); + + b.Property("AllowSelfModification") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("GlobalPermissionSettings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Level") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ObjectAccessLevel"); + + b.HasData( + new + { + Id = 1, + Level = 10, + Name = "Deny" + }, + new + { + Id = 2, + Level = 1000, + Name = "Read" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AllowDisabled") + .HasColumnType("INTEGER"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("ObjectAccessLevelId") + .HasColumnType("INTEGER"); + + b.Property("ObjectType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ObjectAccessLevelId"); + + b.ToTable("AccessLevelObjectMapping"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ObjectActionFlag"); + + b.HasData( + new + { + Id = 1, + Action = 4, + Name = "Assign" + }, + new + { + Id = 2, + Action = 3, + Name = "UnAssign" + }, + new + { + Id = 3, + Action = 8, + Name = "Unlock" + }, + new + { + Id = 4, + Action = 5, + Name = "Enable" + }, + new + { + Id = 5, + Action = 6, + Name = "Disable" + }, + new + { + Id = 6, + Action = 7, + Name = "Rename" + }, + new + { + Id = 7, + Action = 0, + Name = "Move" + }, + new + { + Id = 8, + Action = 2, + Name = "Create" + }, + new + { + Id = 9, + Action = 1, + Name = "Delete" + }, + new + { + Id = 10, + Action = 9, + Name = "Set Password" + }); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.PermissionDelegate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DelegateName") + .HasColumnType("TEXT"); + + b.Property("DelegateSid") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("IsSuperAdmin") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DelegateSid") + .IsUnique(); + + b.ToTable("PermissionDelegate"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.PermissionMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("OU") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("PermissionMap"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AllowCustomGroups") + .HasColumnType("INTEGER"); + + b.Property("AllowUsernameOverride") + .HasColumnType("INTEGER"); + + b.Property("AskForAlternateEmail") + .HasColumnType("INTEGER"); + + b.Property("Category") + .HasColumnType("TEXT"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("DisplayNameFormula") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ObjectType") + .HasColumnType("INTEGER"); + + b.Property("ParentOU") + .HasColumnType("TEXT"); + + b.Property("ParentTemplateId") + .HasColumnType("INTEGER"); + + b.Property("PasswordFormula") + .HasColumnType("TEXT"); + + b.Property("RequirePasswordChange") + .HasColumnType("INTEGER"); + + b.Property("SendWelcomeEmail") + .HasColumnType("INTEGER"); + + b.Property("UsernameFormula") + .HasColumnType("TEXT"); + + b.Property("Visible") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("ParentTemplateId"); + + b.ToTable("DirectoryTemplates"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateFieldValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CustomFieldId") + .HasColumnType("INTEGER"); + + b.Property("DirectoryTemplateId") + .HasColumnType("INTEGER"); + + b.Property("Editable") + .HasColumnType("INTEGER"); + + b.Property("FieldId") + .HasColumnType("INTEGER"); + + b.Property("Required") + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CustomFieldId"); + + b.HasIndex("DirectoryTemplateId"); + + b.HasIndex("FieldId"); + + b.ToTable("DirectoryTemplateFieldValues"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DirectoryTemplateId") + .HasColumnType("INTEGER"); + + b.Property("GroupSid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryTemplateId"); + + b.ToTable("DirectoryTemplateGroups"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("ExpiresAt") + .HasColumnType("TEXT"); + + b.Property("IsRevoked") + .HasColumnType("INTEGER"); + + b.Property("LastUsedAt") + .HasColumnType("TEXT"); + + b.Property("Token") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TokenHash") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ApiTokens"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DarkMode") + .HasColumnType("INTEGER"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("ProfilePicture") + .HasColumnType("BLOB"); + + b.Property("SearchDisabledComputers") + .HasColumnType("INTEGER"); + + b.Property("SearchDisabledUsers") + .HasColumnType("INTEGER"); + + b.Property("Theme") + .HasColumnType("TEXT"); + + b.Property("UserGUID") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Username") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserGUID") + .IsUnique(); + + b.ToTable("UserSettings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.NotificationMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .HasColumnType("INTEGER"); + + b.Property("Created") + .HasColumnType("TEXT"); + + b.Property("CreatorId") + .HasColumnType("INTEGER"); + + b.Property("Dismissable") + .HasColumnType("INTEGER"); + + b.Property("Expires") + .HasColumnType("TEXT"); + + b.Property("Level") + .HasColumnType("INTEGER"); + + b.Property("Link") + .HasColumnType("TEXT"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("MessageType") + .HasColumnType("INTEGER"); + + b.Property("TargetDN") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.ToTable("NotificationMessages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ReadNewsItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("NewsItemId") + .HasColumnType("REAL"); + + b.Property("NewsItemUpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ReadNewsItems"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserDashboardWidget", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ItemsPerPage") + .HasColumnType("INTEGER"); + + b.Property("Order") + .HasColumnType("INTEGER"); + + b.Property("Slot") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.Property("WidgetType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserDashboardWidgets"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserFavoriteEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DN") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserFavoriteEntries"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IsRead") + .HasColumnType("INTEGER"); + + b.Property("NotificationId") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("NotificationId"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotifications"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChatRoomId") + .HasColumnType("INTEGER"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChatRoomId"); + + b.HasIndex("UserId"); + + b.ToTable("ChatMessages"); + }); + + modelBuilder.Entity("PermissionDelegatePermissionMapping", b => + { + b.Property("PermissionDelegatesId") + .HasColumnType("INTEGER"); + + b.Property("PermissionsMapsId") + .HasColumnType("INTEGER"); + + b.HasKey("PermissionDelegatesId", "PermissionsMapsId"); + + b.HasIndex("PermissionsMapsId"); + + b.ToTable("PermissionDelegatePermissionMapping"); + }); + + modelBuilder.Entity("AccessLevelFieldAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.FieldAccessMapping", null) + .WithMany() + .HasForeignKey("FieldMapId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AccessLevelObjectAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", null) + .WithMany() + .HasForeignKey("ObjectMapId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AccessLevelPermissionMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany() + .HasForeignKey("AccessLevelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionMapping", null) + .WithMany() + .HasForeignKey("PermissionMapsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AppUserChatRoom", b => + { + b.HasOne("BLAZAM.Database.Models.Chat.ChatRoom", null) + .WithMany() + .HasForeignKey("ChatRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", null) + .WithMany() + .HasForeignKey("MembersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.ActiveDirectoryFieldObjectType", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", null) + .WithMany("ObjectTypes") + .HasForeignKey("CustomActiveDirectoryFieldId"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.UnreadChatMessage", b => + { + b.HasOne("BLAZAM.Server.Data.ChatMessage", "ChatMessage") + .WithMany("NotReadBy") + .HasForeignKey("ChatMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatMessage"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("NotificationSubscriptions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionNotificationType", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.NotificationSubscription", "NotificationSubscription") + .WithMany("NotificationTypes") + .HasForeignKey("NotificationSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationSubscription"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionWebHookType", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.WebHookSubscription", "WebHookSubscription") + .WithMany("NotificationTypes") + .HasForeignKey("WebHookSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebHookSubscription"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookAttempt", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.WebHookSubscription", "WebHookSubscription") + .WithMany("WebHookAttempts") + .HasForeignKey("WebHookSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebHookSubscription"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ActionAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) + .WithMany("ActionMap") + .HasForeignKey("AccessLevelId"); + + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAction", "ObjectAction") + .WithMany() + .HasForeignKey("ObjectActionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ObjectAction"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.FieldAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", "CustomField") + .WithMany() + .HasForeignKey("CustomFieldId"); + + b.HasOne("BLAZAM.Database.Models.Permissions.FieldAccessLevel", "FieldAccessLevel") + .WithMany() + .HasForeignKey("FieldAccessLevelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.ActiveDirectoryField", "Field") + .WithMany() + .HasForeignKey("FieldId"); + + b.Navigation("CustomField"); + + b.Navigation("Field"); + + b.Navigation("FieldAccessLevel"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", "ObjectAccessLevel") + .WithMany("ObjectAccessMappings") + .HasForeignKey("ObjectAccessLevelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ObjectAccessLevel"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", "ParentTemplate") + .WithMany() + .HasForeignKey("ParentTemplateId"); + + b.Navigation("ParentTemplate"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateFieldValue", b => + { + b.HasOne("BLAZAM.Database.Models.CustomActiveDirectoryField", "CustomField") + .WithMany() + .HasForeignKey("CustomFieldId"); + + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", null) + .WithMany("FieldValues") + .HasForeignKey("DirectoryTemplateId"); + + b.HasOne("BLAZAM.Database.Models.ActiveDirectoryField", "Field") + .WithMany() + .HasForeignKey("FieldId"); + + b.Navigation("CustomField"); + + b.Navigation("Field"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplateGroup", b => + { + b.HasOne("BLAZAM.Database.Models.Templates.DirectoryTemplate", null) + .WithMany("AssignedGroupSids") + .HasForeignKey("DirectoryTemplateId"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("APITokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.NotificationMessage", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "Creator") + .WithMany() + .HasForeignKey("CreatorId"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.ReadNewsItem", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("ReadNewsItems") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserDashboardWidget", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("DashboardWidgets") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserFavoriteEntry", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("FavoriteEntries") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.UserNotification", b => + { + b.HasOne("BLAZAM.Database.Models.User.NotificationMessage", "Notification") + .WithMany() + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("Notifications") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.HasOne("BLAZAM.Database.Models.Chat.ChatRoom", "ChatRoom") + .WithMany("Messages") + .HasForeignKey("ChatRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatRoom"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("PermissionDelegatePermissionMapping", b => + { + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionDelegate", null) + .WithMany() + .HasForeignKey("PermissionDelegatesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BLAZAM.Database.Models.Permissions.PermissionMapping", null) + .WithMany() + .HasForeignKey("PermissionsMapsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Chat.ChatRoom", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.CustomActiveDirectoryField", b => + { + b.Navigation("ObjectTypes"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.NotificationSubscription", b => + { + b.Navigation("NotificationTypes"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookSubscription", b => + { + b.Navigation("NotificationTypes"); + + b.Navigation("WebHookAttempts"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => + { + b.Navigation("ActionMap"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ObjectAccessLevel", b => + { + b.Navigation("ObjectAccessMappings"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Templates.DirectoryTemplate", b => + { + b.Navigation("AssignedGroupSids"); + + b.Navigation("FieldValues"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + { + b.Navigation("APITokens"); + + b.Navigation("DashboardWidgets"); + + b.Navigation("FavoriteEntries"); + + b.Navigation("NotificationSubscriptions"); + + b.Navigation("Notifications"); + + b.Navigation("ReadNewsItems"); + }); + + modelBuilder.Entity("BLAZAM.Server.Data.ChatMessage", b => + { + b.Navigation("NotReadBy"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BLAZAMDatabase/Migrations/Sqlite/20241123221352_Add_Webhooks_Sqlite.cs b/BLAZAMDatabase/Migrations/Sqlite/20241123221352_Add_Webhooks_Sqlite.cs new file mode 100644 index 000000000..7989910f0 --- /dev/null +++ b/BLAZAMDatabase/Migrations/Sqlite/20241123221352_Add_Webhooks_Sqlite.cs @@ -0,0 +1,112 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BLAZAM.Database.Migrations.Sqlite +{ + /// + public partial class Add_Webhooks_Sqlite : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "WebHookSubscriptions", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + OU = table.Column(type: "TEXT", nullable: true), + IgnoreSSLVerification = table.Column(type: "INTEGER", nullable: false), + URL = table.Column(type: "TEXT", nullable: false), + WebHookMethod = table.Column(type: "INTEGER", nullable: false), + WebHookSignature = table.Column(type: "INTEGER", nullable: false), + WebHookAuthorization = table.Column(type: "INTEGER", nullable: false), + AuthorizationToken = table.Column(type: "TEXT", nullable: true), + HmacKey = table.Column(type: "TEXT", nullable: true), + PrivateKey = table.Column(type: "TEXT", nullable: true), + PublicKey = table.Column(type: "TEXT", nullable: true), + DeletedAt = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_WebHookSubscriptions", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "SubscriptionWebHookType", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + WebHookSubscriptionId = table.Column(type: "INTEGER", nullable: false), + NotificationType = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SubscriptionWebHookType", x => x.Id); + table.ForeignKey( + name: "FK_SubscriptionWebHookType_WebHookSubscriptions_WebHookSubscriptionId", + column: x => x.WebHookSubscriptionId, + principalTable: "WebHookSubscriptions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "WebHookAttempts", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + MessageGuid = table.Column(type: "TEXT", nullable: false), + LastAttemptTimestamp = table.Column(type: "TEXT", nullable: false), + EventTimestamp = table.Column(type: "TEXT", nullable: false), + EventType = table.Column(type: "TEXT", nullable: false), + WebHookSubscriptionId = table.Column(type: "INTEGER", nullable: false), + Uri = table.Column(type: "TEXT", nullable: false), + Delivered = table.Column(type: "INTEGER", nullable: false), + Body = table.Column(type: "TEXT", nullable: false), + Signature = table.Column(type: "TEXT", nullable: true), + RetryCount = table.Column(type: "INTEGER", nullable: false), + ResponseCode = table.Column(type: "INTEGER", nullable: false), + ResponseHeaders = table.Column(type: "TEXT", nullable: true), + ResponseMessage = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_WebHookAttempts", x => x.Id); + table.ForeignKey( + name: "FK_WebHookAttempts_WebHookSubscriptions_WebHookSubscriptionId", + column: x => x.WebHookSubscriptionId, + principalTable: "WebHookSubscriptions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_SubscriptionWebHookType_WebHookSubscriptionId", + table: "SubscriptionWebHookType", + column: "WebHookSubscriptionId"); + + migrationBuilder.CreateIndex( + name: "IX_WebHookAttempts_WebHookSubscriptionId", + table: "WebHookAttempts", + column: "WebHookSubscriptionId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SubscriptionWebHookType"); + + migrationBuilder.DropTable( + name: "WebHookAttempts"); + + migrationBuilder.DropTable( + name: "WebHookSubscriptions"); + } + } +} diff --git a/BLAZAMDatabase/Migrations/Sqlite/SqliteDatabaseContextModelSnapshot.cs b/BLAZAMDatabase/Migrations/Sqlite/SqliteDatabaseContextModelSnapshot.cs index d61591607..10f8432be 100644 --- a/BLAZAMDatabase/Migrations/Sqlite/SqliteDatabaseContextModelSnapshot.cs +++ b/BLAZAMDatabase/Migrations/Sqlite/SqliteDatabaseContextModelSnapshot.cs @@ -15,7 +15,7 @@ partial class SqliteDatabaseContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.10"); + modelBuilder.HasAnnotation("ProductVersion", "8.0.11"); modelBuilder.Entity("AccessLevelFieldAccessMapping", b => { @@ -946,6 +946,125 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("SubscriptionNotificationType"); }); + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionWebHookType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("NotificationType") + .HasColumnType("INTEGER"); + + b.Property("WebHookSubscriptionId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("WebHookSubscriptionId"); + + b.ToTable("SubscriptionWebHookType"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookAttempt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Body") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Delivered") + .HasColumnType("INTEGER"); + + b.Property("EventTimestamp") + .HasColumnType("TEXT"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastAttemptTimestamp") + .HasColumnType("TEXT"); + + b.Property("MessageGuid") + .HasColumnType("TEXT"); + + b.Property("ResponseCode") + .HasColumnType("INTEGER"); + + b.Property("ResponseHeaders") + .HasColumnType("TEXT"); + + b.Property("ResponseMessage") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("Signature") + .HasColumnType("TEXT"); + + b.Property("Uri") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WebHookSubscriptionId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("WebHookSubscriptionId"); + + b.ToTable("WebHookAttempts"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AuthorizationToken") + .HasColumnType("TEXT"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("HmacKey") + .HasColumnType("TEXT"); + + b.Property("IgnoreSSLVerification") + .HasColumnType("INTEGER"); + + b.Property("OU") + .HasColumnType("TEXT"); + + b.Property("PrivateKey") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("URL") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WebHookAuthorization") + .HasColumnType("INTEGER"); + + b.Property("WebHookMethod") + .HasColumnType("INTEGER"); + + b.Property("WebHookSignature") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("WebHookSubscriptions"); + }); + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => { b.Property("Id") @@ -1395,15 +1514,53 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("DirectoryTemplateGroups"); }); - modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("APIToken") + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("ExpiresAt") + .HasColumnType("TEXT"); + + b.Property("IsRevoked") + .HasColumnType("INTEGER"); + + b.Property("LastUsedAt") .HasColumnType("TEXT"); + b.Property("Token") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TokenHash") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ApiTokens"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + b.Property("DarkMode") .HasColumnType("INTEGER"); @@ -1721,6 +1878,28 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("NotificationSubscription"); }); + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.SubscriptionWebHookType", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.WebHookSubscription", "WebHookSubscription") + .WithMany("NotificationTypes") + .HasForeignKey("WebHookSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebHookSubscription"); + }); + + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookAttempt", b => + { + b.HasOne("BLAZAM.Database.Models.Notifications.WebHookSubscription", "WebHookSubscription") + .WithMany("WebHookAttempts") + .HasForeignKey("WebHookSubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebHookSubscription"); + }); + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.ActionAccessMapping", b => { b.HasOne("BLAZAM.Database.Models.Permissions.AccessLevel", null) @@ -1805,6 +1984,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("DirectoryTemplateId"); }); + modelBuilder.Entity("BLAZAM.Database.Models.User.ApiToken", b => + { + b.HasOne("BLAZAM.Database.Models.User.AppUser", "User") + .WithMany("APITokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + modelBuilder.Entity("BLAZAM.Database.Models.User.NotificationMessage", b => { b.HasOne("BLAZAM.Database.Models.User.AppUser", "Creator") @@ -1915,6 +2105,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("NotificationTypes"); }); + modelBuilder.Entity("BLAZAM.Database.Models.Notifications.WebHookSubscription", b => + { + b.Navigation("NotificationTypes"); + + b.Navigation("WebHookAttempts"); + }); + modelBuilder.Entity("BLAZAM.Database.Models.Permissions.AccessLevel", b => { b.Navigation("ActionMap"); @@ -1934,6 +2131,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("BLAZAM.Database.Models.User.AppUser", b => { + b.Navigation("APITokens"); + b.Navigation("DashboardWidgets"); b.Navigation("FavoriteEntries"); diff --git a/BLAZAMDatabase/Models/Audit/EmailAuditLog.cs b/BLAZAMDatabase/Models/Audit/EmailAuditLog.cs new file mode 100644 index 000000000..74fa3f091 --- /dev/null +++ b/BLAZAMDatabase/Models/Audit/EmailAuditLog.cs @@ -0,0 +1,19 @@ + + +namespace BLAZAM.Database.Models.Audit +{ + public class EmailAuditLog : AppDbSetBase + { + public string? MessageGuid { get; set; } + public string? From { get; set; } + public string? To { get; set; } + + public string? Cc { get; set; } + public string? Bcc { get; set; } + public string? Subject { get; set; } + public string? HtmlBody { get; set; } + public bool IsRead { get; set; } + public DateTime? ReadTimestamp { get; set; } + public string? ServerResponse { get; set; } + } +} diff --git a/BLAZAMDatabase/Models/Audit/EmailLog.cs b/BLAZAMDatabase/Models/Audit/EmailLog.cs deleted file mode 100644 index d45d6d568..000000000 --- a/BLAZAMDatabase/Models/Audit/EmailLog.cs +++ /dev/null @@ -1,14 +0,0 @@ - - -namespace BLAZAM.Database.Models.Audit -{ - internal class EmailLog : AppDbSetBase - { - public string? To { get; set; } - - public string? Cc { get; set; } - public string? Bcc { get; set; } - public string? Subject { get; set; } - public string? HtmlBody { get; set; } - } -} diff --git a/BLAZAMDatabase/Models/Notifications/SubscriptionWebHookType.cs b/BLAZAMDatabase/Models/Notifications/SubscriptionWebHookType.cs new file mode 100644 index 000000000..94d10b3c6 --- /dev/null +++ b/BLAZAMDatabase/Models/Notifications/SubscriptionWebHookType.cs @@ -0,0 +1,15 @@ +using BLAZAM.Database.Models.User; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BLAZAM.Database.Models.Notifications +{ + public class SubscriptionWebHookType : AppDbSetBase + { + public WebHookSubscription WebHookSubscription { get; set; } + public NotificationType NotificationType { get; set; } + } +} diff --git a/BLAZAMDatabase/Models/Notifications/WebHookAttempt.cs b/BLAZAMDatabase/Models/Notifications/WebHookAttempt.cs new file mode 100644 index 000000000..526a54143 --- /dev/null +++ b/BLAZAMDatabase/Models/Notifications/WebHookAttempt.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace BLAZAM.Database.Models.Notifications +{ + public class WebHookAttempt:AppDbSetBase + { + public Guid MessageGuid { get; set; } + public DateTime LastAttemptTimestamp { get; set; } + public DateTime EventTimestamp { get; set; } + public WebHookSubscription WebHookSubscription { get;set;} + public string EventType { get; set; } + public int WebHookSubscriptionId {get;set;} + public string Uri { get; set; } + public bool Delivered { get; set; } + public string Body { get; set; } + public string? Signature { get; set; } + public int RetryCount { get; set; } + public HttpStatusCode ResponseCode { get; set; } + public string? ResponseHeaders { get; set; } + public string? ResponseMessage { get; set; } + } +} diff --git a/BLAZAMDatabase/Models/Notifications/WebHookSubscription.cs b/BLAZAMDatabase/Models/Notifications/WebHookSubscription.cs new file mode 100644 index 000000000..82def7d1a --- /dev/null +++ b/BLAZAMDatabase/Models/Notifications/WebHookSubscription.cs @@ -0,0 +1,92 @@ +using BLAZAM.Database.Models.User; +using BLAZAM.Helpers; +using Microsoft.AspNetCore.Mvc.Rendering; +using System.ComponentModel.DataAnnotations.Schema; + +namespace BLAZAM.Database.Models.Notifications +{ + public enum WebHookMethod + { + GET, + POST + } + + public enum WebHookSignature + { + None, + HMAC, + Asymmetric + } + public enum WebHookAuthorization + { + None, + Basic, + Bearer + } + public class WebHookSubscription : RecoverableAppDbSetBase + { + /// + /// The notification types this subscription should trigger on + /// + public List NotificationTypes { get; set; } = new(); + /// + /// Currently not used, but could be used for future per OU mapping of webhook subscriptions + /// + public string? OU { get; set; } + public List WebHookAttempts { get; set; } = new(); + + /// + /// Whether or not to ignore SSL Certificate verification when sending the webhook + /// + public bool IgnoreSSLVerification { get; set; } + + /// + /// The full URL with protocol appended + /// + /// + /// eg: https://blazam.org/consumer + /// + public string URL { get; set; } + /// + /// The HTTP method to use + /// + public WebHookMethod WebHookMethod { get; set; } = WebHookMethod.POST; + + /// + /// The signature to type to include in the message to verify the + /// authenticity of the message by the receiver + /// + public WebHookSignature WebHookSignature { get; set; } + + /// + /// The authorization type to use. None, basic, or bearer. + /// + public WebHookAuthorization WebHookAuthorization { get; set; } + /// + /// The destination authorization token in encrypted form + /// + public string? AuthorizationToken { get; set; } + + /// + /// The authenticity HMAC key for this webhook connection + /// in encrypted form + /// + public string? HmacKey { get; set; } + + public string? PrivateKey { get; set; } + public string? PublicKey { get; set; } + + //public bool Block { get; set; } = false; + [NotMapped] + public bool IsValid + { + get + { + var valid= ((WebHookAuthorization == WebHookAuthorization.None || !AuthorizationToken.IsNullOrEmpty()) && + NotificationTypes.Count>0 && + !URL.IsNullOrEmpty()); + return valid; + } + } + } +} diff --git a/BLAZAMDatabase/Models/Templates/DirectoryTemplate.cs b/BLAZAMDatabase/Models/Templates/DirectoryTemplate.cs index 8323cdff3..dd03ce7d1 100644 --- a/BLAZAMDatabase/Models/Templates/DirectoryTemplate.cs +++ b/BLAZAMDatabase/Models/Templates/DirectoryTemplate.cs @@ -324,7 +324,11 @@ private static int RandomNumber() { return _random.Next(9); } - public bool HasEmptyFields() + public bool HasRequiredFields() + { + return EffectiveFieldValues.Any(fv => fv.Required && fv.Editable); + } + public bool HasEditableFields() { return EffectiveFieldValues.Any(fv => fv.Editable); } diff --git a/BLAZAMDatabase/Models/Templates/DirectoryTemplateFieldValue.cs b/BLAZAMDatabase/Models/Templates/DirectoryTemplateFieldValue.cs index 79252cd09..a510cc800 100644 --- a/BLAZAMDatabase/Models/Templates/DirectoryTemplateFieldValue.cs +++ b/BLAZAMDatabase/Models/Templates/DirectoryTemplateFieldValue.cs @@ -19,9 +19,11 @@ public class DirectoryTemplateFieldValue : AppDbSetBase /// Require the field to have a value /// public bool Required { get; set; } + [NotMapped] + public string? FieldName => Field != null ? Field?.FieldName : CustomField?.FieldName; [NotMapped] - public string FieldDisplayName => Field != null ? Field?.DisplayName : CustomField?.DisplayName; + public string? FieldDisplayName => Field != null ? Field?.DisplayName : CustomField?.DisplayName; public object Clone(IDatabaseContext context) { diff --git a/BLAZAMDatabase/Models/User/ApiToken.cs b/BLAZAMDatabase/Models/User/ApiToken.cs new file mode 100644 index 000000000..2d525c951 --- /dev/null +++ b/BLAZAMDatabase/Models/User/ApiToken.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BLAZAM.Database.Models.User +{ + public class ApiToken : RecoverableAppDbSetBase + { + /// + /// The string in encrypted form. + /// + public string Token { get; set; } + + + /// + /// The AppHashCode for this token + /// + public int TokenHash { get; set; } + + /// + /// The UTC the token was created. + /// + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + /// + /// The UTC the token will expire at. + /// + public DateTime ExpiresAt { get; set; } + + + /// + /// The UTC the token was last used. + /// + /// + /// Null if never used. + /// + public DateTime? LastUsedAt { get; set; } + + /// + /// The the token belongs to. + /// + public AppUser User { get; set; } + + /// + /// The of the . + /// + public int UserId { get; set; } + + /// + /// A description of the token's purpose. + /// + public string? Description { get; set; } + + /// + /// Indicates whether the token has been revoked. + /// + public bool IsRevoked { get; set; } + } +} diff --git a/BLAZAMDatabase/Models/User/AppUser.cs b/BLAZAMDatabase/Models/User/AppUser.cs index 3d16261e5..80d377076 100644 --- a/BLAZAMDatabase/Models/User/AppUser.cs +++ b/BLAZAMDatabase/Models/User/AppUser.cs @@ -9,7 +9,7 @@ namespace BLAZAM.Database.Models.User public class AppUser : AppDbSetBase { public string UserGUID { get; set; } - public string? APIToken { get; set; } + public List? APITokens { get; set; } public string? Theme { get; set; } public bool DarkMode { get; set; } public bool SearchDisabledUsers { get; set; } diff --git a/BLAZAMEmailMessage/BLAZAMEmailMessage.csproj b/BLAZAMEmailMessage/BLAZAMEmailMessage.csproj index 37d16f22e..4784f64c5 100644 --- a/BLAZAMEmailMessage/BLAZAMEmailMessage.csproj +++ b/BLAZAMEmailMessage/BLAZAMEmailMessage.csproj @@ -13,9 +13,9 @@ - - - + + + diff --git a/BLAZAMEmailMessage/Email/Base/EmailHeaderIcon.razor b/BLAZAMEmailMessage/Email/Base/EmailHeaderIcon.razor index fc2e6a7e1..32bed06cc 100644 --- a/BLAZAMEmailMessage/Email/Base/EmailHeaderIcon.razor +++ b/BLAZAMEmailMessage/Email/Base/EmailHeaderIcon.razor @@ -1,8 +1,12 @@ @using BLAZAM.Static; -
- {{ApplicationLogo}} -
+
+ {{ApplicationLogo}} +
+
+ {{TrackingImgLink}} +
+ @code { byte[]? appIconBytes; diff --git a/BLAZAMFileSystem/BLAZAMFileSystem.csproj b/BLAZAMFileSystem/BLAZAMFileSystem.csproj index 8a213e5bb..21dcd2299 100644 --- a/BLAZAMFileSystem/BLAZAMFileSystem.csproj +++ b/BLAZAMFileSystem/BLAZAMFileSystem.csproj @@ -8,7 +8,7 @@ - + diff --git a/BLAZAMGui/BLAZAMGui.csproj b/BLAZAMGui/BLAZAMGui.csproj index d5e2599c7..dbd8e8d4a 100644 --- a/BLAZAMGui/BLAZAMGui.csproj +++ b/BLAZAMGui/BLAZAMGui.csproj @@ -24,19 +24,18 @@
- - - - - - + + + + + + - diff --git a/BLAZAMGui/Layouts/AppUserApiModalContent.razor b/BLAZAMGui/Layouts/AppUserApiModalContent.razor new file mode 100644 index 000000000..bd8fd032d --- /dev/null +++ b/BLAZAMGui/Layouts/AppUserApiModalContent.razor @@ -0,0 +1,20 @@ +@inherits AppModalContent + + @AppLocalization["Click to view API documentation"]. + + +@code{ + + UserApiTokenDataGrid? apiDataGrid; + protected override void OnInitialized() + { + base.OnInitialized(); + Modal.SetYesText(AppLocalization["Save"]); + Modal.OnYes += SaveSettings; + Modal.YesEnabled = () => { return !LoadingData; }; + } + private void SaveSettings() + { + apiDataGrid?.Save(); + } +} \ No newline at end of file diff --git a/BLAZAMGui/Layouts/AppUserButton.razor b/BLAZAMGui/Layouts/AppUserButton.razor index d72fa3153..c85a67875 100644 --- a/BLAZAMGui/Layouts/AppUserButton.razor +++ b/BLAZAMGui/Layouts/AppUserButton.razor @@ -21,18 +21,23 @@ @CurrentUser.Username - + @AppLocalization["Profile Settings"] - + @AppLocalization["Documentation"] - + + + @AppLocalization["API"] + + @if (UserState?.Impersonator == null) { @@ -54,14 +59,19 @@ - - + + + + @code { AppModal? profileModal; - AppModal? notificationsModal; + AppModal? apiModal; MudMenu? _userMenu; [CascadingParameter] public MainLayout mainLayout { get; set; } diff --git a/BLAZAMGui/Layouts/MainLayout.razor b/BLAZAMGui/Layouts/MainLayout.razor index b30af3245..168203d79 100644 --- a/BLAZAMGui/Layouts/MainLayout.razor +++ b/BLAZAMGui/Layouts/MainLayout.razor @@ -2,7 +2,7 @@ @using System.Security.Claims; @inherits LayoutComponentBase -@inject AppNavigationManager Nav +@inject NavigationManager Nav @inject IActiveDirectoryContext directory @inject IApplicationUserStateService UserStateService @inject IAppDatabaseFactory DbFactory @@ -13,6 +13,7 @@ @{ string topMarginForContent = ""; } + @DatabaseCache.ApplicationSettings.AppAbbreviation diff --git a/BLAZAMGui/Navs/Buttons/NavHomeButton.razor b/BLAZAMGui/Navs/Buttons/NavHomeButton.razor deleted file mode 100644 index c64c1d9ef..000000000 --- a/BLAZAMGui/Navs/Buttons/NavHomeButton.razor +++ /dev/null @@ -1,2 +0,0 @@ -@inherits AppComponentBase -@AppLocalization["Home"] diff --git a/BLAZAMGui/Navs/NavMenu.razor b/BLAZAMGui/Navs/NavMenu.razor index 9c6eb622b..3f6439e8d 100644 --- a/BLAZAMGui/Navs/NavMenu.razor +++ b/BLAZAMGui/Navs/NavMenu.razor @@ -23,7 +23,11 @@ - + + @AppLocalization["Home"] + {SelectedEntryChanged.InvokeAsync(entry);}) BrowseExpandedChanged=@((value)=>{BrowseExpandedChanged.InvokeAsync(value);}) /> @@ -31,31 +35,70 @@ Title="@AppLocalization["Create"]" Disabled=@(Monitor.DirectoryConnectionStatus != ServiceConnectionState.Up)> - @AppLocalization["Create User"] + + @AppLocalization["Create User"] + - @AppLocalization["Create Group"] + + @AppLocalization["Create Group"] + - @AppLocalization["Create OU"] + + @AppLocalization["Create OU"] + - @AppLocalization["Recycle Bin"] - @AppLocalization["Settings"] - @AppLocalization["Notifications"] - @AppLocalization["Permissions"] - @AppLocalization["Fields"] - @AppLocalization["Templates"] + + @AppLocalization["Settings"] + + + + @AppLocalization["Permissions"] + + + + @AppLocalization["Fields"] + + + + @AppLocalization["Templates"] + + + + @AppLocalization["Notifications"] + + + + @AppLocalization["Webhooks"] + + + + @AppLocalization["Api Tokens"] + - @AppLocalization["Audit"] diff --git a/BLAZAMGui/UI/AppComponentBase.cs b/BLAZAMGui/UI/AppComponentBase.cs index 0d7573f89..daa42dcf8 100644 --- a/BLAZAMGui/UI/AppComponentBase.cs +++ b/BLAZAMGui/UI/AppComponentBase.cs @@ -1,6 +1,5 @@ using BLAZAM.ActiveDirectory; -using BLAZAM.Nav; using BLAZAM.Services.Audit; using BLAZAM.Services.Chat; @@ -16,7 +15,7 @@ public class AppComponentBase : ComponentBase, IDisposable protected SearchService SearchService { get; set; } [Inject] - protected AppNavigationManager Nav { get; set; } + protected NavigationManager Nav { get; set; } [Inject] protected ConnMonitor Monitor { get; set; } @@ -67,6 +66,9 @@ public class AppComponentBase : ComponentBase, IDisposable [Inject] protected IEncryptionService EncryptionService { get; set; } + [Inject] + protected JwtTokenService JwtTokenService { get; set; } + [Inject] protected AppSnackBarService SnackBarService { get; set; } diff --git a/BLAZAMGui/UI/AppModalContent.razor b/BLAZAMGui/UI/AppModalContent.razor index 4f1279cdb..302879600 100644 --- a/BLAZAMGui/UI/AppModalContent.razor +++ b/BLAZAMGui/UI/AppModalContent.razor @@ -26,12 +26,14 @@ base.OnAfterRender(firstRender); ValidateModal(); } - + protected MudForm? Form{ get; set; } protected virtual bool IsValid { get; set; } = true; private bool _lastIsValid; - protected virtual bool ValidateModal() + private bool ValidateModal() { + Form?.Validate(); + if (_lastIsValid != IsValid) { _lastIsValid = IsValid; diff --git a/BLAZAMGui/UI/Computers/RegisterPrintersModalContent.razor b/BLAZAMGui/UI/Computers/RegisterPrintersModalContent.razor index deddd6589..32505cebd 100644 --- a/BLAZAMGui/UI/Computers/RegisterPrintersModalContent.razor +++ b/BLAZAMGui/UI/Computers/RegisterPrintersModalContent.razor @@ -46,7 +46,6 @@ // Modal.OnYes = SaveChanges; Modal.YesText = AppLocalization["Next"]; Modal.Title = "Register Printers"; - Modal.YesEnabled = ValidateModal; Modal.OnYes = (() => { activePanel = 1; InvokeAsync(StateHasChanged); }); base.OnInitialized(); diff --git a/BLAZAMGui/UI/Dashboard/CurrentUserDashboardWidgets.razor b/BLAZAMGui/UI/Dashboard/CurrentUserDashboardWidgets.razor index 41f93ce25..0a05b0688 100644 --- a/BLAZAMGui/UI/Dashboard/CurrentUserDashboardWidgets.razor +++ b/BLAZAMGui/UI/Dashboard/CurrentUserDashboardWidgets.razor @@ -27,7 +27,7 @@ - + - + u.Disabled).ToList(); LoadingData = false; - await InvokeAsync(StateHasChanged); } } diff --git a/BLAZAMGui/UI/Dashboard/Widgets/NewGroupsWidget.razor b/BLAZAMGui/UI/Dashboard/Widgets/NewGroupsWidget.razor index 5121e890d..65a118c07 100644 --- a/BLAZAMGui/UI/Dashboard/Widgets/NewGroupsWidget.razor +++ b/BLAZAMGui/UI/Dashboard/Widgets/NewGroupsWidget.razor @@ -32,11 +32,9 @@ async Task RefreshData() { LoadingData = true; - await InvokeAsync(StateHasChanged); NewGroups = (await Directory.Groups.FindNewGroupsAsync()).Where(u => u.CanRead).ToList(); LoadingData = false; - await InvokeAsync(StateHasChanged); } void GoTo(DataGridRowClickEventArgs args) diff --git a/BLAZAMGui/UI/Dashboard/Widgets/NewOUsWidget.razor b/BLAZAMGui/UI/Dashboard/Widgets/NewOUsWidget.razor index aa2cd1540..0eb7df688 100644 --- a/BLAZAMGui/UI/Dashboard/Widgets/NewOUsWidget.razor +++ b/BLAZAMGui/UI/Dashboard/Widgets/NewOUsWidget.razor @@ -32,11 +32,9 @@ async Task RefreshData() { LoadingData = true; - await InvokeAsync(StateHasChanged); NewOUs = (await Directory.OUs.FindNewOUsAsync()).Where(u => u.CanRead).ToList(); LoadingData = false; - await InvokeAsync(StateHasChanged); } void GoTo(DataGridRowClickEventArgs args) diff --git a/BLAZAMGui/UI/Dashboard/Widgets/NewUsersWidget.razor b/BLAZAMGui/UI/Dashboard/Widgets/NewUsersWidget.razor index 4035aacec..862c33ffa 100644 --- a/BLAZAMGui/UI/Dashboard/Widgets/NewUsersWidget.razor +++ b/BLAZAMGui/UI/Dashboard/Widgets/NewUsersWidget.razor @@ -34,11 +34,9 @@ async Task RefreshData() { LoadingData = true; - await InvokeAsync(StateHasChanged); NewUsers = (await Directory.Users.FindNewUsersAsync(14, false)).Where(u => u.CanRead).OrderByDescending(u => u.Created).ToList(); LoadingData = false; - await InvokeAsync(StateHasChanged); } void GoTo(DataGridRowClickEventArgs args) diff --git a/BLAZAMGui/UI/Dev/DatabaseBrowserModalContent.razor b/BLAZAMGui/UI/Dev/DatabaseBrowserModalContent.razor index 9839862d7..b4df0be76 100644 --- a/BLAZAMGui/UI/Dev/DatabaseBrowserModalContent.razor +++ b/BLAZAMGui/UI/Dev/DatabaseBrowserModalContent.razor @@ -10,7 +10,6 @@ - diff --git a/BLAZAMGui/UI/DirectoryModelComponent.razor.cs b/BLAZAMGui/UI/DirectoryModelComponent.razor.cs index bf052e285..4c86ca30f 100644 --- a/BLAZAMGui/UI/DirectoryModelComponent.razor.cs +++ b/BLAZAMGui/UI/DirectoryModelComponent.razor.cs @@ -128,13 +128,11 @@ protected async Task RefreshUserGroups() { LoadingData = true; - await InvokeAsync(StateHasChanged); if (User != null) memberOfGroups = User.MemberOf; LoadingData = false; - await InvokeAsync(StateHasChanged); } @@ -149,12 +147,10 @@ protected async Task RefreshGroupGroups() { LoadingData = true; - await InvokeAsync(StateHasChanged); if (Group != null) memberOfGroups = Group.MemberOf; LoadingData = false; - await InvokeAsync(StateHasChanged); } @@ -162,7 +158,6 @@ protected async Task RefreshComputerGroups() { LoadingData = true; - await InvokeAsync(StateHasChanged); if (Computer != null) memberOfGroups = Computer.MemberOf; @@ -170,7 +165,6 @@ protected async Task RefreshComputerGroups() LoadingData = false; - await InvokeAsync(StateHasChanged); } diff --git a/BLAZAMGui/UI/DirectorySearchPage.razor b/BLAZAMGui/UI/DirectorySearchPage.razor index 77867685d..e6741240b 100644 --- a/BLAZAMGui/UI/DirectorySearchPage.razor +++ b/BLAZAMGui/UI/DirectorySearchPage.razor @@ -119,7 +119,6 @@ results.Clear(); LoadingData = true; - await InvokeAsync(StateHasChanged); if (!SearchTermParameter.IsNullOrEmpty() && SearchTermParameter.Length > 2) { await InvokeSearch(); @@ -132,7 +131,6 @@ LoadingData = false; - await InvokeAsync(StateHasChanged); diff --git a/BLAZAMGui/UI/Groups/AssignToModalContent.razor b/BLAZAMGui/UI/Groups/AssignToModalContent.razor index f86991eb3..b178cbe29 100644 --- a/BLAZAMGui/UI/Groups/AssignToModalContent.razor +++ b/BLAZAMGui/UI/Groups/AssignToModalContent.razor @@ -171,7 +171,6 @@ Modal.OnYes = AssignToGroup; Modal.YesText = AppLocalization["Assign"]; Modal.Title = "Assign To"; - Modal.YesEnabled = ValidateModal; } diff --git a/BLAZAMGui/UI/Groups/GroupMembersDataGrid.razor b/BLAZAMGui/UI/Groups/GroupMembersDataGrid.razor index b4f776664..f6aa4d76f 100644 --- a/BLAZAMGui/UI/Groups/GroupMembersDataGrid.razor +++ b/BLAZAMGui/UI/Groups/GroupMembersDataGrid.razor @@ -67,6 +67,37 @@ + + @AppLocalization["Members"]: @Members.Count + + @if(Members.Any(m=>m is IADGroup)) + { + + + @AppLocalization["Groups"]: @Members.Where(m=>m is IADGroup).Count() + + } + @if(Members.Any(m=>m is IADUser)) + { + + + @AppLocalization["Users"]: @Members.Where(m => m is IADUser).Count() + + } + @if(Members.Any(m=>m is IADComputer)) + { + + + @AppLocalization["Computers"]: @Members.Where(m => m is IADComputer).Count() + + } + @if(Members.Any(m=>m is IADPrinter)) + { + + + @AppLocalization["Printers"]: @Members.Where(m => m is IADPrinter).Count() + + } if (Members.Where(m => !m.CanRead).Count() > 0) { diff --git a/BLAZAMGui/UI/Inputs/AppIconButton.razor b/BLAZAMGui/UI/Inputs/AppIconButton.razor index a568f4cd3..b89a4d45c 100644 --- a/BLAZAMGui/UI/Inputs/AppIconButton.razor +++ b/BLAZAMGui/UI/Inputs/AppIconButton.razor @@ -1,7 +1,19 @@ +@if(!Tooltip.IsNullOrEmpty()){ + - + + + +}else{ - - +} + + @code { [Parameter] diff --git a/BLAZAMGui/UI/Inputs/TreeViews/ADTreeView.razor.cs b/BLAZAMGui/UI/Inputs/TreeViews/ADTreeView.razor.cs index 5d3853700..af0b32c4b 100644 --- a/BLAZAMGui/UI/Inputs/TreeViews/ADTreeView.razor.cs +++ b/BLAZAMGui/UI/Inputs/TreeViews/ADTreeView.razor.cs @@ -57,7 +57,7 @@ protected IEnumerable GetChildren(IDirectoryEntryAdapter if (ShowAllEntries) { var children = parentNode.Children - .Where(c => (c.ObjectType == ActiveDirectoryObjectType.OU && ShouldShowOU(c)) || c.CanRead) + .Where(c => (c.ObjectType == ActiveDirectoryObjectType.OU && ShouldShowOU(c)) || (c.CanRead)) .MoveToTop(c => c.ObjectType == ActiveDirectoryObjectType.Group) .MoveToTop(c => c.ObjectType == ActiveDirectoryObjectType.OU); ; return children; diff --git a/BLAZAMGui/UI/Modals/ApiTokenDataGrid.razor b/BLAZAMGui/UI/Modals/ApiTokenDataGrid.razor new file mode 100644 index 000000000..ede3c9d86 --- /dev/null +++ b/BLAZAMGui/UI/Modals/ApiTokenDataGrid.razor @@ -0,0 +1,93 @@ +@inherits AppComponentBase + + + @if (ShowUser) + { + + + @(context.Item.User.Username) + + + } + + + @{ + var decrypted = context.Item.Token.Decrypt(); + } + + @(decrypted.Substring(0, 10))... + + + + + + + @(context.Item.CreatedAt.ToLocalTime()) + + + + + @(context.Item.ExpiresAt.ToLocalTime()) + + + + + + + + + + + @(context.Item.LastUsedAt != null ? + context.Item.LastUsedAt.Value.ToLocalTime() : + AppLocalization["Never used"]) + + + + + + + + + + + @if(context.Item.DeletedAt==null){ + + } + else + { + + + @AppLocalization["Deleted"] + @context.Item.DeletedAt.Value.ToLocalTime().ToString() + + } + + + + + + @AppLocalization[ShowUser==true?"There are no active API tokens." :"You don't have any active API tokens."] + + + + +@code { + [Parameter] + public IEnumerable Tokens { get; set; } + [Parameter] + public bool ShowUser{ get; set; } + + [Parameter] + public EventCallback OnTokenDeleted { get; set; } + private async Task Delete(ApiToken? tokenToDelete) + { + OnTokenDeleted.InvokeAsync(tokenToDelete); + } +} \ No newline at end of file diff --git a/BLAZAMGui/UI/Modals/AppUserProfileModalContent.razor b/BLAZAMGui/UI/Modals/AppUserProfileModalContent.razor index ad589f046..019e260a5 100644 --- a/BLAZAMGui/UI/Modals/AppUserProfileModalContent.razor +++ b/BLAZAMGui/UI/Modals/AppUserProfileModalContent.razor @@ -56,6 +56,11 @@ + @* *@ + + @* *@ + + @* *@ @code { diff --git a/BLAZAMGui/UI/OU/RenameOUModalContent.razor b/BLAZAMGui/UI/OU/RenameOUModalContent.razor index 3b58ef145..02d1edd21 100644 --- a/BLAZAMGui/UI/OU/RenameOUModalContent.razor +++ b/BLAZAMGui/UI/OU/RenameOUModalContent.razor @@ -28,7 +28,6 @@ Modal.OnYes = SaveChanges; Modal.YesText = AppLocalization["Save Changes"]; Modal.Title = AppLocalization["Rename OU"]; - Modal.YesEnabled = ValidateModal; } diff --git a/BLAZAMGui/UI/Outputs/ApplicationNewsItem.razor b/BLAZAMGui/UI/Outputs/ApplicationNewsItem.razor index 0a94fefb0..7ed4755da 100644 --- a/BLAZAMGui/UI/Outputs/ApplicationNewsItem.razor +++ b/BLAZAMGui/UI/Outputs/ApplicationNewsItem.razor @@ -1,9 +1,9 @@ @using ApplicationNews @inject AppDialogService MessageService - + Class="px-1 py-0 cursor-pointer" + @onclick="@ShowNews"> @if (Read) @@ -20,7 +20,7 @@ - + @code { diff --git a/BLAZAMGui/UI/Outputs/ApplicationNewsProvider.razor b/BLAZAMGui/UI/Outputs/ApplicationNewsProvider.razor index a3e38a880..09549ed97 100644 --- a/BLAZAMGui/UI/Outputs/ApplicationNewsProvider.razor +++ b/BLAZAMGui/UI/Outputs/ApplicationNewsProvider.razor @@ -34,6 +34,9 @@ Blazam @AppLocalization["News"] + + @AppLocalization["Important updates and changes are posted here."] + @foreach (var item in unreadItems.OrderByDescending(x => x.UpdatedAt)) { - + } @if (showRead) { foreach (var item in readItems.OrderByDescending(x => x.UpdatedAt)) { + } } @@ -55,7 +59,7 @@ { if (unreadItems.Count == 0) { - + @AppLocalization["There is nothing new to see here"] } @@ -66,6 +70,7 @@ @if (readItems.Count > 0) { >? StatusFunc { get; set; } + [Parameter] public string? Text { get; set; } - private Color Color = Color.Error; - private string Icon = Icons.Material.Filled.Close; - + private Color Color = Color.Warning; + private string Icon = Icons.Material.Filled.QuestionMark; + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + if (StatusFunc != null) + { + Status = await StatusFunc.Invoke(); + } + await InvokeAsync(StateHasChanged); + } protected override async void OnParametersSet() { diff --git a/BLAZAMGui/UI/Outputs/UserApiTokenDataGrid.razor b/BLAZAMGui/UI/Outputs/UserApiTokenDataGrid.razor new file mode 100644 index 000000000..9036acad9 --- /dev/null +++ b/BLAZAMGui/UI/Outputs/UserApiTokenDataGrid.razor @@ -0,0 +1,90 @@ +@inherits DatabaseComponentBase + + + + + + @if (ApplicationInfo.InDebugMode) + { + 30 @AppLocalization["seconds"] + + } + 30 @AppLocalization["days"] + 60 @AppLocalization["days"] + 90 @AppLocalization["days"] + 180 @AppLocalization["days"] + 1 @AppLocalization["year"] + 2 @AppLocalization["years"] + 5 @AppLocalization["years"] + 10 @AppLocalization["years"] + @AppLocalization["Forever"] + + + @AppLocalization["Generate New API Token"] + +@code { + List _tokens = new(); + TimeSpan _apiValidDays = TimeSpan.FromDays(30); + + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + GetTokens(); + } + + private async Task GenerateToken() + { + var token = JwtTokenService.GenerateJwtToken(_apiValidDays); + var decodedToken = JwtTokenService.DecodeJwtToken(token); + var raw = decodedToken.Claims.FirstOrDefault(c => c.Type == "exp")?.Value; + var lng = long.Parse(raw); + + + var dt = DateTime.UnixEpoch.AddSeconds(lng); + + var newToken = new ApiToken() + { + Token = token.Encrypt(), + UserId = CurrentUser.State.Id, + ExpiresAt = dt, + TokenHash = token.GetAppHashCode() + }; + Context.ApiTokens.Add(newToken); + await Context.SaveChangesAsync(); + GetTokens(); + //CurrentUser.State.Preferences.APIToken = token; + //CurrentUser.State.SaveBasicUserPreferences(); + } + public async Task Save() + { + await Context.SaveChangesAsync(); + SnackBarService.Success(AppLocalization["Changes have been saved"]); + } + + private async Task Delete(ApiToken? tokenToDelete) + { + if (await MessageService.Confirm(AppLocalization["Are you sure you want to delete this API token?"], AppLocalization["Confirm delete"])) + { + if (tokenToDelete != null) + { + tokenToDelete.DeletedAt = DateTime.UtcNow; + Context.SaveChangesAsync(); + GetTokens(); + } + } + + } + + private void GetTokens() + { + Task.Run(async () => + { + _tokens = Context.ApiTokens.Where(t => t.UserId == CurrentUser.State.Id).ToList(); + await InvokeAsync(StateHasChanged); + }); + } + +} \ No newline at end of file diff --git a/BLAZAMGui/UI/Settings/DatabaseStatus.razor b/BLAZAMGui/UI/Settings/DatabaseStatus.razor index fb47f0c5f..a1e2e54d1 100644 --- a/BLAZAMGui/UI/Settings/DatabaseStatus.razor +++ b/BLAZAMGui/UI/Settings/DatabaseStatus.razor @@ -63,7 +63,6 @@ LoadingData = false; - await InvokeAsync(StateHasChanged); } async void Export() { diff --git a/BLAZAMGui/UI/Settings/Modals/DeleteAccessLevelModalContent.razor b/BLAZAMGui/UI/Settings/Modals/DeleteAccessLevelModalContent.razor index 778a1660a..4458eaeb5 100644 --- a/BLAZAMGui/UI/Settings/Modals/DeleteAccessLevelModalContent.razor +++ b/BLAZAMGui/UI/Settings/Modals/DeleteAccessLevelModalContent.razor @@ -48,7 +48,6 @@ Modal.RefreshView(); LoadingData = true; - await InvokeAsync(StateHasChanged); Task.Run(() => { _usedInMappings = Context.PermissionMap.Where(x => x.AccessLevels.Any(al => al.Id == AccessLevel.Id)).Include(x=>x.PermissionDelegates).ToList(); diff --git a/BLAZAMGui/UI/Settings/Permissions/DelegateEditor.razor b/BLAZAMGui/UI/Settings/Permissions/DelegateEditor.razor index 91eed96e9..87f09dcc0 100644 --- a/BLAZAMGui/UI/Settings/Permissions/DelegateEditor.razor +++ b/BLAZAMGui/UI/Settings/Permissions/DelegateEditor.razor @@ -82,13 +82,11 @@ private async Task RefreshDelegates() { LoadingData = true; - await InvokeAsync(StateHasChanged); AllDelegates.Clear(); AllDelegates = await settings.ToListAsync(); LoadingData = false; - await InvokeAsync(StateHasChanged); } diff --git a/BLAZAMGui/UI/Settings/Permissions/EffectivePermissionSimulator.razor b/BLAZAMGui/UI/Settings/Permissions/EffectivePermissionSimulator.razor index e9168d08d..34cdbbbc6 100644 --- a/BLAZAMGui/UI/Settings/Permissions/EffectivePermissionSimulator.razor +++ b/BLAZAMGui/UI/Settings/Permissions/EffectivePermissionSimulator.razor @@ -3,6 +3,7 @@ @AppLocalization["Effective Permissions"] + { - ADGroup = Directory.Groups.FindGroupBySID(Group.GroupSid); - InvokeAsync(StateHasChanged); + try + { + var sid = Group.GroupSid; + ADGroup = Directory.Groups.FindGroupBySID(sid); + InvokeAsync(StateHasChanged); + }catch(Exception ex) + { + Loggers.ActiveDirectoryLogger.Warning("Unable to find group {Error}", ex); + } }); } } \ No newline at end of file diff --git a/BLAZAMGui/UI/Settings/Templates/EditDirectoryTemplate.razor b/BLAZAMGui/UI/Settings/Templates/EditDirectoryTemplate.razor index 0a356e007..e8273fa76 100644 --- a/BLAZAMGui/UI/Settings/Templates/EditDirectoryTemplate.razor +++ b/BLAZAMGui/UI/Settings/Templates/EditDirectoryTemplate.razor @@ -25,6 +25,9 @@ await ParentTemplateChanged(null); Value="@DirectoryTemplate.ParentTemplate" ValueChanged="@ParentTemplateChanged" /> + @@ -498,15 +501,15 @@ await ParentTemplateChanged(null); - - @{ + + @{ var testName = new NewUserName() { GivenName = _testFirstName, MiddleName = _testMiddleName, Surname = _testLastName }; - } + } @@ -570,7 +573,7 @@ await ParentTemplateChanged(null); DirectoryTemplate.FieldValues.Add(new(){ CustomField=customField}); } })> - @field.DisplayName + @field.DisplayName } @@ -602,7 +605,10 @@ await ParentTemplateChanged(null); var fromTemplateName = dropdownTemplates.Where(ot => ot.FieldValues.Contains(cellContext.Item)).FirstOrDefault(); } - @cellContext.Item.FieldDisplayName + + + @cellContext.Item.FieldDisplayName + @@ -684,20 +690,20 @@ await ParentTemplateChanged(null);
- - @foreach (var group in DirectoryTemplate.EffectiveAssignedGroupSids) - { - var groupFromTemplate = dropdownTemplates.Where(ot => ot.AssignedGroupSids.Contains(group)).FirstOrDefault(); - + @foreach (var group in DirectoryTemplate.EffectiveAssignedGroupSids) + { - + var groupFromTemplate = dropdownTemplates.Where(ot => ot.AssignedGroupSids.Contains(group)).FirstOrDefault(); + - - - } + + + + + } @AppLocalization["User Activity"] - - + + - + @context.Item.Impersonator?.Identity?.Name - + - + @context.Item.LastAccessed.ToLocalTime() - +@* + + @(DateTimeOffset.UtcNow - context.Item.Ticket?.Properties.ExpiresUtc) + + *@ + diff --git a/BLAZAMGui/UI/Settings/WebHooks/LastMessageStatus.razor b/BLAZAMGui/UI/Settings/WebHooks/LastMessageStatus.razor new file mode 100644 index 000000000..ffb6367fd --- /dev/null +++ b/BLAZAMGui/UI/Settings/WebHooks/LastMessageStatus.razor @@ -0,0 +1,24 @@ +@inherits AppComponentBase + + +@code { + [Parameter] + public WebHookSubscription Subscription{ get; set; } + + WebHookAttempt? _lastAttemp; + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + using var dbContext = await DbFactory.CreateDbContextAsync(); + var lastAttempt = dbContext.WebHookAttempts + .Where(x => x.WebHookSubscriptionId == Subscription.Id) + .OrderByDescending(x => x.LastAttemptTimestamp) + .FirstOrDefault(); + if (lastAttempt != null) + { + _lastAttemp = lastAttempt; + } + + } +} diff --git a/BLAZAMGui/UI/Settings/WebHooks/NewWebHookModalContent.razor b/BLAZAMGui/UI/Settings/WebHooks/NewWebHookModalContent.razor new file mode 100644 index 000000000..4865ce852 --- /dev/null +++ b/BLAZAMGui/UI/Settings/WebHooks/NewWebHookModalContent.razor @@ -0,0 +1,122 @@ +@inherits AppModalContent + + + @AppLocalization["Trigger On"] + @foreach (var type in Enum.GetValues(typeof(NotificationType))) + { + var originalEnum = (NotificationType)type; + + {ToggleType(originalEnum,val);}) /> + + + } + @if (_newSubscription.NotificationTypes.Count == 0) + { + + @AppLocalization["You must choose at least one notification type."] + + } + + @* @AppLocalization["GET"] *@ + @AppLocalization["POST"] + + @if (_newSubscription.WebHookMethod == WebHookMethod.GET) + { + + + @AppLocalization["The webhook contents will be sent as URL parameters eg:?object=Username&objectOU=OU"] + + + } + else + { + + + @AppLocalization["The webhook contents will be sent a JSON object in the posted form body"] + + + + } + + + + @if (_newSubscription.IgnoreSSLVerification) + { + + @AppLocalization["Ignoring SSL verification could expose sensitive data to unintended recipients"] + + } + + + + @AppLocalization["None"] + @AppLocalization["Basic"] + @AppLocalization["Bearer"] + + + + {_newSubscription.AuthorizationToken = val.Encrypt(); }) /> + + + + @code { + WebHookSubscription _newSubscription = new(); + [Parameter] + public EventCallback OnCreateNewSubscription { get; set; } + protected override void OnInitialized() + { + base.OnInitialized(); + Modal.SetYesText(AppLocalization["Add"]); + Modal.OnYes += SaveSettings; + + + } + protected override bool IsValid => _newSubscription.IsValid; + private void SaveSettings() + { + if (!_newSubscription.URL.IsNullOrEmpty() && _newSubscription.NotificationTypes.Count > 0) + { + OnCreateNewSubscription.InvokeAsync(_newSubscription); + Modal.Close(); + } + + } + private void ToggleType(NotificationType type, bool value) + { + if (!value) + { + _newSubscription.NotificationTypes.RemoveAll(x => x.NotificationType == type); + } + else + { + if (!_newSubscription.NotificationTypes.Any(x => x.NotificationType == type)) + { + SubscriptionWebHookType notificationType = new(); + notificationType.NotificationType = type; + _newSubscription.NotificationTypes.Add(notificationType); + } + } + } +} \ No newline at end of file diff --git a/BLAZAMGui/UI/Settings/WebHooks/WebHookSettings.razor b/BLAZAMGui/UI/Settings/WebHooks/WebHookSettings.razor new file mode 100644 index 000000000..a589369ae --- /dev/null +++ b/BLAZAMGui/UI/Settings/WebHooks/WebHookSettings.razor @@ -0,0 +1,16 @@ +@inherits DatabaseComponentBase +@AppLocalization["Manage Webhooks"] + + + +@code { + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + } + + + + + +} diff --git a/BLAZAMGui/UI/Settings/WebHooks/WebHookSubscriptionEditor.razor b/BLAZAMGui/UI/Settings/WebHooks/WebHookSubscriptionEditor.razor new file mode 100644 index 000000000..914b9521c --- /dev/null +++ b/BLAZAMGui/UI/Settings/WebHooks/WebHookSubscriptionEditor.razor @@ -0,0 +1,217 @@ +@inherits DatabaseComponentBase + + + + + + + @context.Item.Id + + + + @foreach (var type in Enum.GetValues(typeof(NotificationType))) + { + var originalEnum = (NotificationType)type; + + + {ToggleType(context.Item,originalEnum,val);}) /> + + + + } + + + + @* @AppLocalization["GET"] *@ + @AppLocalization["POST"] + + + + + + + + + + + + + + + + + @AppLocalization["None"] + @AppLocalization["Basic"] + @AppLocalization["Bearer"] + + + + + + + + + + + + + + @* *@ + + + + + + + + + + + + + + + + + @AppLocalization["There are no Webhook subscriptions."] + + + + + + + + + +@code { + AppModal? webhookModal; + [Parameter] + public AppUser User { get; set; } + + private List _subscriptions = new(); + IDirectoryEntryAdapter? _selectedOU = null; + + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + _ = GetSubscriptions(); + } + private async Task AddSubscription(WebHookSubscription _newSubscription) + { + try + { + Context.WebHookSubscriptions.Add(_newSubscription); + await Context.SaveChangesAsync(); + GetSubscriptions(); + } + catch (Exception ex) + { + Loggers.SystemLogger.Warning("Error while adding webhook subscription {@Error}", ex); + SnackBarService.Error(ex.Message); + } + } + private async Task GetLastMessageStatus(WebHookSubscription sub) + { + using var dbContext = await DbFactory.CreateDbContextAsync(); + var lastAttempt= dbContext.WebHookAttempts + .Where(x=>x.WebHookSubscriptionId==sub.Id) + .OrderByDescending(x=>x.LastAttemptTimestamp) + .FirstOrDefault(); + if (lastAttempt != null) + { + return lastAttempt.Delivered; + } + return false; + } + private async Task Save() + { + try + { + await Context.SaveChangesAsync(); + SnackBarService.Success(AppLocalization["Changes saved successfully"]); + + GetSubscriptions(); + } + catch (Exception ex) + { + Loggers.SystemLogger.Warning("Error while saving webhook subscriptions {@Error}", ex); + SnackBarService.Error(ex.Message); + } + } + private async Task Delete(WebHookSubscription subscription) + { + if (await MessageService.Confirm(AppLocalization["Are you sure you want to delete this webhook subscription?"])) + { + + + try + { + subscription.DeletedAt = DateTime.UtcNow; + await Context.SaveChangesAsync(); + SnackBarService.Success(AppLocalization["Changes saved successfully"]); + + GetSubscriptions(); + } + catch (Exception ex) + { + Loggers.SystemLogger.Warning("Error while saving webhook subscriptions {@Error}", ex); + SnackBarService.Error(ex.Message); + } + } + } + + private async Task GetSubscriptions() + { + LoadingData = true; + _subscriptions = await Context.WebHookSubscriptions.Where(x => x.DeletedAt == null) + .Include(x => x.NotificationTypes) + .ToListAsync(); + LoadingData = false; + } + + private async void ouChanged(IDirectoryEntryAdapter entry) + { + var ou = (IADOrganizationalUnit)entry; + if (ou != null) + { + _selectedOU = ou; + await InvokeAsync(StateHasChanged); + } + } + private void ToggleType(WebHookSubscription subscription, NotificationType type, bool value) + { + if (!value) + { + subscription.NotificationTypes.RemoveAll(x => x.NotificationType == type); + } + else + { + if (!subscription.NotificationTypes.Any(x => x.NotificationType == type)) + { + SubscriptionWebHookType notificationType = new(); + notificationType.NotificationType = type; + subscription.NotificationTypes.Add(notificationType); + } + } + } +} \ No newline at end of file diff --git a/BLAZAMGui/_Imports.razor b/BLAZAMGui/_Imports.razor index b7d7a6de9..a497677a1 100644 --- a/BLAZAMGui/_Imports.razor +++ b/BLAZAMGui/_Imports.razor @@ -43,7 +43,6 @@ @using BLAZAM.Helpers @using BLAZAM.Server.Data @using BLAZAM.Jobs; -@using BLAZAM.Nav; @using BLAZAM.Gui.UI.Outputs.Jobs; diff --git a/BLAZAMJobs/BLAZAMJobs.csproj b/BLAZAMJobs/BLAZAMJobs.csproj index 9479bdfe5..83ef863cb 100644 --- a/BLAZAMJobs/BLAZAMJobs.csproj +++ b/BLAZAMJobs/BLAZAMJobs.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/BLAZAMNav/AppNavigationManager.cs b/BLAZAMNav/AppNavigationManager.cs deleted file mode 100644 index a6a3fd41e..000000000 --- a/BLAZAMNav/AppNavigationManager.cs +++ /dev/null @@ -1,69 +0,0 @@ - -using BLAZAM.Localization; -using BLAZAM.Logger; -using BLAZAM.Notifications.Services; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Routing; -using Microsoft.Extensions.Localization; -using Microsoft.JSInterop; - -namespace BLAZAM.Nav -{ - public class AppNavigationManager - { - private NavigationManager Nav; - private bool warnOnNavigation; - - public IJSRuntime JS { get; } - public EventHandler LocationChanged { get; set; } - - //public bool WarnOnNavigation - //{ - // get => warnOnNavigation; set - // { - // warnOnNavigation = value; - // JS.InvokeVoidAsync("window.warnOnNavigation", new object[] { warnOnNavigation }); - // } - //} - - - public string ToBaseRelativePath(string uri) - { - return Nav.ToBaseRelativePath(uri); - } - public string Uri => Nav.Uri; - - public AppDialogService MessageService { get; } - public IStringLocalizer AppLocalization { get; } - - public AppNavigationManager(IJSRuntime js, IStringLocalizer appLocalization, NavigationManager nav, AppDialogService messageService) - { - Nav = nav; - JS = js; - try - { - Nav.LocationChanged += (args, other) => { LocationChanged?.Invoke(args, other); }; - } - catch (Exception ex) - { - Loggers.SystemLogger.Error("Error trying to listen for location changes {@Error}", ex); - } - MessageService = messageService; - AppLocalization = appLocalization; - } - - public async void NavigateTo(string uri, bool forceLoad = false) - { - - Nav.NavigateTo(uri, forceLoad); - - //if (await MessageService.Confirm(AppLocalization["Are you sure you want to navigate away?"], AppLocalization["You have unsaved changes"]) == true) - //{ - // WarnOnNavigation = false; - // Nav.NavigateTo(uri, forceLoad); - //} - - } - - } -} diff --git a/BLAZAMNav/BLAZAMNav.csproj b/BLAZAMNav/BLAZAMNav.csproj deleted file mode 100644 index b934e5dbb..000000000 --- a/BLAZAMNav/BLAZAMNav.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - net8.0 - enable - enable - BLAZAM.Nav - - - - - - - - - diff --git a/BLAZAMNotifications/BLAZAMNotifications.csproj b/BLAZAMNotifications/BLAZAMNotifications.csproj index 2ac8296a2..4d4f485b3 100644 --- a/BLAZAMNotifications/BLAZAMNotifications.csproj +++ b/BLAZAMNotifications/BLAZAMNotifications.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/BLAZAMServices/AdminTokenService.cs b/BLAZAMServices/AdminTokenService.cs index b882a5967..90d742994 100644 --- a/BLAZAMServices/AdminTokenService.cs +++ b/BLAZAMServices/AdminTokenService.cs @@ -13,6 +13,7 @@ namespace BLAZAM.Services /// /// If a real RestAPI is implemente this will need to change to jwt tokens. ///
+ [Obsolete("Use JwtTokenService instead.")] public class AdminTokenService { private readonly ApplicationManager _applicationManager; diff --git a/BLAZAMServices/Audit/AuditLogger.cs b/BLAZAMServices/Audit/AuditLogger.cs index 79d5b5074..9db3cbd48 100644 --- a/BLAZAMServices/Audit/AuditLogger.cs +++ b/BLAZAMServices/Audit/AuditLogger.cs @@ -21,6 +21,7 @@ public class AuditLogger public PrinterAudit Printer; public LogonAudit Logon; public BitLockerAudit BitLocker; + public EmailAudit Email; public AuditLogger(IAppDatabaseFactory factory, IApplicationUserStateService userStateService) { diff --git a/BLAZAMServices/Audit/EmailAudit.cs b/BLAZAMServices/Audit/EmailAudit.cs new file mode 100644 index 000000000..2ee200b06 --- /dev/null +++ b/BLAZAMServices/Audit/EmailAudit.cs @@ -0,0 +1,60 @@ +using BLAZAM.ActiveDirectory.Interfaces; +using BLAZAM.Database.Context; +using BLAZAM.Database.Models.Audit; +using BLAZAM.Session.Interfaces; +using Microsoft.EntityFrameworkCore; +using System.Web; + +namespace BLAZAM.Services.Audit +{ + public class EmailAudit : BaseAudit + { + protected IApplicationUserStateService UserStateService { get; private set; } + /// + /// The Email being auditted + /// + /// + /// The default value is the current web user from the + /// + protected IApplicationUserState? CurrentUser { get; set; } + public EmailAudit(IAppDatabaseFactory factory, IApplicationUserStateService userStateService) : base(factory) + { + UserStateService = userStateService; + CurrentUser = UserStateService.CurrentUserState; + + } + + public async void EmailSent(string emailId, string? from, string? to, string? cc, string? bcc, string? subject, string? body, string? response) + { + await Log(emailId, from, to, cc, bcc, subject, body, response); + } + protected virtual async Task Log(string? emailId, string? from, string? to, string? cc, string? bcc, string? subject, string? body, string? response) + { + + try + { + using var context = await Factory.CreateDbContextAsync(); + //var table = context.EmailAuditLog; + //var auditEntry = new EmailAuditLog() + //{ + // MessageGuid = emailId, + // From = from, + // To = to, + // Cc = cc, + // Bcc = bcc, + // Subject = subject, + // HtmlBody = body, + // ServerResponse = response + //}; + //table.Add(auditEntry); + context.SaveChanges(); + return true; + + } + catch + { + return false; + } + } + } +} \ No newline at end of file diff --git a/BLAZAMServices/BLAZAMServices.csproj b/BLAZAMServices/BLAZAMServices.csproj index 29dd1cd99..83cbac7a4 100644 --- a/BLAZAMServices/BLAZAMServices.csproj +++ b/BLAZAMServices/BLAZAMServices.csproj @@ -10,7 +10,9 @@ + + diff --git a/BLAZAMServices/Background/EmailService.cs b/BLAZAMServices/Background/EmailService.cs index 4cd0d935a..c12b34527 100644 --- a/BLAZAMServices/Background/EmailService.cs +++ b/BLAZAMServices/Background/EmailService.cs @@ -9,6 +9,7 @@ using BLAZAM.EmailMessage.Email.Base; using BLAZAM.FileSystem; using BLAZAM.Helpers; +using BLAZAM.Services.Audit; using BLAZAM.Static; using BlazorTemplater; using MailKit.Net.Smtp; @@ -24,6 +25,7 @@ public class EmailService { public static EmailService? Instance { get; set; } private IAppDatabaseFactory Factory { get; set; } + public bool IsConfigured { get @@ -132,6 +134,7 @@ private MimeMessage BuildMessage(string subject, string to, string body, string? { var email = new MimeMessage(); + email.MessageId = Guid.NewGuid().ToString(); if (IsConfigured) { EmailSettings? settings = GetSettings(); @@ -157,6 +160,7 @@ private MimeMessage BuildMessage(string subject, string to, string body, string? image.ContentId = MimeUtils.GenerateMessageId(); //Replace logo placeholder in template with referenced img tag body = body.Replace("{{ApplicationLogo}}", ""); + body = body.Replace("{{TrackingImgLink}}", ""); body = PrepareHTMLForEmail(body); builder.HtmlBody = body; //Compile body @@ -199,9 +203,11 @@ public async Task SendMessage(string subject, string to, MarkupString head } } - private static async Task TrySend(SmtpClient client, MimeMessage message) + private async Task TrySend(SmtpClient client, MimeMessage message) { var response = await client.SendAsync(message); + //AuditLogger.Email.EmailSent(message.MessageId, message.From.ToString(), message.To.ToString(), message.Cc.ToString(), message.Bcc.ToString(), message.Subject, message.HtmlBody,response); + //TODO Audit to database return true; } diff --git a/BLAZAMServices/Background/NotificationGenerationService.cs b/BLAZAMServices/Background/NotificationGenerationService.cs index 749cd18f1..bff929b75 100644 --- a/BLAZAMServices/Background/NotificationGenerationService.cs +++ b/BLAZAMServices/Background/NotificationGenerationService.cs @@ -25,13 +25,15 @@ public class NotificationGenerationService private readonly INotificationPublisher _notificationPublisher; private readonly IStringLocalizer _appLocalization; private readonly EmailService _emailService; + private readonly WebHookPublisher _webHookPublisher; - public NotificationGenerationService(IAppDatabaseFactory databaseFactory, INotificationPublisher notificationPublisher, IStringLocalizer appLocalization, EmailService emailService) + public NotificationGenerationService(IAppDatabaseFactory databaseFactory, INotificationPublisher notificationPublisher, IStringLocalizer appLocalization, EmailService emailService, WebHookPublisher webHookPublisher) { _databaseFactory = databaseFactory; _notificationPublisher = notificationPublisher; _appLocalization = appLocalization; _emailService = emailService; + _webHookPublisher = webHookPublisher; } private IDatabaseContext Context => _databaseFactory.CreateDbContext(); @@ -80,11 +82,27 @@ await Task.Run(async () => } } }); - + PostWebHooks(source, notificationType, actor , target ); }); } + private async Task PostWebHooks(IDirectoryEntryAdapter source, NotificationType notificationType, IApplicationUserState? actor = null, IDirectoryEntryAdapter? target = null) + { + var webhooks = await Context.WebHookSubscriptions.Where(w => w.DeletedAt == null) + .Include(w=>w.NotificationTypes) + .Where(x => x.DeletedAt == null) + .ToListAsync(); + if (webhooks.Any(w => w.NotificationTypes.Any(nt => nt.NotificationType == notificationType))) + { + var subscribedWebhooks = webhooks.Where(w => w.NotificationTypes.Any(nt => nt.NotificationType == notificationType)); + Parallel.ForEach(subscribedWebhooks, async webhook => { + _webHookPublisher.PublishWebhook(webhook, source, notificationType,actor, target); + }); + + + } + } /// /// Package a notification from event parameters /// diff --git a/BLAZAMServices/Duo/DuoClientProvider.cs b/BLAZAMServices/Duo/DuoClientProvider.cs index 43d2cc582..7461d9d12 100644 --- a/BLAZAMServices/Duo/DuoClientProvider.cs +++ b/BLAZAMServices/Duo/DuoClientProvider.cs @@ -1,6 +1,5 @@ using BLAZAM.Common.Data.Database; using BLAZAM.Database.Context; -using BLAZAM.Nav; using BLAZAM.Session.Interfaces; using DuoUniversal; using Microsoft.AspNetCore.Components; diff --git a/BLAZAMServices/EncryptionService.cs b/BLAZAMServices/EncryptionService.cs index 33b3d9d7b..976db4701 100644 --- a/BLAZAMServices/EncryptionService.cs +++ b/BLAZAMServices/EncryptionService.cs @@ -7,7 +7,7 @@ namespace BLAZAM.Services public class EncryptionService : IEncryptionService { private Encryption Encryption { get; set; } - + public byte[] Key => Encryption.Key; public ServiceConnectionState Status { get diff --git a/BLAZAMServices/Exceptions/WebhookVerificationException.cs b/BLAZAMServices/Exceptions/WebhookVerificationException.cs new file mode 100644 index 000000000..4f7de7603 --- /dev/null +++ b/BLAZAMServices/Exceptions/WebhookVerificationException.cs @@ -0,0 +1,14 @@ + +namespace BLAZAM.Notifications.Services +{ + [Serializable] + public class WebhookVerificationException : Exception + { + public WebhookVerificationException() : base() { } + public WebhookVerificationException(string message) : base(message) { } + public WebhookVerificationException(string message, Exception inner) : base(message, inner) { } + + protected WebhookVerificationException(System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/BLAZAMServices/JwtAuthenticationEventsHandler.cs b/BLAZAMServices/JwtAuthenticationEventsHandler.cs new file mode 100644 index 000000000..ac76a4571 --- /dev/null +++ b/BLAZAMServices/JwtAuthenticationEventsHandler.cs @@ -0,0 +1,76 @@ +using BLAZAM.Database.Context; +using BLAZAM.Helpers; +using BLAZAM.Server.Data.Services; +using BLAZAM.Session.Interfaces; +using Microsoft.AspNetCore.Authentication.BearerToken; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BLAZAM.Services +{ + + public class JwtAuthenticationEventsHandler : JwtBearerEvents + { + private readonly IApplicationUserStateService _userStateService; // Inject any service you need + private readonly ICurrentUserStateService _currentUserStateService; // Inject any service you need + private readonly IHttpContextAccessor _httpContextAccessor; // Inject any service you need + private readonly IAppDatabaseFactory _dbFactory; // Inject any service you need + + public JwtAuthenticationEventsHandler(IHttpContextAccessor httpContextAccessor, IApplicationUserStateService userStateService, IAppDatabaseFactory dbFactory, ICurrentUserStateService currentUserStateService) + { + _userStateService = userStateService; + _httpContextAccessor = httpContextAccessor; + _currentUserStateService = currentUserStateService; + _dbFactory = dbFactory; + } + /// + /// Checks the database for the existence of the token and whether it's been revoked. + /// + /// + /// + public override async Task TokenValidated(TokenValidatedContext context) + { + // This method is called after the JWT token has been successfully validated + + var userId = context.Principal?.Identity?.Name; // Get the user ID + // ... access other claims from context.Principal + var userState = new ApplicationUserState(_dbFactory); + userState.User = context.Principal; + userState.IPAddress = context.HttpContext?.Connection?.RemoteIpAddress?.ToString(); + _userStateService.SetUserState(userState); + using var dbContext = await _dbFactory.CreateDbContextAsync(); + var tokenStr2 = context.SecurityToken.UnsafeToString().ToSecureString(); + var tokenHash = tokenStr2.ToPlainText().GetAppHashCode(); + var matchingToken = dbContext.ApiTokens.FirstOrDefault(t => t.TokenHash.Equals(tokenHash)); + if (matchingToken == null) + { + context.Response.StatusCode = 401; + context.Response.ContentType = "application/json"; + await context.Response.WriteAsync("Invalid Token"); + context.Fail("Invalid Token"); + } + else if (matchingToken.IsRevoked || matchingToken.DeletedAt != null) + { + + context.Response.StatusCode = 401; + context.Response.ContentType = "application/json"; + await context.Response.WriteAsync("Token Revoked"); + context.Fail("Token Revoked"); + } + else + { + matchingToken.LastUsedAt = DateTime.UtcNow; + await dbContext.SaveChangesAsync(); + } + //return Task.CompletedTask; + } + + + } + +} diff --git a/BLAZAMServices/JwtTokenService.cs b/BLAZAMServices/JwtTokenService.cs new file mode 100644 index 000000000..9f1a3a069 --- /dev/null +++ b/BLAZAMServices/JwtTokenService.cs @@ -0,0 +1,93 @@ +using BLAZAM.Common.Data; +using BLAZAM.Common.Data.Services; +using BLAZAM.Database.Context; +using BLAZAM.Logger; +using BLAZAM.Server.Data.Services; +using BLAZAM.Session.Interfaces; +using Microsoft.IdentityModel.Tokens; +using System; +using System.Collections.Generic; +using System.DirectoryServices; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; + +namespace BLAZAM.Services +{ + public class JwtTokenService + { + private readonly ICurrentUserStateService _currentUserStateService; + private readonly ApplicationInfo _applicationInfo; + + public JwtTokenService(ApplicationInfo applicationInfo, ICurrentUserStateService currentUserStateService) + + { + _currentUserStateService = currentUserStateService; + _applicationInfo = applicationInfo; + } + /// + /// Generates a new for the user and places + /// it into the database + /// + /// The amount of time the token should be allowed to be used. Defaults to 365 days. + /// The newly generated + public string GenerateJwtToken(TimeSpan? lifetime=null) + { + if (lifetime == null) {lifetime = TimeSpan.FromDays(365); } + var tokenHandler = new JwtSecurityTokenHandler(); + + var currentUser = _currentUserStateService.State; + var claims = new Dictionary + { + { ClaimTypes.Sid, currentUser.Preferences.UserGUID} + }; + // Get key from config + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = currentUser.User.Claims.FirstOrDefault()?.Subject, + Claims = claims, + IssuedAt = DateTime.UtcNow, + Issuer = DatabaseCache.ApplicationSettings.AppName, + Expires = (DateTime.UtcNow+lifetime.Value), // Set expiration + SigningCredentials = new SigningCredentials(_applicationInfo.TokenKey, SecurityAlgorithms.HmacSha256Signature) + }; + var token = tokenHandler.CreateToken(tokenDescriptor); + var jwtToken = tokenHandler.WriteToken(token); + + + + return jwtToken; + } + + public ClaimsPrincipal DecodeJwtToken(string + token) + { + var tokenHandler = new JwtSecurityTokenHandler(); + + + try + { + tokenHandler.ValidateToken(token, new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKey = _applicationInfo.TokenKey, + ValidateIssuer = false, + ValidateAudience = false, + ClockSkew = TimeSpan.Zero // Set clock skew to zero + }, out SecurityToken validatedToken); + + var jwtToken = (JwtSecurityToken)validatedToken; + return new ClaimsPrincipal(new ClaimsIdentity(jwtToken.Claims)); + + } + catch (Exception ex) + { + // Handle token validation errors (e.g., log the error) + Loggers.SystemLogger.Error($"Error validating token: {ex.Message}"); + return null; + } + } + } +} diff --git a/BLAZAMServices/SearchService.cs b/BLAZAMServices/SearchService.cs index 3b0ae3c31..f36890130 100644 --- a/BLAZAMServices/SearchService.cs +++ b/BLAZAMServices/SearchService.cs @@ -1,7 +1,6 @@ using BLAZAM.Common; using BLAZAM.Common.Data; using BLAZAM.Common.Data.Services; -using BLAZAM.Nav; using BLAZAM.Session.Interfaces; using Microsoft.AspNetCore.Components; @@ -10,7 +9,7 @@ namespace BLAZAM.Services public class SearchService { private readonly IApplicationUserStateService _userStateService; - private readonly AppNavigationManager _nav; + private readonly NavigationManager _nav; private bool includeDisabled = false; private string? searchTerm; @@ -31,7 +30,7 @@ public string? SearchTerm public ActiveDirectoryObjectType SeachObjectType { get; set; } = ActiveDirectoryObjectType.All; - public SearchService(IApplicationUserStateService userStateService, AppNavigationManager nav) + public SearchService(IApplicationUserStateService userStateService, NavigationManager nav) { _userStateService = userStateService; _nav = nav; diff --git a/BLAZAMServices/WebHookPublisher.cs b/BLAZAMServices/WebHookPublisher.cs new file mode 100644 index 000000000..040680838 --- /dev/null +++ b/BLAZAMServices/WebHookPublisher.cs @@ -0,0 +1,359 @@ +using Azure.Core.Pipeline; +using BLAZAM.ActiveDirectory.Interfaces; +using BLAZAM.Common.Data; +using BLAZAM.Database.Models.Notifications; +using BLAZAM.Helpers; +using BLAZAM.Logger; +using BLAZAM.Session.Interfaces; +using Microsoft.AspNetCore; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.Extensions.Http; +using Microsoft.Extensions.Configuration; +using MudBlazor.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using BLAZAM.Database.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.FileSystemGlobbing.Internal; +using System.Security.Authentication; +using Azure; +using Polly; + + +namespace BLAZAM.Notifications.Services +{ + public class WebHookPublisher : IDisposable + { + internal static readonly UTF8Encoding SafeUTF8Encoding = new UTF8Encoding(false, true); + internal const string UNBRANDED_ID_HEADER_KEY = "webhook-id"; + internal const string UNBRANDED_ATTEMPT_ID_HEADER_KEY = "webhook-attempt-id"; + internal const string UNBRANDED_SIGNATURE_HEADER_KEY = "webhook-signature"; + internal const string UNBRANDED_TIMESTAMP_HEADER_KEY = "webhook-timestamp"; + internal const string UNBRANDED_ATTEMPT_TIMESTAMP_HEADER_KEY = "webhook-attempt-timestamp"; + private const string webhookDateTimeFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"; + + private const int TOLERANCE_IN_SECONDS = 60 * 5; + private static string prefix = "whsec_"; + + private readonly IHttpClientFactory _httpClientFactory; + private readonly IAppDatabaseFactory _appDatabaseFactory; + + private bool _running; + public WebHookPublisher(IHttpClientFactory httpClientFactory, IAppDatabaseFactory appDatabaseFactory) + { + _httpClientFactory = httpClientFactory; + _appDatabaseFactory = appDatabaseFactory; + _ = Run(); + } + private async Task Run() + { + if (!_running) + { + _running = true; + while (_running) + { + try + { + using var context = _appDatabaseFactory.CreateDbContext(); + var undeliveredWebhooks = context.WebHookAttempts + .Include(w => w.WebHookSubscription) + .Where(w => w.Delivered == false && + w.RetryCount < 15) + .ToList(); + Parallel.ForEachAsync(undeliveredWebhooks, async (attempt, cancel) => + { + var attemptId = Guid.NewGuid(); + + await SendWebHook(attempt.WebHookSubscription, attempt.MessageGuid, attemptId, attempt.EventTimestamp, attempt.Body, attempt.EventType ,attempt.Signature); + + }); + } + catch (Exception ex) + { + Loggers.SystemLogger.Error("Unexpected error retrying webhook {Error}", ex); + } + var rand = new Random(); + + await Task.Delay(600000 + rand.Next(-10000, 10000)); + } + } + } + + + public async Task PublishWebhook(WebHookSubscription subscription, + IDirectoryEntryAdapter source, + NotificationType notificationType, + IApplicationUserState? actor = null, + IDirectoryEntryAdapter? target = null) + { + + var msgId = Guid.NewGuid(); + var eventTimestamp = DateTime.UtcNow; + + var eventType = source.ObjectType.ToString().ToLower() + "." + notificationType.ToString().ToLower(); + + + string? signature = null; + + Dictionary payload = new() + { + { "timestamp", eventTimestamp.ToString(webhookDateTimeFormat)}, + + { "type", eventType} + }; + var attemptId = Guid.NewGuid(); + Dictionary data = new() + { + { "id", msgId }, + { "actor", actor?.Username }, // Use ?. to handle null actor + { "entry", source?.CanonicalName }, // Use ?. to handle null source + { "entryOU", source?.OU }, // Use ?. to handle null source + { "entryDN", source?.DN }, // Use ?. to handle null source + { "entryType", source?.ObjectType.ToString()}, // Use ?. to handle null source + }; + if (target != null) + { + data.Add("target", target.CanonicalName); + data.Add("targetOU", target.OU); + data.Add("targetDN", target.DN); + data.Add("targetType", target.ObjectType.ToString()); + } + payload.Add("data", data); + var payloadString = System.Text.Json.JsonSerializer.Serialize(payload); + if (subscription.WebHookSignature == WebHookSignature.HMAC) + { + if (subscription.HmacKey.IsNullOrEmpty()) + throw new ApplicationException("HMAC Key not supplied to subscription set to use it."); + var key = subscription.HmacKey.Decrypt(); + if (key.StartsWith(prefix)) + { + key = key.Substring(prefix.Length); + } + var bytekey = Convert.FromBase64String(key); + signature = Sign(bytekey, msgId.ToString(), DateTime.UtcNow, payloadString); + + } + + await SendWebHook(subscription, msgId, attemptId, eventTimestamp, eventType, payloadString, signature); + } + + private async Task SendWebHook(WebHookSubscription subscription, Guid msgId, Guid attemptId, DateTime eventTimestamp, string eventType, string payloadString, string? signature) + { + var httpClientHandler = new HttpClientHandler(); + var httpClient = CreateAPIClient(subscription.IgnoreSSLVerification); + using var context = await _appDatabaseFactory.CreateDbContextAsync(); + + var thisMessage = await context.WebHookAttempts.FirstOrDefaultAsync(a => a.MessageGuid == msgId); + + + + var request = new HttpRequestMessage + { + RequestUri = new Uri(subscription.URL), + Method = subscription.WebHookMethod == WebHookMethod.GET ? HttpMethod.Get : HttpMethod.Post, + Content = new StringContent(payloadString, Encoding.UTF8, "application/json") + + }; + + request.Headers.Add(UNBRANDED_ID_HEADER_KEY, msgId.ToString()); + request.Headers.Add(UNBRANDED_ATTEMPT_ID_HEADER_KEY, attemptId.ToString()); + request.Headers.Add(UNBRANDED_TIMESTAMP_HEADER_KEY, eventTimestamp.ToString(webhookDateTimeFormat)); + request.Headers.Add(UNBRANDED_ATTEMPT_TIMESTAMP_HEADER_KEY, DateTime.UtcNow.ToString(webhookDateTimeFormat)); + if (!signature.IsNullOrEmpty()) + { + request.Headers.Add(UNBRANDED_SIGNATURE_HEADER_KEY, signature); + + } + // Add authorization header if needed + if (subscription.WebHookAuthorization == WebHookAuthorization.Basic) + { + // Assuming AuthorizationToken is in the format "username:password" + var base64Auth = Convert.ToBase64String(Encoding.ASCII.GetBytes(subscription.AuthorizationToken)); + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", base64Auth); + } + else if (subscription.WebHookAuthorization == WebHookAuthorization.Bearer) + { + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", subscription.AuthorizationToken); + } + + try + { + if (thisMessage == null) + { + var webHookAttempt = new WebHookAttempt() + { + Body = payloadString, + MessageGuid = msgId, + EventType= eventType, + Uri = request.RequestUri.ToString(), + Delivered = false, + WebHookSubscriptionId = subscription.Id, + LastAttemptTimestamp = DateTime.UtcNow, + EventTimestamp = eventTimestamp, + Signature = signature + }; + thisMessage = webHookAttempt; + context.WebHookAttempts.Add(thisMessage); + + } + else + { + thisMessage.RetryCount++; + thisMessage.Uri = request.RequestUri.ToString(); + } + await context.SaveChangesAsync(); + + + var response = await httpClient.SendAsync(request); + + + + + + + if (response.IsSuccessStatusCode) + { + thisMessage.Delivered = true; + thisMessage.ResponseCode = response.StatusCode; + thisMessage.ResponseMessage = null; + await context.SaveChangesAsync(); + } + else + { + Loggers.SystemLogger.Information("Webhook failed {Subscription}", subscription); + thisMessage.Delivered = false; + thisMessage.ResponseCode = response.StatusCode; + thisMessage.ResponseMessage = null; + await context.SaveChangesAsync(); + } + } + catch (HttpRequestException ex) + { + if (ex.InnerException != null) + { + if (thisMessage != null) + { + thisMessage.ResponseCode = HttpStatusCode.UnprocessableEntity; + thisMessage.ResponseMessage = ex.InnerException.Message; + } + + } + else + { + if (thisMessage != null) + { + thisMessage.ResponseCode = HttpStatusCode.UnprocessableEntity; + thisMessage.ResponseMessage = ex.Message; + } + } + await context.SaveChangesAsync(); + } + catch (Exception ex) + { + Loggers.SystemLogger.Error("Unexpected Webhook error occurred {Error}", ex); + } + } + + private HttpClient CreateAPIClient(bool ignoreSSL) + { + if (!ignoreSSL) + { + // Use the default client if validating ssl + return _httpClientFactory.CreateClient(HttpClientNames.WebHookHttpClientName); + } + else + { + + // Use the client with validation disabled + var client = _httpClientFactory.CreateClient(HttpClientNames.WebHookHttpClientNoSSLCheckName); + + return client; + } + } + //public void Verify(string payload, WebHeaderCollection headers) + //{ + // string msgId = headers.Get(UNBRANDED_ID_HEADER_KEY); + // string msgSignature = headers.Get(UNBRANDED_SIGNATURE_HEADER_KEY); + // string msgTimestamp = headers.Get(UNBRANDED_TIMESTAMP_HEADER_KEY); + // if (String.IsNullOrEmpty(msgId) || String.IsNullOrEmpty(msgSignature) || String.IsNullOrEmpty(msgTimestamp)) + // { + // throw new WebhookVerificationException("Missing Required Headers"); + // } + + // var timestamp = Webhook.VerifyTimestamp(msgTimestamp); + + // var signature = this.Sign(msgId, timestamp, payload); + // var expectedSignature = signature.Split(',')[1]; + + // var passedSignatures = msgSignature.Split(' '); + // foreach (string versionedSignature in passedSignatures) + // { + // var parts = versionedSignature.Split(','); + // if (parts.Length < 2) + // { + // throw new WebhookVerificationException("Invalid Signature Headers"); + // } + // var version = parts[0]; + // var passedSignature = parts[1]; + + // if (version != "v1") + // { + // continue; + // } + // if (Utils.SecureCompare(expectedSignature, passedSignature)) + // { + // return; + // } + + // } + // throw new WebhookVerificationException("No matching signature found"); + //} + + //private static DateTimeOffset VerifyTimestamp(string timestampHeader) + //{ + // DateTimeOffset timestamp; + // var now = DateTimeOffset.UtcNow; + // try + // { + // var timestampInt = long.Parse(timestampHeader); + // timestamp = DateTimeOffset.FromUnixTimeSeconds(timestampInt); + // } + // catch + // { + // throw new WebhookVerificationException("Invalid Signature Headers"); + // } + + // if (timestamp < (now.AddSeconds(-1 * TOLERANCE_IN_SECONDS))) + // { + // throw new WebhookVerificationException("Message timestamp too old"); + // } + // if (timestamp > (now.AddSeconds(TOLERANCE_IN_SECONDS))) + // { + // throw new WebhookVerificationException("Message timestamp too new"); + // } + // return timestamp; + //} + + public string Sign(byte[] key, string msgId, DateTimeOffset timestamp, string payload) + { + var toSign = $"{msgId}.{timestamp.ToUnixTimeSeconds().ToString()}.{payload}"; + var toSignBytes = SafeUTF8Encoding.GetBytes(toSign); + using (var hmac = new HMACSHA256(key)) + { + var hash = hmac.ComputeHash(toSignBytes); + var signature = Convert.ToBase64String(hash); + return $"v1,{signature}"; + } + } + + public void Dispose() + { + _running = false; + } + } +} diff --git a/BLAZAMSession/Interfaces/IApplicationUserState.cs b/BLAZAMSession/Interfaces/IApplicationUserState.cs index 7716581f1..78fc7946e 100644 --- a/BLAZAMSession/Interfaces/IApplicationUserState.cs +++ b/BLAZAMSession/Interfaces/IApplicationUserState.cs @@ -13,6 +13,9 @@ namespace BLAZAM.Session.Interfaces { public interface IApplicationUserState { + /// + /// This user's id as in the database + /// public int Id { get; } AppEvent OnSettingsChanged { get; set; } diff --git a/BLAZAMThemes/BLAZAMThemes.csproj b/BLAZAMThemes/BLAZAMThemes.csproj index 32adb17d8..6e3c01bb7 100644 --- a/BLAZAMThemes/BLAZAMThemes.csproj +++ b/BLAZAMThemes/BLAZAMThemes.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/PlaywrightTests/PlaywrightTests.csproj b/PlaywrightTests/PlaywrightTests.csproj index a3c724173..5a927e3d9 100644 --- a/PlaywrightTests/PlaywrightTests.csproj +++ b/PlaywrightTests/PlaywrightTests.csproj @@ -13,7 +13,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/README.md b/README.md index 6fad0c550..a7bcfe1d2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # BLAZAM Proudly serving -![image](https://github.com/user-attachments/assets/9b7a0006-b32f-4f4a-8cef-bf3ead418ba0) +![image](https://github.com/user-attachments/assets/0c5e2de1-2174-4238-a944-8e9cc4b8cd4a) ## Version 1.0.0 Release Notice