mirror of
https://github.com/bitwarden/server.git
synced 2025-02-23 03:01:23 +01:00
tool to generate licenses (#874)
* tool to generate licenses * code review feedback
This commit is contained in:
parent
c65c52d997
commit
2872bda6fe
@ -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<IActionResult> 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
37
src/Admin/Models/LicenseModel.cs
Normal file
37
src/Admin/Models/LicenseModel.cs
Normal file
@ -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<ValidationResult> 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -55,6 +55,9 @@
|
||||
<a class="dropdown-item" asp-controller="Tools" asp-action="PromoteAdmin">
|
||||
Promote Admin
|
||||
</a>
|
||||
<a class="dropdown-item" asp-controller="Tools" asp-action="GenerateLicense">
|
||||
Generate License
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item" active-controller="Logs">
|
||||
|
39
src/Admin/Views/Tools/GenerateLicense.cshtml
Normal file
39
src/Admin/Views/Tools/GenerateLicense.cshtml
Normal file
@ -0,0 +1,39 @@
|
||||
@model LicenseModel
|
||||
@{
|
||||
ViewData["Title"] = "Generate License File";
|
||||
}
|
||||
|
||||
<h1>Generate License File</h1>
|
||||
|
||||
<form method="post">
|
||||
<div asp-validation-summary="All" class="alert alert-danger"></div>
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<div class="form-group">
|
||||
<label asp-for="UserId"></label>
|
||||
<input type="text" class="form-control" asp-for="UserId">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md">
|
||||
<div class="form-group">
|
||||
<label asp-for="OrganizationId"></label>
|
||||
<input type="text" class="form-control" asp-for="OrganizationId">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<div class="form-group">
|
||||
<label asp-for="InstallationId"></label>
|
||||
<input type="text" class="form-control" asp-for="InstallationId">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md">
|
||||
<div class="form-group">
|
||||
<label asp-for="Version"></label>
|
||||
<input type="number" class="form-control" asp-for="Version">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary mb-2">Generate</button>
|
||||
</form>
|
@ -17,9 +17,9 @@ namespace Bit.Core.Models.Business
|
||||
{ }
|
||||
|
||||
public OrganizationLicense(Organization org, SubscriptionInfo subscriptionInfo, Guid installationId,
|
||||
ILicensingService licenseService)
|
||||
ILicensingService licenseService, int? version = null)
|
||||
{
|
||||
Version = 6; // TODO: bump to version 7
|
||||
Version = version.GetValueOrDefault(6); // TODO: bump to version 7
|
||||
LicenseKey = org.LicenseKey;
|
||||
InstallationId = installationId;
|
||||
Id = org.Id;
|
||||
|
@ -15,13 +15,14 @@ namespace Bit.Core.Models.Business
|
||||
public UserLicense()
|
||||
{ }
|
||||
|
||||
public UserLicense(User user, SubscriptionInfo subscriptionInfo, ILicensingService licenseService)
|
||||
public UserLicense(User user, SubscriptionInfo subscriptionInfo, ILicensingService licenseService,
|
||||
int? version = null)
|
||||
{
|
||||
LicenseKey = user.LicenseKey;
|
||||
Id = user.Id;
|
||||
Name = user.Name;
|
||||
Email = user.Email;
|
||||
Version = 1;
|
||||
Version = version.GetValueOrDefault(1);
|
||||
Premium = user.Premium;
|
||||
MaxStorageGb = user.MaxStorageGb;
|
||||
Issued = DateTime.UtcNow;
|
||||
@ -34,13 +35,13 @@ namespace Bit.Core.Models.Business
|
||||
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
|
||||
}
|
||||
|
||||
public UserLicense(User user, ILicensingService licenseService)
|
||||
public UserLicense(User user, ILicensingService licenseService, int? version = null)
|
||||
{
|
||||
LicenseKey = user.LicenseKey;
|
||||
Id = user.Id;
|
||||
Name = user.Name;
|
||||
Email = user.Email;
|
||||
Version = 1;
|
||||
Version = version.GetValueOrDefault(1);
|
||||
Premium = user.Premium;
|
||||
MaxStorageGb = user.MaxStorageGb;
|
||||
Issued = DateTime.UtcNow;
|
||||
|
@ -45,7 +45,8 @@ namespace Bit.Core.Services
|
||||
Task DeleteUserAsync(Guid organizationId, Guid userId);
|
||||
Task UpdateUserGroupsAsync(OrganizationUser organizationUser, IEnumerable<Guid> groupIds);
|
||||
Task<OrganizationLicense> GenerateLicenseAsync(Guid organizationId, Guid installationId);
|
||||
Task<OrganizationLicense> GenerateLicenseAsync(Organization organization, Guid installationId);
|
||||
Task<OrganizationLicense> GenerateLicenseAsync(Organization organization, Guid installationId,
|
||||
int? version = null);
|
||||
Task ImportAsync(Guid organizationId, Guid? importingUserId, IEnumerable<ImportedGroup> groups,
|
||||
IEnumerable<ImportedOrganizationUser> newUsers, IEnumerable<string> removeUserExternalIds,
|
||||
bool overwriteExisting);
|
||||
|
@ -61,7 +61,8 @@ namespace Bit.Core.Services
|
||||
Task DisablePremiumAsync(Guid userId, DateTime? expirationDate);
|
||||
Task DisablePremiumAsync(User user, DateTime? expirationDate);
|
||||
Task UpdatePremiumExpirationAsync(Guid userId, DateTime? expirationDate);
|
||||
Task<UserLicense> GenerateLicenseAsync(User user, SubscriptionInfo subscriptionInfo = null);
|
||||
Task<UserLicense> GenerateLicenseAsync(User user, SubscriptionInfo subscriptionInfo = null,
|
||||
int? version = null);
|
||||
Task<bool> CheckPasswordAsync(User user, string password);
|
||||
Task<bool> CanAccessPremium(ITwoFactorProvidersUser user);
|
||||
Task<bool> TwoFactorIsEnabledAsync(ITwoFactorProvidersUser user);
|
||||
|
@ -1298,7 +1298,8 @@ namespace Bit.Core.Services
|
||||
return await GenerateLicenseAsync(organization, installationId);
|
||||
}
|
||||
|
||||
public async Task<OrganizationLicense> GenerateLicenseAsync(Organization organization, Guid installationId)
|
||||
public async Task<OrganizationLicense> GenerateLicenseAsync(Organization organization, Guid installationId,
|
||||
int? version = null)
|
||||
{
|
||||
if (organization == null)
|
||||
{
|
||||
|
@ -985,7 +985,8 @@ namespace Bit.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<UserLicense> GenerateLicenseAsync(User user, SubscriptionInfo subscriptionInfo = null)
|
||||
public async Task<UserLicense> GenerateLicenseAsync(User user, SubscriptionInfo subscriptionInfo = null,
|
||||
int? version = null)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user