diff --git a/src/Admin/Controllers/ToolsController.cs b/src/Admin/Controllers/ToolsController.cs index b1695e71bd..951d533e9b 100644 --- a/src/Admin/Controllers/ToolsController.cs +++ b/src/Admin/Controllers/ToolsController.cs @@ -1,13 +1,17 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Threading.Tasks; using Bit.Admin.Models; using Bit.Core; +using Bit.Core.Models.Table; using Bit.Core.Repositories; +using Bit.Core.Services; using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; namespace Bit.Admin.Controllers { @@ -16,16 +20,28 @@ namespace Bit.Admin.Controllers public class ToolsController : Controller { private readonly GlobalSettings _globalSettings; + private readonly IOrganizationRepository _organizationRepository; + private readonly IOrganizationService _organizationService; + private readonly IUserService _userService; private readonly ITransactionRepository _transactionRepository; + private readonly IInstallationRepository _installationRepository; private readonly IOrganizationUserRepository _organizationUserRepository; public ToolsController( GlobalSettings globalSettings, + IOrganizationRepository organizationRepository, + IOrganizationService organizationService, + IUserService userService, ITransactionRepository transactionRepository, + IInstallationRepository installationRepository, IOrganizationUserRepository organizationUserRepository) { _globalSettings = globalSettings; + _organizationRepository = organizationRepository; + _organizationService = organizationService; + _userService = userService; _transactionRepository = transactionRepository; + _installationRepository = installationRepository; _organizationUserRepository = organizationUserRepository; } @@ -144,7 +160,7 @@ namespace Bit.Admin.Controllers public IActionResult PromoteAdmin() { - return View("PromoteAdmin"); + return View(); } [HttpPost] @@ -152,7 +168,7 @@ namespace Bit.Admin.Controllers { if (!ModelState.IsValid) { - return View("PromoteAdmin", model); + return View(model); } var orgUsers = await _organizationUserRepository.GetManyByOrganizationAsync( @@ -169,12 +185,84 @@ namespace Bit.Admin.Controllers if (!ModelState.IsValid) { - return View("PromoteAdmin", model); + return View(model); } user.Type = Core.Enums.OrganizationUserType.Owner; await _organizationUserRepository.ReplaceAsync(user); return RedirectToAction("Edit", "Organizations", new { id = model.OrganizationId.Value }); } + + public IActionResult GenerateLicense() + { + return View(); + } + + [HttpPost] + public async Task GenerateLicense(LicenseModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + User user = null; + Organization organization = null; + if (model.UserId.HasValue) + { + user = await _userService.GetUserByIdAsync(model.UserId.Value); + if (user == null) + { + ModelState.AddModelError(nameof(model.UserId), "User Id not found."); + } + } + else if (model.OrganizationId.HasValue) + { + organization = await _organizationRepository.GetByIdAsync(model.OrganizationId.Value); + if (organization == null) + { + ModelState.AddModelError(nameof(model.OrganizationId), "Organization not found."); + } + else if (!organization.Enabled) + { + ModelState.AddModelError(nameof(model.OrganizationId), "Organization is disabled."); + } + } + if (model.InstallationId.HasValue) + { + var installation = await _installationRepository.GetByIdAsync(model.InstallationId.Value); + if (installation == null) + { + ModelState.AddModelError(nameof(model.InstallationId), "Installation not found."); + } + else if (!installation.Enabled) + { + ModelState.AddModelError(nameof(model.OrganizationId), "Installation is disabled."); + } + } + + if (!ModelState.IsValid) + { + return View(model); + } + + if (organization != null) + { + var license = await _organizationService.GenerateLicenseAsync(organization, + model.InstallationId.Value, model.Version); + return File(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(license, Formatting.Indented)), + "text/plain", "bitwarden_organization_license.json"); + } + else if (user != null) + { + var license = await _userService.GenerateLicenseAsync(user, null, model.Version); + return File(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(license, Formatting.Indented)), + "text/plain", "bitwarden_premium_license.json"); + } + else + { + throw new Exception("No license to generate."); + } + } } } diff --git a/src/Admin/Models/LicenseModel.cs b/src/Admin/Models/LicenseModel.cs new file mode 100644 index 0000000000..c8a598449f --- /dev/null +++ b/src/Admin/Models/LicenseModel.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace Bit.Admin.Models +{ + public class LicenseModel : IValidatableObject + { + [Display(Name = "User Id")] + public Guid? UserId { get; set; } + [Display(Name = "Organization Id")] + public Guid? OrganizationId { get; set; } + [Display(Name = "Installation Id")] + public Guid? InstallationId { get; set; } + [Required] + [Display(Name = "Version")] + public int Version { get; set; } + + public IEnumerable Validate(ValidationContext validationContext) + { + if (UserId.HasValue && OrganizationId.HasValue) + { + yield return new ValidationResult("Use either User Id or Organization Id. Not both."); + } + + if (!UserId.HasValue && !OrganizationId.HasValue) + { + yield return new ValidationResult("User Id or Organization Id is required."); + } + + if (OrganizationId.HasValue && !InstallationId.HasValue) + { + yield return new ValidationResult("Installation Id is required for organization licenses."); + } + } + } +} diff --git a/src/Admin/Views/Shared/_Layout.cshtml b/src/Admin/Views/Shared/_Layout.cshtml index c88c444dfe..aaa4079ffb 100644 --- a/src/Admin/Views/Shared/_Layout.cshtml +++ b/src/Admin/Views/Shared/_Layout.cshtml @@ -55,6 +55,9 @@ Promote Admin + + Generate License +