mirror of
https://github.com/bitwarden/server
synced 2026-01-02 16:43:25 +00:00
Run formatting (#2230)
This commit is contained in:
@@ -1,16 +1,15 @@
|
||||
namespace Bit.Admin
|
||||
{
|
||||
public class AdminSettings
|
||||
{
|
||||
public virtual string Admins { get; set; }
|
||||
public virtual CloudflareSettings Cloudflare { get; set; }
|
||||
public int? DeleteTrashDaysAgo { get; set; }
|
||||
namespace Bit.Admin;
|
||||
|
||||
public class CloudflareSettings
|
||||
{
|
||||
public string ZoneId { get; set; }
|
||||
public string AuthEmail { get; set; }
|
||||
public string AuthKey { get; set; }
|
||||
}
|
||||
public class AdminSettings
|
||||
{
|
||||
public virtual string Admins { get; set; }
|
||||
public virtual CloudflareSettings Cloudflare { get; set; }
|
||||
public int? DeleteTrashDaysAgo { get; set; }
|
||||
|
||||
public class CloudflareSettings
|
||||
{
|
||||
public string ZoneId { get; set; }
|
||||
public string AuthEmail { get; set; }
|
||||
public string AuthKey { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Admin.Controllers
|
||||
{
|
||||
public class ErrorController : Controller
|
||||
{
|
||||
[Route("/error")]
|
||||
public IActionResult Error(int? statusCode = null)
|
||||
{
|
||||
var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
|
||||
TempData["Error"] = HttpContext.Features.Get<IExceptionHandlerFeature>()?.Error.Message;
|
||||
namespace Bit.Admin.Controllers;
|
||||
|
||||
if (exceptionHandlerPathFeature != null)
|
||||
{
|
||||
return Redirect(exceptionHandlerPathFeature.Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Redirect("/Home");
|
||||
}
|
||||
public class ErrorController : Controller
|
||||
{
|
||||
[Route("/error")]
|
||||
public IActionResult Error(int? statusCode = null)
|
||||
{
|
||||
var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
|
||||
TempData["Error"] = HttpContext.Features.Get<IExceptionHandlerFeature>()?.Error.Message;
|
||||
|
||||
if (exceptionHandlerPathFeature != null)
|
||||
{
|
||||
return Redirect(exceptionHandlerPathFeature.Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Redirect("/Home");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,109 +6,108 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Admin.Controllers
|
||||
namespace Bit.Admin.Controllers;
|
||||
|
||||
public class HomeController : Controller
|
||||
{
|
||||
public class HomeController : Controller
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly HttpClient _httpClient = new HttpClient();
|
||||
private readonly ILogger<HomeController> _logger;
|
||||
|
||||
public HomeController(GlobalSettings globalSettings, ILogger<HomeController> logger)
|
||||
{
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly HttpClient _httpClient = new HttpClient();
|
||||
private readonly ILogger<HomeController> _logger;
|
||||
|
||||
public HomeController(GlobalSettings globalSettings, ILogger<HomeController> logger)
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View(new HomeModel
|
||||
{
|
||||
GlobalSettings = _globalSettings,
|
||||
CurrentVersion = Core.Utilities.CoreHelpers.GetVersion()
|
||||
});
|
||||
}
|
||||
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View(new ErrorViewModel
|
||||
{
|
||||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public async Task<IActionResult> GetLatestVersion(ProjectType project, CancellationToken cancellationToken)
|
||||
{
|
||||
var requestUri = $"https://selfhost.bitwarden.com/version.json";
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.GetAsync(requestUri, cancellationToken);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var latestVersions = JsonConvert.DeserializeObject<LatestVersions>(await response.Content.ReadAsStringAsync());
|
||||
return project switch
|
||||
{
|
||||
ProjectType.Core => new JsonResult(latestVersions.Versions.CoreVersion),
|
||||
ProjectType.Web => new JsonResult(latestVersions.Versions.WebVersion),
|
||||
_ => throw new System.NotImplementedException(),
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
_logger.LogError(e, $"Error encountered while sending GET request to {requestUri}");
|
||||
return new JsonResult("Unable to fetch latest version") { StatusCode = StatusCodes.Status500InternalServerError };
|
||||
}
|
||||
|
||||
return new JsonResult("-");
|
||||
}
|
||||
|
||||
public async Task<IActionResult> GetInstalledWebVersion(CancellationToken cancellationToken)
|
||||
{
|
||||
var requestUri = $"{_globalSettings.BaseServiceUri.InternalVault}/version.json";
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.GetAsync(requestUri, cancellationToken);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
using var jsonDocument = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync(cancellationToken), cancellationToken: cancellationToken);
|
||||
var root = jsonDocument.RootElement;
|
||||
return new JsonResult(root.GetProperty("version").GetString());
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
_logger.LogError(e, $"Error encountered while sending GET request to {requestUri}");
|
||||
return new JsonResult("Unable to fetch installed version") { StatusCode = StatusCodes.Status500InternalServerError };
|
||||
}
|
||||
|
||||
return new JsonResult("-");
|
||||
}
|
||||
|
||||
private class LatestVersions
|
||||
{
|
||||
[JsonProperty("versions")]
|
||||
public Versions Versions { get; set; }
|
||||
}
|
||||
|
||||
private class Versions
|
||||
{
|
||||
[JsonProperty("coreVersion")]
|
||||
public string CoreVersion { get; set; }
|
||||
|
||||
[JsonProperty("webVersion")]
|
||||
public string WebVersion { get; set; }
|
||||
|
||||
[JsonProperty("keyConnectorVersion")]
|
||||
public string KeyConnectorVersion { get; set; }
|
||||
}
|
||||
_globalSettings = globalSettings;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public enum ProjectType
|
||||
[Authorize]
|
||||
public IActionResult Index()
|
||||
{
|
||||
Core,
|
||||
Web,
|
||||
return View(new HomeModel
|
||||
{
|
||||
GlobalSettings = _globalSettings,
|
||||
CurrentVersion = Core.Utilities.CoreHelpers.GetVersion()
|
||||
});
|
||||
}
|
||||
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View(new ErrorViewModel
|
||||
{
|
||||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public async Task<IActionResult> GetLatestVersion(ProjectType project, CancellationToken cancellationToken)
|
||||
{
|
||||
var requestUri = $"https://selfhost.bitwarden.com/version.json";
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.GetAsync(requestUri, cancellationToken);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var latestVersions = JsonConvert.DeserializeObject<LatestVersions>(await response.Content.ReadAsStringAsync());
|
||||
return project switch
|
||||
{
|
||||
ProjectType.Core => new JsonResult(latestVersions.Versions.CoreVersion),
|
||||
ProjectType.Web => new JsonResult(latestVersions.Versions.WebVersion),
|
||||
_ => throw new System.NotImplementedException(),
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
_logger.LogError(e, $"Error encountered while sending GET request to {requestUri}");
|
||||
return new JsonResult("Unable to fetch latest version") { StatusCode = StatusCodes.Status500InternalServerError };
|
||||
}
|
||||
|
||||
return new JsonResult("-");
|
||||
}
|
||||
|
||||
public async Task<IActionResult> GetInstalledWebVersion(CancellationToken cancellationToken)
|
||||
{
|
||||
var requestUri = $"{_globalSettings.BaseServiceUri.InternalVault}/version.json";
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.GetAsync(requestUri, cancellationToken);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
using var jsonDocument = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync(cancellationToken), cancellationToken: cancellationToken);
|
||||
var root = jsonDocument.RootElement;
|
||||
return new JsonResult(root.GetProperty("version").GetString());
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
_logger.LogError(e, $"Error encountered while sending GET request to {requestUri}");
|
||||
return new JsonResult("Unable to fetch installed version") { StatusCode = StatusCodes.Status500InternalServerError };
|
||||
}
|
||||
|
||||
return new JsonResult("-");
|
||||
}
|
||||
|
||||
private class LatestVersions
|
||||
{
|
||||
[JsonProperty("versions")]
|
||||
public Versions Versions { get; set; }
|
||||
}
|
||||
|
||||
private class Versions
|
||||
{
|
||||
[JsonProperty("coreVersion")]
|
||||
public string CoreVersion { get; set; }
|
||||
|
||||
[JsonProperty("webVersion")]
|
||||
public string WebVersion { get; set; }
|
||||
|
||||
[JsonProperty("keyConnectorVersion")]
|
||||
public string KeyConnectorVersion { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public enum ProjectType
|
||||
{
|
||||
Core,
|
||||
Web,
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Admin.Controllers
|
||||
{
|
||||
public class InfoController : Controller
|
||||
{
|
||||
[HttpGet("~/alive")]
|
||||
[HttpGet("~/now")]
|
||||
public DateTime GetAlive()
|
||||
{
|
||||
return DateTime.UtcNow;
|
||||
}
|
||||
namespace Bit.Admin.Controllers;
|
||||
|
||||
[HttpGet("~/version")]
|
||||
public JsonResult GetVersion()
|
||||
{
|
||||
return Json(CoreHelpers.GetVersion());
|
||||
}
|
||||
public class InfoController : Controller
|
||||
{
|
||||
[HttpGet("~/alive")]
|
||||
[HttpGet("~/now")]
|
||||
public DateTime GetAlive()
|
||||
{
|
||||
return DateTime.UtcNow;
|
||||
}
|
||||
|
||||
[HttpGet("~/version")]
|
||||
public JsonResult GetVersion()
|
||||
{
|
||||
return Json(CoreHelpers.GetVersion());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,91 +3,90 @@ using Bit.Core.Identity;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Admin.Controllers
|
||||
namespace Bit.Admin.Controllers;
|
||||
|
||||
public class LoginController : Controller
|
||||
{
|
||||
public class LoginController : Controller
|
||||
private readonly PasswordlessSignInManager<IdentityUser> _signInManager;
|
||||
|
||||
public LoginController(
|
||||
PasswordlessSignInManager<IdentityUser> signInManager)
|
||||
{
|
||||
private readonly PasswordlessSignInManager<IdentityUser> _signInManager;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
public LoginController(
|
||||
PasswordlessSignInManager<IdentityUser> signInManager)
|
||||
public IActionResult Index(string returnUrl = null, int? error = null, int? success = null,
|
||||
bool accessDenied = false)
|
||||
{
|
||||
if (!error.HasValue && accessDenied)
|
||||
{
|
||||
_signInManager = signInManager;
|
||||
error = 4;
|
||||
}
|
||||
|
||||
public IActionResult Index(string returnUrl = null, int? error = null, int? success = null,
|
||||
bool accessDenied = false)
|
||||
return View(new LoginModel
|
||||
{
|
||||
if (!error.HasValue && accessDenied)
|
||||
{
|
||||
error = 4;
|
||||
}
|
||||
ReturnUrl = returnUrl,
|
||||
Error = GetMessage(error),
|
||||
Success = GetMessage(success)
|
||||
});
|
||||
}
|
||||
|
||||
return View(new LoginModel
|
||||
{
|
||||
ReturnUrl = returnUrl,
|
||||
Error = GetMessage(error),
|
||||
Success = GetMessage(success)
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Index(LoginModel model)
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Index(LoginModel model)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
await _signInManager.PasswordlessSignInAsync(model.Email, model.ReturnUrl);
|
||||
return RedirectToAction("Index", new
|
||||
{
|
||||
success = 3
|
||||
});
|
||||
}
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Confirm(string email, string token, string returnUrl)
|
||||
{
|
||||
var result = await _signInManager.PasswordlessSignInAsync(email, token, true);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return RedirectToAction("Index", new
|
||||
{
|
||||
error = 2
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(returnUrl) && Url.IsLocalUrl(returnUrl))
|
||||
{
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
await _signInManager.SignOutAsync();
|
||||
await _signInManager.PasswordlessSignInAsync(model.Email, model.ReturnUrl);
|
||||
return RedirectToAction("Index", new
|
||||
{
|
||||
success = 1
|
||||
success = 3
|
||||
});
|
||||
}
|
||||
|
||||
private string GetMessage(int? messageCode)
|
||||
return View(model);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Confirm(string email, string token, string returnUrl)
|
||||
{
|
||||
var result = await _signInManager.PasswordlessSignInAsync(email, token, true);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return messageCode switch
|
||||
return RedirectToAction("Index", new
|
||||
{
|
||||
1 => "You have been logged out.",
|
||||
2 => "This login confirmation link is invalid. Try logging in again.",
|
||||
3 => "If a valid admin user with this email address exists, " +
|
||||
"we've sent you an email with a secure link to log in.",
|
||||
4 => "Access denied. Please log in.",
|
||||
_ => null,
|
||||
};
|
||||
error = 2
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(returnUrl) && Url.IsLocalUrl(returnUrl))
|
||||
{
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
await _signInManager.SignOutAsync();
|
||||
return RedirectToAction("Index", new
|
||||
{
|
||||
success = 1
|
||||
});
|
||||
}
|
||||
|
||||
private string GetMessage(int? messageCode)
|
||||
{
|
||||
return messageCode switch
|
||||
{
|
||||
1 => "You have been logged out.",
|
||||
2 => "This login confirmation link is invalid. Try logging in again.",
|
||||
3 => "If a valid admin user with this email address exists, " +
|
||||
"we've sent you an email with a secure link to log in.",
|
||||
4 => "Access denied. Please log in.",
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,87 +7,86 @@ using Microsoft.Azure.Cosmos;
|
||||
using Microsoft.Azure.Cosmos.Linq;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace Bit.Admin.Controllers
|
||||
namespace Bit.Admin.Controllers;
|
||||
|
||||
[Authorize]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class LogsController : Controller
|
||||
{
|
||||
[Authorize]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class LogsController : Controller
|
||||
private const string Database = "Diagnostics";
|
||||
private const string Container = "Logs";
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public LogsController(GlobalSettings globalSettings)
|
||||
{
|
||||
private const string Database = "Diagnostics";
|
||||
private const string Container = "Logs";
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public LogsController(GlobalSettings globalSettings)
|
||||
public async Task<IActionResult> Index(string cursor = null, int count = 50,
|
||||
LogEventLevel? level = null, string project = null, DateTime? start = null, DateTime? end = null)
|
||||
{
|
||||
using (var client = new CosmosClient(_globalSettings.DocumentDb.Uri,
|
||||
_globalSettings.DocumentDb.Key))
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
var cosmosContainer = client.GetContainer(Database, Container);
|
||||
var query = cosmosContainer.GetItemLinqQueryable<LogModel>(
|
||||
requestOptions: new QueryRequestOptions()
|
||||
{
|
||||
MaxItemCount = count
|
||||
},
|
||||
continuationToken: cursor
|
||||
).AsQueryable();
|
||||
|
||||
public async Task<IActionResult> Index(string cursor = null, int count = 50,
|
||||
LogEventLevel? level = null, string project = null, DateTime? start = null, DateTime? end = null)
|
||||
{
|
||||
using (var client = new CosmosClient(_globalSettings.DocumentDb.Uri,
|
||||
_globalSettings.DocumentDb.Key))
|
||||
if (level.HasValue)
|
||||
{
|
||||
var cosmosContainer = client.GetContainer(Database, Container);
|
||||
var query = cosmosContainer.GetItemLinqQueryable<LogModel>(
|
||||
requestOptions: new QueryRequestOptions()
|
||||
{
|
||||
MaxItemCount = count
|
||||
},
|
||||
continuationToken: cursor
|
||||
).AsQueryable();
|
||||
|
||||
if (level.HasValue)
|
||||
{
|
||||
query = query.Where(l => l.Level == level.Value.ToString());
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(project))
|
||||
{
|
||||
query = query.Where(l => l.Properties != null && l.Properties["Project"] == (object)project);
|
||||
}
|
||||
if (start.HasValue)
|
||||
{
|
||||
query = query.Where(l => l.Timestamp >= start.Value);
|
||||
}
|
||||
if (end.HasValue)
|
||||
{
|
||||
query = query.Where(l => l.Timestamp <= end.Value);
|
||||
}
|
||||
var feedIterator = query.OrderByDescending(l => l.Timestamp).ToFeedIterator();
|
||||
var response = await feedIterator.ReadNextAsync();
|
||||
|
||||
return View(new LogsModel
|
||||
{
|
||||
Level = level,
|
||||
Project = project,
|
||||
Start = start,
|
||||
End = end,
|
||||
Items = response.ToList(),
|
||||
Count = count,
|
||||
Cursor = cursor,
|
||||
NextCursor = response.ContinuationToken
|
||||
});
|
||||
query = query.Where(l => l.Level == level.Value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IActionResult> View(Guid id)
|
||||
{
|
||||
using (var client = new CosmosClient(_globalSettings.DocumentDb.Uri,
|
||||
_globalSettings.DocumentDb.Key))
|
||||
if (!string.IsNullOrWhiteSpace(project))
|
||||
{
|
||||
var cosmosContainer = client.GetContainer(Database, Container);
|
||||
var query = cosmosContainer.GetItemLinqQueryable<LogDetailsModel>()
|
||||
.AsQueryable()
|
||||
.Where(l => l.Id == id.ToString());
|
||||
|
||||
var response = await query.ToFeedIterator().ReadNextAsync();
|
||||
if (response == null || response.Count == 0)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
return View(response.First());
|
||||
query = query.Where(l => l.Properties != null && l.Properties["Project"] == (object)project);
|
||||
}
|
||||
if (start.HasValue)
|
||||
{
|
||||
query = query.Where(l => l.Timestamp >= start.Value);
|
||||
}
|
||||
if (end.HasValue)
|
||||
{
|
||||
query = query.Where(l => l.Timestamp <= end.Value);
|
||||
}
|
||||
var feedIterator = query.OrderByDescending(l => l.Timestamp).ToFeedIterator();
|
||||
var response = await feedIterator.ReadNextAsync();
|
||||
|
||||
return View(new LogsModel
|
||||
{
|
||||
Level = level,
|
||||
Project = project,
|
||||
Start = start,
|
||||
End = end,
|
||||
Items = response.ToList(),
|
||||
Count = count,
|
||||
Cursor = cursor,
|
||||
NextCursor = response.ContinuationToken
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IActionResult> View(Guid id)
|
||||
{
|
||||
using (var client = new CosmosClient(_globalSettings.DocumentDb.Uri,
|
||||
_globalSettings.DocumentDb.Key))
|
||||
{
|
||||
var cosmosContainer = client.GetContainer(Database, Container);
|
||||
var query = cosmosContainer.GetItemLinqQueryable<LogDetailsModel>()
|
||||
.AsQueryable()
|
||||
.Where(l => l.Id == id.ToString());
|
||||
|
||||
var response = await query.ToFeedIterator().ReadNextAsync();
|
||||
if (response == null || response.Count == 0)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
return View(response.First());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,207 +11,206 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Admin.Controllers
|
||||
namespace Bit.Admin.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class OrganizationsController : Controller
|
||||
{
|
||||
[Authorize]
|
||||
public class OrganizationsController : Controller
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
|
||||
private readonly ISelfHostedSyncSponsorshipsCommand _syncSponsorshipsCommand;
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
private readonly IGroupRepository _groupRepository;
|
||||
private readonly IPolicyRepository _policyRepository;
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly ILicensingService _licensingService;
|
||||
private readonly IApplicationCacheService _applicationCacheService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IReferenceEventService _referenceEventService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ILogger<OrganizationsController> _logger;
|
||||
|
||||
public OrganizationsController(
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IOrganizationConnectionRepository organizationConnectionRepository,
|
||||
ISelfHostedSyncSponsorshipsCommand syncSponsorshipsCommand,
|
||||
ICipherRepository cipherRepository,
|
||||
ICollectionRepository collectionRepository,
|
||||
IGroupRepository groupRepository,
|
||||
IPolicyRepository policyRepository,
|
||||
IPaymentService paymentService,
|
||||
ILicensingService licensingService,
|
||||
IApplicationCacheService applicationCacheService,
|
||||
GlobalSettings globalSettings,
|
||||
IReferenceEventService referenceEventService,
|
||||
IUserService userService,
|
||||
ILogger<OrganizationsController> logger)
|
||||
{
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
|
||||
private readonly ISelfHostedSyncSponsorshipsCommand _syncSponsorshipsCommand;
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
private readonly IGroupRepository _groupRepository;
|
||||
private readonly IPolicyRepository _policyRepository;
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly ILicensingService _licensingService;
|
||||
private readonly IApplicationCacheService _applicationCacheService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IReferenceEventService _referenceEventService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ILogger<OrganizationsController> _logger;
|
||||
|
||||
public OrganizationsController(
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IOrganizationConnectionRepository organizationConnectionRepository,
|
||||
ISelfHostedSyncSponsorshipsCommand syncSponsorshipsCommand,
|
||||
ICipherRepository cipherRepository,
|
||||
ICollectionRepository collectionRepository,
|
||||
IGroupRepository groupRepository,
|
||||
IPolicyRepository policyRepository,
|
||||
IPaymentService paymentService,
|
||||
ILicensingService licensingService,
|
||||
IApplicationCacheService applicationCacheService,
|
||||
GlobalSettings globalSettings,
|
||||
IReferenceEventService referenceEventService,
|
||||
IUserService userService,
|
||||
ILogger<OrganizationsController> logger)
|
||||
{
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_organizationConnectionRepository = organizationConnectionRepository;
|
||||
_syncSponsorshipsCommand = syncSponsorshipsCommand;
|
||||
_cipherRepository = cipherRepository;
|
||||
_collectionRepository = collectionRepository;
|
||||
_groupRepository = groupRepository;
|
||||
_policyRepository = policyRepository;
|
||||
_paymentService = paymentService;
|
||||
_licensingService = licensingService;
|
||||
_applicationCacheService = applicationCacheService;
|
||||
_globalSettings = globalSettings;
|
||||
_referenceEventService = referenceEventService;
|
||||
_userService = userService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index(string name = null, string userEmail = null, bool? paid = null,
|
||||
int page = 1, int count = 25)
|
||||
{
|
||||
if (page < 1)
|
||||
{
|
||||
page = 1;
|
||||
}
|
||||
|
||||
if (count < 1)
|
||||
{
|
||||
count = 1;
|
||||
}
|
||||
|
||||
var skip = (page - 1) * count;
|
||||
var organizations = await _organizationRepository.SearchAsync(name, userEmail, paid, skip, count);
|
||||
return View(new OrganizationsModel
|
||||
{
|
||||
Items = organizations as List<Organization>,
|
||||
Name = string.IsNullOrWhiteSpace(name) ? null : name,
|
||||
UserEmail = string.IsNullOrWhiteSpace(userEmail) ? null : userEmail,
|
||||
Paid = paid,
|
||||
Page = page,
|
||||
Count = count,
|
||||
Action = _globalSettings.SelfHosted ? "View" : "Edit",
|
||||
SelfHosted = _globalSettings.SelfHosted
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IActionResult> View(Guid id)
|
||||
{
|
||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||
if (organization == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
var ciphers = await _cipherRepository.GetManyByOrganizationIdAsync(id);
|
||||
var collections = await _collectionRepository.GetManyByOrganizationIdAsync(id);
|
||||
IEnumerable<Group> groups = null;
|
||||
if (organization.UseGroups)
|
||||
{
|
||||
groups = await _groupRepository.GetManyByOrganizationIdAsync(id);
|
||||
}
|
||||
IEnumerable<Policy> policies = null;
|
||||
if (organization.UsePolicies)
|
||||
{
|
||||
policies = await _policyRepository.GetManyByOrganizationIdAsync(id);
|
||||
}
|
||||
var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id);
|
||||
var billingSyncConnection = _globalSettings.EnableCloudCommunication ? await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync) : null;
|
||||
return View(new OrganizationViewModel(organization, billingSyncConnection, users, ciphers, collections, groups, policies));
|
||||
}
|
||||
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<IActionResult> Edit(Guid id)
|
||||
{
|
||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||
if (organization == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
var ciphers = await _cipherRepository.GetManyByOrganizationIdAsync(id);
|
||||
var collections = await _collectionRepository.GetManyByOrganizationIdAsync(id);
|
||||
IEnumerable<Group> groups = null;
|
||||
if (organization.UseGroups)
|
||||
{
|
||||
groups = await _groupRepository.GetManyByOrganizationIdAsync(id);
|
||||
}
|
||||
IEnumerable<Policy> policies = null;
|
||||
if (organization.UsePolicies)
|
||||
{
|
||||
policies = await _policyRepository.GetManyByOrganizationIdAsync(id);
|
||||
}
|
||||
var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id);
|
||||
var billingInfo = await _paymentService.GetBillingAsync(organization);
|
||||
var billingSyncConnection = _globalSettings.EnableCloudCommunication ? await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync) : null;
|
||||
return View(new OrganizationEditModel(organization, users, ciphers, collections, groups, policies,
|
||||
billingInfo, billingSyncConnection, _globalSettings));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<IActionResult> Edit(Guid id, OrganizationEditModel model)
|
||||
{
|
||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||
model.ToOrganization(organization);
|
||||
await _organizationRepository.ReplaceAsync(organization);
|
||||
await _applicationCacheService.UpsertOrganizationAbilityAsync(organization);
|
||||
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.OrganizationEditedByAdmin, organization)
|
||||
{
|
||||
EventRaisedByUser = _userService.GetUserName(User),
|
||||
SalesAssistedTrialStarted = model.SalesAssistedTrialStarted,
|
||||
});
|
||||
return RedirectToAction("Edit", new { id });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Delete(Guid id)
|
||||
{
|
||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||
if (organization != null)
|
||||
{
|
||||
await _organizationRepository.DeleteAsync(organization);
|
||||
await _applicationCacheService.DeleteOrganizationAbilityAsync(organization.Id);
|
||||
}
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
public async Task<IActionResult> TriggerBillingSync(Guid id)
|
||||
{
|
||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||
if (organization == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
var connection = (await _organizationConnectionRepository.GetEnabledByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync)).FirstOrDefault();
|
||||
if (connection != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = connection.GetConfig<BillingSyncConfig>();
|
||||
await _syncSponsorshipsCommand.SyncOrganization(id, config.CloudOrganizationId, connection);
|
||||
TempData["ConnectionActivated"] = id;
|
||||
TempData["ConnectionError"] = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData["ConnectionError"] = ex.Message;
|
||||
_logger.LogWarning(ex, "Error while attempting to do billing sync for organization with id '{OrganizationId}'", id);
|
||||
}
|
||||
|
||||
if (_globalSettings.SelfHosted)
|
||||
{
|
||||
return RedirectToAction("View", new { id });
|
||||
}
|
||||
else
|
||||
{
|
||||
return RedirectToAction("Edit", new { id });
|
||||
}
|
||||
}
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_organizationConnectionRepository = organizationConnectionRepository;
|
||||
_syncSponsorshipsCommand = syncSponsorshipsCommand;
|
||||
_cipherRepository = cipherRepository;
|
||||
_collectionRepository = collectionRepository;
|
||||
_groupRepository = groupRepository;
|
||||
_policyRepository = policyRepository;
|
||||
_paymentService = paymentService;
|
||||
_licensingService = licensingService;
|
||||
_applicationCacheService = applicationCacheService;
|
||||
_globalSettings = globalSettings;
|
||||
_referenceEventService = referenceEventService;
|
||||
_userService = userService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index(string name = null, string userEmail = null, bool? paid = null,
|
||||
int page = 1, int count = 25)
|
||||
{
|
||||
if (page < 1)
|
||||
{
|
||||
page = 1;
|
||||
}
|
||||
|
||||
if (count < 1)
|
||||
{
|
||||
count = 1;
|
||||
}
|
||||
|
||||
var skip = (page - 1) * count;
|
||||
var organizations = await _organizationRepository.SearchAsync(name, userEmail, paid, skip, count);
|
||||
return View(new OrganizationsModel
|
||||
{
|
||||
Items = organizations as List<Organization>,
|
||||
Name = string.IsNullOrWhiteSpace(name) ? null : name,
|
||||
UserEmail = string.IsNullOrWhiteSpace(userEmail) ? null : userEmail,
|
||||
Paid = paid,
|
||||
Page = page,
|
||||
Count = count,
|
||||
Action = _globalSettings.SelfHosted ? "View" : "Edit",
|
||||
SelfHosted = _globalSettings.SelfHosted
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IActionResult> View(Guid id)
|
||||
{
|
||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||
if (organization == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
var ciphers = await _cipherRepository.GetManyByOrganizationIdAsync(id);
|
||||
var collections = await _collectionRepository.GetManyByOrganizationIdAsync(id);
|
||||
IEnumerable<Group> groups = null;
|
||||
if (organization.UseGroups)
|
||||
{
|
||||
groups = await _groupRepository.GetManyByOrganizationIdAsync(id);
|
||||
}
|
||||
IEnumerable<Policy> policies = null;
|
||||
if (organization.UsePolicies)
|
||||
{
|
||||
policies = await _policyRepository.GetManyByOrganizationIdAsync(id);
|
||||
}
|
||||
var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id);
|
||||
var billingSyncConnection = _globalSettings.EnableCloudCommunication ? await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync) : null;
|
||||
return View(new OrganizationViewModel(organization, billingSyncConnection, users, ciphers, collections, groups, policies));
|
||||
}
|
||||
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<IActionResult> Edit(Guid id)
|
||||
{
|
||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||
if (organization == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
var ciphers = await _cipherRepository.GetManyByOrganizationIdAsync(id);
|
||||
var collections = await _collectionRepository.GetManyByOrganizationIdAsync(id);
|
||||
IEnumerable<Group> groups = null;
|
||||
if (organization.UseGroups)
|
||||
{
|
||||
groups = await _groupRepository.GetManyByOrganizationIdAsync(id);
|
||||
}
|
||||
IEnumerable<Policy> policies = null;
|
||||
if (organization.UsePolicies)
|
||||
{
|
||||
policies = await _policyRepository.GetManyByOrganizationIdAsync(id);
|
||||
}
|
||||
var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id);
|
||||
var billingInfo = await _paymentService.GetBillingAsync(organization);
|
||||
var billingSyncConnection = _globalSettings.EnableCloudCommunication ? await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync) : null;
|
||||
return View(new OrganizationEditModel(organization, users, ciphers, collections, groups, policies,
|
||||
billingInfo, billingSyncConnection, _globalSettings));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<IActionResult> Edit(Guid id, OrganizationEditModel model)
|
||||
{
|
||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||
model.ToOrganization(organization);
|
||||
await _organizationRepository.ReplaceAsync(organization);
|
||||
await _applicationCacheService.UpsertOrganizationAbilityAsync(organization);
|
||||
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.OrganizationEditedByAdmin, organization)
|
||||
{
|
||||
EventRaisedByUser = _userService.GetUserName(User),
|
||||
SalesAssistedTrialStarted = model.SalesAssistedTrialStarted,
|
||||
});
|
||||
return RedirectToAction("Edit", new { id });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Delete(Guid id)
|
||||
{
|
||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||
if (organization != null)
|
||||
{
|
||||
await _organizationRepository.DeleteAsync(organization);
|
||||
await _applicationCacheService.DeleteOrganizationAbilityAsync(organization.Id);
|
||||
}
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
public async Task<IActionResult> TriggerBillingSync(Guid id)
|
||||
{
|
||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||
if (organization == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
var connection = (await _organizationConnectionRepository.GetEnabledByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync)).FirstOrDefault();
|
||||
if (connection != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = connection.GetConfig<BillingSyncConfig>();
|
||||
await _syncSponsorshipsCommand.SyncOrganization(id, config.CloudOrganizationId, connection);
|
||||
TempData["ConnectionActivated"] = id;
|
||||
TempData["ConnectionError"] = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData["ConnectionError"] = ex.Message;
|
||||
_logger.LogWarning(ex, "Error while attempting to do billing sync for organization with id '{OrganizationId}'", id);
|
||||
}
|
||||
|
||||
if (_globalSettings.SelfHosted)
|
||||
{
|
||||
return RedirectToAction("View", new { id });
|
||||
}
|
||||
else
|
||||
{
|
||||
return RedirectToAction("Edit", new { id });
|
||||
}
|
||||
}
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,128 +7,127 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Admin.Controllers
|
||||
namespace Bit.Admin.Controllers;
|
||||
|
||||
[Authorize]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class ProvidersController : Controller
|
||||
{
|
||||
[Authorize]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class ProvidersController : Controller
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IApplicationCacheService _applicationCacheService;
|
||||
private readonly IProviderService _providerService;
|
||||
|
||||
public ProvidersController(IProviderRepository providerRepository, IProviderUserRepository providerUserRepository,
|
||||
IProviderOrganizationRepository providerOrganizationRepository, IProviderService providerService,
|
||||
GlobalSettings globalSettings, IApplicationCacheService applicationCacheService)
|
||||
{
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IApplicationCacheService _applicationCacheService;
|
||||
private readonly IProviderService _providerService;
|
||||
_providerRepository = providerRepository;
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_providerOrganizationRepository = providerOrganizationRepository;
|
||||
_providerService = providerService;
|
||||
_globalSettings = globalSettings;
|
||||
_applicationCacheService = applicationCacheService;
|
||||
}
|
||||
|
||||
public ProvidersController(IProviderRepository providerRepository, IProviderUserRepository providerUserRepository,
|
||||
IProviderOrganizationRepository providerOrganizationRepository, IProviderService providerService,
|
||||
GlobalSettings globalSettings, IApplicationCacheService applicationCacheService)
|
||||
public async Task<IActionResult> Index(string name = null, string userEmail = null, int page = 1, int count = 25)
|
||||
{
|
||||
if (page < 1)
|
||||
{
|
||||
_providerRepository = providerRepository;
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_providerOrganizationRepository = providerOrganizationRepository;
|
||||
_providerService = providerService;
|
||||
_globalSettings = globalSettings;
|
||||
_applicationCacheService = applicationCacheService;
|
||||
page = 1;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index(string name = null, string userEmail = null, int page = 1, int count = 25)
|
||||
if (count < 1)
|
||||
{
|
||||
if (page < 1)
|
||||
{
|
||||
page = 1;
|
||||
}
|
||||
|
||||
if (count < 1)
|
||||
{
|
||||
count = 1;
|
||||
}
|
||||
|
||||
var skip = (page - 1) * count;
|
||||
var providers = await _providerRepository.SearchAsync(name, userEmail, skip, count);
|
||||
return View(new ProvidersModel
|
||||
{
|
||||
Items = providers as List<Provider>,
|
||||
Name = string.IsNullOrWhiteSpace(name) ? null : name,
|
||||
UserEmail = string.IsNullOrWhiteSpace(userEmail) ? null : userEmail,
|
||||
Page = page,
|
||||
Count = count,
|
||||
Action = _globalSettings.SelfHosted ? "View" : "Edit",
|
||||
SelfHosted = _globalSettings.SelfHosted
|
||||
});
|
||||
count = 1;
|
||||
}
|
||||
|
||||
public IActionResult Create(string ownerEmail = null)
|
||||
var skip = (page - 1) * count;
|
||||
var providers = await _providerRepository.SearchAsync(name, userEmail, skip, count);
|
||||
return View(new ProvidersModel
|
||||
{
|
||||
return View(new CreateProviderModel
|
||||
{
|
||||
OwnerEmail = ownerEmail
|
||||
});
|
||||
Items = providers as List<Provider>,
|
||||
Name = string.IsNullOrWhiteSpace(name) ? null : name,
|
||||
UserEmail = string.IsNullOrWhiteSpace(userEmail) ? null : userEmail,
|
||||
Page = page,
|
||||
Count = count,
|
||||
Action = _globalSettings.SelfHosted ? "View" : "Edit",
|
||||
SelfHosted = _globalSettings.SelfHosted
|
||||
});
|
||||
}
|
||||
|
||||
public IActionResult Create(string ownerEmail = null)
|
||||
{
|
||||
return View(new CreateProviderModel
|
||||
{
|
||||
OwnerEmail = ownerEmail
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create(CreateProviderModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create(CreateProviderModel model)
|
||||
await _providerService.CreateAsync(model.OwnerEmail);
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
public async Task<IActionResult> View(Guid id)
|
||||
{
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider == null)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
await _providerService.CreateAsync(model.OwnerEmail);
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
public async Task<IActionResult> View(Guid id)
|
||||
{
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
var users = await _providerUserRepository.GetManyDetailsByProviderAsync(id);
|
||||
var providerOrganizations = await _providerOrganizationRepository.GetManyDetailsByProviderAsync(id);
|
||||
return View(new ProviderViewModel(provider, users, providerOrganizations));
|
||||
}
|
||||
|
||||
var users = await _providerUserRepository.GetManyDetailsByProviderAsync(id);
|
||||
var providerOrganizations = await _providerOrganizationRepository.GetManyDetailsByProviderAsync(id);
|
||||
return View(new ProviderViewModel(provider, users, providerOrganizations));
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<IActionResult> Edit(Guid id)
|
||||
{
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<IActionResult> Edit(Guid id)
|
||||
{
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
var users = await _providerUserRepository.GetManyDetailsByProviderAsync(id);
|
||||
var providerOrganizations = await _providerOrganizationRepository.GetManyDetailsByProviderAsync(id);
|
||||
return View(new ProviderEditModel(provider, users, providerOrganizations));
|
||||
}
|
||||
|
||||
var users = await _providerUserRepository.GetManyDetailsByProviderAsync(id);
|
||||
var providerOrganizations = await _providerOrganizationRepository.GetManyDetailsByProviderAsync(id);
|
||||
return View(new ProviderEditModel(provider, users, providerOrganizations));
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<IActionResult> Edit(Guid id, ProviderEditModel model)
|
||||
{
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<IActionResult> Edit(Guid id, ProviderEditModel model)
|
||||
{
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
model.ToProvider(provider);
|
||||
await _providerRepository.ReplaceAsync(provider);
|
||||
await _applicationCacheService.UpsertProviderAbilityAsync(provider);
|
||||
return RedirectToAction("Edit", new { id });
|
||||
}
|
||||
|
||||
model.ToProvider(provider);
|
||||
await _providerRepository.ReplaceAsync(provider);
|
||||
await _applicationCacheService.UpsertProviderAbilityAsync(provider);
|
||||
return RedirectToAction("Edit", new { id });
|
||||
}
|
||||
|
||||
public async Task<IActionResult> ResendInvite(Guid ownerId, Guid providerId)
|
||||
{
|
||||
await _providerService.ResendProviderSetupInviteEmailAsync(providerId, ownerId);
|
||||
TempData["InviteResentTo"] = ownerId;
|
||||
return RedirectToAction("Edit", new { id = providerId });
|
||||
}
|
||||
public async Task<IActionResult> ResendInvite(Guid ownerId, Guid providerId)
|
||||
{
|
||||
await _providerService.ResendProviderSetupInviteEmailAsync(providerId, ownerId);
|
||||
TempData["InviteResentTo"] = ownerId;
|
||||
return RedirectToAction("Edit", new { id = providerId });
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,105 +7,104 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Admin.Controllers
|
||||
namespace Bit.Admin.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class UsersController : Controller
|
||||
{
|
||||
[Authorize]
|
||||
public class UsersController : Controller
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public UsersController(
|
||||
IUserRepository userRepository,
|
||||
ICipherRepository cipherRepository,
|
||||
IPaymentService paymentService,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
_userRepository = userRepository;
|
||||
_cipherRepository = cipherRepository;
|
||||
_paymentService = paymentService;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
public UsersController(
|
||||
IUserRepository userRepository,
|
||||
ICipherRepository cipherRepository,
|
||||
IPaymentService paymentService,
|
||||
GlobalSettings globalSettings)
|
||||
public async Task<IActionResult> Index(string email, int page = 1, int count = 25)
|
||||
{
|
||||
if (page < 1)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
_cipherRepository = cipherRepository;
|
||||
_paymentService = paymentService;
|
||||
_globalSettings = globalSettings;
|
||||
page = 1;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index(string email, int page = 1, int count = 25)
|
||||
if (count < 1)
|
||||
{
|
||||
if (page < 1)
|
||||
{
|
||||
page = 1;
|
||||
}
|
||||
|
||||
if (count < 1)
|
||||
{
|
||||
count = 1;
|
||||
}
|
||||
|
||||
var skip = (page - 1) * count;
|
||||
var users = await _userRepository.SearchAsync(email, skip, count);
|
||||
return View(new UsersModel
|
||||
{
|
||||
Items = users as List<User>,
|
||||
Email = string.IsNullOrWhiteSpace(email) ? null : email,
|
||||
Page = page,
|
||||
Count = count,
|
||||
Action = _globalSettings.SelfHosted ? "View" : "Edit"
|
||||
});
|
||||
count = 1;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> View(Guid id)
|
||||
var skip = (page - 1) * count;
|
||||
var users = await _userRepository.SearchAsync(email, skip, count);
|
||||
return View(new UsersModel
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(id);
|
||||
if (user == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
Items = users as List<User>,
|
||||
Email = string.IsNullOrWhiteSpace(email) ? null : email,
|
||||
Page = page,
|
||||
Count = count,
|
||||
Action = _globalSettings.SelfHosted ? "View" : "Edit"
|
||||
});
|
||||
}
|
||||
|
||||
var ciphers = await _cipherRepository.GetManyByUserIdAsync(id);
|
||||
return View(new UserViewModel(user, ciphers));
|
||||
}
|
||||
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<IActionResult> Edit(Guid id)
|
||||
public async Task<IActionResult> View(Guid id)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(id);
|
||||
if (user == null)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(id);
|
||||
if (user == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
var ciphers = await _cipherRepository.GetManyByUserIdAsync(id);
|
||||
var billingInfo = await _paymentService.GetBillingAsync(user);
|
||||
return View(new UserEditModel(user, ciphers, billingInfo, _globalSettings));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<IActionResult> Edit(Guid id, UserEditModel model)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(id);
|
||||
if (user == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
model.ToUser(user);
|
||||
await _userRepository.ReplaceAsync(user);
|
||||
return RedirectToAction("Edit", new { id });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Delete(Guid id)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(id);
|
||||
if (user != null)
|
||||
{
|
||||
await _userRepository.DeleteAsync(user);
|
||||
}
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
var ciphers = await _cipherRepository.GetManyByUserIdAsync(id);
|
||||
return View(new UserViewModel(user, ciphers));
|
||||
}
|
||||
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<IActionResult> Edit(Guid id)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(id);
|
||||
if (user == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
var ciphers = await _cipherRepository.GetManyByUserIdAsync(id);
|
||||
var billingInfo = await _paymentService.GetBillingAsync(user);
|
||||
return View(new UserEditModel(user, ciphers, billingInfo, _globalSettings));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<IActionResult> Edit(Guid id, UserEditModel model)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(id);
|
||||
if (user == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
model.ToUser(user);
|
||||
await _userRepository.ReplaceAsync(user);
|
||||
return RedirectToAction("Edit", new { id });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Delete(Guid id)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(id);
|
||||
if (user != null)
|
||||
{
|
||||
await _userRepository.DeleteAsync(user);
|
||||
}
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,81 +4,80 @@ using Amazon.SQS.Model;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Bit.Admin.HostedServices
|
||||
namespace Bit.Admin.HostedServices;
|
||||
|
||||
public class AmazonSqsBlockIpHostedService : BlockIpHostedService
|
||||
{
|
||||
public class AmazonSqsBlockIpHostedService : BlockIpHostedService
|
||||
private AmazonSQSClient _client;
|
||||
|
||||
public AmazonSqsBlockIpHostedService(
|
||||
ILogger<AmazonSqsBlockIpHostedService> logger,
|
||||
IOptions<AdminSettings> adminSettings,
|
||||
GlobalSettings globalSettings)
|
||||
: base(logger, adminSettings, globalSettings)
|
||||
{ }
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
private AmazonSQSClient _client;
|
||||
_client?.Dispose();
|
||||
}
|
||||
|
||||
public AmazonSqsBlockIpHostedService(
|
||||
ILogger<AmazonSqsBlockIpHostedService> logger,
|
||||
IOptions<AdminSettings> adminSettings,
|
||||
GlobalSettings globalSettings)
|
||||
: base(logger, adminSettings, globalSettings)
|
||||
{ }
|
||||
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_client = new AmazonSQSClient(_globalSettings.Amazon.AccessKeyId,
|
||||
_globalSettings.Amazon.AccessKeySecret, RegionEndpoint.GetBySystemName(_globalSettings.Amazon.Region));
|
||||
var blockIpQueue = await _client.GetQueueUrlAsync("block-ip", cancellationToken);
|
||||
var blockIpQueueUrl = blockIpQueue.QueueUrl;
|
||||
var unblockIpQueue = await _client.GetQueueUrlAsync("unblock-ip", cancellationToken);
|
||||
var unblockIpQueueUrl = unblockIpQueue.QueueUrl;
|
||||
|
||||
public override void Dispose()
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
_client?.Dispose();
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_client = new AmazonSQSClient(_globalSettings.Amazon.AccessKeyId,
|
||||
_globalSettings.Amazon.AccessKeySecret, RegionEndpoint.GetBySystemName(_globalSettings.Amazon.Region));
|
||||
var blockIpQueue = await _client.GetQueueUrlAsync("block-ip", cancellationToken);
|
||||
var blockIpQueueUrl = blockIpQueue.QueueUrl;
|
||||
var unblockIpQueue = await _client.GetQueueUrlAsync("unblock-ip", cancellationToken);
|
||||
var unblockIpQueueUrl = unblockIpQueue.QueueUrl;
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
var blockMessageResponse = await _client.ReceiveMessageAsync(new ReceiveMessageRequest
|
||||
{
|
||||
var blockMessageResponse = await _client.ReceiveMessageAsync(new ReceiveMessageRequest
|
||||
QueueUrl = blockIpQueueUrl,
|
||||
MaxNumberOfMessages = 10,
|
||||
WaitTimeSeconds = 15
|
||||
}, cancellationToken);
|
||||
if (blockMessageResponse.Messages.Any())
|
||||
{
|
||||
foreach (var message in blockMessageResponse.Messages)
|
||||
{
|
||||
QueueUrl = blockIpQueueUrl,
|
||||
MaxNumberOfMessages = 10,
|
||||
WaitTimeSeconds = 15
|
||||
}, cancellationToken);
|
||||
if (blockMessageResponse.Messages.Any())
|
||||
{
|
||||
foreach (var message in blockMessageResponse.Messages)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
await BlockIpAsync(message.Body, cancellationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Failed to block IP.");
|
||||
}
|
||||
await _client.DeleteMessageAsync(blockIpQueueUrl, message.ReceiptHandle, cancellationToken);
|
||||
await BlockIpAsync(message.Body, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
var unblockMessageResponse = await _client.ReceiveMessageAsync(new ReceiveMessageRequest
|
||||
{
|
||||
QueueUrl = unblockIpQueueUrl,
|
||||
MaxNumberOfMessages = 10,
|
||||
WaitTimeSeconds = 15
|
||||
}, cancellationToken);
|
||||
if (unblockMessageResponse.Messages.Any())
|
||||
{
|
||||
foreach (var message in unblockMessageResponse.Messages)
|
||||
catch (Exception e)
|
||||
{
|
||||
try
|
||||
{
|
||||
await UnblockIpAsync(message.Body, cancellationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Failed to unblock IP.");
|
||||
}
|
||||
await _client.DeleteMessageAsync(unblockIpQueueUrl, message.ReceiptHandle, cancellationToken);
|
||||
_logger.LogError(e, "Failed to block IP.");
|
||||
}
|
||||
await _client.DeleteMessageAsync(blockIpQueueUrl, message.ReceiptHandle, cancellationToken);
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(15));
|
||||
}
|
||||
|
||||
var unblockMessageResponse = await _client.ReceiveMessageAsync(new ReceiveMessageRequest
|
||||
{
|
||||
QueueUrl = unblockIpQueueUrl,
|
||||
MaxNumberOfMessages = 10,
|
||||
WaitTimeSeconds = 15
|
||||
}, cancellationToken);
|
||||
if (unblockMessageResponse.Messages.Any())
|
||||
{
|
||||
foreach (var message in unblockMessageResponse.Messages)
|
||||
{
|
||||
try
|
||||
{
|
||||
await UnblockIpAsync(message.Body, cancellationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Failed to unblock IP.");
|
||||
}
|
||||
await _client.DeleteMessageAsync(unblockIpQueueUrl, message.ReceiptHandle, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(15));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,63 +2,62 @@
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Bit.Admin.HostedServices
|
||||
namespace Bit.Admin.HostedServices;
|
||||
|
||||
public class AzureQueueBlockIpHostedService : BlockIpHostedService
|
||||
{
|
||||
public class AzureQueueBlockIpHostedService : BlockIpHostedService
|
||||
private QueueClient _blockIpQueueClient;
|
||||
private QueueClient _unblockIpQueueClient;
|
||||
|
||||
public AzureQueueBlockIpHostedService(
|
||||
ILogger<AzureQueueBlockIpHostedService> logger,
|
||||
IOptions<AdminSettings> adminSettings,
|
||||
GlobalSettings globalSettings)
|
||||
: base(logger, adminSettings, globalSettings)
|
||||
{ }
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
private QueueClient _blockIpQueueClient;
|
||||
private QueueClient _unblockIpQueueClient;
|
||||
_blockIpQueueClient = new QueueClient(_globalSettings.Storage.ConnectionString, "blockip");
|
||||
_unblockIpQueueClient = new QueueClient(_globalSettings.Storage.ConnectionString, "unblockip");
|
||||
|
||||
public AzureQueueBlockIpHostedService(
|
||||
ILogger<AzureQueueBlockIpHostedService> logger,
|
||||
IOptions<AdminSettings> adminSettings,
|
||||
GlobalSettings globalSettings)
|
||||
: base(logger, adminSettings, globalSettings)
|
||||
{ }
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
_blockIpQueueClient = new QueueClient(_globalSettings.Storage.ConnectionString, "blockip");
|
||||
_unblockIpQueueClient = new QueueClient(_globalSettings.Storage.ConnectionString, "unblockip");
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
var blockMessages = await _blockIpQueueClient.ReceiveMessagesAsync(maxMessages: 32);
|
||||
if (blockMessages.Value?.Any() ?? false)
|
||||
{
|
||||
var blockMessages = await _blockIpQueueClient.ReceiveMessagesAsync(maxMessages: 32);
|
||||
if (blockMessages.Value?.Any() ?? false)
|
||||
foreach (var message in blockMessages.Value)
|
||||
{
|
||||
foreach (var message in blockMessages.Value)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
await BlockIpAsync(message.MessageText, cancellationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Failed to block IP.");
|
||||
}
|
||||
await _blockIpQueueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt);
|
||||
await BlockIpAsync(message.MessageText, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
var unblockMessages = await _unblockIpQueueClient.ReceiveMessagesAsync(maxMessages: 32);
|
||||
if (unblockMessages.Value?.Any() ?? false)
|
||||
{
|
||||
foreach (var message in unblockMessages.Value)
|
||||
catch (Exception e)
|
||||
{
|
||||
try
|
||||
{
|
||||
await UnblockIpAsync(message.MessageText, cancellationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Failed to unblock IP.");
|
||||
}
|
||||
await _unblockIpQueueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt);
|
||||
_logger.LogError(e, "Failed to block IP.");
|
||||
}
|
||||
await _blockIpQueueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt);
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(15));
|
||||
}
|
||||
|
||||
var unblockMessages = await _unblockIpQueueClient.ReceiveMessagesAsync(maxMessages: 32);
|
||||
if (unblockMessages.Value?.Any() ?? false)
|
||||
{
|
||||
foreach (var message in unblockMessages.Value)
|
||||
{
|
||||
try
|
||||
{
|
||||
await UnblockIpAsync(message.MessageText, cancellationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Failed to unblock IP.");
|
||||
}
|
||||
await _unblockIpQueueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt);
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(15));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,97 +6,96 @@ using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Admin.HostedServices
|
||||
namespace Bit.Admin.HostedServices;
|
||||
|
||||
public class AzureQueueMailHostedService : IHostedService
|
||||
{
|
||||
public class AzureQueueMailHostedService : IHostedService
|
||||
private readonly ILogger<AzureQueueMailHostedService> _logger;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IMailService _mailService;
|
||||
private CancellationTokenSource _cts;
|
||||
private Task _executingTask;
|
||||
|
||||
private QueueClient _mailQueueClient;
|
||||
|
||||
public AzureQueueMailHostedService(
|
||||
ILogger<AzureQueueMailHostedService> logger,
|
||||
IMailService mailService,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
private readonly ILogger<AzureQueueMailHostedService> _logger;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IMailService _mailService;
|
||||
private CancellationTokenSource _cts;
|
||||
private Task _executingTask;
|
||||
_logger = logger;
|
||||
_mailService = mailService;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
private QueueClient _mailQueueClient;
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
_executingTask = ExecuteAsync(_cts.Token);
|
||||
return _executingTask.IsCompleted ? _executingTask : Task.CompletedTask;
|
||||
}
|
||||
|
||||
public AzureQueueMailHostedService(
|
||||
ILogger<AzureQueueMailHostedService> logger,
|
||||
IMailService mailService,
|
||||
GlobalSettings globalSettings)
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_executingTask == null)
|
||||
{
|
||||
_logger = logger;
|
||||
_mailService = mailService;
|
||||
_globalSettings = globalSettings;
|
||||
return;
|
||||
}
|
||||
_cts.Cancel();
|
||||
await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken));
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
_executingTask = ExecuteAsync(_cts.Token);
|
||||
return _executingTask.IsCompleted ? _executingTask : Task.CompletedTask;
|
||||
}
|
||||
private async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_mailQueueClient = new QueueClient(_globalSettings.Mail.ConnectionString, "mail");
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
QueueMessage[] mailMessages;
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
if (_executingTask == null)
|
||||
if (!(mailMessages = await RetrieveMessagesAsync()).Any())
|
||||
{
|
||||
return;
|
||||
await Task.Delay(TimeSpan.FromSeconds(15));
|
||||
}
|
||||
_cts.Cancel();
|
||||
await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken));
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
private async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_mailQueueClient = new QueueClient(_globalSettings.Mail.ConnectionString, "mail");
|
||||
|
||||
QueueMessage[] mailMessages;
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
foreach (var message in mailMessages)
|
||||
{
|
||||
if (!(mailMessages = await RetrieveMessagesAsync()).Any())
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(15));
|
||||
}
|
||||
using var document = JsonDocument.Parse(message.DecodeMessageText());
|
||||
var root = document.RootElement;
|
||||
|
||||
foreach (var message in mailMessages)
|
||||
{
|
||||
try
|
||||
if (root.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
using var document = JsonDocument.Parse(message.DecodeMessageText());
|
||||
var root = document.RootElement;
|
||||
|
||||
if (root.ValueKind == JsonValueKind.Array)
|
||||
foreach (var mailQueueMessage in root.ToObject<List<MailQueueMessage>>())
|
||||
{
|
||||
foreach (var mailQueueMessage in root.ToObject<List<MailQueueMessage>>())
|
||||
{
|
||||
await _mailService.SendEnqueuedMailMessageAsync(mailQueueMessage);
|
||||
}
|
||||
}
|
||||
else if (root.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
var mailQueueMessage = root.ToObject<MailQueueMessage>();
|
||||
await _mailService.SendEnqueuedMailMessageAsync(mailQueueMessage);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
else if (root.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
_logger.LogError(e, "Failed to send email");
|
||||
// TODO: retries?
|
||||
var mailQueueMessage = root.ToObject<MailQueueMessage>();
|
||||
await _mailService.SendEnqueuedMailMessageAsync(mailQueueMessage);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Failed to send email");
|
||||
// TODO: retries?
|
||||
}
|
||||
|
||||
await _mailQueueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt);
|
||||
await _mailQueueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<QueueMessage[]> RetrieveMessagesAsync()
|
||||
{
|
||||
return (await _mailQueueClient.ReceiveMessagesAsync(maxMessages: 32))?.Value ?? new QueueMessage[] { };
|
||||
}
|
||||
private async Task<QueueMessage[]> RetrieveMessagesAsync()
|
||||
{
|
||||
return (await _mailQueueClient.ReceiveMessagesAsync(maxMessages: 32))?.Value ?? new QueueMessage[] { };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,71 +1,105 @@
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Bit.Admin.HostedServices
|
||||
namespace Bit.Admin.HostedServices;
|
||||
|
||||
public abstract class BlockIpHostedService : IHostedService, IDisposable
|
||||
{
|
||||
public abstract class BlockIpHostedService : IHostedService, IDisposable
|
||||
protected readonly ILogger<BlockIpHostedService> _logger;
|
||||
protected readonly GlobalSettings _globalSettings;
|
||||
private readonly AdminSettings _adminSettings;
|
||||
|
||||
private Task _executingTask;
|
||||
private CancellationTokenSource _cts;
|
||||
private HttpClient _httpClient = new HttpClient();
|
||||
|
||||
public BlockIpHostedService(
|
||||
ILogger<BlockIpHostedService> logger,
|
||||
IOptions<AdminSettings> adminSettings,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
protected readonly ILogger<BlockIpHostedService> _logger;
|
||||
protected readonly GlobalSettings _globalSettings;
|
||||
private readonly AdminSettings _adminSettings;
|
||||
_logger = logger;
|
||||
_globalSettings = globalSettings;
|
||||
_adminSettings = adminSettings?.Value;
|
||||
}
|
||||
|
||||
private Task _executingTask;
|
||||
private CancellationTokenSource _cts;
|
||||
private HttpClient _httpClient = new HttpClient();
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
_executingTask = ExecuteAsync(_cts.Token);
|
||||
return _executingTask.IsCompleted ? _executingTask : Task.CompletedTask;
|
||||
}
|
||||
|
||||
public BlockIpHostedService(
|
||||
ILogger<BlockIpHostedService> logger,
|
||||
IOptions<AdminSettings> adminSettings,
|
||||
GlobalSettings globalSettings)
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_executingTask == null)
|
||||
{
|
||||
_logger = logger;
|
||||
_globalSettings = globalSettings;
|
||||
_adminSettings = adminSettings?.Value;
|
||||
return;
|
||||
}
|
||||
_cts.Cancel();
|
||||
await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken));
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
_executingTask = ExecuteAsync(_cts.Token);
|
||||
return _executingTask.IsCompleted ? _executingTask : Task.CompletedTask;
|
||||
}
|
||||
public virtual void Dispose()
|
||||
{ }
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
protected abstract Task ExecuteAsync(CancellationToken cancellationToken);
|
||||
|
||||
protected async Task BlockIpAsync(string message, CancellationToken cancellationToken)
|
||||
{
|
||||
var request = new HttpRequestMessage();
|
||||
request.Headers.Accept.Clear();
|
||||
request.Headers.Add("X-Auth-Email", _adminSettings.Cloudflare.AuthEmail);
|
||||
request.Headers.Add("X-Auth-Key", _adminSettings.Cloudflare.AuthKey);
|
||||
request.Method = HttpMethod.Post;
|
||||
request.RequestUri = new Uri("https://api.cloudflare.com/" +
|
||||
$"client/v4/zones/{_adminSettings.Cloudflare.ZoneId}/firewall/access_rules/rules");
|
||||
|
||||
request.Content = JsonContent.Create(new
|
||||
{
|
||||
if (_executingTask == null)
|
||||
mode = "block",
|
||||
configuration = new
|
||||
{
|
||||
return;
|
||||
}
|
||||
_cts.Cancel();
|
||||
await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken));
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
target = "ip",
|
||||
value = message
|
||||
},
|
||||
notes = $"Rate limit abuse on {DateTime.UtcNow.ToString()}."
|
||||
});
|
||||
|
||||
var response = await _httpClient.SendAsync(request, cancellationToken);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{ }
|
||||
|
||||
protected abstract Task ExecuteAsync(CancellationToken cancellationToken);
|
||||
|
||||
protected async Task BlockIpAsync(string message, CancellationToken cancellationToken)
|
||||
var accessRuleResponse = await response.Content.ReadFromJsonAsync<AccessRuleResponse>(cancellationToken: cancellationToken);
|
||||
if (!accessRuleResponse.Success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Send `accessRuleResponse.Result?.Id` message to unblock queue
|
||||
}
|
||||
|
||||
protected async Task UnblockIpAsync(string message, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.Contains(".") || message.Contains(":"))
|
||||
{
|
||||
// IP address messages
|
||||
var request = new HttpRequestMessage();
|
||||
request.Headers.Accept.Clear();
|
||||
request.Headers.Add("X-Auth-Email", _adminSettings.Cloudflare.AuthEmail);
|
||||
request.Headers.Add("X-Auth-Key", _adminSettings.Cloudflare.AuthKey);
|
||||
request.Method = HttpMethod.Post;
|
||||
request.Method = HttpMethod.Get;
|
||||
request.RequestUri = new Uri("https://api.cloudflare.com/" +
|
||||
$"client/v4/zones/{_adminSettings.Cloudflare.ZoneId}/firewall/access_rules/rules");
|
||||
|
||||
request.Content = JsonContent.Create(new
|
||||
{
|
||||
mode = "block",
|
||||
configuration = new
|
||||
{
|
||||
target = "ip",
|
||||
value = message
|
||||
},
|
||||
notes = $"Rate limit abuse on {DateTime.UtcNow.ToString()}."
|
||||
});
|
||||
$"client/v4/zones/{_adminSettings.Cloudflare.ZoneId}/firewall/access_rules/rules?" +
|
||||
$"configuration_target=ip&configuration_value={message}");
|
||||
|
||||
var response = await _httpClient.SendAsync(request, cancellationToken);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
@@ -73,93 +107,58 @@ namespace Bit.Admin.HostedServices
|
||||
return;
|
||||
}
|
||||
|
||||
var accessRuleResponse = await response.Content.ReadFromJsonAsync<AccessRuleResponse>(cancellationToken: cancellationToken);
|
||||
if (!accessRuleResponse.Success)
|
||||
var listResponse = await response.Content.ReadFromJsonAsync<ListResponse>(cancellationToken: cancellationToken);
|
||||
if (!listResponse.Success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Send `accessRuleResponse.Result?.Id` message to unblock queue
|
||||
}
|
||||
|
||||
protected async Task UnblockIpAsync(string message, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
foreach (var rule in listResponse.Result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.Contains(".") || message.Contains(":"))
|
||||
{
|
||||
// IP address messages
|
||||
var request = new HttpRequestMessage();
|
||||
request.Headers.Accept.Clear();
|
||||
request.Headers.Add("X-Auth-Email", _adminSettings.Cloudflare.AuthEmail);
|
||||
request.Headers.Add("X-Auth-Key", _adminSettings.Cloudflare.AuthKey);
|
||||
request.Method = HttpMethod.Get;
|
||||
request.RequestUri = new Uri("https://api.cloudflare.com/" +
|
||||
$"client/v4/zones/{_adminSettings.Cloudflare.ZoneId}/firewall/access_rules/rules?" +
|
||||
$"configuration_target=ip&configuration_value={message}");
|
||||
|
||||
var response = await _httpClient.SendAsync(request, cancellationToken);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var listResponse = await response.Content.ReadFromJsonAsync<ListResponse>(cancellationToken: cancellationToken);
|
||||
if (!listResponse.Success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var rule in listResponse.Result)
|
||||
{
|
||||
await DeleteAccessRuleAsync(rule.Id, cancellationToken);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rule Id messages
|
||||
await DeleteAccessRuleAsync(message, cancellationToken);
|
||||
await DeleteAccessRuleAsync(rule.Id, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task DeleteAccessRuleAsync(string ruleId, CancellationToken cancellationToken)
|
||||
else
|
||||
{
|
||||
var request = new HttpRequestMessage();
|
||||
request.Headers.Accept.Clear();
|
||||
request.Headers.Add("X-Auth-Email", _adminSettings.Cloudflare.AuthEmail);
|
||||
request.Headers.Add("X-Auth-Key", _adminSettings.Cloudflare.AuthKey);
|
||||
request.Method = HttpMethod.Delete;
|
||||
request.RequestUri = new Uri("https://api.cloudflare.com/" +
|
||||
$"client/v4/zones/{_adminSettings.Cloudflare.ZoneId}/firewall/access_rules/rules/{ruleId}");
|
||||
await _httpClient.SendAsync(request, cancellationToken);
|
||||
// Rule Id messages
|
||||
await DeleteAccessRuleAsync(message, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
public class ListResponse
|
||||
protected async Task DeleteAccessRuleAsync(string ruleId, CancellationToken cancellationToken)
|
||||
{
|
||||
var request = new HttpRequestMessage();
|
||||
request.Headers.Accept.Clear();
|
||||
request.Headers.Add("X-Auth-Email", _adminSettings.Cloudflare.AuthEmail);
|
||||
request.Headers.Add("X-Auth-Key", _adminSettings.Cloudflare.AuthKey);
|
||||
request.Method = HttpMethod.Delete;
|
||||
request.RequestUri = new Uri("https://api.cloudflare.com/" +
|
||||
$"client/v4/zones/{_adminSettings.Cloudflare.ZoneId}/firewall/access_rules/rules/{ruleId}");
|
||||
await _httpClient.SendAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
public class ListResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public List<AccessRuleResultResponse> Result { get; set; }
|
||||
}
|
||||
|
||||
public class AccessRuleResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public AccessRuleResultResponse Result { get; set; }
|
||||
}
|
||||
|
||||
public class AccessRuleResultResponse
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public ConfigurationResponse Configuration { get; set; }
|
||||
|
||||
public class ConfigurationResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public List<AccessRuleResultResponse> Result { get; set; }
|
||||
}
|
||||
|
||||
public class AccessRuleResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public AccessRuleResultResponse Result { get; set; }
|
||||
}
|
||||
|
||||
public class AccessRuleResultResponse
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public ConfigurationResponse Configuration { get; set; }
|
||||
|
||||
public class ConfigurationResponse
|
||||
{
|
||||
public string Target { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
public string Target { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,62 +3,61 @@ using Bit.Core.Jobs;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Migrator;
|
||||
|
||||
namespace Bit.Admin.HostedServices
|
||||
namespace Bit.Admin.HostedServices;
|
||||
|
||||
public class DatabaseMigrationHostedService : IHostedService, IDisposable
|
||||
{
|
||||
public class DatabaseMigrationHostedService : IHostedService, IDisposable
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ILogger<DatabaseMigrationHostedService> _logger;
|
||||
private readonly DbMigrator _dbMigrator;
|
||||
|
||||
public DatabaseMigrationHostedService(
|
||||
GlobalSettings globalSettings,
|
||||
ILogger<DatabaseMigrationHostedService> logger,
|
||||
ILogger<DbMigrator> migratorLogger,
|
||||
ILogger<JobListener> listenerLogger)
|
||||
{
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ILogger<DatabaseMigrationHostedService> _logger;
|
||||
private readonly DbMigrator _dbMigrator;
|
||||
_globalSettings = globalSettings;
|
||||
_logger = logger;
|
||||
_dbMigrator = new DbMigrator(globalSettings.SqlServer.ConnectionString, migratorLogger);
|
||||
}
|
||||
|
||||
public DatabaseMigrationHostedService(
|
||||
GlobalSettings globalSettings,
|
||||
ILogger<DatabaseMigrationHostedService> logger,
|
||||
ILogger<DbMigrator> migratorLogger,
|
||||
ILogger<JobListener> listenerLogger)
|
||||
public virtual async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// Wait 20 seconds to allow database to come online
|
||||
await Task.Delay(20000);
|
||||
|
||||
var maxMigrationAttempts = 10;
|
||||
for (var i = 1; i <= maxMigrationAttempts; i++)
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
_logger = logger;
|
||||
_dbMigrator = new DbMigrator(globalSettings.SqlServer.ConnectionString, migratorLogger);
|
||||
}
|
||||
|
||||
public virtual async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// Wait 20 seconds to allow database to come online
|
||||
await Task.Delay(20000);
|
||||
|
||||
var maxMigrationAttempts = 10;
|
||||
for (var i = 1; i <= maxMigrationAttempts; i++)
|
||||
try
|
||||
{
|
||||
try
|
||||
_dbMigrator.MigrateMsSqlDatabase(true, cancellationToken);
|
||||
// TODO: Maybe flip a flag somewhere to indicate migration is complete??
|
||||
break;
|
||||
}
|
||||
catch (SqlException e)
|
||||
{
|
||||
if (i >= maxMigrationAttempts)
|
||||
{
|
||||
_dbMigrator.MigrateMsSqlDatabase(true, cancellationToken);
|
||||
// TODO: Maybe flip a flag somewhere to indicate migration is complete??
|
||||
break;
|
||||
_logger.LogError(e, "Database failed to migrate.");
|
||||
throw;
|
||||
}
|
||||
catch (SqlException e)
|
||||
else
|
||||
{
|
||||
if (i >= maxMigrationAttempts)
|
||||
{
|
||||
_logger.LogError(e, "Database failed to migrate.");
|
||||
throw;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError(e,
|
||||
"Database unavailable for migration. Trying again (attempt #{0})...", i + 1);
|
||||
await Task.Delay(20000);
|
||||
}
|
||||
_logger.LogError(e,
|
||||
"Database unavailable for migration. Trying again (attempt #{0})...", i + 1);
|
||||
await Task.Delay(20000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{ }
|
||||
}
|
||||
|
||||
public virtual Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{ }
|
||||
}
|
||||
|
||||
@@ -3,27 +3,26 @@ using Bit.Core.Jobs;
|
||||
using Bit.Core.Settings;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Admin.Jobs
|
||||
namespace Bit.Admin.Jobs;
|
||||
|
||||
public class AliveJob : BaseJob
|
||||
{
|
||||
public class AliveJob : BaseJob
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private HttpClient _httpClient = new HttpClient();
|
||||
|
||||
public AliveJob(
|
||||
GlobalSettings globalSettings,
|
||||
ILogger<AliveJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private HttpClient _httpClient = new HttpClient();
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
public AliveJob(
|
||||
GlobalSettings globalSettings,
|
||||
ILogger<AliveJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: Keep alive");
|
||||
var response = await _httpClient.GetAsync(_globalSettings.BaseServiceUri.Admin);
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Finished job task: Keep alive, " +
|
||||
response.StatusCode);
|
||||
}
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: Keep alive");
|
||||
var response = await _httpClient.GetAsync(_globalSettings.BaseServiceUri.Admin);
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Finished job task: Keep alive, " +
|
||||
response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,25 +3,24 @@ using Bit.Core.Jobs;
|
||||
using Bit.Core.Repositories;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Admin.Jobs
|
||||
namespace Bit.Admin.Jobs;
|
||||
|
||||
public class DatabaseExpiredGrantsJob : BaseJob
|
||||
{
|
||||
public class DatabaseExpiredGrantsJob : BaseJob
|
||||
private readonly IMaintenanceRepository _maintenanceRepository;
|
||||
|
||||
public DatabaseExpiredGrantsJob(
|
||||
IMaintenanceRepository maintenanceRepository,
|
||||
ILogger<DatabaseExpiredGrantsJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
private readonly IMaintenanceRepository _maintenanceRepository;
|
||||
_maintenanceRepository = maintenanceRepository;
|
||||
}
|
||||
|
||||
public DatabaseExpiredGrantsJob(
|
||||
IMaintenanceRepository maintenanceRepository,
|
||||
ILogger<DatabaseExpiredGrantsJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
_maintenanceRepository = maintenanceRepository;
|
||||
}
|
||||
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteExpiredGrantsAsync");
|
||||
await _maintenanceRepository.DeleteExpiredGrantsAsync();
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Finished job task: DeleteExpiredGrantsAsync");
|
||||
}
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteExpiredGrantsAsync");
|
||||
await _maintenanceRepository.DeleteExpiredGrantsAsync();
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Finished job task: DeleteExpiredGrantsAsync");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,36 +4,35 @@ using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Admin.Jobs
|
||||
namespace Bit.Admin.Jobs;
|
||||
|
||||
public class DatabaseExpiredSponsorshipsJob : BaseJob
|
||||
{
|
||||
public class DatabaseExpiredSponsorshipsJob : BaseJob
|
||||
private GlobalSettings _globalSettings;
|
||||
private readonly IMaintenanceRepository _maintenanceRepository;
|
||||
|
||||
public DatabaseExpiredSponsorshipsJob(
|
||||
IMaintenanceRepository maintenanceRepository,
|
||||
ILogger<DatabaseExpiredSponsorshipsJob> logger,
|
||||
GlobalSettings globalSettings)
|
||||
: base(logger)
|
||||
{
|
||||
private GlobalSettings _globalSettings;
|
||||
private readonly IMaintenanceRepository _maintenanceRepository;
|
||||
_maintenanceRepository = maintenanceRepository;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
public DatabaseExpiredSponsorshipsJob(
|
||||
IMaintenanceRepository maintenanceRepository,
|
||||
ILogger<DatabaseExpiredSponsorshipsJob> logger,
|
||||
GlobalSettings globalSettings)
|
||||
: base(logger)
|
||||
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
if (_globalSettings.SelfHosted && !_globalSettings.EnableCloudCommunication)
|
||||
{
|
||||
_maintenanceRepository = maintenanceRepository;
|
||||
_globalSettings = globalSettings;
|
||||
return;
|
||||
}
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteExpiredSponsorshipsAsync");
|
||||
|
||||
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
if (_globalSettings.SelfHosted && !_globalSettings.EnableCloudCommunication)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteExpiredSponsorshipsAsync");
|
||||
// allow a 90 day grace period before deleting
|
||||
var deleteDate = DateTime.UtcNow.AddDays(-90);
|
||||
|
||||
// allow a 90 day grace period before deleting
|
||||
var deleteDate = DateTime.UtcNow.AddDays(-90);
|
||||
|
||||
await _maintenanceRepository.DeleteExpiredSponsorshipsAsync(deleteDate);
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Finished job task: DeleteExpiredSponsorshipsAsync");
|
||||
}
|
||||
await _maintenanceRepository.DeleteExpiredSponsorshipsAsync(deleteDate);
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Finished job task: DeleteExpiredSponsorshipsAsync");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,25 +3,24 @@ using Bit.Core.Jobs;
|
||||
using Bit.Core.Repositories;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Admin.Jobs
|
||||
namespace Bit.Admin.Jobs;
|
||||
|
||||
public class DatabaseRebuildlIndexesJob : BaseJob
|
||||
{
|
||||
public class DatabaseRebuildlIndexesJob : BaseJob
|
||||
private readonly IMaintenanceRepository _maintenanceRepository;
|
||||
|
||||
public DatabaseRebuildlIndexesJob(
|
||||
IMaintenanceRepository maintenanceRepository,
|
||||
ILogger<DatabaseRebuildlIndexesJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
private readonly IMaintenanceRepository _maintenanceRepository;
|
||||
_maintenanceRepository = maintenanceRepository;
|
||||
}
|
||||
|
||||
public DatabaseRebuildlIndexesJob(
|
||||
IMaintenanceRepository maintenanceRepository,
|
||||
ILogger<DatabaseRebuildlIndexesJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
_maintenanceRepository = maintenanceRepository;
|
||||
}
|
||||
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: RebuildIndexesAsync");
|
||||
await _maintenanceRepository.RebuildIndexesAsync();
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Finished job task: RebuildIndexesAsync");
|
||||
}
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: RebuildIndexesAsync");
|
||||
await _maintenanceRepository.RebuildIndexesAsync();
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Finished job task: RebuildIndexesAsync");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,28 +3,27 @@ using Bit.Core.Jobs;
|
||||
using Bit.Core.Repositories;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Admin.Jobs
|
||||
namespace Bit.Admin.Jobs;
|
||||
|
||||
public class DatabaseUpdateStatisticsJob : BaseJob
|
||||
{
|
||||
public class DatabaseUpdateStatisticsJob : BaseJob
|
||||
private readonly IMaintenanceRepository _maintenanceRepository;
|
||||
|
||||
public DatabaseUpdateStatisticsJob(
|
||||
IMaintenanceRepository maintenanceRepository,
|
||||
ILogger<DatabaseUpdateStatisticsJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
private readonly IMaintenanceRepository _maintenanceRepository;
|
||||
_maintenanceRepository = maintenanceRepository;
|
||||
}
|
||||
|
||||
public DatabaseUpdateStatisticsJob(
|
||||
IMaintenanceRepository maintenanceRepository,
|
||||
ILogger<DatabaseUpdateStatisticsJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
_maintenanceRepository = maintenanceRepository;
|
||||
}
|
||||
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: UpdateStatisticsAsync");
|
||||
await _maintenanceRepository.UpdateStatisticsAsync();
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Finished job task: UpdateStatisticsAsync");
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DisableCipherAutoStatsAsync");
|
||||
await _maintenanceRepository.DisableCipherAutoStatsAsync();
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Finished job task: DisableCipherAutoStatsAsync");
|
||||
}
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: UpdateStatisticsAsync");
|
||||
await _maintenanceRepository.UpdateStatisticsAsync();
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Finished job task: UpdateStatisticsAsync");
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DisableCipherAutoStatsAsync");
|
||||
await _maintenanceRepository.DisableCipherAutoStatsAsync();
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Finished job task: DisableCipherAutoStatsAsync");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,34 +4,33 @@ using Bit.Core.Repositories;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Admin.Jobs
|
||||
namespace Bit.Admin.Jobs;
|
||||
|
||||
public class DeleteCiphersJob : BaseJob
|
||||
{
|
||||
public class DeleteCiphersJob : BaseJob
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly AdminSettings _adminSettings;
|
||||
|
||||
public DeleteCiphersJob(
|
||||
ICipherRepository cipherRepository,
|
||||
IOptions<AdminSettings> adminSettings,
|
||||
ILogger<DeleteCiphersJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly AdminSettings _adminSettings;
|
||||
_cipherRepository = cipherRepository;
|
||||
_adminSettings = adminSettings?.Value;
|
||||
}
|
||||
|
||||
public DeleteCiphersJob(
|
||||
ICipherRepository cipherRepository,
|
||||
IOptions<AdminSettings> adminSettings,
|
||||
ILogger<DeleteCiphersJob> logger)
|
||||
: base(logger)
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteDeletedAsync");
|
||||
var deleteDate = DateTime.UtcNow.AddDays(-30);
|
||||
var daysAgoSetting = (_adminSettings?.DeleteTrashDaysAgo).GetValueOrDefault();
|
||||
if (daysAgoSetting > 0)
|
||||
{
|
||||
_cipherRepository = cipherRepository;
|
||||
_adminSettings = adminSettings?.Value;
|
||||
}
|
||||
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteDeletedAsync");
|
||||
var deleteDate = DateTime.UtcNow.AddDays(-30);
|
||||
var daysAgoSetting = (_adminSettings?.DeleteTrashDaysAgo).GetValueOrDefault();
|
||||
if (daysAgoSetting > 0)
|
||||
{
|
||||
deleteDate = DateTime.UtcNow.AddDays(-1 * daysAgoSetting);
|
||||
}
|
||||
await _cipherRepository.DeleteDeletedAsync(deleteDate);
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Finished job task: DeleteDeletedAsync");
|
||||
deleteDate = DateTime.UtcNow.AddDays(-1 * daysAgoSetting);
|
||||
}
|
||||
await _cipherRepository.DeleteDeletedAsync(deleteDate);
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Finished job task: DeleteDeletedAsync");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,38 +4,37 @@ using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Admin.Jobs
|
||||
namespace Bit.Admin.Jobs;
|
||||
|
||||
public class DeleteSendsJob : BaseJob
|
||||
{
|
||||
public class DeleteSendsJob : BaseJob
|
||||
private readonly ISendRepository _sendRepository;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public DeleteSendsJob(
|
||||
ISendRepository sendRepository,
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<DatabaseExpiredGrantsJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
private readonly ISendRepository _sendRepository;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
_sendRepository = sendRepository;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public DeleteSendsJob(
|
||||
ISendRepository sendRepository,
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<DatabaseExpiredGrantsJob> logger)
|
||||
: base(logger)
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
var sends = await _sendRepository.GetManyByDeletionDateAsync(DateTime.UtcNow);
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Deleting {0} sends.", sends.Count);
|
||||
if (!sends.Any())
|
||||
{
|
||||
_sendRepository = sendRepository;
|
||||
_serviceProvider = serviceProvider;
|
||||
return;
|
||||
}
|
||||
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
using (var scope = _serviceProvider.CreateScope())
|
||||
{
|
||||
var sends = await _sendRepository.GetManyByDeletionDateAsync(DateTime.UtcNow);
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Deleting {0} sends.", sends.Count);
|
||||
if (!sends.Any())
|
||||
var sendService = scope.ServiceProvider.GetRequiredService<ISendService>();
|
||||
foreach (var send in sends)
|
||||
{
|
||||
return;
|
||||
}
|
||||
using (var scope = _serviceProvider.CreateScope())
|
||||
{
|
||||
var sendService = scope.ServiceProvider.GetRequiredService<ISendService>();
|
||||
foreach (var send in sends)
|
||||
{
|
||||
await sendService.DeleteSendAsync(send);
|
||||
}
|
||||
await sendService.DeleteSendAsync(send);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,94 +3,93 @@ using Bit.Core.Jobs;
|
||||
using Bit.Core.Settings;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Admin.Jobs
|
||||
namespace Bit.Admin.Jobs;
|
||||
|
||||
public class JobsHostedService : BaseJobsHostedService
|
||||
{
|
||||
public class JobsHostedService : BaseJobsHostedService
|
||||
public JobsHostedService(
|
||||
GlobalSettings globalSettings,
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<JobsHostedService> logger,
|
||||
ILogger<JobListener> listenerLogger)
|
||||
: base(globalSettings, serviceProvider, logger, listenerLogger) { }
|
||||
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
public JobsHostedService(
|
||||
GlobalSettings globalSettings,
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<JobsHostedService> logger,
|
||||
ILogger<JobListener> listenerLogger)
|
||||
: base(globalSettings, serviceProvider, logger, listenerLogger) { }
|
||||
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
var timeZone = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
||||
TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time") :
|
||||
TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
|
||||
if (_globalSettings.SelfHosted)
|
||||
{
|
||||
var timeZone = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
||||
TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time") :
|
||||
TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
|
||||
if (_globalSettings.SelfHosted)
|
||||
{
|
||||
timeZone = TimeZoneInfo.Local;
|
||||
}
|
||||
|
||||
var everyTopOfTheHourTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryTopOfTheHourTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 * * * ?")
|
||||
.Build();
|
||||
var everyFiveMinutesTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryFiveMinutesTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 */5 * * * ?")
|
||||
.Build();
|
||||
var everyFridayAt10pmTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryFridayAt10pmTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 22 ? * FRI", x => x.InTimeZone(timeZone))
|
||||
.Build();
|
||||
var everySaturdayAtMidnightTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EverySaturdayAtMidnightTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 0 ? * SAT", x => x.InTimeZone(timeZone))
|
||||
.Build();
|
||||
var everySundayAtMidnightTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EverySundayAtMidnightTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 0 ? * SUN", x => x.InTimeZone(timeZone))
|
||||
.Build();
|
||||
var everyMondayAtMidnightTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryMondayAtMidnightTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 0 ? * MON", x => x.InTimeZone(timeZone))
|
||||
.Build();
|
||||
var everyDayAtMidnightUtc = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryDayAtMidnightUtc")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 0 * * ?")
|
||||
.Build();
|
||||
|
||||
var jobs = new List<Tuple<Type, ITrigger>>
|
||||
{
|
||||
new Tuple<Type, ITrigger>(typeof(DeleteSendsJob), everyFiveMinutesTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(DatabaseExpiredGrantsJob), everyFridayAt10pmTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(DatabaseUpdateStatisticsJob), everySaturdayAtMidnightTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(DatabaseRebuildlIndexesJob), everySundayAtMidnightTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(DeleteCiphersJob), everyDayAtMidnightUtc),
|
||||
new Tuple<Type, ITrigger>(typeof(DatabaseExpiredSponsorshipsJob), everyMondayAtMidnightTrigger)
|
||||
};
|
||||
|
||||
if (!_globalSettings.SelfHosted)
|
||||
{
|
||||
jobs.Add(new Tuple<Type, ITrigger>(typeof(AliveJob), everyTopOfTheHourTrigger));
|
||||
}
|
||||
|
||||
Jobs = jobs;
|
||||
await base.StartAsync(cancellationToken);
|
||||
timeZone = TimeZoneInfo.Local;
|
||||
}
|
||||
|
||||
public static void AddJobsServices(IServiceCollection services, bool selfHosted)
|
||||
var everyTopOfTheHourTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryTopOfTheHourTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 * * * ?")
|
||||
.Build();
|
||||
var everyFiveMinutesTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryFiveMinutesTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 */5 * * * ?")
|
||||
.Build();
|
||||
var everyFridayAt10pmTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryFridayAt10pmTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 22 ? * FRI", x => x.InTimeZone(timeZone))
|
||||
.Build();
|
||||
var everySaturdayAtMidnightTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EverySaturdayAtMidnightTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 0 ? * SAT", x => x.InTimeZone(timeZone))
|
||||
.Build();
|
||||
var everySundayAtMidnightTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EverySundayAtMidnightTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 0 ? * SUN", x => x.InTimeZone(timeZone))
|
||||
.Build();
|
||||
var everyMondayAtMidnightTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryMondayAtMidnightTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 0 ? * MON", x => x.InTimeZone(timeZone))
|
||||
.Build();
|
||||
var everyDayAtMidnightUtc = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryDayAtMidnightUtc")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 0 * * ?")
|
||||
.Build();
|
||||
|
||||
var jobs = new List<Tuple<Type, ITrigger>>
|
||||
{
|
||||
if (!selfHosted)
|
||||
{
|
||||
services.AddTransient<AliveJob>();
|
||||
}
|
||||
services.AddTransient<DatabaseUpdateStatisticsJob>();
|
||||
services.AddTransient<DatabaseRebuildlIndexesJob>();
|
||||
services.AddTransient<DatabaseExpiredGrantsJob>();
|
||||
services.AddTransient<DatabaseExpiredSponsorshipsJob>();
|
||||
services.AddTransient<DeleteSendsJob>();
|
||||
services.AddTransient<DeleteCiphersJob>();
|
||||
new Tuple<Type, ITrigger>(typeof(DeleteSendsJob), everyFiveMinutesTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(DatabaseExpiredGrantsJob), everyFridayAt10pmTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(DatabaseUpdateStatisticsJob), everySaturdayAtMidnightTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(DatabaseRebuildlIndexesJob), everySundayAtMidnightTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(DeleteCiphersJob), everyDayAtMidnightUtc),
|
||||
new Tuple<Type, ITrigger>(typeof(DatabaseExpiredSponsorshipsJob), everyMondayAtMidnightTrigger)
|
||||
};
|
||||
|
||||
if (!_globalSettings.SelfHosted)
|
||||
{
|
||||
jobs.Add(new Tuple<Type, ITrigger>(typeof(AliveJob), everyTopOfTheHourTrigger));
|
||||
}
|
||||
|
||||
Jobs = jobs;
|
||||
await base.StartAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public static void AddJobsServices(IServiceCollection services, bool selfHosted)
|
||||
{
|
||||
if (!selfHosted)
|
||||
{
|
||||
services.AddTransient<AliveJob>();
|
||||
}
|
||||
services.AddTransient<DatabaseUpdateStatisticsJob>();
|
||||
services.AddTransient<DatabaseRebuildlIndexesJob>();
|
||||
services.AddTransient<DatabaseExpiredGrantsJob>();
|
||||
services.AddTransient<DatabaseExpiredSponsorshipsJob>();
|
||||
services.AddTransient<DeleteSendsJob>();
|
||||
services.AddTransient<DeleteCiphersJob>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using Bit.Core.Models.Business;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class BillingInformationModel
|
||||
{
|
||||
public class BillingInformationModel
|
||||
{
|
||||
public BillingInfo BillingInfo { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public Guid? OrganizationId { get; set; }
|
||||
}
|
||||
public BillingInfo BillingInfo { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public Guid? OrganizationId { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
{
|
||||
public class ChargeBraintreeModel : IValidatableObject
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "Braintree Customer Id")]
|
||||
public string Id { get; set; }
|
||||
[Required]
|
||||
[Display(Name = "Amount")]
|
||||
public decimal? Amount { get; set; }
|
||||
public string TransactionId { get; set; }
|
||||
public string PayPalTransactionId { get; set; }
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
public class ChargeBraintreeModel : IValidatableObject
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "Braintree Customer Id")]
|
||||
public string Id { get; set; }
|
||||
[Required]
|
||||
[Display(Name = "Amount")]
|
||||
public decimal? Amount { get; set; }
|
||||
public string TransactionId { get; set; }
|
||||
public string PayPalTransactionId { get; set; }
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (Id != null)
|
||||
{
|
||||
if (Id != null)
|
||||
if (Id.Length != 36 || (Id[0] != 'o' && Id[0] != 'u') ||
|
||||
!Guid.TryParse(Id.Substring(1, 32), out var guid))
|
||||
{
|
||||
if (Id.Length != 36 || (Id[0] != 'o' && Id[0] != 'u') ||
|
||||
!Guid.TryParse(Id.Substring(1, 32), out var guid))
|
||||
{
|
||||
yield return new ValidationResult("Customer Id is not a valid format.");
|
||||
}
|
||||
yield return new ValidationResult("Customer Id is not a valid format.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
{
|
||||
public class CreateProviderModel
|
||||
{
|
||||
public CreateProviderModel() { }
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
[Display(Name = "Owner Email")]
|
||||
[Required]
|
||||
public string OwnerEmail { get; set; }
|
||||
}
|
||||
public class CreateProviderModel
|
||||
{
|
||||
public CreateProviderModel() { }
|
||||
|
||||
[Display(Name = "Owner Email")]
|
||||
[Required]
|
||||
public string OwnerEmail { get; set; }
|
||||
}
|
||||
|
||||
@@ -2,77 +2,76 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class CreateUpdateTransactionModel : IValidatableObject
|
||||
{
|
||||
public class CreateUpdateTransactionModel : IValidatableObject
|
||||
public CreateUpdateTransactionModel() { }
|
||||
|
||||
public CreateUpdateTransactionModel(Transaction transaction)
|
||||
{
|
||||
public CreateUpdateTransactionModel() { }
|
||||
Edit = true;
|
||||
UserId = transaction.UserId;
|
||||
OrganizationId = transaction.OrganizationId;
|
||||
Amount = transaction.Amount;
|
||||
RefundedAmount = transaction.RefundedAmount;
|
||||
Refunded = transaction.Refunded.GetValueOrDefault();
|
||||
Details = transaction.Details;
|
||||
Date = transaction.CreationDate;
|
||||
PaymentMethod = transaction.PaymentMethodType;
|
||||
Gateway = transaction.Gateway;
|
||||
GatewayId = transaction.GatewayId;
|
||||
Type = transaction.Type;
|
||||
}
|
||||
|
||||
public CreateUpdateTransactionModel(Transaction transaction)
|
||||
public bool Edit { get; set; }
|
||||
|
||||
[Display(Name = "User Id")]
|
||||
public Guid? UserId { get; set; }
|
||||
[Display(Name = "Organization Id")]
|
||||
public Guid? OrganizationId { get; set; }
|
||||
[Required]
|
||||
public decimal? Amount { get; set; }
|
||||
[Display(Name = "Refunded Amount")]
|
||||
public decimal? RefundedAmount { get; set; }
|
||||
public bool Refunded { get; set; }
|
||||
[Required]
|
||||
public string Details { get; set; }
|
||||
[Required]
|
||||
public DateTime? Date { get; set; }
|
||||
[Display(Name = "Payment Method")]
|
||||
public PaymentMethodType? PaymentMethod { get; set; }
|
||||
public GatewayType? Gateway { get; set; }
|
||||
[Display(Name = "Gateway Id")]
|
||||
public string GatewayId { get; set; }
|
||||
[Required]
|
||||
public TransactionType? Type { get; set; }
|
||||
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if ((!UserId.HasValue && !OrganizationId.HasValue) || (UserId.HasValue && OrganizationId.HasValue))
|
||||
{
|
||||
Edit = true;
|
||||
UserId = transaction.UserId;
|
||||
OrganizationId = transaction.OrganizationId;
|
||||
Amount = transaction.Amount;
|
||||
RefundedAmount = transaction.RefundedAmount;
|
||||
Refunded = transaction.Refunded.GetValueOrDefault();
|
||||
Details = transaction.Details;
|
||||
Date = transaction.CreationDate;
|
||||
PaymentMethod = transaction.PaymentMethodType;
|
||||
Gateway = transaction.Gateway;
|
||||
GatewayId = transaction.GatewayId;
|
||||
Type = transaction.Type;
|
||||
}
|
||||
|
||||
public bool Edit { get; set; }
|
||||
|
||||
[Display(Name = "User Id")]
|
||||
public Guid? UserId { get; set; }
|
||||
[Display(Name = "Organization Id")]
|
||||
public Guid? OrganizationId { get; set; }
|
||||
[Required]
|
||||
public decimal? Amount { get; set; }
|
||||
[Display(Name = "Refunded Amount")]
|
||||
public decimal? RefundedAmount { get; set; }
|
||||
public bool Refunded { get; set; }
|
||||
[Required]
|
||||
public string Details { get; set; }
|
||||
[Required]
|
||||
public DateTime? Date { get; set; }
|
||||
[Display(Name = "Payment Method")]
|
||||
public PaymentMethodType? PaymentMethod { get; set; }
|
||||
public GatewayType? Gateway { get; set; }
|
||||
[Display(Name = "Gateway Id")]
|
||||
public string GatewayId { get; set; }
|
||||
[Required]
|
||||
public TransactionType? Type { get; set; }
|
||||
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if ((!UserId.HasValue && !OrganizationId.HasValue) || (UserId.HasValue && OrganizationId.HasValue))
|
||||
{
|
||||
yield return new ValidationResult("Must provide either User Id, or Organization Id.");
|
||||
}
|
||||
}
|
||||
|
||||
public Transaction ToTransaction(Guid? id = null)
|
||||
{
|
||||
return new Transaction
|
||||
{
|
||||
Id = id.GetValueOrDefault(),
|
||||
UserId = UserId,
|
||||
OrganizationId = OrganizationId,
|
||||
Amount = Amount.Value,
|
||||
RefundedAmount = RefundedAmount,
|
||||
Refunded = Refunded ? true : (bool?)null,
|
||||
Details = Details,
|
||||
CreationDate = Date.Value,
|
||||
PaymentMethodType = PaymentMethod,
|
||||
Gateway = Gateway,
|
||||
GatewayId = GatewayId,
|
||||
Type = Type.Value
|
||||
};
|
||||
yield return new ValidationResult("Must provide either User Id, or Organization Id.");
|
||||
}
|
||||
}
|
||||
|
||||
public Transaction ToTransaction(Guid? id = null)
|
||||
{
|
||||
return new Transaction
|
||||
{
|
||||
Id = id.GetValueOrDefault(),
|
||||
UserId = UserId,
|
||||
OrganizationId = OrganizationId,
|
||||
Amount = Amount.Value,
|
||||
RefundedAmount = RefundedAmount,
|
||||
Refunded = Refunded ? true : (bool?)null,
|
||||
Details = Details,
|
||||
CreationDate = Date.Value,
|
||||
PaymentMethodType = PaymentMethod,
|
||||
Gateway = Gateway,
|
||||
GatewayId = GatewayId,
|
||||
Type = Type.Value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class CursorPagedModel<T>
|
||||
{
|
||||
public class CursorPagedModel<T>
|
||||
{
|
||||
public List<T> Items { get; set; }
|
||||
public int Count { get; set; }
|
||||
public string Cursor { get; set; }
|
||||
public string NextCursor { get; set; }
|
||||
}
|
||||
public List<T> Items { get; set; }
|
||||
public int Count { get; set; }
|
||||
public string Cursor { get; set; }
|
||||
public string NextCursor { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
namespace Bit.Admin.Models
|
||||
{
|
||||
public class ErrorViewModel
|
||||
{
|
||||
public string RequestId { get; set; }
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
}
|
||||
public class ErrorViewModel
|
||||
{
|
||||
public string RequestId { get; set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Bit.Core.Settings;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class HomeModel
|
||||
{
|
||||
public class HomeModel
|
||||
{
|
||||
public string CurrentVersion { get; set; }
|
||||
public GlobalSettings GlobalSettings { get; set; }
|
||||
}
|
||||
public string CurrentVersion { get; set; }
|
||||
public GlobalSettings GlobalSettings { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class LicenseModel : IValidatableObject
|
||||
{
|
||||
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)
|
||||
{
|
||||
[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)
|
||||
{
|
||||
if (UserId.HasValue && OrganizationId.HasValue)
|
||||
{
|
||||
yield return new ValidationResult("Use either User Id or Organization Id. Not both.");
|
||||
}
|
||||
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 (!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.");
|
||||
}
|
||||
if (OrganizationId.HasValue && !InstallationId.HasValue)
|
||||
{
|
||||
yield return new ValidationResult("Installation Id is required for organization licenses.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,54 @@
|
||||
using Microsoft.Azure.Documents;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class LogModel : Resource
|
||||
{
|
||||
public class LogModel : Resource
|
||||
{
|
||||
public long EventIdHash { get; set; }
|
||||
public string Level { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string MessageTruncated => Message.Length > 200 ? $"{Message.Substring(0, 200)}..." : Message;
|
||||
public string MessageTemplate { get; set; }
|
||||
public IDictionary<string, object> Properties { get; set; }
|
||||
public string Project => Properties?.ContainsKey("Project") ?? false ? Properties["Project"].ToString() : null;
|
||||
}
|
||||
public long EventIdHash { get; set; }
|
||||
public string Level { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string MessageTruncated => Message.Length > 200 ? $"{Message.Substring(0, 200)}..." : Message;
|
||||
public string MessageTemplate { get; set; }
|
||||
public IDictionary<string, object> Properties { get; set; }
|
||||
public string Project => Properties?.ContainsKey("Project") ?? false ? Properties["Project"].ToString() : null;
|
||||
}
|
||||
|
||||
public class LogDetailsModel : LogModel
|
||||
{
|
||||
public JObject Exception { get; set; }
|
||||
public class LogDetailsModel : LogModel
|
||||
{
|
||||
public JObject Exception { get; set; }
|
||||
|
||||
public string ExceptionToString(JObject e)
|
||||
public string ExceptionToString(JObject e)
|
||||
{
|
||||
if (e == null)
|
||||
{
|
||||
if (e == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var val = string.Empty;
|
||||
if (e["Message"] != null && e["Message"].ToObject<string>() != null)
|
||||
{
|
||||
val += "Message:\n";
|
||||
val += e["Message"] + "\n";
|
||||
}
|
||||
|
||||
if (e["StackTrace"] != null && e["StackTrace"].ToObject<string>() != null)
|
||||
{
|
||||
val += "\nStack Trace:\n";
|
||||
val += e["StackTrace"];
|
||||
}
|
||||
else if (e["StackTraceString"] != null && e["StackTraceString"].ToObject<string>() != null)
|
||||
{
|
||||
val += "\nStack Trace String:\n";
|
||||
val += e["StackTraceString"];
|
||||
}
|
||||
|
||||
if (e["InnerException"] != null && e["InnerException"].ToObject<JObject>() != null)
|
||||
{
|
||||
val += "\n\n=== Inner Exception ===\n\n";
|
||||
val += ExceptionToString(e["InnerException"].ToObject<JObject>());
|
||||
}
|
||||
|
||||
return val;
|
||||
return null;
|
||||
}
|
||||
|
||||
var val = string.Empty;
|
||||
if (e["Message"] != null && e["Message"].ToObject<string>() != null)
|
||||
{
|
||||
val += "Message:\n";
|
||||
val += e["Message"] + "\n";
|
||||
}
|
||||
|
||||
if (e["StackTrace"] != null && e["StackTrace"].ToObject<string>() != null)
|
||||
{
|
||||
val += "\nStack Trace:\n";
|
||||
val += e["StackTrace"];
|
||||
}
|
||||
else if (e["StackTraceString"] != null && e["StackTraceString"].ToObject<string>() != null)
|
||||
{
|
||||
val += "\nStack Trace String:\n";
|
||||
val += e["StackTraceString"];
|
||||
}
|
||||
|
||||
if (e["InnerException"] != null && e["InnerException"].ToObject<JObject>() != null)
|
||||
{
|
||||
val += "\n\n=== Inner Exception ===\n\n";
|
||||
val += ExceptionToString(e["InnerException"].ToObject<JObject>());
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class LoginModel
|
||||
{
|
||||
public class LoginModel
|
||||
{
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
public string Error { get; set; }
|
||||
public string Success { get; set; }
|
||||
}
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
public string Error { get; set; }
|
||||
public string Success { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Serilog.Events;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class LogsModel : CursorPagedModel<LogModel>
|
||||
{
|
||||
public class LogsModel : CursorPagedModel<LogModel>
|
||||
{
|
||||
public LogEventLevel? Level { get; set; }
|
||||
public string Project { get; set; }
|
||||
public DateTime? Start { get; set; }
|
||||
public DateTime? End { get; set; }
|
||||
}
|
||||
public LogEventLevel? Level { get; set; }
|
||||
public string Project { get; set; }
|
||||
public DateTime? Start { get; set; }
|
||||
public DateTime? End { get; set; }
|
||||
}
|
||||
|
||||
@@ -6,148 +6,147 @@ using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class OrganizationEditModel : OrganizationViewModel
|
||||
{
|
||||
public class OrganizationEditModel : OrganizationViewModel
|
||||
public OrganizationEditModel() { }
|
||||
|
||||
public OrganizationEditModel(Organization org, IEnumerable<OrganizationUserUserDetails> orgUsers,
|
||||
IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections, IEnumerable<Group> groups,
|
||||
IEnumerable<Policy> policies, BillingInfo billingInfo, IEnumerable<OrganizationConnection> connections,
|
||||
GlobalSettings globalSettings)
|
||||
: base(org, connections, orgUsers, ciphers, collections, groups, policies)
|
||||
{
|
||||
public OrganizationEditModel() { }
|
||||
BillingInfo = billingInfo;
|
||||
BraintreeMerchantId = globalSettings.Braintree.MerchantId;
|
||||
|
||||
public OrganizationEditModel(Organization org, IEnumerable<OrganizationUserUserDetails> orgUsers,
|
||||
IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections, IEnumerable<Group> groups,
|
||||
IEnumerable<Policy> policies, BillingInfo billingInfo, IEnumerable<OrganizationConnection> connections,
|
||||
GlobalSettings globalSettings)
|
||||
: base(org, connections, orgUsers, ciphers, collections, groups, policies)
|
||||
{
|
||||
BillingInfo = billingInfo;
|
||||
BraintreeMerchantId = globalSettings.Braintree.MerchantId;
|
||||
Name = org.Name;
|
||||
BusinessName = org.BusinessName;
|
||||
BillingEmail = org.BillingEmail;
|
||||
PlanType = org.PlanType;
|
||||
Plan = org.Plan;
|
||||
Seats = org.Seats;
|
||||
MaxAutoscaleSeats = org.MaxAutoscaleSeats;
|
||||
MaxCollections = org.MaxCollections;
|
||||
UsePolicies = org.UsePolicies;
|
||||
UseSso = org.UseSso;
|
||||
UseKeyConnector = org.UseKeyConnector;
|
||||
UseScim = org.UseScim;
|
||||
UseGroups = org.UseGroups;
|
||||
UseDirectory = org.UseDirectory;
|
||||
UseEvents = org.UseEvents;
|
||||
UseTotp = org.UseTotp;
|
||||
Use2fa = org.Use2fa;
|
||||
UseApi = org.UseApi;
|
||||
UseResetPassword = org.UseResetPassword;
|
||||
SelfHost = org.SelfHost;
|
||||
UsersGetPremium = org.UsersGetPremium;
|
||||
MaxStorageGb = org.MaxStorageGb;
|
||||
Gateway = org.Gateway;
|
||||
GatewayCustomerId = org.GatewayCustomerId;
|
||||
GatewaySubscriptionId = org.GatewaySubscriptionId;
|
||||
Enabled = org.Enabled;
|
||||
LicenseKey = org.LicenseKey;
|
||||
ExpirationDate = org.ExpirationDate;
|
||||
}
|
||||
|
||||
Name = org.Name;
|
||||
BusinessName = org.BusinessName;
|
||||
BillingEmail = org.BillingEmail;
|
||||
PlanType = org.PlanType;
|
||||
Plan = org.Plan;
|
||||
Seats = org.Seats;
|
||||
MaxAutoscaleSeats = org.MaxAutoscaleSeats;
|
||||
MaxCollections = org.MaxCollections;
|
||||
UsePolicies = org.UsePolicies;
|
||||
UseSso = org.UseSso;
|
||||
UseKeyConnector = org.UseKeyConnector;
|
||||
UseScim = org.UseScim;
|
||||
UseGroups = org.UseGroups;
|
||||
UseDirectory = org.UseDirectory;
|
||||
UseEvents = org.UseEvents;
|
||||
UseTotp = org.UseTotp;
|
||||
Use2fa = org.Use2fa;
|
||||
UseApi = org.UseApi;
|
||||
UseResetPassword = org.UseResetPassword;
|
||||
SelfHost = org.SelfHost;
|
||||
UsersGetPremium = org.UsersGetPremium;
|
||||
MaxStorageGb = org.MaxStorageGb;
|
||||
Gateway = org.Gateway;
|
||||
GatewayCustomerId = org.GatewayCustomerId;
|
||||
GatewaySubscriptionId = org.GatewaySubscriptionId;
|
||||
Enabled = org.Enabled;
|
||||
LicenseKey = org.LicenseKey;
|
||||
ExpirationDate = org.ExpirationDate;
|
||||
}
|
||||
public BillingInfo BillingInfo { get; set; }
|
||||
public string RandomLicenseKey => CoreHelpers.SecureRandomString(20);
|
||||
public string FourteenDayExpirationDate => DateTime.Now.AddDays(14).ToString("yyyy-MM-ddTHH:mm");
|
||||
public string BraintreeMerchantId { get; set; }
|
||||
|
||||
public BillingInfo BillingInfo { get; set; }
|
||||
public string RandomLicenseKey => CoreHelpers.SecureRandomString(20);
|
||||
public string FourteenDayExpirationDate => DateTime.Now.AddDays(14).ToString("yyyy-MM-ddTHH:mm");
|
||||
public string BraintreeMerchantId { get; set; }
|
||||
[Required]
|
||||
[Display(Name = "Name")]
|
||||
public string Name { get; set; }
|
||||
[Display(Name = "Business Name")]
|
||||
public string BusinessName { get; set; }
|
||||
[Display(Name = "Billing Email")]
|
||||
public string BillingEmail { get; set; }
|
||||
[Required]
|
||||
[Display(Name = "Plan")]
|
||||
public PlanType? PlanType { get; set; }
|
||||
[Required]
|
||||
[Display(Name = "Plan Name")]
|
||||
public string Plan { get; set; }
|
||||
[Display(Name = "Seats")]
|
||||
public int? Seats { get; set; }
|
||||
[Display(Name = "Max. Autoscale Seats")]
|
||||
public int? MaxAutoscaleSeats { get; set; }
|
||||
[Display(Name = "Max. Collections")]
|
||||
public short? MaxCollections { get; set; }
|
||||
[Display(Name = "Policies")]
|
||||
public bool UsePolicies { get; set; }
|
||||
[Display(Name = "SSO")]
|
||||
public bool UseSso { get; set; }
|
||||
[Display(Name = "Key Connector with Customer Encryption")]
|
||||
public bool UseKeyConnector { get; set; }
|
||||
[Display(Name = "Groups")]
|
||||
public bool UseGroups { get; set; }
|
||||
[Display(Name = "Directory")]
|
||||
public bool UseDirectory { get; set; }
|
||||
[Display(Name = "Events")]
|
||||
public bool UseEvents { get; set; }
|
||||
[Display(Name = "TOTP")]
|
||||
public bool UseTotp { get; set; }
|
||||
[Display(Name = "2FA")]
|
||||
public bool Use2fa { get; set; }
|
||||
[Display(Name = "API")]
|
||||
public bool UseApi { get; set; }
|
||||
[Display(Name = "Reset Password")]
|
||||
public bool UseResetPassword { get; set; }
|
||||
[Display(Name = "SCIM")]
|
||||
public bool UseScim { get; set; }
|
||||
[Display(Name = "Self Host")]
|
||||
public bool SelfHost { get; set; }
|
||||
[Display(Name = "Users Get Premium")]
|
||||
public bool UsersGetPremium { get; set; }
|
||||
[Display(Name = "Max. Storage GB")]
|
||||
public short? MaxStorageGb { get; set; }
|
||||
[Display(Name = "Gateway")]
|
||||
public GatewayType? Gateway { get; set; }
|
||||
[Display(Name = "Gateway Customer Id")]
|
||||
public string GatewayCustomerId { get; set; }
|
||||
[Display(Name = "Gateway Subscription Id")]
|
||||
public string GatewaySubscriptionId { get; set; }
|
||||
[Display(Name = "Enabled")]
|
||||
public bool Enabled { get; set; }
|
||||
[Display(Name = "License Key")]
|
||||
public string LicenseKey { get; set; }
|
||||
[Display(Name = "Expiration Date")]
|
||||
public DateTime? ExpirationDate { get; set; }
|
||||
public bool SalesAssistedTrialStarted { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Name")]
|
||||
public string Name { get; set; }
|
||||
[Display(Name = "Business Name")]
|
||||
public string BusinessName { get; set; }
|
||||
[Display(Name = "Billing Email")]
|
||||
public string BillingEmail { get; set; }
|
||||
[Required]
|
||||
[Display(Name = "Plan")]
|
||||
public PlanType? PlanType { get; set; }
|
||||
[Required]
|
||||
[Display(Name = "Plan Name")]
|
||||
public string Plan { get; set; }
|
||||
[Display(Name = "Seats")]
|
||||
public int? Seats { get; set; }
|
||||
[Display(Name = "Max. Autoscale Seats")]
|
||||
public int? MaxAutoscaleSeats { get; set; }
|
||||
[Display(Name = "Max. Collections")]
|
||||
public short? MaxCollections { get; set; }
|
||||
[Display(Name = "Policies")]
|
||||
public bool UsePolicies { get; set; }
|
||||
[Display(Name = "SSO")]
|
||||
public bool UseSso { get; set; }
|
||||
[Display(Name = "Key Connector with Customer Encryption")]
|
||||
public bool UseKeyConnector { get; set; }
|
||||
[Display(Name = "Groups")]
|
||||
public bool UseGroups { get; set; }
|
||||
[Display(Name = "Directory")]
|
||||
public bool UseDirectory { get; set; }
|
||||
[Display(Name = "Events")]
|
||||
public bool UseEvents { get; set; }
|
||||
[Display(Name = "TOTP")]
|
||||
public bool UseTotp { get; set; }
|
||||
[Display(Name = "2FA")]
|
||||
public bool Use2fa { get; set; }
|
||||
[Display(Name = "API")]
|
||||
public bool UseApi { get; set; }
|
||||
[Display(Name = "Reset Password")]
|
||||
public bool UseResetPassword { get; set; }
|
||||
[Display(Name = "SCIM")]
|
||||
public bool UseScim { get; set; }
|
||||
[Display(Name = "Self Host")]
|
||||
public bool SelfHost { get; set; }
|
||||
[Display(Name = "Users Get Premium")]
|
||||
public bool UsersGetPremium { get; set; }
|
||||
[Display(Name = "Max. Storage GB")]
|
||||
public short? MaxStorageGb { get; set; }
|
||||
[Display(Name = "Gateway")]
|
||||
public GatewayType? Gateway { get; set; }
|
||||
[Display(Name = "Gateway Customer Id")]
|
||||
public string GatewayCustomerId { get; set; }
|
||||
[Display(Name = "Gateway Subscription Id")]
|
||||
public string GatewaySubscriptionId { get; set; }
|
||||
[Display(Name = "Enabled")]
|
||||
public bool Enabled { get; set; }
|
||||
[Display(Name = "License Key")]
|
||||
public string LicenseKey { get; set; }
|
||||
[Display(Name = "Expiration Date")]
|
||||
public DateTime? ExpirationDate { get; set; }
|
||||
public bool SalesAssistedTrialStarted { get; set; }
|
||||
|
||||
public Organization ToOrganization(Organization existingOrganization)
|
||||
{
|
||||
existingOrganization.Name = Name;
|
||||
existingOrganization.BusinessName = BusinessName;
|
||||
existingOrganization.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim();
|
||||
existingOrganization.PlanType = PlanType.Value;
|
||||
existingOrganization.Plan = Plan;
|
||||
existingOrganization.Seats = Seats;
|
||||
existingOrganization.MaxCollections = MaxCollections;
|
||||
existingOrganization.UsePolicies = UsePolicies;
|
||||
existingOrganization.UseSso = UseSso;
|
||||
existingOrganization.UseKeyConnector = UseKeyConnector;
|
||||
existingOrganization.UseScim = UseScim;
|
||||
existingOrganization.UseGroups = UseGroups;
|
||||
existingOrganization.UseDirectory = UseDirectory;
|
||||
existingOrganization.UseEvents = UseEvents;
|
||||
existingOrganization.UseTotp = UseTotp;
|
||||
existingOrganization.Use2fa = Use2fa;
|
||||
existingOrganization.UseApi = UseApi;
|
||||
existingOrganization.UseResetPassword = UseResetPassword;
|
||||
existingOrganization.SelfHost = SelfHost;
|
||||
existingOrganization.UsersGetPremium = UsersGetPremium;
|
||||
existingOrganization.MaxStorageGb = MaxStorageGb;
|
||||
existingOrganization.Gateway = Gateway;
|
||||
existingOrganization.GatewayCustomerId = GatewayCustomerId;
|
||||
existingOrganization.GatewaySubscriptionId = GatewaySubscriptionId;
|
||||
existingOrganization.Enabled = Enabled;
|
||||
existingOrganization.LicenseKey = LicenseKey;
|
||||
existingOrganization.ExpirationDate = ExpirationDate;
|
||||
existingOrganization.MaxAutoscaleSeats = MaxAutoscaleSeats;
|
||||
return existingOrganization;
|
||||
}
|
||||
public Organization ToOrganization(Organization existingOrganization)
|
||||
{
|
||||
existingOrganization.Name = Name;
|
||||
existingOrganization.BusinessName = BusinessName;
|
||||
existingOrganization.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim();
|
||||
existingOrganization.PlanType = PlanType.Value;
|
||||
existingOrganization.Plan = Plan;
|
||||
existingOrganization.Seats = Seats;
|
||||
existingOrganization.MaxCollections = MaxCollections;
|
||||
existingOrganization.UsePolicies = UsePolicies;
|
||||
existingOrganization.UseSso = UseSso;
|
||||
existingOrganization.UseKeyConnector = UseKeyConnector;
|
||||
existingOrganization.UseScim = UseScim;
|
||||
existingOrganization.UseGroups = UseGroups;
|
||||
existingOrganization.UseDirectory = UseDirectory;
|
||||
existingOrganization.UseEvents = UseEvents;
|
||||
existingOrganization.UseTotp = UseTotp;
|
||||
existingOrganization.Use2fa = Use2fa;
|
||||
existingOrganization.UseApi = UseApi;
|
||||
existingOrganization.UseResetPassword = UseResetPassword;
|
||||
existingOrganization.SelfHost = SelfHost;
|
||||
existingOrganization.UsersGetPremium = UsersGetPremium;
|
||||
existingOrganization.MaxStorageGb = MaxStorageGb;
|
||||
existingOrganization.Gateway = Gateway;
|
||||
existingOrganization.GatewayCustomerId = GatewayCustomerId;
|
||||
existingOrganization.GatewaySubscriptionId = GatewaySubscriptionId;
|
||||
existingOrganization.Enabled = Enabled;
|
||||
existingOrganization.LicenseKey = LicenseKey;
|
||||
existingOrganization.ExpirationDate = ExpirationDate;
|
||||
existingOrganization.MaxAutoscaleSeats = MaxAutoscaleSeats;
|
||||
return existingOrganization;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,49 +2,48 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class OrganizationViewModel
|
||||
{
|
||||
public class OrganizationViewModel
|
||||
public OrganizationViewModel() { }
|
||||
|
||||
public OrganizationViewModel(Organization org, IEnumerable<OrganizationConnection> connections,
|
||||
IEnumerable<OrganizationUserUserDetails> orgUsers, IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections,
|
||||
IEnumerable<Group> groups, IEnumerable<Policy> policies)
|
||||
{
|
||||
public OrganizationViewModel() { }
|
||||
|
||||
public OrganizationViewModel(Organization org, IEnumerable<OrganizationConnection> connections,
|
||||
IEnumerable<OrganizationUserUserDetails> orgUsers, IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections,
|
||||
IEnumerable<Group> groups, IEnumerable<Policy> policies)
|
||||
{
|
||||
Organization = org;
|
||||
Connections = connections ?? Enumerable.Empty<OrganizationConnection>();
|
||||
HasPublicPrivateKeys = org.PublicKey != null && org.PrivateKey != null;
|
||||
UserInvitedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Invited);
|
||||
UserAcceptedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Accepted);
|
||||
UserConfirmedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Confirmed);
|
||||
UserCount = orgUsers.Count();
|
||||
CipherCount = ciphers.Count();
|
||||
CollectionCount = collections.Count();
|
||||
GroupCount = groups?.Count() ?? 0;
|
||||
PolicyCount = policies?.Count() ?? 0;
|
||||
Owners = string.Join(", ",
|
||||
orgUsers
|
||||
.Where(u => u.Type == OrganizationUserType.Owner && u.Status == OrganizationUserStatusType.Confirmed)
|
||||
.Select(u => u.Email));
|
||||
Admins = string.Join(", ",
|
||||
orgUsers
|
||||
.Where(u => u.Type == OrganizationUserType.Admin && u.Status == OrganizationUserStatusType.Confirmed)
|
||||
.Select(u => u.Email));
|
||||
}
|
||||
|
||||
public Organization Organization { get; set; }
|
||||
public IEnumerable<OrganizationConnection> Connections { get; set; }
|
||||
public string Owners { get; set; }
|
||||
public string Admins { get; set; }
|
||||
public int UserInvitedCount { get; set; }
|
||||
public int UserConfirmedCount { get; set; }
|
||||
public int UserAcceptedCount { get; set; }
|
||||
public int UserCount { get; set; }
|
||||
public int CipherCount { get; set; }
|
||||
public int CollectionCount { get; set; }
|
||||
public int GroupCount { get; set; }
|
||||
public int PolicyCount { get; set; }
|
||||
public bool HasPublicPrivateKeys { get; set; }
|
||||
Organization = org;
|
||||
Connections = connections ?? Enumerable.Empty<OrganizationConnection>();
|
||||
HasPublicPrivateKeys = org.PublicKey != null && org.PrivateKey != null;
|
||||
UserInvitedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Invited);
|
||||
UserAcceptedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Accepted);
|
||||
UserConfirmedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Confirmed);
|
||||
UserCount = orgUsers.Count();
|
||||
CipherCount = ciphers.Count();
|
||||
CollectionCount = collections.Count();
|
||||
GroupCount = groups?.Count() ?? 0;
|
||||
PolicyCount = policies?.Count() ?? 0;
|
||||
Owners = string.Join(", ",
|
||||
orgUsers
|
||||
.Where(u => u.Type == OrganizationUserType.Owner && u.Status == OrganizationUserStatusType.Confirmed)
|
||||
.Select(u => u.Email));
|
||||
Admins = string.Join(", ",
|
||||
orgUsers
|
||||
.Where(u => u.Type == OrganizationUserType.Admin && u.Status == OrganizationUserStatusType.Confirmed)
|
||||
.Select(u => u.Email));
|
||||
}
|
||||
|
||||
public Organization Organization { get; set; }
|
||||
public IEnumerable<OrganizationConnection> Connections { get; set; }
|
||||
public string Owners { get; set; }
|
||||
public string Admins { get; set; }
|
||||
public int UserInvitedCount { get; set; }
|
||||
public int UserConfirmedCount { get; set; }
|
||||
public int UserAcceptedCount { get; set; }
|
||||
public int UserCount { get; set; }
|
||||
public int CipherCount { get; set; }
|
||||
public int CollectionCount { get; set; }
|
||||
public int GroupCount { get; set; }
|
||||
public int PolicyCount { get; set; }
|
||||
public bool HasPublicPrivateKeys { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using Bit.Core.Entities;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class OrganizationsModel : PagedModel<Organization>
|
||||
{
|
||||
public class OrganizationsModel : PagedModel<Organization>
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string UserEmail { get; set; }
|
||||
public bool? Paid { get; set; }
|
||||
public string Action { get; set; }
|
||||
public bool SelfHosted { get; set; }
|
||||
}
|
||||
public string Name { get; set; }
|
||||
public string UserEmail { get; set; }
|
||||
public bool? Paid { get; set; }
|
||||
public string Action { get; set; }
|
||||
public bool SelfHosted { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public abstract class PagedModel<T>
|
||||
{
|
||||
public abstract class PagedModel<T>
|
||||
{
|
||||
public List<T> Items { get; set; }
|
||||
public int Page { get; set; }
|
||||
public int Count { get; set; }
|
||||
public int? PreviousPage => Page < 2 ? (int?)null : Page - 1;
|
||||
public int? NextPage => Items.Count < Count ? (int?)null : Page + 1;
|
||||
}
|
||||
public List<T> Items { get; set; }
|
||||
public int Page { get; set; }
|
||||
public int Count { get; set; }
|
||||
public int? PreviousPage => Page < 2 ? (int?)null : Page - 1;
|
||||
public int? NextPage => Items.Count < Count ? (int?)null : Page + 1;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class PromoteAdminModel
|
||||
{
|
||||
public class PromoteAdminModel
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "Admin User Id")]
|
||||
public Guid? UserId { get; set; }
|
||||
[Required]
|
||||
[Display(Name = "Organization Id")]
|
||||
public Guid? OrganizationId { get; set; }
|
||||
}
|
||||
[Required]
|
||||
[Display(Name = "Admin User Id")]
|
||||
public Guid? UserId { get; set; }
|
||||
[Required]
|
||||
[Display(Name = "Organization Id")]
|
||||
public Guid? OrganizationId { get; set; }
|
||||
}
|
||||
|
||||
@@ -2,33 +2,32 @@
|
||||
using Bit.Core.Entities.Provider;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class ProviderEditModel : ProviderViewModel
|
||||
{
|
||||
public class ProviderEditModel : ProviderViewModel
|
||||
public ProviderEditModel() { }
|
||||
|
||||
public ProviderEditModel(Provider provider, IEnumerable<ProviderUserUserDetails> providerUsers, IEnumerable<ProviderOrganizationOrganizationDetails> organizations)
|
||||
: base(provider, providerUsers, organizations)
|
||||
{
|
||||
public ProviderEditModel() { }
|
||||
Name = provider.Name;
|
||||
BusinessName = provider.BusinessName;
|
||||
BillingEmail = provider.BillingEmail;
|
||||
}
|
||||
|
||||
public ProviderEditModel(Provider provider, IEnumerable<ProviderUserUserDetails> providerUsers, IEnumerable<ProviderOrganizationOrganizationDetails> organizations)
|
||||
: base(provider, providerUsers, organizations)
|
||||
{
|
||||
Name = provider.Name;
|
||||
BusinessName = provider.BusinessName;
|
||||
BillingEmail = provider.BillingEmail;
|
||||
}
|
||||
[Display(Name = "Billing Email")]
|
||||
public string BillingEmail { get; set; }
|
||||
[Display(Name = "Business Name")]
|
||||
public string BusinessName { get; set; }
|
||||
public string Name { get; set; }
|
||||
[Display(Name = "Events")]
|
||||
|
||||
[Display(Name = "Billing Email")]
|
||||
public string BillingEmail { get; set; }
|
||||
[Display(Name = "Business Name")]
|
||||
public string BusinessName { get; set; }
|
||||
public string Name { get; set; }
|
||||
[Display(Name = "Events")]
|
||||
|
||||
public Provider ToProvider(Provider existingProvider)
|
||||
{
|
||||
existingProvider.Name = Name;
|
||||
existingProvider.BusinessName = BusinessName;
|
||||
existingProvider.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim();
|
||||
return existingProvider;
|
||||
}
|
||||
public Provider ToProvider(Provider existingProvider)
|
||||
{
|
||||
existingProvider.Name = Name;
|
||||
existingProvider.BusinessName = BusinessName;
|
||||
existingProvider.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim();
|
||||
return existingProvider;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,23 @@
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class ProviderViewModel
|
||||
{
|
||||
public class ProviderViewModel
|
||||
public ProviderViewModel() { }
|
||||
|
||||
public ProviderViewModel(Provider provider, IEnumerable<ProviderUserUserDetails> providerUsers, IEnumerable<ProviderOrganizationOrganizationDetails> organizations)
|
||||
{
|
||||
public ProviderViewModel() { }
|
||||
Provider = provider;
|
||||
UserCount = providerUsers.Count();
|
||||
ProviderAdmins = providerUsers.Where(u => u.Type == ProviderUserType.ProviderAdmin);
|
||||
|
||||
public ProviderViewModel(Provider provider, IEnumerable<ProviderUserUserDetails> providerUsers, IEnumerable<ProviderOrganizationOrganizationDetails> organizations)
|
||||
{
|
||||
Provider = provider;
|
||||
UserCount = providerUsers.Count();
|
||||
ProviderAdmins = providerUsers.Where(u => u.Type == ProviderUserType.ProviderAdmin);
|
||||
|
||||
ProviderOrganizations = organizations.Where(o => o.ProviderId == provider.Id);
|
||||
}
|
||||
|
||||
public int UserCount { get; set; }
|
||||
public Provider Provider { get; set; }
|
||||
public IEnumerable<ProviderUserUserDetails> ProviderAdmins { get; set; }
|
||||
public IEnumerable<ProviderOrganizationOrganizationDetails> ProviderOrganizations { get; set; }
|
||||
ProviderOrganizations = organizations.Where(o => o.ProviderId == provider.Id);
|
||||
}
|
||||
|
||||
public int UserCount { get; set; }
|
||||
public Provider Provider { get; set; }
|
||||
public IEnumerable<ProviderUserUserDetails> ProviderAdmins { get; set; }
|
||||
public IEnumerable<ProviderOrganizationOrganizationDetails> ProviderOrganizations { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using Bit.Core.Entities.Provider;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class ProvidersModel : PagedModel<Provider>
|
||||
{
|
||||
public class ProvidersModel : PagedModel<Provider>
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string UserEmail { get; set; }
|
||||
public bool? Paid { get; set; }
|
||||
public string Action { get; set; }
|
||||
public bool SelfHosted { get; set; }
|
||||
}
|
||||
public string Name { get; set; }
|
||||
public string UserEmail { get; set; }
|
||||
public bool? Paid { get; set; }
|
||||
public string Action { get; set; }
|
||||
public bool SelfHosted { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,43 +1,42 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Models.BitStripe;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class StripeSubscriptionRowModel
|
||||
{
|
||||
public class StripeSubscriptionRowModel
|
||||
{
|
||||
public Stripe.Subscription Subscription { get; set; }
|
||||
public bool Selected { get; set; }
|
||||
public Stripe.Subscription Subscription { get; set; }
|
||||
public bool Selected { get; set; }
|
||||
|
||||
public StripeSubscriptionRowModel() { }
|
||||
public StripeSubscriptionRowModel(Stripe.Subscription subscription)
|
||||
{
|
||||
Subscription = subscription;
|
||||
}
|
||||
public StripeSubscriptionRowModel() { }
|
||||
public StripeSubscriptionRowModel(Stripe.Subscription subscription)
|
||||
{
|
||||
Subscription = subscription;
|
||||
}
|
||||
}
|
||||
|
||||
public enum StripeSubscriptionsAction
|
||||
{
|
||||
Search,
|
||||
PreviousPage,
|
||||
NextPage,
|
||||
Export,
|
||||
BulkCancel
|
||||
}
|
||||
public enum StripeSubscriptionsAction
|
||||
{
|
||||
Search,
|
||||
PreviousPage,
|
||||
NextPage,
|
||||
Export,
|
||||
BulkCancel
|
||||
}
|
||||
|
||||
public class StripeSubscriptionsModel : IValidatableObject
|
||||
public class StripeSubscriptionsModel : IValidatableObject
|
||||
{
|
||||
public List<StripeSubscriptionRowModel> Items { get; set; }
|
||||
public StripeSubscriptionsAction Action { get; set; } = StripeSubscriptionsAction.Search;
|
||||
public string Message { get; set; }
|
||||
public List<Stripe.Price> Prices { get; set; }
|
||||
public List<Stripe.TestHelpers.TestClock> TestClocks { get; set; }
|
||||
public StripeSubscriptionListOptions Filter { get; set; } = new StripeSubscriptionListOptions();
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
public List<StripeSubscriptionRowModel> Items { get; set; }
|
||||
public StripeSubscriptionsAction Action { get; set; } = StripeSubscriptionsAction.Search;
|
||||
public string Message { get; set; }
|
||||
public List<Stripe.Price> Prices { get; set; }
|
||||
public List<Stripe.TestHelpers.TestClock> TestClocks { get; set; }
|
||||
public StripeSubscriptionListOptions Filter { get; set; } = new StripeSubscriptionListOptions();
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
if (Action == StripeSubscriptionsAction.BulkCancel && Filter.Status != "unpaid")
|
||||
{
|
||||
if (Action == StripeSubscriptionsAction.BulkCancel && Filter.Status != "unpaid")
|
||||
{
|
||||
yield return new ValidationResult("Bulk cancel is currently only supported for unpaid subscriptions");
|
||||
}
|
||||
yield return new ValidationResult("Bulk cancel is currently only supported for unpaid subscriptions");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class TaxRateAddEditModel
|
||||
{
|
||||
public class TaxRateAddEditModel
|
||||
{
|
||||
public string StripeTaxRateId { get; set; }
|
||||
public string Country { get; set; }
|
||||
public string State { get; set; }
|
||||
public string PostalCode { get; set; }
|
||||
public decimal Rate { get; set; }
|
||||
}
|
||||
public string StripeTaxRateId { get; set; }
|
||||
public string Country { get; set; }
|
||||
public string State { get; set; }
|
||||
public string PostalCode { get; set; }
|
||||
public decimal Rate { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using Bit.Core.Entities;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class TaxRatesModel : PagedModel<TaxRate>
|
||||
{
|
||||
public class TaxRatesModel : PagedModel<TaxRate>
|
||||
{
|
||||
public string Message { get; set; }
|
||||
}
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
@@ -4,71 +4,70 @@ using Bit.Core.Models.Business;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class UserEditModel : UserViewModel
|
||||
{
|
||||
public class UserEditModel : UserViewModel
|
||||
public UserEditModel() { }
|
||||
|
||||
public UserEditModel(User user, IEnumerable<Cipher> ciphers, BillingInfo billingInfo,
|
||||
GlobalSettings globalSettings)
|
||||
: base(user, ciphers)
|
||||
{
|
||||
public UserEditModel() { }
|
||||
BillingInfo = billingInfo;
|
||||
BraintreeMerchantId = globalSettings.Braintree.MerchantId;
|
||||
|
||||
public UserEditModel(User user, IEnumerable<Cipher> ciphers, BillingInfo billingInfo,
|
||||
GlobalSettings globalSettings)
|
||||
: base(user, ciphers)
|
||||
{
|
||||
BillingInfo = billingInfo;
|
||||
BraintreeMerchantId = globalSettings.Braintree.MerchantId;
|
||||
Name = user.Name;
|
||||
Email = user.Email;
|
||||
EmailVerified = user.EmailVerified;
|
||||
Premium = user.Premium;
|
||||
MaxStorageGb = user.MaxStorageGb;
|
||||
Gateway = user.Gateway;
|
||||
GatewayCustomerId = user.GatewayCustomerId;
|
||||
GatewaySubscriptionId = user.GatewaySubscriptionId;
|
||||
LicenseKey = user.LicenseKey;
|
||||
PremiumExpirationDate = user.PremiumExpirationDate;
|
||||
}
|
||||
|
||||
Name = user.Name;
|
||||
Email = user.Email;
|
||||
EmailVerified = user.EmailVerified;
|
||||
Premium = user.Premium;
|
||||
MaxStorageGb = user.MaxStorageGb;
|
||||
Gateway = user.Gateway;
|
||||
GatewayCustomerId = user.GatewayCustomerId;
|
||||
GatewaySubscriptionId = user.GatewaySubscriptionId;
|
||||
LicenseKey = user.LicenseKey;
|
||||
PremiumExpirationDate = user.PremiumExpirationDate;
|
||||
}
|
||||
public BillingInfo BillingInfo { get; set; }
|
||||
public string RandomLicenseKey => CoreHelpers.SecureRandomString(20);
|
||||
public string OneYearExpirationDate => DateTime.Now.AddYears(1).ToString("yyyy-MM-ddTHH:mm");
|
||||
public string BraintreeMerchantId { get; set; }
|
||||
|
||||
public BillingInfo BillingInfo { get; set; }
|
||||
public string RandomLicenseKey => CoreHelpers.SecureRandomString(20);
|
||||
public string OneYearExpirationDate => DateTime.Now.AddYears(1).ToString("yyyy-MM-ddTHH:mm");
|
||||
public string BraintreeMerchantId { get; set; }
|
||||
[Display(Name = "Name")]
|
||||
public string Name { get; set; }
|
||||
[Required]
|
||||
[Display(Name = "Email")]
|
||||
public string Email { get; set; }
|
||||
[Display(Name = "Email Verified")]
|
||||
public bool EmailVerified { get; set; }
|
||||
[Display(Name = "Premium")]
|
||||
public bool Premium { get; set; }
|
||||
[Display(Name = "Max. Storage GB")]
|
||||
public short? MaxStorageGb { get; set; }
|
||||
[Display(Name = "Gateway")]
|
||||
public Core.Enums.GatewayType? Gateway { get; set; }
|
||||
[Display(Name = "Gateway Customer Id")]
|
||||
public string GatewayCustomerId { get; set; }
|
||||
[Display(Name = "Gateway Subscription Id")]
|
||||
public string GatewaySubscriptionId { get; set; }
|
||||
[Display(Name = "License Key")]
|
||||
public string LicenseKey { get; set; }
|
||||
[Display(Name = "Premium Expiration Date")]
|
||||
public DateTime? PremiumExpirationDate { get; set; }
|
||||
|
||||
[Display(Name = "Name")]
|
||||
public string Name { get; set; }
|
||||
[Required]
|
||||
[Display(Name = "Email")]
|
||||
public string Email { get; set; }
|
||||
[Display(Name = "Email Verified")]
|
||||
public bool EmailVerified { get; set; }
|
||||
[Display(Name = "Premium")]
|
||||
public bool Premium { get; set; }
|
||||
[Display(Name = "Max. Storage GB")]
|
||||
public short? MaxStorageGb { get; set; }
|
||||
[Display(Name = "Gateway")]
|
||||
public Core.Enums.GatewayType? Gateway { get; set; }
|
||||
[Display(Name = "Gateway Customer Id")]
|
||||
public string GatewayCustomerId { get; set; }
|
||||
[Display(Name = "Gateway Subscription Id")]
|
||||
public string GatewaySubscriptionId { get; set; }
|
||||
[Display(Name = "License Key")]
|
||||
public string LicenseKey { get; set; }
|
||||
[Display(Name = "Premium Expiration Date")]
|
||||
public DateTime? PremiumExpirationDate { get; set; }
|
||||
|
||||
public User ToUser(User existingUser)
|
||||
{
|
||||
existingUser.Name = Name;
|
||||
existingUser.Email = Email;
|
||||
existingUser.EmailVerified = EmailVerified;
|
||||
existingUser.Premium = Premium;
|
||||
existingUser.MaxStorageGb = MaxStorageGb;
|
||||
existingUser.Gateway = Gateway;
|
||||
existingUser.GatewayCustomerId = GatewayCustomerId;
|
||||
existingUser.GatewaySubscriptionId = GatewaySubscriptionId;
|
||||
existingUser.LicenseKey = LicenseKey;
|
||||
existingUser.PremiumExpirationDate = PremiumExpirationDate;
|
||||
return existingUser;
|
||||
}
|
||||
public User ToUser(User existingUser)
|
||||
{
|
||||
existingUser.Name = Name;
|
||||
existingUser.Email = Email;
|
||||
existingUser.EmailVerified = EmailVerified;
|
||||
existingUser.Premium = Premium;
|
||||
existingUser.MaxStorageGb = MaxStorageGb;
|
||||
existingUser.Gateway = Gateway;
|
||||
existingUser.GatewayCustomerId = GatewayCustomerId;
|
||||
existingUser.GatewaySubscriptionId = GatewaySubscriptionId;
|
||||
existingUser.LicenseKey = LicenseKey;
|
||||
existingUser.PremiumExpirationDate = PremiumExpirationDate;
|
||||
return existingUser;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
using Bit.Core.Entities;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class UserViewModel
|
||||
{
|
||||
public class UserViewModel
|
||||
public UserViewModel() { }
|
||||
|
||||
public UserViewModel(User user, IEnumerable<Cipher> ciphers)
|
||||
{
|
||||
public UserViewModel() { }
|
||||
|
||||
public UserViewModel(User user, IEnumerable<Cipher> ciphers)
|
||||
{
|
||||
User = user;
|
||||
CipherCount = ciphers.Count();
|
||||
}
|
||||
|
||||
public User User { get; set; }
|
||||
public int CipherCount { get; set; }
|
||||
User = user;
|
||||
CipherCount = ciphers.Count();
|
||||
}
|
||||
|
||||
public User User { get; set; }
|
||||
public int CipherCount { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Bit.Core.Entities;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
namespace Bit.Admin.Models;
|
||||
|
||||
public class UsersModel : PagedModel<User>
|
||||
{
|
||||
public class UsersModel : PagedModel<User>
|
||||
{
|
||||
public string Email { get; set; }
|
||||
public string Action { get; set; }
|
||||
}
|
||||
public string Email { get; set; }
|
||||
public string Action { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,37 +1,36 @@
|
||||
using Bit.Core.Utilities;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace Bit.Admin
|
||||
namespace Bit.Admin;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public class Program
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Host
|
||||
.CreateDefaultBuilder(args)
|
||||
.ConfigureCustomAppConfiguration(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
Host
|
||||
.CreateDefaultBuilder(args)
|
||||
.ConfigureCustomAppConfiguration(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.ConfigureKestrel(o =>
|
||||
{
|
||||
webBuilder.ConfigureKestrel(o =>
|
||||
o.Limits.MaxRequestLineSize = 20_000;
|
||||
});
|
||||
webBuilder.UseStartup<Startup>();
|
||||
webBuilder.ConfigureLogging((hostingContext, logging) =>
|
||||
logging.AddSerilog(hostingContext, e =>
|
||||
{
|
||||
var context = e.Properties["SourceContext"].ToString();
|
||||
if (e.Properties.ContainsKey("RequestPath") &&
|
||||
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
|
||||
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
||||
{
|
||||
o.Limits.MaxRequestLineSize = 20_000;
|
||||
});
|
||||
webBuilder.UseStartup<Startup>();
|
||||
webBuilder.ConfigureLogging((hostingContext, logging) =>
|
||||
logging.AddSerilog(hostingContext, e =>
|
||||
{
|
||||
var context = e.Properties["SourceContext"].ToString();
|
||||
if (e.Properties.ContainsKey("RequestPath") &&
|
||||
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
|
||||
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return e.Level >= LogEventLevel.Error;
|
||||
}));
|
||||
})
|
||||
.Build()
|
||||
.Run();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return e.Level >= LogEventLevel.Error;
|
||||
}));
|
||||
})
|
||||
.Build()
|
||||
.Run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,128 +11,127 @@ using Stripe;
|
||||
using Bit.Commercial.Core.Utilities;
|
||||
#endif
|
||||
|
||||
namespace Bit.Admin
|
||||
namespace Bit.Admin;
|
||||
|
||||
public class Startup
|
||||
{
|
||||
public class Startup
|
||||
public Startup(IWebHostEnvironment env, IConfiguration configuration)
|
||||
{
|
||||
public Startup(IWebHostEnvironment env, IConfiguration configuration)
|
||||
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
|
||||
Configuration = configuration;
|
||||
Environment = env;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; private set; }
|
||||
public IWebHostEnvironment Environment { get; set; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// Options
|
||||
services.AddOptions();
|
||||
|
||||
// Settings
|
||||
var globalSettings = services.AddGlobalSettingsServices(Configuration, Environment);
|
||||
services.Configure<AdminSettings>(Configuration.GetSection("AdminSettings"));
|
||||
|
||||
// Data Protection
|
||||
services.AddCustomDataProtectionServices(Environment, globalSettings);
|
||||
|
||||
// Stripe Billing
|
||||
StripeConfiguration.ApiKey = globalSettings.Stripe.ApiKey;
|
||||
StripeConfiguration.MaxNetworkRetries = globalSettings.Stripe.MaxNetworkRetries;
|
||||
|
||||
// Repositories
|
||||
services.AddSqlServerRepositories(globalSettings);
|
||||
|
||||
// Context
|
||||
services.AddScoped<ICurrentContext, CurrentContext>();
|
||||
|
||||
// Identity
|
||||
services.AddPasswordlessIdentityServices<ReadOnlyEnvIdentityUserStore>(globalSettings);
|
||||
services.Configure<SecurityStampValidatorOptions>(options =>
|
||||
{
|
||||
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
|
||||
Configuration = configuration;
|
||||
Environment = env;
|
||||
options.ValidationInterval = TimeSpan.FromMinutes(5);
|
||||
});
|
||||
if (globalSettings.SelfHosted)
|
||||
{
|
||||
services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
options.Cookie.Path = "/admin";
|
||||
});
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; private set; }
|
||||
public IWebHostEnvironment Environment { get; set; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// Options
|
||||
services.AddOptions();
|
||||
|
||||
// Settings
|
||||
var globalSettings = services.AddGlobalSettingsServices(Configuration, Environment);
|
||||
services.Configure<AdminSettings>(Configuration.GetSection("AdminSettings"));
|
||||
|
||||
// Data Protection
|
||||
services.AddCustomDataProtectionServices(Environment, globalSettings);
|
||||
|
||||
// Stripe Billing
|
||||
StripeConfiguration.ApiKey = globalSettings.Stripe.ApiKey;
|
||||
StripeConfiguration.MaxNetworkRetries = globalSettings.Stripe.MaxNetworkRetries;
|
||||
|
||||
// Repositories
|
||||
services.AddSqlServerRepositories(globalSettings);
|
||||
|
||||
// Context
|
||||
services.AddScoped<ICurrentContext, CurrentContext>();
|
||||
|
||||
// Identity
|
||||
services.AddPasswordlessIdentityServices<ReadOnlyEnvIdentityUserStore>(globalSettings);
|
||||
services.Configure<SecurityStampValidatorOptions>(options =>
|
||||
{
|
||||
options.ValidationInterval = TimeSpan.FromMinutes(5);
|
||||
});
|
||||
if (globalSettings.SelfHosted)
|
||||
{
|
||||
services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
options.Cookie.Path = "/admin";
|
||||
});
|
||||
}
|
||||
|
||||
// Services
|
||||
services.AddBaseServices(globalSettings);
|
||||
services.AddDefaultServices(globalSettings);
|
||||
// Services
|
||||
services.AddBaseServices(globalSettings);
|
||||
services.AddDefaultServices(globalSettings);
|
||||
|
||||
#if OSS
|
||||
services.AddOosServices();
|
||||
services.AddOosServices();
|
||||
#else
|
||||
services.AddCommCoreServices();
|
||||
services.AddCommCoreServices();
|
||||
#endif
|
||||
|
||||
// Mvc
|
||||
services.AddMvc(config =>
|
||||
{
|
||||
config.Filters.Add(new LoggingExceptionHandlerFilterAttribute());
|
||||
});
|
||||
services.Configure<RouteOptions>(options => options.LowercaseUrls = true);
|
||||
|
||||
// Jobs service
|
||||
Jobs.JobsHostedService.AddJobsServices(services, globalSettings.SelfHosted);
|
||||
services.AddHostedService<Jobs.JobsHostedService>();
|
||||
if (globalSettings.SelfHosted)
|
||||
{
|
||||
services.AddHostedService<HostedServices.DatabaseMigrationHostedService>();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CoreHelpers.SettingHasValue(globalSettings.Storage.ConnectionString))
|
||||
{
|
||||
services.AddHostedService<HostedServices.AzureQueueBlockIpHostedService>();
|
||||
}
|
||||
else if (CoreHelpers.SettingHasValue(globalSettings.Amazon?.AccessKeySecret))
|
||||
{
|
||||
services.AddHostedService<HostedServices.AmazonSqsBlockIpHostedService>();
|
||||
}
|
||||
if (CoreHelpers.SettingHasValue(globalSettings.Mail.ConnectionString))
|
||||
{
|
||||
services.AddHostedService<HostedServices.AzureQueueMailHostedService>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Configure(
|
||||
IApplicationBuilder app,
|
||||
IWebHostEnvironment env,
|
||||
IHostApplicationLifetime appLifetime,
|
||||
GlobalSettings globalSettings)
|
||||
// Mvc
|
||||
services.AddMvc(config =>
|
||||
{
|
||||
app.UseSerilog(env, appLifetime, globalSettings);
|
||||
config.Filters.Add(new LoggingExceptionHandlerFilterAttribute());
|
||||
});
|
||||
services.Configure<RouteOptions>(options => options.LowercaseUrls = true);
|
||||
|
||||
// Add general security headers
|
||||
app.UseMiddleware<SecurityHeadersMiddleware>();
|
||||
|
||||
if (globalSettings.SelfHosted)
|
||||
// Jobs service
|
||||
Jobs.JobsHostedService.AddJobsServices(services, globalSettings.SelfHosted);
|
||||
services.AddHostedService<Jobs.JobsHostedService>();
|
||||
if (globalSettings.SelfHosted)
|
||||
{
|
||||
services.AddHostedService<HostedServices.DatabaseMigrationHostedService>();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CoreHelpers.SettingHasValue(globalSettings.Storage.ConnectionString))
|
||||
{
|
||||
app.UsePathBase("/admin");
|
||||
app.UseForwardedHeaders(globalSettings);
|
||||
services.AddHostedService<HostedServices.AzureQueueBlockIpHostedService>();
|
||||
}
|
||||
|
||||
if (env.IsDevelopment())
|
||||
else if (CoreHelpers.SettingHasValue(globalSettings.Amazon?.AccessKeySecret))
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
services.AddHostedService<HostedServices.AmazonSqsBlockIpHostedService>();
|
||||
}
|
||||
else
|
||||
if (CoreHelpers.SettingHasValue(globalSettings.Mail.ConnectionString))
|
||||
{
|
||||
app.UseExceptionHandler("/error");
|
||||
services.AddHostedService<HostedServices.AzureQueueMailHostedService>();
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());
|
||||
}
|
||||
}
|
||||
|
||||
public void Configure(
|
||||
IApplicationBuilder app,
|
||||
IWebHostEnvironment env,
|
||||
IHostApplicationLifetime appLifetime,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
app.UseSerilog(env, appLifetime, globalSettings);
|
||||
|
||||
// Add general security headers
|
||||
app.UseMiddleware<SecurityHeadersMiddleware>();
|
||||
|
||||
if (globalSettings.SelfHosted)
|
||||
{
|
||||
app.UsePathBase("/admin");
|
||||
app.UseForwardedHeaders(globalSettings);
|
||||
}
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/error");
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,72 +3,71 @@ using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
|
||||
namespace Bit.Admin.TagHelpers
|
||||
namespace Bit.Admin.TagHelpers;
|
||||
|
||||
[HtmlTargetElement("li", Attributes = ActiveControllerName)]
|
||||
[HtmlTargetElement("li", Attributes = ActiveActionName)]
|
||||
public class ActivePageTagHelper : TagHelper
|
||||
{
|
||||
[HtmlTargetElement("li", Attributes = ActiveControllerName)]
|
||||
[HtmlTargetElement("li", Attributes = ActiveActionName)]
|
||||
public class ActivePageTagHelper : TagHelper
|
||||
private const string ActiveControllerName = "active-controller";
|
||||
private const string ActiveActionName = "active-action";
|
||||
|
||||
private readonly IHtmlGenerator _generator;
|
||||
|
||||
public ActivePageTagHelper(IHtmlGenerator generator)
|
||||
{
|
||||
private const string ActiveControllerName = "active-controller";
|
||||
private const string ActiveActionName = "active-action";
|
||||
_generator = generator;
|
||||
}
|
||||
|
||||
private readonly IHtmlGenerator _generator;
|
||||
[HtmlAttributeNotBound]
|
||||
[ViewContext]
|
||||
public ViewContext ViewContext { get; set; }
|
||||
[HtmlAttributeName(ActiveControllerName)]
|
||||
public string ActiveController { get; set; }
|
||||
[HtmlAttributeName(ActiveActionName)]
|
||||
public string ActiveAction { get; set; }
|
||||
|
||||
public ActivePageTagHelper(IHtmlGenerator generator)
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
_generator = generator;
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
[HtmlAttributeNotBound]
|
||||
[ViewContext]
|
||||
public ViewContext ViewContext { get; set; }
|
||||
[HtmlAttributeName(ActiveControllerName)]
|
||||
public string ActiveController { get; set; }
|
||||
[HtmlAttributeName(ActiveActionName)]
|
||||
public string ActiveAction { get; set; }
|
||||
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
if (output == null)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (output == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(output));
|
||||
}
|
||||
|
||||
if (ActiveAction == null && ActiveController == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var descriptor = ViewContext.ActionDescriptor as ControllerActionDescriptor;
|
||||
if (descriptor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var controllerMatch = ActiveMatch(ActiveController, descriptor.ControllerName);
|
||||
var actionMatch = ActiveMatch(ActiveAction, descriptor.ActionName);
|
||||
if (controllerMatch && actionMatch)
|
||||
{
|
||||
var classValue = "active";
|
||||
if (output.Attributes["class"] != null)
|
||||
{
|
||||
classValue += " " + output.Attributes["class"].Value;
|
||||
output.Attributes.Remove(output.Attributes["class"]);
|
||||
}
|
||||
|
||||
output.Attributes.Add("class", classValue);
|
||||
}
|
||||
throw new ArgumentNullException(nameof(output));
|
||||
}
|
||||
|
||||
private bool ActiveMatch(string route, string descriptor)
|
||||
if (ActiveAction == null && ActiveController == null)
|
||||
{
|
||||
return route == null || route == "*" ||
|
||||
route.Split(',').Any(c => c.Trim().ToLower() == descriptor.ToLower());
|
||||
return;
|
||||
}
|
||||
|
||||
var descriptor = ViewContext.ActionDescriptor as ControllerActionDescriptor;
|
||||
if (descriptor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var controllerMatch = ActiveMatch(ActiveController, descriptor.ControllerName);
|
||||
var actionMatch = ActiveMatch(ActiveAction, descriptor.ActionName);
|
||||
if (controllerMatch && actionMatch)
|
||||
{
|
||||
var classValue = "active";
|
||||
if (output.Attributes["class"] != null)
|
||||
{
|
||||
classValue += " " + output.Attributes["class"].Value;
|
||||
output.Attributes.Remove(output.Attributes["class"]);
|
||||
}
|
||||
|
||||
output.Attributes.Add("class", classValue);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ActiveMatch(string route, string descriptor)
|
||||
{
|
||||
return route == null || route == "*" ||
|
||||
route.Split(',').Any(c => c.Trim().ToLower() == descriptor.ToLower());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,42 @@
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
|
||||
namespace Bit.Admin.TagHelpers
|
||||
namespace Bit.Admin.TagHelpers;
|
||||
|
||||
[HtmlTargetElement("option", Attributes = SelectedName)]
|
||||
public class OptionSelectedTagHelper : TagHelper
|
||||
{
|
||||
[HtmlTargetElement("option", Attributes = SelectedName)]
|
||||
public class OptionSelectedTagHelper : TagHelper
|
||||
private const string SelectedName = "asp-selected";
|
||||
|
||||
private readonly IHtmlGenerator _generator;
|
||||
|
||||
public OptionSelectedTagHelper(IHtmlGenerator generator)
|
||||
{
|
||||
private const string SelectedName = "asp-selected";
|
||||
_generator = generator;
|
||||
}
|
||||
|
||||
private readonly IHtmlGenerator _generator;
|
||||
[HtmlAttributeName(SelectedName)]
|
||||
public bool Selected { get; set; }
|
||||
|
||||
public OptionSelectedTagHelper(IHtmlGenerator generator)
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
_generator = generator;
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
[HtmlAttributeName(SelectedName)]
|
||||
public bool Selected { get; set; }
|
||||
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
if (output == null)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
throw new ArgumentNullException(nameof(output));
|
||||
}
|
||||
|
||||
if (output == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(output));
|
||||
}
|
||||
|
||||
if (Selected)
|
||||
{
|
||||
output.Attributes.Add("selected", "selected");
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Attributes.RemoveAll("selected");
|
||||
}
|
||||
if (Selected)
|
||||
{
|
||||
output.Attributes.Add("selected", "selected");
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Attributes.RemoveAll("selected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,49 +4,48 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("accounts/billing")]
|
||||
[Authorize("Application")]
|
||||
public class AccountsBillingController : Controller
|
||||
{
|
||||
[Route("accounts/billing")]
|
||||
[Authorize("Application")]
|
||||
public class AccountsBillingController : Controller
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public AccountsBillingController(
|
||||
IPaymentService paymentService,
|
||||
IUserService userService)
|
||||
{
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly IUserService _userService;
|
||||
_paymentService = paymentService;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public AccountsBillingController(
|
||||
IPaymentService paymentService,
|
||||
IUserService userService)
|
||||
[HttpGet("history")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<BillingHistoryResponseModel> GetBillingHistory()
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
_paymentService = paymentService;
|
||||
_userService = userService;
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
[HttpGet("history")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<BillingHistoryResponseModel> GetBillingHistory()
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
var billingInfo = await _paymentService.GetBillingHistoryAsync(user);
|
||||
return new BillingHistoryResponseModel(billingInfo);
|
||||
}
|
||||
|
||||
var billingInfo = await _paymentService.GetBillingHistoryAsync(user);
|
||||
return new BillingHistoryResponseModel(billingInfo);
|
||||
[HttpGet("payment-method")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<BillingPaymentResponseModel> GetPaymentMethod()
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
[HttpGet("payment-method")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<BillingPaymentResponseModel> GetPaymentMethod()
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
var billingInfo = await _paymentService.GetBillingBalanceAndSourceAsync(user);
|
||||
return new BillingPaymentResponseModel(billingInfo);
|
||||
}
|
||||
var billingInfo = await _paymentService.GetBillingBalanceAndSourceAsync(user);
|
||||
return new BillingPaymentResponseModel(billingInfo);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,261 +8,260 @@ using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("organizations/{orgId}/collections")]
|
||||
[Authorize("Application")]
|
||||
public class CollectionsController : Controller
|
||||
{
|
||||
[Route("organizations/{orgId}/collections")]
|
||||
[Authorize("Application")]
|
||||
public class CollectionsController : Controller
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
private readonly ICollectionService _collectionService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public CollectionsController(
|
||||
ICollectionRepository collectionRepository,
|
||||
ICollectionService collectionService,
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
private readonly ICollectionService _collectionService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
_collectionRepository = collectionRepository;
|
||||
_collectionService = collectionService;
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
public CollectionsController(
|
||||
ICollectionRepository collectionRepository,
|
||||
ICollectionService collectionService,
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext)
|
||||
[HttpGet("{id}")]
|
||||
public async Task<CollectionResponseModel> Get(Guid orgId, Guid id)
|
||||
{
|
||||
if (!await CanViewCollectionAsync(orgId, id))
|
||||
{
|
||||
_collectionRepository = collectionRepository;
|
||||
_collectionService = collectionService;
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<CollectionResponseModel> Get(Guid orgId, Guid id)
|
||||
var collection = await GetCollectionAsync(id, orgId);
|
||||
return new CollectionResponseModel(collection);
|
||||
}
|
||||
|
||||
[HttpGet("{id}/details")]
|
||||
public async Task<CollectionGroupDetailsResponseModel> GetDetails(Guid orgId, Guid id)
|
||||
{
|
||||
if (!await ViewAtLeastOneCollectionAsync(orgId) && !await _currentContext.ManageUsers(orgId))
|
||||
{
|
||||
if (!await CanViewCollectionAsync(orgId, id))
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (await _currentContext.ViewAllCollections(orgId))
|
||||
{
|
||||
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(id);
|
||||
if (collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var collection = await GetCollectionAsync(id, orgId);
|
||||
return new CollectionResponseModel(collection);
|
||||
return new CollectionGroupDetailsResponseModel(collectionDetails.Item1, collectionDetails.Item2);
|
||||
}
|
||||
|
||||
[HttpGet("{id}/details")]
|
||||
public async Task<CollectionGroupDetailsResponseModel> GetDetails(Guid orgId, Guid id)
|
||||
else
|
||||
{
|
||||
if (!await ViewAtLeastOneCollectionAsync(orgId) && !await _currentContext.ManageUsers(orgId))
|
||||
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(id,
|
||||
_currentContext.UserId.Value);
|
||||
if (collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (await _currentContext.ViewAllCollections(orgId))
|
||||
{
|
||||
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(id);
|
||||
if (collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return new CollectionGroupDetailsResponseModel(collectionDetails.Item1, collectionDetails.Item2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(id,
|
||||
_currentContext.UserId.Value);
|
||||
if (collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return new CollectionGroupDetailsResponseModel(collectionDetails.Item1, collectionDetails.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<CollectionResponseModel>> Get(Guid orgId)
|
||||
{
|
||||
IEnumerable<Collection> orgCollections = await _collectionService.GetOrganizationCollections(orgId);
|
||||
|
||||
var responses = orgCollections.Select(c => new CollectionResponseModel(c));
|
||||
return new ListResponseModel<CollectionResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpGet("~/collections")]
|
||||
public async Task<ListResponseModel<CollectionDetailsResponseModel>> GetUser()
|
||||
{
|
||||
var collections = await _collectionRepository.GetManyByUserIdAsync(
|
||||
_userService.GetProperUserId(User).Value);
|
||||
var responses = collections.Select(c => new CollectionDetailsResponseModel(c));
|
||||
return new ListResponseModel<CollectionDetailsResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpGet("{id}/users")]
|
||||
public async Task<IEnumerable<SelectionReadOnlyResponseModel>> GetUsers(Guid orgId, Guid id)
|
||||
{
|
||||
var collection = await GetCollectionAsync(id, orgId);
|
||||
var collectionUsers = await _collectionRepository.GetManyUsersByIdAsync(collection.Id);
|
||||
var responses = collectionUsers.Select(cu => new SelectionReadOnlyResponseModel(cu));
|
||||
return responses;
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
public async Task<CollectionResponseModel> Post(Guid orgId, [FromBody] CollectionRequestModel model)
|
||||
{
|
||||
var collection = model.ToCollection(orgId);
|
||||
|
||||
if (!await CanCreateCollection(orgId, collection.Id) &&
|
||||
!await CanEditCollectionAsync(orgId, collection.Id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var assignUserToCollection = !(await _currentContext.EditAnyCollection(orgId)) &&
|
||||
await _currentContext.EditAssignedCollections(orgId);
|
||||
|
||||
await _collectionService.SaveAsync(collection, model.Groups?.Select(g => g.ToSelectionReadOnly()),
|
||||
assignUserToCollection ? _currentContext.UserId : null);
|
||||
return new CollectionResponseModel(collection);
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
[HttpPost("{id}")]
|
||||
public async Task<CollectionResponseModel> Put(Guid orgId, Guid id, [FromBody] CollectionRequestModel model)
|
||||
{
|
||||
if (!await CanEditCollectionAsync(orgId, id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var collection = await GetCollectionAsync(id, orgId);
|
||||
await _collectionService.SaveAsync(model.ToCollection(collection),
|
||||
model.Groups?.Select(g => g.ToSelectionReadOnly()));
|
||||
return new CollectionResponseModel(collection);
|
||||
}
|
||||
|
||||
[HttpPut("{id}/users")]
|
||||
public async Task PutUsers(Guid orgId, Guid id, [FromBody] IEnumerable<SelectionReadOnlyRequestModel> model)
|
||||
{
|
||||
if (!await CanEditCollectionAsync(orgId, id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var collection = await GetCollectionAsync(id, orgId);
|
||||
await _collectionRepository.UpdateUsersAsync(collection.Id, model?.Select(g => g.ToSelectionReadOnly()));
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[HttpPost("{id}/delete")]
|
||||
public async Task Delete(Guid orgId, Guid id)
|
||||
{
|
||||
if (!await CanDeleteCollectionAsync(orgId, id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var collection = await GetCollectionAsync(id, orgId);
|
||||
await _collectionService.DeleteAsync(collection);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}/user/{orgUserId}")]
|
||||
[HttpPost("{id}/delete-user/{orgUserId}")]
|
||||
public async Task Delete(string orgId, string id, string orgUserId)
|
||||
{
|
||||
var collection = await GetCollectionAsync(new Guid(id), new Guid(orgId));
|
||||
await _collectionService.DeleteUserAsync(collection, new Guid(orgUserId));
|
||||
}
|
||||
|
||||
private async Task<Collection> GetCollectionAsync(Guid id, Guid orgId)
|
||||
{
|
||||
Collection collection = default;
|
||||
if (await _currentContext.ViewAllCollections(orgId))
|
||||
{
|
||||
collection = await _collectionRepository.GetByIdAsync(id);
|
||||
}
|
||||
else if (await _currentContext.ViewAssignedCollections(orgId))
|
||||
{
|
||||
collection = await _collectionRepository.GetByIdAsync(id, _currentContext.UserId.Value);
|
||||
}
|
||||
|
||||
if (collection == null || collection.OrganizationId != orgId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
|
||||
private async Task<bool> CanCreateCollection(Guid orgId, Guid collectionId)
|
||||
{
|
||||
if (collectionId != default)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await _currentContext.CreateNewCollections(orgId);
|
||||
}
|
||||
|
||||
private async Task<bool> CanEditCollectionAsync(Guid orgId, Guid collectionId)
|
||||
{
|
||||
if (collectionId == default)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (await _currentContext.EditAnyCollection(orgId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (await _currentContext.EditAssignedCollections(orgId))
|
||||
{
|
||||
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
|
||||
return collectionDetails != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> CanDeleteCollectionAsync(Guid orgId, Guid collectionId)
|
||||
{
|
||||
if (collectionId == default)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (await _currentContext.DeleteAnyCollection(orgId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (await _currentContext.DeleteAssignedCollections(orgId))
|
||||
{
|
||||
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
|
||||
return collectionDetails != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> CanViewCollectionAsync(Guid orgId, Guid collectionId)
|
||||
{
|
||||
if (collectionId == default)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (await _currentContext.ViewAllCollections(orgId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (await _currentContext.ViewAssignedCollections(orgId))
|
||||
{
|
||||
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
|
||||
return collectionDetails != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> ViewAtLeastOneCollectionAsync(Guid orgId)
|
||||
{
|
||||
return await _currentContext.ViewAllCollections(orgId) || await _currentContext.ViewAssignedCollections(orgId);
|
||||
return new CollectionGroupDetailsResponseModel(collectionDetails.Item1, collectionDetails.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<CollectionResponseModel>> Get(Guid orgId)
|
||||
{
|
||||
IEnumerable<Collection> orgCollections = await _collectionService.GetOrganizationCollections(orgId);
|
||||
|
||||
var responses = orgCollections.Select(c => new CollectionResponseModel(c));
|
||||
return new ListResponseModel<CollectionResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpGet("~/collections")]
|
||||
public async Task<ListResponseModel<CollectionDetailsResponseModel>> GetUser()
|
||||
{
|
||||
var collections = await _collectionRepository.GetManyByUserIdAsync(
|
||||
_userService.GetProperUserId(User).Value);
|
||||
var responses = collections.Select(c => new CollectionDetailsResponseModel(c));
|
||||
return new ListResponseModel<CollectionDetailsResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpGet("{id}/users")]
|
||||
public async Task<IEnumerable<SelectionReadOnlyResponseModel>> GetUsers(Guid orgId, Guid id)
|
||||
{
|
||||
var collection = await GetCollectionAsync(id, orgId);
|
||||
var collectionUsers = await _collectionRepository.GetManyUsersByIdAsync(collection.Id);
|
||||
var responses = collectionUsers.Select(cu => new SelectionReadOnlyResponseModel(cu));
|
||||
return responses;
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
public async Task<CollectionResponseModel> Post(Guid orgId, [FromBody] CollectionRequestModel model)
|
||||
{
|
||||
var collection = model.ToCollection(orgId);
|
||||
|
||||
if (!await CanCreateCollection(orgId, collection.Id) &&
|
||||
!await CanEditCollectionAsync(orgId, collection.Id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var assignUserToCollection = !(await _currentContext.EditAnyCollection(orgId)) &&
|
||||
await _currentContext.EditAssignedCollections(orgId);
|
||||
|
||||
await _collectionService.SaveAsync(collection, model.Groups?.Select(g => g.ToSelectionReadOnly()),
|
||||
assignUserToCollection ? _currentContext.UserId : null);
|
||||
return new CollectionResponseModel(collection);
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
[HttpPost("{id}")]
|
||||
public async Task<CollectionResponseModel> Put(Guid orgId, Guid id, [FromBody] CollectionRequestModel model)
|
||||
{
|
||||
if (!await CanEditCollectionAsync(orgId, id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var collection = await GetCollectionAsync(id, orgId);
|
||||
await _collectionService.SaveAsync(model.ToCollection(collection),
|
||||
model.Groups?.Select(g => g.ToSelectionReadOnly()));
|
||||
return new CollectionResponseModel(collection);
|
||||
}
|
||||
|
||||
[HttpPut("{id}/users")]
|
||||
public async Task PutUsers(Guid orgId, Guid id, [FromBody] IEnumerable<SelectionReadOnlyRequestModel> model)
|
||||
{
|
||||
if (!await CanEditCollectionAsync(orgId, id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var collection = await GetCollectionAsync(id, orgId);
|
||||
await _collectionRepository.UpdateUsersAsync(collection.Id, model?.Select(g => g.ToSelectionReadOnly()));
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[HttpPost("{id}/delete")]
|
||||
public async Task Delete(Guid orgId, Guid id)
|
||||
{
|
||||
if (!await CanDeleteCollectionAsync(orgId, id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var collection = await GetCollectionAsync(id, orgId);
|
||||
await _collectionService.DeleteAsync(collection);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}/user/{orgUserId}")]
|
||||
[HttpPost("{id}/delete-user/{orgUserId}")]
|
||||
public async Task Delete(string orgId, string id, string orgUserId)
|
||||
{
|
||||
var collection = await GetCollectionAsync(new Guid(id), new Guid(orgId));
|
||||
await _collectionService.DeleteUserAsync(collection, new Guid(orgUserId));
|
||||
}
|
||||
|
||||
private async Task<Collection> GetCollectionAsync(Guid id, Guid orgId)
|
||||
{
|
||||
Collection collection = default;
|
||||
if (await _currentContext.ViewAllCollections(orgId))
|
||||
{
|
||||
collection = await _collectionRepository.GetByIdAsync(id);
|
||||
}
|
||||
else if (await _currentContext.ViewAssignedCollections(orgId))
|
||||
{
|
||||
collection = await _collectionRepository.GetByIdAsync(id, _currentContext.UserId.Value);
|
||||
}
|
||||
|
||||
if (collection == null || collection.OrganizationId != orgId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
|
||||
private async Task<bool> CanCreateCollection(Guid orgId, Guid collectionId)
|
||||
{
|
||||
if (collectionId != default)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await _currentContext.CreateNewCollections(orgId);
|
||||
}
|
||||
|
||||
private async Task<bool> CanEditCollectionAsync(Guid orgId, Guid collectionId)
|
||||
{
|
||||
if (collectionId == default)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (await _currentContext.EditAnyCollection(orgId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (await _currentContext.EditAssignedCollections(orgId))
|
||||
{
|
||||
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
|
||||
return collectionDetails != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> CanDeleteCollectionAsync(Guid orgId, Guid collectionId)
|
||||
{
|
||||
if (collectionId == default)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (await _currentContext.DeleteAnyCollection(orgId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (await _currentContext.DeleteAssignedCollections(orgId))
|
||||
{
|
||||
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
|
||||
return collectionDetails != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> CanViewCollectionAsync(Guid orgId, Guid collectionId)
|
||||
{
|
||||
if (collectionId == default)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (await _currentContext.ViewAllCollections(orgId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (await _currentContext.ViewAssignedCollections(orgId))
|
||||
{
|
||||
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
|
||||
return collectionDetails != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> ViewAtLeastOneCollectionAsync(Guid orgId)
|
||||
{
|
||||
return await _currentContext.ViewAllCollections(orgId) || await _currentContext.ViewAssignedCollections(orgId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,124 +7,123 @@ using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("devices")]
|
||||
[Authorize("Application")]
|
||||
public class DevicesController : Controller
|
||||
{
|
||||
[Route("devices")]
|
||||
[Authorize("Application")]
|
||||
public class DevicesController : Controller
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public DevicesController(
|
||||
IDeviceRepository deviceRepository,
|
||||
IDeviceService deviceService,
|
||||
IUserService userService)
|
||||
{
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IUserService _userService;
|
||||
_deviceRepository = deviceRepository;
|
||||
_deviceService = deviceService;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public DevicesController(
|
||||
IDeviceRepository deviceRepository,
|
||||
IDeviceService deviceService,
|
||||
IUserService userService)
|
||||
[HttpGet("{id}")]
|
||||
public async Task<DeviceResponseModel> Get(string id)
|
||||
{
|
||||
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
|
||||
if (device == null)
|
||||
{
|
||||
_deviceRepository = deviceRepository;
|
||||
_deviceService = deviceService;
|
||||
_userService = userService;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<DeviceResponseModel> Get(string id)
|
||||
{
|
||||
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
|
||||
if (device == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var response = new DeviceResponseModel(device);
|
||||
return response;
|
||||
}
|
||||
|
||||
var response = new DeviceResponseModel(device);
|
||||
return response;
|
||||
[HttpGet("identifier/{identifier}")]
|
||||
public async Task<DeviceResponseModel> GetByIdentifier(string identifier)
|
||||
{
|
||||
var device = await _deviceRepository.GetByIdentifierAsync(identifier, _userService.GetProperUserId(User).Value);
|
||||
if (device == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("identifier/{identifier}")]
|
||||
public async Task<DeviceResponseModel> GetByIdentifier(string identifier)
|
||||
{
|
||||
var device = await _deviceRepository.GetByIdentifierAsync(identifier, _userService.GetProperUserId(User).Value);
|
||||
if (device == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var response = new DeviceResponseModel(device);
|
||||
return response;
|
||||
}
|
||||
|
||||
var response = new DeviceResponseModel(device);
|
||||
return response;
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<DeviceResponseModel>> Get()
|
||||
{
|
||||
ICollection<Device> devices = await _deviceRepository.GetManyByUserIdAsync(_userService.GetProperUserId(User).Value);
|
||||
var responses = devices.Select(d => new DeviceResponseModel(d));
|
||||
return new ListResponseModel<DeviceResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
public async Task<DeviceResponseModel> Post([FromBody] DeviceRequestModel model)
|
||||
{
|
||||
var device = model.ToDevice(_userService.GetProperUserId(User));
|
||||
await _deviceService.SaveAsync(device);
|
||||
|
||||
var response = new DeviceResponseModel(device);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
[HttpPost("{id}")]
|
||||
public async Task<DeviceResponseModel> Put(string id, [FromBody] DeviceRequestModel model)
|
||||
{
|
||||
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
|
||||
if (device == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<DeviceResponseModel>> Get()
|
||||
await _deviceService.SaveAsync(model.ToDevice(device));
|
||||
|
||||
var response = new DeviceResponseModel(device);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPut("identifier/{identifier}/token")]
|
||||
[HttpPost("identifier/{identifier}/token")]
|
||||
public async Task PutToken(string identifier, [FromBody] DeviceTokenRequestModel model)
|
||||
{
|
||||
var device = await _deviceRepository.GetByIdentifierAsync(identifier, _userService.GetProperUserId(User).Value);
|
||||
if (device == null)
|
||||
{
|
||||
ICollection<Device> devices = await _deviceRepository.GetManyByUserIdAsync(_userService.GetProperUserId(User).Value);
|
||||
var responses = devices.Select(d => new DeviceResponseModel(d));
|
||||
return new ListResponseModel<DeviceResponseModel>(responses);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
public async Task<DeviceResponseModel> Post([FromBody] DeviceRequestModel model)
|
||||
{
|
||||
var device = model.ToDevice(_userService.GetProperUserId(User));
|
||||
await _deviceService.SaveAsync(device);
|
||||
await _deviceService.SaveAsync(model.ToDevice(device));
|
||||
}
|
||||
|
||||
var response = new DeviceResponseModel(device);
|
||||
return response;
|
||||
[AllowAnonymous]
|
||||
[HttpPut("identifier/{identifier}/clear-token")]
|
||||
[HttpPost("identifier/{identifier}/clear-token")]
|
||||
public async Task PutClearToken(string identifier)
|
||||
{
|
||||
var device = await _deviceRepository.GetByIdentifierAsync(identifier);
|
||||
if (device == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
[HttpPost("{id}")]
|
||||
public async Task<DeviceResponseModel> Put(string id, [FromBody] DeviceRequestModel model)
|
||||
await _deviceService.ClearTokenAsync(device);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[HttpPost("{id}/delete")]
|
||||
public async Task Delete(string id)
|
||||
{
|
||||
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
|
||||
if (device == null)
|
||||
{
|
||||
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
|
||||
if (device == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _deviceService.SaveAsync(model.ToDevice(device));
|
||||
|
||||
var response = new DeviceResponseModel(device);
|
||||
return response;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPut("identifier/{identifier}/token")]
|
||||
[HttpPost("identifier/{identifier}/token")]
|
||||
public async Task PutToken(string identifier, [FromBody] DeviceTokenRequestModel model)
|
||||
{
|
||||
var device = await _deviceRepository.GetByIdentifierAsync(identifier, _userService.GetProperUserId(User).Value);
|
||||
if (device == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _deviceService.SaveAsync(model.ToDevice(device));
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPut("identifier/{identifier}/clear-token")]
|
||||
[HttpPost("identifier/{identifier}/clear-token")]
|
||||
public async Task PutClearToken(string identifier)
|
||||
{
|
||||
var device = await _deviceRepository.GetByIdentifierAsync(identifier);
|
||||
if (device == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _deviceService.ClearTokenAsync(device);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[HttpPost("{id}/delete")]
|
||||
public async Task Delete(string id)
|
||||
{
|
||||
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
|
||||
if (device == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _deviceService.DeleteAsync(device);
|
||||
}
|
||||
await _deviceService.DeleteAsync(device);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,170 +9,169 @@ using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("emergency-access")]
|
||||
[Authorize("Application")]
|
||||
public class EmergencyAccessController : Controller
|
||||
{
|
||||
[Route("emergency-access")]
|
||||
[Authorize("Application")]
|
||||
public class EmergencyAccessController : Controller
|
||||
private readonly IUserService _userService;
|
||||
private readonly IEmergencyAccessRepository _emergencyAccessRepository;
|
||||
private readonly IEmergencyAccessService _emergencyAccessService;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
|
||||
public EmergencyAccessController(
|
||||
IUserService userService,
|
||||
IEmergencyAccessRepository emergencyAccessRepository,
|
||||
IEmergencyAccessService emergencyAccessService,
|
||||
IGlobalSettings globalSettings)
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IEmergencyAccessRepository _emergencyAccessRepository;
|
||||
private readonly IEmergencyAccessService _emergencyAccessService;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
_userService = userService;
|
||||
_emergencyAccessRepository = emergencyAccessRepository;
|
||||
_emergencyAccessService = emergencyAccessService;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
public EmergencyAccessController(
|
||||
IUserService userService,
|
||||
IEmergencyAccessRepository emergencyAccessRepository,
|
||||
IEmergencyAccessService emergencyAccessService,
|
||||
IGlobalSettings globalSettings)
|
||||
[HttpGet("trusted")]
|
||||
public async Task<ListResponseModel<EmergencyAccessGranteeDetailsResponseModel>> GetContacts()
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var granteeDetails = await _emergencyAccessRepository.GetManyDetailsByGrantorIdAsync(userId.Value);
|
||||
|
||||
var responses = granteeDetails.Select(d =>
|
||||
new EmergencyAccessGranteeDetailsResponseModel(d));
|
||||
|
||||
return new ListResponseModel<EmergencyAccessGranteeDetailsResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpGet("granted")]
|
||||
public async Task<ListResponseModel<EmergencyAccessGrantorDetailsResponseModel>> GetGrantees()
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var granteeDetails = await _emergencyAccessRepository.GetManyDetailsByGranteeIdAsync(userId.Value);
|
||||
|
||||
var responses = granteeDetails.Select(d => new EmergencyAccessGrantorDetailsResponseModel(d));
|
||||
|
||||
return new ListResponseModel<EmergencyAccessGrantorDetailsResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<EmergencyAccessGranteeDetailsResponseModel> Get(Guid id)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _emergencyAccessService.GetAsync(id, userId.Value);
|
||||
return new EmergencyAccessGranteeDetailsResponseModel(result);
|
||||
}
|
||||
|
||||
[HttpGet("{id}/policies")]
|
||||
public async Task<ListResponseModel<PolicyResponseModel>> Policies(Guid id)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
var policies = await _emergencyAccessService.GetPoliciesAsync(id, user);
|
||||
var responses = policies.Select<Policy, PolicyResponseModel>(policy => new PolicyResponseModel(policy));
|
||||
return new ListResponseModel<PolicyResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
[HttpPost("{id}")]
|
||||
public async Task Put(Guid id, [FromBody] EmergencyAccessUpdateRequestModel model)
|
||||
{
|
||||
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id);
|
||||
if (emergencyAccess == null)
|
||||
{
|
||||
_userService = userService;
|
||||
_emergencyAccessRepository = emergencyAccessRepository;
|
||||
_emergencyAccessService = emergencyAccessService;
|
||||
_globalSettings = globalSettings;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("trusted")]
|
||||
public async Task<ListResponseModel<EmergencyAccessGranteeDetailsResponseModel>> GetContacts()
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var granteeDetails = await _emergencyAccessRepository.GetManyDetailsByGrantorIdAsync(userId.Value);
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.SaveAsync(model.ToEmergencyAccess(emergencyAccess), user);
|
||||
}
|
||||
|
||||
var responses = granteeDetails.Select(d =>
|
||||
new EmergencyAccessGranteeDetailsResponseModel(d));
|
||||
[HttpDelete("{id}")]
|
||||
[HttpPost("{id}/delete")]
|
||||
public async Task Delete(Guid id)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _emergencyAccessService.DeleteAsync(id, userId.Value);
|
||||
}
|
||||
|
||||
return new ListResponseModel<EmergencyAccessGranteeDetailsResponseModel>(responses);
|
||||
}
|
||||
[HttpPost("invite")]
|
||||
public async Task Invite([FromBody] EmergencyAccessInviteRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.InviteAsync(user, model.Email, model.Type.Value, model.WaitTimeDays);
|
||||
}
|
||||
|
||||
[HttpGet("granted")]
|
||||
public async Task<ListResponseModel<EmergencyAccessGrantorDetailsResponseModel>> GetGrantees()
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var granteeDetails = await _emergencyAccessRepository.GetManyDetailsByGranteeIdAsync(userId.Value);
|
||||
[HttpPost("{id}/reinvite")]
|
||||
public async Task Reinvite(Guid id)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.ResendInviteAsync(user, id);
|
||||
}
|
||||
|
||||
var responses = granteeDetails.Select(d => new EmergencyAccessGrantorDetailsResponseModel(d));
|
||||
[HttpPost("{id}/accept")]
|
||||
public async Task Accept(Guid id, [FromBody] OrganizationUserAcceptRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.AcceptUserAsync(id, user, model.Token, _userService);
|
||||
}
|
||||
|
||||
return new ListResponseModel<EmergencyAccessGrantorDetailsResponseModel>(responses);
|
||||
}
|
||||
[HttpPost("{id}/confirm")]
|
||||
public async Task Confirm(Guid id, [FromBody] OrganizationUserConfirmRequestModel model)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _emergencyAccessService.ConfirmUserAsync(id, model.Key, userId.Value);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<EmergencyAccessGranteeDetailsResponseModel> Get(Guid id)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _emergencyAccessService.GetAsync(id, userId.Value);
|
||||
return new EmergencyAccessGranteeDetailsResponseModel(result);
|
||||
}
|
||||
[HttpPost("{id}/initiate")]
|
||||
public async Task Initiate(Guid id)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.InitiateAsync(id, user);
|
||||
}
|
||||
|
||||
[HttpGet("{id}/policies")]
|
||||
public async Task<ListResponseModel<PolicyResponseModel>> Policies(Guid id)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
var policies = await _emergencyAccessService.GetPoliciesAsync(id, user);
|
||||
var responses = policies.Select<Policy, PolicyResponseModel>(policy => new PolicyResponseModel(policy));
|
||||
return new ListResponseModel<PolicyResponseModel>(responses);
|
||||
}
|
||||
[HttpPost("{id}/approve")]
|
||||
public async Task Accept(Guid id)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.ApproveAsync(id, user);
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
[HttpPost("{id}")]
|
||||
public async Task Put(Guid id, [FromBody] EmergencyAccessUpdateRequestModel model)
|
||||
{
|
||||
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id);
|
||||
if (emergencyAccess == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
[HttpPost("{id}/reject")]
|
||||
public async Task Reject(Guid id)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.RejectAsync(id, user);
|
||||
}
|
||||
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.SaveAsync(model.ToEmergencyAccess(emergencyAccess), user);
|
||||
}
|
||||
[HttpPost("{id}/takeover")]
|
||||
public async Task<EmergencyAccessTakeoverResponseModel> Takeover(Guid id)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
var (result, grantor) = await _emergencyAccessService.TakeoverAsync(id, user);
|
||||
return new EmergencyAccessTakeoverResponseModel(result, grantor);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[HttpPost("{id}/delete")]
|
||||
public async Task Delete(Guid id)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _emergencyAccessService.DeleteAsync(id, userId.Value);
|
||||
}
|
||||
[HttpPost("{id}/password")]
|
||||
public async Task Password(Guid id, [FromBody] EmergencyAccessPasswordRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.PasswordAsync(id, user, model.NewMasterPasswordHash, model.Key);
|
||||
}
|
||||
|
||||
[HttpPost("invite")]
|
||||
public async Task Invite([FromBody] EmergencyAccessInviteRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.InviteAsync(user, model.Email, model.Type.Value, model.WaitTimeDays);
|
||||
}
|
||||
[HttpPost("{id}/view")]
|
||||
public async Task<EmergencyAccessViewResponseModel> ViewCiphers(Guid id)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
var viewResult = await _emergencyAccessService.ViewAsync(id, user);
|
||||
return new EmergencyAccessViewResponseModel(_globalSettings, viewResult.EmergencyAccess, viewResult.Ciphers);
|
||||
}
|
||||
|
||||
[HttpPost("{id}/reinvite")]
|
||||
public async Task Reinvite(Guid id)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.ResendInviteAsync(user, id);
|
||||
}
|
||||
|
||||
[HttpPost("{id}/accept")]
|
||||
public async Task Accept(Guid id, [FromBody] OrganizationUserAcceptRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.AcceptUserAsync(id, user, model.Token, _userService);
|
||||
}
|
||||
|
||||
[HttpPost("{id}/confirm")]
|
||||
public async Task Confirm(Guid id, [FromBody] OrganizationUserConfirmRequestModel model)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _emergencyAccessService.ConfirmUserAsync(id, model.Key, userId.Value);
|
||||
}
|
||||
|
||||
[HttpPost("{id}/initiate")]
|
||||
public async Task Initiate(Guid id)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.InitiateAsync(id, user);
|
||||
}
|
||||
|
||||
[HttpPost("{id}/approve")]
|
||||
public async Task Accept(Guid id)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.ApproveAsync(id, user);
|
||||
}
|
||||
|
||||
[HttpPost("{id}/reject")]
|
||||
public async Task Reject(Guid id)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.RejectAsync(id, user);
|
||||
}
|
||||
|
||||
[HttpPost("{id}/takeover")]
|
||||
public async Task<EmergencyAccessTakeoverResponseModel> Takeover(Guid id)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
var (result, grantor) = await _emergencyAccessService.TakeoverAsync(id, user);
|
||||
return new EmergencyAccessTakeoverResponseModel(result, grantor);
|
||||
}
|
||||
|
||||
[HttpPost("{id}/password")]
|
||||
public async Task Password(Guid id, [FromBody] EmergencyAccessPasswordRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
await _emergencyAccessService.PasswordAsync(id, user, model.NewMasterPasswordHash, model.Key);
|
||||
}
|
||||
|
||||
[HttpPost("{id}/view")]
|
||||
public async Task<EmergencyAccessViewResponseModel> ViewCiphers(Guid id)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
var viewResult = await _emergencyAccessService.ViewAsync(id, user);
|
||||
return new EmergencyAccessViewResponseModel(_globalSettings, viewResult.EmergencyAccess, viewResult.Ciphers);
|
||||
}
|
||||
|
||||
[HttpGet("{id}/{cipherId}/attachment/{attachmentId}")]
|
||||
public async Task<AttachmentResponseModel> GetAttachmentData(Guid id, Guid cipherId, string attachmentId)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
var result =
|
||||
await _emergencyAccessService.GetAttachmentDownloadAsync(id, cipherId, attachmentId, user);
|
||||
return new AttachmentResponseModel(result);
|
||||
}
|
||||
[HttpGet("{id}/{cipherId}/attachment/{attachmentId}")]
|
||||
public async Task<AttachmentResponseModel> GetAttachmentData(Guid id, Guid cipherId, string attachmentId)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
var result =
|
||||
await _emergencyAccessService.GetAttachmentDownloadAsync(id, cipherId, attachmentId, user);
|
||||
return new AttachmentResponseModel(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,171 +7,170 @@ using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("events")]
|
||||
[Authorize("Application")]
|
||||
public class EventsController : Controller
|
||||
{
|
||||
[Route("events")]
|
||||
[Authorize("Application")]
|
||||
public class EventsController : Controller
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IEventRepository _eventRepository;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public EventsController(
|
||||
IUserService userService,
|
||||
ICipherRepository cipherRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IProviderUserRepository providerUserRepository,
|
||||
IEventRepository eventRepository,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IEventRepository _eventRepository;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
_userService = userService;
|
||||
_cipherRepository = cipherRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_eventRepository = eventRepository;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
public EventsController(
|
||||
IUserService userService,
|
||||
ICipherRepository cipherRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IProviderUserRepository providerUserRepository,
|
||||
IEventRepository eventRepository,
|
||||
ICurrentContext currentContext)
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetUser(
|
||||
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
||||
{
|
||||
var dateRange = GetDateRange(start, end);
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var result = await _eventRepository.GetManyByUserAsync(userId, dateRange.Item1, dateRange.Item2,
|
||||
new PageOptions { ContinuationToken = continuationToken });
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
}
|
||||
|
||||
[HttpGet("~/ciphers/{id}/events")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetCipher(string id,
|
||||
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
||||
{
|
||||
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
|
||||
if (cipher == null)
|
||||
{
|
||||
_userService = userService;
|
||||
_cipherRepository = cipherRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_eventRepository = eventRepository;
|
||||
_currentContext = currentContext;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetUser(
|
||||
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
||||
var canView = false;
|
||||
if (cipher.OrganizationId.HasValue)
|
||||
{
|
||||
canView = await _currentContext.AccessEventLogs(cipher.OrganizationId.Value);
|
||||
}
|
||||
else if (cipher.UserId.HasValue)
|
||||
{
|
||||
var dateRange = GetDateRange(start, end);
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var result = await _eventRepository.GetManyByUserAsync(userId, dateRange.Item1, dateRange.Item2,
|
||||
new PageOptions { ContinuationToken = continuationToken });
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
canView = userId == cipher.UserId.Value;
|
||||
}
|
||||
|
||||
[HttpGet("~/ciphers/{id}/events")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetCipher(string id,
|
||||
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
||||
if (!canView)
|
||||
{
|
||||
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
|
||||
if (cipher == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var canView = false;
|
||||
if (cipher.OrganizationId.HasValue)
|
||||
{
|
||||
canView = await _currentContext.AccessEventLogs(cipher.OrganizationId.Value);
|
||||
}
|
||||
else if (cipher.UserId.HasValue)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
canView = userId == cipher.UserId.Value;
|
||||
}
|
||||
|
||||
if (!canView)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var dateRange = GetDateRange(start, end);
|
||||
var result = await _eventRepository.GetManyByCipherAsync(cipher, dateRange.Item1, dateRange.Item2,
|
||||
new PageOptions { ContinuationToken = continuationToken });
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("~/organizations/{id}/events")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetOrganization(string id,
|
||||
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
||||
var dateRange = GetDateRange(start, end);
|
||||
var result = await _eventRepository.GetManyByCipherAsync(cipher, dateRange.Item1, dateRange.Item2,
|
||||
new PageOptions { ContinuationToken = continuationToken });
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
}
|
||||
|
||||
[HttpGet("~/organizations/{id}/events")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetOrganization(string id,
|
||||
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
||||
{
|
||||
var orgId = new Guid(id);
|
||||
if (!await _currentContext.AccessEventLogs(orgId))
|
||||
{
|
||||
var orgId = new Guid(id);
|
||||
if (!await _currentContext.AccessEventLogs(orgId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var dateRange = GetDateRange(start, end);
|
||||
var result = await _eventRepository.GetManyByOrganizationAsync(orgId, dateRange.Item1, dateRange.Item2,
|
||||
new PageOptions { ContinuationToken = continuationToken });
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("~/organizations/{orgId}/users/{id}/events")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetOrganizationUser(string orgId, string id,
|
||||
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
||||
var dateRange = GetDateRange(start, end);
|
||||
var result = await _eventRepository.GetManyByOrganizationAsync(orgId, dateRange.Item1, dateRange.Item2,
|
||||
new PageOptions { ContinuationToken = continuationToken });
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
}
|
||||
|
||||
[HttpGet("~/organizations/{orgId}/users/{id}/events")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetOrganizationUser(string orgId, string id,
|
||||
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
||||
{
|
||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
||||
if (organizationUser == null || !organizationUser.UserId.HasValue ||
|
||||
!await _currentContext.AccessEventLogs(organizationUser.OrganizationId))
|
||||
{
|
||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
||||
if (organizationUser == null || !organizationUser.UserId.HasValue ||
|
||||
!await _currentContext.AccessEventLogs(organizationUser.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var dateRange = GetDateRange(start, end);
|
||||
var result = await _eventRepository.GetManyByOrganizationActingUserAsync(organizationUser.OrganizationId,
|
||||
organizationUser.UserId.Value, dateRange.Item1, dateRange.Item2,
|
||||
new PageOptions { ContinuationToken = continuationToken });
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("~/providers/{providerId:guid}/events")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetProvider(Guid providerId,
|
||||
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
||||
var dateRange = GetDateRange(start, end);
|
||||
var result = await _eventRepository.GetManyByOrganizationActingUserAsync(organizationUser.OrganizationId,
|
||||
organizationUser.UserId.Value, dateRange.Item1, dateRange.Item2,
|
||||
new PageOptions { ContinuationToken = continuationToken });
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
}
|
||||
|
||||
[HttpGet("~/providers/{providerId:guid}/events")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetProvider(Guid providerId,
|
||||
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
||||
{
|
||||
if (!_currentContext.ProviderAccessEventLogs(providerId))
|
||||
{
|
||||
if (!_currentContext.ProviderAccessEventLogs(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var dateRange = GetDateRange(start, end);
|
||||
var result = await _eventRepository.GetManyByProviderAsync(providerId, dateRange.Item1, dateRange.Item2,
|
||||
new PageOptions { ContinuationToken = continuationToken });
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("~/providers/{providerId:guid}/users/{id:guid}/events")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetProviderUser(Guid providerId, Guid id,
|
||||
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
||||
var dateRange = GetDateRange(start, end);
|
||||
var result = await _eventRepository.GetManyByProviderAsync(providerId, dateRange.Item1, dateRange.Item2,
|
||||
new PageOptions { ContinuationToken = continuationToken });
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
}
|
||||
|
||||
[HttpGet("~/providers/{providerId:guid}/users/{id:guid}/events")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetProviderUser(Guid providerId, Guid id,
|
||||
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
||||
{
|
||||
var providerUser = await _providerUserRepository.GetByIdAsync(id);
|
||||
if (providerUser == null || !providerUser.UserId.HasValue ||
|
||||
!_currentContext.ProviderAccessEventLogs(providerUser.ProviderId))
|
||||
{
|
||||
var providerUser = await _providerUserRepository.GetByIdAsync(id);
|
||||
if (providerUser == null || !providerUser.UserId.HasValue ||
|
||||
!_currentContext.ProviderAccessEventLogs(providerUser.ProviderId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var dateRange = GetDateRange(start, end);
|
||||
var result = await _eventRepository.GetManyByProviderActingUserAsync(providerUser.ProviderId,
|
||||
providerUser.UserId.Value, dateRange.Item1, dateRange.Item2,
|
||||
new PageOptions { ContinuationToken = continuationToken });
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
private Tuple<DateTime, DateTime> GetDateRange(DateTime? start, DateTime? end)
|
||||
var dateRange = GetDateRange(start, end);
|
||||
var result = await _eventRepository.GetManyByProviderActingUserAsync(providerUser.ProviderId,
|
||||
providerUser.UserId.Value, dateRange.Item1, dateRange.Item2,
|
||||
new PageOptions { ContinuationToken = continuationToken });
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
}
|
||||
|
||||
private Tuple<DateTime, DateTime> GetDateRange(DateTime? start, DateTime? end)
|
||||
{
|
||||
if (!end.HasValue || !start.HasValue)
|
||||
{
|
||||
if (!end.HasValue || !start.HasValue)
|
||||
{
|
||||
end = DateTime.UtcNow.Date.AddDays(1).AddMilliseconds(-1);
|
||||
start = DateTime.UtcNow.Date.AddDays(-30);
|
||||
}
|
||||
else if (start.Value > end.Value)
|
||||
{
|
||||
var newEnd = start;
|
||||
start = end;
|
||||
end = newEnd;
|
||||
}
|
||||
|
||||
if ((end.Value - start.Value) > TimeSpan.FromDays(367))
|
||||
{
|
||||
throw new BadRequestException("Range too large.");
|
||||
}
|
||||
|
||||
return new Tuple<DateTime, DateTime>(start.Value, end.Value);
|
||||
end = DateTime.UtcNow.Date.AddDays(1).AddMilliseconds(-1);
|
||||
start = DateTime.UtcNow.Date.AddDays(-30);
|
||||
}
|
||||
else if (start.Value > end.Value)
|
||||
{
|
||||
var newEnd = start;
|
||||
start = end;
|
||||
end = newEnd;
|
||||
}
|
||||
|
||||
if ((end.Value - start.Value) > TimeSpan.FromDays(367))
|
||||
{
|
||||
throw new BadRequestException("Range too large.");
|
||||
}
|
||||
|
||||
return new Tuple<DateTime, DateTime>(start.Value, end.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,84 +6,83 @@ using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("folders")]
|
||||
[Authorize("Application")]
|
||||
public class FoldersController : Controller
|
||||
{
|
||||
[Route("folders")]
|
||||
[Authorize("Application")]
|
||||
public class FoldersController : Controller
|
||||
private readonly IFolderRepository _folderRepository;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public FoldersController(
|
||||
IFolderRepository folderRepository,
|
||||
ICipherService cipherService,
|
||||
IUserService userService)
|
||||
{
|
||||
private readonly IFolderRepository _folderRepository;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IUserService _userService;
|
||||
_folderRepository = folderRepository;
|
||||
_cipherService = cipherService;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public FoldersController(
|
||||
IFolderRepository folderRepository,
|
||||
ICipherService cipherService,
|
||||
IUserService userService)
|
||||
[HttpGet("{id}")]
|
||||
public async Task<FolderResponseModel> Get(string id)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
|
||||
if (folder == null)
|
||||
{
|
||||
_folderRepository = folderRepository;
|
||||
_cipherService = cipherService;
|
||||
_userService = userService;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<FolderResponseModel> Get(string id)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
|
||||
if (folder == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return new FolderResponseModel(folder);
|
||||
}
|
||||
|
||||
return new FolderResponseModel(folder);
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<FolderResponseModel>> Get()
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var folders = await _folderRepository.GetManyByUserIdAsync(userId);
|
||||
var responses = folders.Select(f => new FolderResponseModel(f));
|
||||
return new ListResponseModel<FolderResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
public async Task<FolderResponseModel> Post([FromBody] FolderRequestModel model)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var folder = model.ToFolder(_userService.GetProperUserId(User).Value);
|
||||
await _cipherService.SaveFolderAsync(folder);
|
||||
return new FolderResponseModel(folder);
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
[HttpPost("{id}")]
|
||||
public async Task<FolderResponseModel> Put(string id, [FromBody] FolderRequestModel model)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
|
||||
if (folder == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<FolderResponseModel>> Get()
|
||||
await _cipherService.SaveFolderAsync(model.ToFolder(folder));
|
||||
return new FolderResponseModel(folder);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[HttpPost("{id}/delete")]
|
||||
public async Task Delete(string id)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
|
||||
if (folder == null)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var folders = await _folderRepository.GetManyByUserIdAsync(userId);
|
||||
var responses = folders.Select(f => new FolderResponseModel(f));
|
||||
return new ListResponseModel<FolderResponseModel>(responses);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
public async Task<FolderResponseModel> Post([FromBody] FolderRequestModel model)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var folder = model.ToFolder(_userService.GetProperUserId(User).Value);
|
||||
await _cipherService.SaveFolderAsync(folder);
|
||||
return new FolderResponseModel(folder);
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
[HttpPost("{id}")]
|
||||
public async Task<FolderResponseModel> Put(string id, [FromBody] FolderRequestModel model)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
|
||||
if (folder == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _cipherService.SaveFolderAsync(model.ToFolder(folder));
|
||||
return new FolderResponseModel(folder);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[HttpPost("{id}/delete")]
|
||||
public async Task Delete(string id)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
|
||||
if (folder == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _cipherService.DeleteFolderAsync(folder);
|
||||
}
|
||||
await _cipherService.DeleteFolderAsync(folder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,146 +7,145 @@ using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("organizations/{orgId}/groups")]
|
||||
[Authorize("Application")]
|
||||
public class GroupsController : Controller
|
||||
{
|
||||
[Route("organizations/{orgId}/groups")]
|
||||
[Authorize("Application")]
|
||||
public class GroupsController : Controller
|
||||
private readonly IGroupRepository _groupRepository;
|
||||
private readonly IGroupService _groupService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public GroupsController(
|
||||
IGroupRepository groupRepository,
|
||||
IGroupService groupService,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
private readonly IGroupRepository _groupRepository;
|
||||
private readonly IGroupService _groupService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
_groupRepository = groupRepository;
|
||||
_groupService = groupService;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
public GroupsController(
|
||||
IGroupRepository groupRepository,
|
||||
IGroupService groupService,
|
||||
ICurrentContext currentContext)
|
||||
[HttpGet("{id}")]
|
||||
public async Task<GroupResponseModel> Get(string orgId, string id)
|
||||
{
|
||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
|
||||
{
|
||||
_groupRepository = groupRepository;
|
||||
_groupService = groupService;
|
||||
_currentContext = currentContext;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<GroupResponseModel> Get(string orgId, string id)
|
||||
{
|
||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return new GroupResponseModel(group);
|
||||
}
|
||||
|
||||
return new GroupResponseModel(group);
|
||||
[HttpGet("{id}/details")]
|
||||
public async Task<GroupDetailsResponseModel> GetDetails(string orgId, string id)
|
||||
{
|
||||
var groupDetails = await _groupRepository.GetByIdWithCollectionsAsync(new Guid(id));
|
||||
if (groupDetails?.Item1 == null || !await _currentContext.ManageGroups(groupDetails.Item1.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{id}/details")]
|
||||
public async Task<GroupDetailsResponseModel> GetDetails(string orgId, string id)
|
||||
{
|
||||
var groupDetails = await _groupRepository.GetByIdWithCollectionsAsync(new Guid(id));
|
||||
if (groupDetails?.Item1 == null || !await _currentContext.ManageGroups(groupDetails.Item1.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return new GroupDetailsResponseModel(groupDetails.Item1, groupDetails.Item2);
|
||||
}
|
||||
|
||||
return new GroupDetailsResponseModel(groupDetails.Item1, groupDetails.Item2);
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<GroupResponseModel>> Get(string orgId)
|
||||
{
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
var canAccess = await _currentContext.ManageGroups(orgIdGuid) ||
|
||||
await _currentContext.ViewAssignedCollections(orgIdGuid) ||
|
||||
await _currentContext.ViewAllCollections(orgIdGuid) ||
|
||||
await _currentContext.ManageUsers(orgIdGuid);
|
||||
|
||||
if (!canAccess)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<GroupResponseModel>> Get(string orgId)
|
||||
var groups = await _groupRepository.GetManyByOrganizationIdAsync(orgIdGuid);
|
||||
var responses = groups.Select(g => new GroupResponseModel(g));
|
||||
return new ListResponseModel<GroupResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpGet("{id}/users")]
|
||||
public async Task<IEnumerable<Guid>> GetUsers(string orgId, string id)
|
||||
{
|
||||
var idGuid = new Guid(id);
|
||||
var group = await _groupRepository.GetByIdAsync(idGuid);
|
||||
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
|
||||
{
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
var canAccess = await _currentContext.ManageGroups(orgIdGuid) ||
|
||||
await _currentContext.ViewAssignedCollections(orgIdGuid) ||
|
||||
await _currentContext.ViewAllCollections(orgIdGuid) ||
|
||||
await _currentContext.ManageUsers(orgIdGuid);
|
||||
|
||||
if (!canAccess)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var groups = await _groupRepository.GetManyByOrganizationIdAsync(orgIdGuid);
|
||||
var responses = groups.Select(g => new GroupResponseModel(g));
|
||||
return new ListResponseModel<GroupResponseModel>(responses);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{id}/users")]
|
||||
public async Task<IEnumerable<Guid>> GetUsers(string orgId, string id)
|
||||
{
|
||||
var idGuid = new Guid(id);
|
||||
var group = await _groupRepository.GetByIdAsync(idGuid);
|
||||
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var groupIds = await _groupRepository.GetManyUserIdsByIdAsync(idGuid);
|
||||
return groupIds;
|
||||
}
|
||||
|
||||
var groupIds = await _groupRepository.GetManyUserIdsByIdAsync(idGuid);
|
||||
return groupIds;
|
||||
[HttpPost("")]
|
||||
public async Task<GroupResponseModel> Post(string orgId, [FromBody] GroupRequestModel model)
|
||||
{
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
if (!await _currentContext.ManageGroups(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
public async Task<GroupResponseModel> Post(string orgId, [FromBody] GroupRequestModel model)
|
||||
{
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
if (!await _currentContext.ManageGroups(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var group = model.ToGroup(orgIdGuid);
|
||||
await _groupService.SaveAsync(group, model.Collections?.Select(c => c.ToSelectionReadOnly()));
|
||||
return new GroupResponseModel(group);
|
||||
}
|
||||
|
||||
var group = model.ToGroup(orgIdGuid);
|
||||
await _groupService.SaveAsync(group, model.Collections?.Select(c => c.ToSelectionReadOnly()));
|
||||
return new GroupResponseModel(group);
|
||||
[HttpPut("{id}")]
|
||||
[HttpPost("{id}")]
|
||||
public async Task<GroupResponseModel> Put(string orgId, string id, [FromBody] GroupRequestModel model)
|
||||
{
|
||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
[HttpPost("{id}")]
|
||||
public async Task<GroupResponseModel> Put(string orgId, string id, [FromBody] GroupRequestModel model)
|
||||
{
|
||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
await _groupService.SaveAsync(model.ToGroup(group), model.Collections?.Select(c => c.ToSelectionReadOnly()));
|
||||
return new GroupResponseModel(group);
|
||||
}
|
||||
|
||||
await _groupService.SaveAsync(model.ToGroup(group), model.Collections?.Select(c => c.ToSelectionReadOnly()));
|
||||
return new GroupResponseModel(group);
|
||||
[HttpPut("{id}/users")]
|
||||
public async Task PutUsers(string orgId, string id, [FromBody] IEnumerable<Guid> model)
|
||||
{
|
||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
await _groupRepository.UpdateUsersAsync(group.Id, model);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[HttpPost("{id}/delete")]
|
||||
public async Task Delete(string orgId, string id)
|
||||
{
|
||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPut("{id}/users")]
|
||||
public async Task PutUsers(string orgId, string id, [FromBody] IEnumerable<Guid> model)
|
||||
await _groupService.DeleteAsync(group);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}/user/{orgUserId}")]
|
||||
[HttpPost("{id}/delete-user/{orgUserId}")]
|
||||
public async Task Delete(string orgId, string id, string orgUserId)
|
||||
{
|
||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
|
||||
{
|
||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
await _groupRepository.UpdateUsersAsync(group.Id, model);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[HttpPost("{id}/delete")]
|
||||
public async Task Delete(string orgId, string id)
|
||||
{
|
||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _groupService.DeleteAsync(group);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}/user/{orgUserId}")]
|
||||
[HttpPost("{id}/delete-user/{orgUserId}")]
|
||||
public async Task Delete(string orgId, string id, string orgUserId)
|
||||
{
|
||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _groupService.DeleteUserAsync(group, new Guid(orgUserId));
|
||||
}
|
||||
await _groupService.DeleteUserAsync(group, new Guid(orgUserId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,91 +8,90 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("hibp")]
|
||||
[Authorize("Application")]
|
||||
public class HibpController : Controller
|
||||
{
|
||||
[Route("hibp")]
|
||||
[Authorize("Application")]
|
||||
public class HibpController : Controller
|
||||
private const string HibpBreachApi = "https://haveibeenpwned.com/api/v3/breachedaccount/{0}" +
|
||||
"?truncateResponse=false&includeUnverified=false";
|
||||
private static HttpClient _httpClient;
|
||||
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly string _userAgent;
|
||||
|
||||
static HibpController()
|
||||
{
|
||||
private const string HibpBreachApi = "https://haveibeenpwned.com/api/v3/breachedaccount/{0}" +
|
||||
"?truncateResponse=false&includeUnverified=false";
|
||||
private static HttpClient _httpClient;
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly string _userAgent;
|
||||
public HibpController(
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
_globalSettings = globalSettings;
|
||||
_userAgent = _globalSettings.SelfHosted ? "Bitwarden Self-Hosted" : "Bitwarden";
|
||||
}
|
||||
|
||||
static HibpController()
|
||||
[HttpGet("breach")]
|
||||
public async Task<IActionResult> Get(string username)
|
||||
{
|
||||
return await SendAsync(WebUtility.UrlEncode(username), true);
|
||||
}
|
||||
|
||||
private async Task<IActionResult> SendAsync(string username, bool retry)
|
||||
{
|
||||
if (!CoreHelpers.SettingHasValue(_globalSettings.HibpApiKey))
|
||||
{
|
||||
_httpClient = new HttpClient();
|
||||
throw new BadRequestException("HaveIBeenPwned API key not set.");
|
||||
}
|
||||
|
||||
public HibpController(
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext,
|
||||
GlobalSettings globalSettings)
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, string.Format(HibpBreachApi, username));
|
||||
request.Headers.Add("hibp-api-key", _globalSettings.HibpApiKey);
|
||||
request.Headers.Add("hibp-client-id", GetClientId());
|
||||
request.Headers.Add("User-Agent", _userAgent);
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
_globalSettings = globalSettings;
|
||||
_userAgent = _globalSettings.SelfHosted ? "Bitwarden Self-Hosted" : "Bitwarden";
|
||||
var data = await response.Content.ReadAsStringAsync();
|
||||
return Content(data, "application/json");
|
||||
}
|
||||
|
||||
[HttpGet("breach")]
|
||||
public async Task<IActionResult> Get(string username)
|
||||
else if (response.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
return await SendAsync(WebUtility.UrlEncode(username), true);
|
||||
return new NotFoundResult();
|
||||
}
|
||||
|
||||
private async Task<IActionResult> SendAsync(string username, bool retry)
|
||||
else if (response.StatusCode == HttpStatusCode.TooManyRequests && retry)
|
||||
{
|
||||
if (!CoreHelpers.SettingHasValue(_globalSettings.HibpApiKey))
|
||||
var delay = 2000;
|
||||
if (response.Headers.Contains("retry-after"))
|
||||
{
|
||||
throw new BadRequestException("HaveIBeenPwned API key not set.");
|
||||
}
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, string.Format(HibpBreachApi, username));
|
||||
request.Headers.Add("hibp-api-key", _globalSettings.HibpApiKey);
|
||||
request.Headers.Add("hibp-client-id", GetClientId());
|
||||
request.Headers.Add("User-Agent", _userAgent);
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var data = await response.Content.ReadAsStringAsync();
|
||||
return Content(data, "application/json");
|
||||
}
|
||||
else if (response.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
return new NotFoundResult();
|
||||
}
|
||||
else if (response.StatusCode == HttpStatusCode.TooManyRequests && retry)
|
||||
{
|
||||
var delay = 2000;
|
||||
if (response.Headers.Contains("retry-after"))
|
||||
var vals = response.Headers.GetValues("retry-after");
|
||||
if (vals.Any() && int.TryParse(vals.FirstOrDefault(), out var secDelay))
|
||||
{
|
||||
var vals = response.Headers.GetValues("retry-after");
|
||||
if (vals.Any() && int.TryParse(vals.FirstOrDefault(), out var secDelay))
|
||||
{
|
||||
delay = (secDelay * 1000) + 200;
|
||||
}
|
||||
delay = (secDelay * 1000) + 200;
|
||||
}
|
||||
await Task.Delay(delay);
|
||||
return await SendAsync(username, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BadRequestException("Request failed. Status code: " + response.StatusCode);
|
||||
}
|
||||
await Task.Delay(delay);
|
||||
return await SendAsync(username, false);
|
||||
}
|
||||
|
||||
private string GetClientId()
|
||||
else
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
using (var sha256 = SHA256.Create())
|
||||
{
|
||||
var hash = sha256.ComputeHash(userId.ToByteArray());
|
||||
return Convert.ToBase64String(hash);
|
||||
}
|
||||
throw new BadRequestException("Request failed. Status code: " + response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetClientId()
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
using (var sha256 = SHA256.Create())
|
||||
{
|
||||
var hash = sha256.ComputeHash(userId.ToByteArray());
|
||||
return Convert.ToBase64String(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
public class InfoController : Controller
|
||||
{
|
||||
public class InfoController : Controller
|
||||
[HttpGet("~/alive")]
|
||||
[HttpGet("~/now")]
|
||||
public DateTime GetAlive()
|
||||
{
|
||||
[HttpGet("~/alive")]
|
||||
[HttpGet("~/now")]
|
||||
public DateTime GetAlive()
|
||||
{
|
||||
return DateTime.UtcNow;
|
||||
}
|
||||
return DateTime.UtcNow;
|
||||
}
|
||||
|
||||
[HttpGet("~/version")]
|
||||
public JsonResult GetVersion()
|
||||
{
|
||||
return Json(CoreHelpers.GetVersion());
|
||||
}
|
||||
[HttpGet("~/version")]
|
||||
public JsonResult GetVersion()
|
||||
{
|
||||
return Json(CoreHelpers.GetVersion());
|
||||
}
|
||||
|
||||
[HttpGet("~/ip")]
|
||||
public JsonResult Ip()
|
||||
[HttpGet("~/ip")]
|
||||
public JsonResult Ip()
|
||||
{
|
||||
var headerSet = new HashSet<string> { "x-forwarded-for", "cf-connecting-ip", "client-ip" };
|
||||
var headers = HttpContext.Request?.Headers
|
||||
.Where(h => headerSet.Contains(h.Key.ToLower()))
|
||||
.ToDictionary(h => h.Key);
|
||||
return new JsonResult(new
|
||||
{
|
||||
var headerSet = new HashSet<string> { "x-forwarded-for", "cf-connecting-ip", "client-ip" };
|
||||
var headers = HttpContext.Request?.Headers
|
||||
.Where(h => headerSet.Contains(h.Key.ToLower()))
|
||||
.ToDictionary(h => h.Key);
|
||||
return new JsonResult(new
|
||||
{
|
||||
Ip = HttpContext.Connection?.RemoteIpAddress?.ToString(),
|
||||
Headers = headers,
|
||||
});
|
||||
}
|
||||
Ip = HttpContext.Connection?.RemoteIpAddress?.ToString(),
|
||||
Headers = headers,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,40 +6,39 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("installations")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class InstallationsController : Controller
|
||||
{
|
||||
[Route("installations")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class InstallationsController : Controller
|
||||
private readonly IInstallationRepository _installationRepository;
|
||||
|
||||
public InstallationsController(
|
||||
IInstallationRepository installationRepository)
|
||||
{
|
||||
private readonly IInstallationRepository _installationRepository;
|
||||
_installationRepository = installationRepository;
|
||||
}
|
||||
|
||||
public InstallationsController(
|
||||
IInstallationRepository installationRepository)
|
||||
[HttpGet("{id}")]
|
||||
[AllowAnonymous]
|
||||
public async Task<InstallationResponseModel> Get(Guid id)
|
||||
{
|
||||
var installation = await _installationRepository.GetByIdAsync(id);
|
||||
if (installation == null)
|
||||
{
|
||||
_installationRepository = installationRepository;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
[AllowAnonymous]
|
||||
public async Task<InstallationResponseModel> Get(Guid id)
|
||||
{
|
||||
var installation = await _installationRepository.GetByIdAsync(id);
|
||||
if (installation == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return new InstallationResponseModel(installation, false);
|
||||
}
|
||||
|
||||
return new InstallationResponseModel(installation, false);
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
[AllowAnonymous]
|
||||
public async Task<InstallationResponseModel> Post([FromBody] InstallationRequestModel model)
|
||||
{
|
||||
var installation = model.ToInstallation();
|
||||
await _installationRepository.CreateAsync(installation);
|
||||
return new InstallationResponseModel(installation, true);
|
||||
}
|
||||
[HttpPost("")]
|
||||
[AllowAnonymous]
|
||||
public async Task<InstallationResponseModel> Post([FromBody] InstallationRequestModel model)
|
||||
{
|
||||
var installation = model.ToInstallation();
|
||||
await _installationRepository.CreateAsync(installation);
|
||||
return new InstallationResponseModel(installation, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,70 +7,69 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("licenses")]
|
||||
[Authorize("Licensing")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class LicensesController : Controller
|
||||
{
|
||||
[Route("licenses")]
|
||||
[Authorize("Licensing")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class LicensesController : Controller
|
||||
private readonly ILicensingService _licensingService;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public LicensesController(
|
||||
ILicensingService licensingService,
|
||||
IUserRepository userRepository,
|
||||
IUserService userService,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationService organizationService,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
private readonly ILicensingService _licensingService;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
_licensingService = licensingService;
|
||||
_userRepository = userRepository;
|
||||
_userService = userService;
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationService = organizationService;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
public LicensesController(
|
||||
ILicensingService licensingService,
|
||||
IUserRepository userRepository,
|
||||
IUserService userService,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationService organizationService,
|
||||
ICurrentContext currentContext)
|
||||
[HttpGet("user/{id}")]
|
||||
public async Task<UserLicense> GetUser(string id, [FromQuery] string key)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(new Guid(id));
|
||||
if (user == null)
|
||||
{
|
||||
_licensingService = licensingService;
|
||||
_userRepository = userRepository;
|
||||
_userService = userService;
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationService = organizationService;
|
||||
_currentContext = currentContext;
|
||||
return null;
|
||||
}
|
||||
else if (!user.LicenseKey.Equals(key))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("Invalid license key.");
|
||||
}
|
||||
|
||||
[HttpGet("user/{id}")]
|
||||
public async Task<UserLicense> GetUser(string id, [FromQuery] string key)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(new Guid(id));
|
||||
if (user == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (!user.LicenseKey.Equals(key))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("Invalid license key.");
|
||||
}
|
||||
var license = await _userService.GenerateLicenseAsync(user, null);
|
||||
return license;
|
||||
}
|
||||
|
||||
var license = await _userService.GenerateLicenseAsync(user, null);
|
||||
return license;
|
||||
[HttpGet("organization/{id}")]
|
||||
public async Task<OrganizationLicense> GetOrganization(string id, [FromQuery] string key)
|
||||
{
|
||||
var org = await _organizationRepository.GetByIdAsync(new Guid(id));
|
||||
if (org == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (!org.LicenseKey.Equals(key))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("Invalid license key.");
|
||||
}
|
||||
|
||||
[HttpGet("organization/{id}")]
|
||||
public async Task<OrganizationLicense> GetOrganization(string id, [FromQuery] string key)
|
||||
{
|
||||
var org = await _organizationRepository.GetByIdAsync(new Guid(id));
|
||||
if (org == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (!org.LicenseKey.Equals(key))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("Invalid license key.");
|
||||
}
|
||||
|
||||
var license = await _organizationService.GenerateLicenseAsync(org, _currentContext.InstallationId.Value);
|
||||
return license;
|
||||
}
|
||||
var license = await _organizationService.GenerateLicenseAsync(org, _currentContext.InstallationId.Value);
|
||||
return license;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,42 +5,41 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Stripe;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
public class MiscController : Controller
|
||||
{
|
||||
public class MiscController : Controller
|
||||
private readonly BitPayClient _bitPayClient;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public MiscController(
|
||||
BitPayClient bitPayClient,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
private readonly BitPayClient _bitPayClient;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
_bitPayClient = bitPayClient;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
public MiscController(
|
||||
BitPayClient bitPayClient,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
_bitPayClient = bitPayClient;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
[Authorize("Application")]
|
||||
[HttpPost("~/bitpay-invoice")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<string> PostBitPayInvoice([FromBody] BitPayInvoiceRequestModel model)
|
||||
{
|
||||
var invoice = await _bitPayClient.CreateInvoiceAsync(model.ToBitpayInvoice(_globalSettings));
|
||||
return invoice.Url;
|
||||
}
|
||||
|
||||
[Authorize("Application")]
|
||||
[HttpPost("~/bitpay-invoice")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<string> PostBitPayInvoice([FromBody] BitPayInvoiceRequestModel model)
|
||||
[Authorize("Application")]
|
||||
[HttpPost("~/setup-payment")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<string> PostSetupPayment()
|
||||
{
|
||||
var options = new SetupIntentCreateOptions
|
||||
{
|
||||
var invoice = await _bitPayClient.CreateInvoiceAsync(model.ToBitpayInvoice(_globalSettings));
|
||||
return invoice.Url;
|
||||
}
|
||||
|
||||
[Authorize("Application")]
|
||||
[HttpPost("~/setup-payment")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<string> PostSetupPayment()
|
||||
{
|
||||
var options = new SetupIntentCreateOptions
|
||||
{
|
||||
Usage = "off_session"
|
||||
};
|
||||
var service = new SetupIntentService();
|
||||
var setupIntent = await service.CreateAsync(options);
|
||||
return setupIntent.ClientSecret;
|
||||
}
|
||||
Usage = "off_session"
|
||||
};
|
||||
var service = new SetupIntentService();
|
||||
var setupIntent = await service.CreateAsync(options);
|
||||
return setupIntent.ClientSecret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,199 +12,198 @@ using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Authorize("Application")]
|
||||
[Route("organizations/connections")]
|
||||
public class OrganizationConnectionsController : Controller
|
||||
{
|
||||
[Authorize("Application")]
|
||||
[Route("organizations/connections")]
|
||||
public class OrganizationConnectionsController : Controller
|
||||
private readonly ICreateOrganizationConnectionCommand _createOrganizationConnectionCommand;
|
||||
private readonly IUpdateOrganizationConnectionCommand _updateOrganizationConnectionCommand;
|
||||
private readonly IDeleteOrganizationConnectionCommand _deleteOrganizationConnectionCommand;
|
||||
private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly ILicensingService _licensingService;
|
||||
|
||||
public OrganizationConnectionsController(
|
||||
ICreateOrganizationConnectionCommand createOrganizationConnectionCommand,
|
||||
IUpdateOrganizationConnectionCommand updateOrganizationConnectionCommand,
|
||||
IDeleteOrganizationConnectionCommand deleteOrganizationConnectionCommand,
|
||||
IOrganizationConnectionRepository organizationConnectionRepository,
|
||||
ICurrentContext currentContext,
|
||||
IGlobalSettings globalSettings,
|
||||
ILicensingService licensingService)
|
||||
{
|
||||
private readonly ICreateOrganizationConnectionCommand _createOrganizationConnectionCommand;
|
||||
private readonly IUpdateOrganizationConnectionCommand _updateOrganizationConnectionCommand;
|
||||
private readonly IDeleteOrganizationConnectionCommand _deleteOrganizationConnectionCommand;
|
||||
private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly ILicensingService _licensingService;
|
||||
_createOrganizationConnectionCommand = createOrganizationConnectionCommand;
|
||||
_updateOrganizationConnectionCommand = updateOrganizationConnectionCommand;
|
||||
_deleteOrganizationConnectionCommand = deleteOrganizationConnectionCommand;
|
||||
_organizationConnectionRepository = organizationConnectionRepository;
|
||||
_currentContext = currentContext;
|
||||
_globalSettings = globalSettings;
|
||||
_licensingService = licensingService;
|
||||
}
|
||||
|
||||
public OrganizationConnectionsController(
|
||||
ICreateOrganizationConnectionCommand createOrganizationConnectionCommand,
|
||||
IUpdateOrganizationConnectionCommand updateOrganizationConnectionCommand,
|
||||
IDeleteOrganizationConnectionCommand deleteOrganizationConnectionCommand,
|
||||
IOrganizationConnectionRepository organizationConnectionRepository,
|
||||
ICurrentContext currentContext,
|
||||
IGlobalSettings globalSettings,
|
||||
ILicensingService licensingService)
|
||||
[HttpGet("enabled")]
|
||||
public bool ConnectionsEnabled()
|
||||
{
|
||||
return _globalSettings.SelfHosted && _globalSettings.EnableCloudCommunication;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<OrganizationConnectionResponseModel> CreateConnection([FromBody] OrganizationConnectionRequestModel model)
|
||||
{
|
||||
if (!await HasPermissionAsync(model?.OrganizationId))
|
||||
{
|
||||
_createOrganizationConnectionCommand = createOrganizationConnectionCommand;
|
||||
_updateOrganizationConnectionCommand = updateOrganizationConnectionCommand;
|
||||
_deleteOrganizationConnectionCommand = deleteOrganizationConnectionCommand;
|
||||
_organizationConnectionRepository = organizationConnectionRepository;
|
||||
_currentContext = currentContext;
|
||||
_globalSettings = globalSettings;
|
||||
_licensingService = licensingService;
|
||||
throw new BadRequestException($"You do not have permission to create a connection of type {model.Type}.");
|
||||
}
|
||||
|
||||
[HttpGet("enabled")]
|
||||
public bool ConnectionsEnabled()
|
||||
if (await HasConnectionTypeAsync(model, null, model.Type))
|
||||
{
|
||||
return _globalSettings.SelfHosted && _globalSettings.EnableCloudCommunication;
|
||||
throw new BadRequestException($"The requested organization already has a connection of type {model.Type}. Only one of each connection type may exist per organization.");
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<OrganizationConnectionResponseModel> CreateConnection([FromBody] OrganizationConnectionRequestModel model)
|
||||
switch (model.Type)
|
||||
{
|
||||
if (!await HasPermissionAsync(model?.OrganizationId))
|
||||
{
|
||||
throw new BadRequestException($"You do not have permission to create a connection of type {model.Type}.");
|
||||
}
|
||||
|
||||
if (await HasConnectionTypeAsync(model, null, model.Type))
|
||||
{
|
||||
throw new BadRequestException($"The requested organization already has a connection of type {model.Type}. Only one of each connection type may exist per organization.");
|
||||
}
|
||||
|
||||
switch (model.Type)
|
||||
{
|
||||
case OrganizationConnectionType.CloudBillingSync:
|
||||
return await CreateOrUpdateOrganizationConnectionAsync<BillingSyncConfig>(null, model, ValidateBillingSyncConfig);
|
||||
case OrganizationConnectionType.Scim:
|
||||
return await CreateOrUpdateOrganizationConnectionAsync<ScimConfig>(null, model);
|
||||
default:
|
||||
throw new BadRequestException($"Unknown Organization connection Type: {model.Type}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{organizationConnectionId}")]
|
||||
public async Task<OrganizationConnectionResponseModel> UpdateConnection(Guid organizationConnectionId, [FromBody] OrganizationConnectionRequestModel model)
|
||||
{
|
||||
var existingOrganizationConnection = await _organizationConnectionRepository.GetByIdAsync(organizationConnectionId);
|
||||
if (existingOrganizationConnection == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (!await HasPermissionAsync(model?.OrganizationId, model?.Type))
|
||||
{
|
||||
throw new BadRequestException("You do not have permission to update this connection.");
|
||||
}
|
||||
|
||||
if (await HasConnectionTypeAsync(model, organizationConnectionId, model.Type))
|
||||
{
|
||||
throw new BadRequestException($"The requested organization already has a connection of type {model.Type}. Only one of each connection type may exist per organization.");
|
||||
}
|
||||
|
||||
switch (model.Type)
|
||||
{
|
||||
case OrganizationConnectionType.CloudBillingSync:
|
||||
return await CreateOrUpdateOrganizationConnectionAsync<BillingSyncConfig>(organizationConnectionId, model);
|
||||
case OrganizationConnectionType.Scim:
|
||||
return await CreateOrUpdateOrganizationConnectionAsync<ScimConfig>(organizationConnectionId, model);
|
||||
default:
|
||||
throw new BadRequestException($"Unkown Organization connection Type: {model.Type}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{organizationId}/{type}")]
|
||||
public async Task<OrganizationConnectionResponseModel> GetConnection(Guid organizationId, OrganizationConnectionType type)
|
||||
{
|
||||
if (!await HasPermissionAsync(organizationId, type))
|
||||
{
|
||||
throw new BadRequestException($"You do not have permission to retrieve a connection of type {type}.");
|
||||
}
|
||||
|
||||
var connections = await GetConnectionsAsync(organizationId, type);
|
||||
var connection = connections.FirstOrDefault(c => c.Type == type);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case OrganizationConnectionType.CloudBillingSync:
|
||||
if (!_globalSettings.SelfHosted)
|
||||
{
|
||||
throw new BadRequestException($"Cannot get a {type} connection outside of a self-hosted instance.");
|
||||
}
|
||||
return new OrganizationConnectionResponseModel(connection, typeof(BillingSyncConfig));
|
||||
case OrganizationConnectionType.Scim:
|
||||
return new OrganizationConnectionResponseModel(connection, typeof(ScimConfig));
|
||||
default:
|
||||
throw new BadRequestException($"Unkown Organization connection Type: {type}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{organizationConnectionId}")]
|
||||
[HttpPost("{organizationConnectionId}/delete")]
|
||||
public async Task DeleteConnection(Guid organizationConnectionId)
|
||||
{
|
||||
var connection = await _organizationConnectionRepository.GetByIdAsync(organizationConnectionId);
|
||||
|
||||
if (connection == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (!await HasPermissionAsync(connection.OrganizationId, connection.Type))
|
||||
{
|
||||
throw new BadRequestException($"You do not have permission to remove this connection of type {connection.Type}.");
|
||||
}
|
||||
|
||||
await _deleteOrganizationConnectionCommand.DeleteAsync(connection);
|
||||
}
|
||||
|
||||
private async Task<ICollection<OrganizationConnection>> GetConnectionsAsync(Guid organizationId, OrganizationConnectionType type) =>
|
||||
await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organizationId, type);
|
||||
|
||||
private async Task<bool> HasConnectionTypeAsync(OrganizationConnectionRequestModel model, Guid? connectionId,
|
||||
OrganizationConnectionType type)
|
||||
{
|
||||
var existingConnections = await GetConnectionsAsync(model.OrganizationId, type);
|
||||
|
||||
return existingConnections.Any(c => c.Type == model.Type && (!connectionId.HasValue || c.Id != connectionId.Value));
|
||||
}
|
||||
|
||||
private async Task<bool> HasPermissionAsync(Guid? organizationId, OrganizationConnectionType? type = null)
|
||||
{
|
||||
if (!organizationId.HasValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return type switch
|
||||
{
|
||||
OrganizationConnectionType.Scim => await _currentContext.ManageScim(organizationId.Value),
|
||||
_ => await _currentContext.OrganizationOwner(organizationId.Value),
|
||||
};
|
||||
}
|
||||
|
||||
private async Task ValidateBillingSyncConfig(OrganizationConnectionRequestModel<BillingSyncConfig> typedModel)
|
||||
{
|
||||
if (!_globalSettings.SelfHosted)
|
||||
{
|
||||
throw new BadRequestException($"Cannot create a {typedModel.Type} connection outside of a self-hosted instance.");
|
||||
}
|
||||
var license = await _licensingService.ReadOrganizationLicenseAsync(typedModel.OrganizationId);
|
||||
if (!_licensingService.VerifyLicense(license))
|
||||
{
|
||||
throw new BadRequestException("Cannot verify license file.");
|
||||
}
|
||||
typedModel.ParsedConfig.CloudOrganizationId = license.Id;
|
||||
}
|
||||
|
||||
private async Task<OrganizationConnectionResponseModel> CreateOrUpdateOrganizationConnectionAsync<T>(
|
||||
Guid? organizationConnectionId,
|
||||
OrganizationConnectionRequestModel model,
|
||||
Func<OrganizationConnectionRequestModel<T>, Task> validateAction = null)
|
||||
where T : new()
|
||||
{
|
||||
var typedModel = new OrganizationConnectionRequestModel<T>(model);
|
||||
if (validateAction != null)
|
||||
{
|
||||
await validateAction(typedModel);
|
||||
}
|
||||
|
||||
var data = typedModel.ToData(organizationConnectionId);
|
||||
var connection = organizationConnectionId.HasValue
|
||||
? await _updateOrganizationConnectionCommand.UpdateAsync(data)
|
||||
: await _createOrganizationConnectionCommand.CreateAsync(data);
|
||||
|
||||
return new OrganizationConnectionResponseModel(connection, typeof(T));
|
||||
case OrganizationConnectionType.CloudBillingSync:
|
||||
return await CreateOrUpdateOrganizationConnectionAsync<BillingSyncConfig>(null, model, ValidateBillingSyncConfig);
|
||||
case OrganizationConnectionType.Scim:
|
||||
return await CreateOrUpdateOrganizationConnectionAsync<ScimConfig>(null, model);
|
||||
default:
|
||||
throw new BadRequestException($"Unknown Organization connection Type: {model.Type}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{organizationConnectionId}")]
|
||||
public async Task<OrganizationConnectionResponseModel> UpdateConnection(Guid organizationConnectionId, [FromBody] OrganizationConnectionRequestModel model)
|
||||
{
|
||||
var existingOrganizationConnection = await _organizationConnectionRepository.GetByIdAsync(organizationConnectionId);
|
||||
if (existingOrganizationConnection == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (!await HasPermissionAsync(model?.OrganizationId, model?.Type))
|
||||
{
|
||||
throw new BadRequestException("You do not have permission to update this connection.");
|
||||
}
|
||||
|
||||
if (await HasConnectionTypeAsync(model, organizationConnectionId, model.Type))
|
||||
{
|
||||
throw new BadRequestException($"The requested organization already has a connection of type {model.Type}. Only one of each connection type may exist per organization.");
|
||||
}
|
||||
|
||||
switch (model.Type)
|
||||
{
|
||||
case OrganizationConnectionType.CloudBillingSync:
|
||||
return await CreateOrUpdateOrganizationConnectionAsync<BillingSyncConfig>(organizationConnectionId, model);
|
||||
case OrganizationConnectionType.Scim:
|
||||
return await CreateOrUpdateOrganizationConnectionAsync<ScimConfig>(organizationConnectionId, model);
|
||||
default:
|
||||
throw new BadRequestException($"Unkown Organization connection Type: {model.Type}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{organizationId}/{type}")]
|
||||
public async Task<OrganizationConnectionResponseModel> GetConnection(Guid organizationId, OrganizationConnectionType type)
|
||||
{
|
||||
if (!await HasPermissionAsync(organizationId, type))
|
||||
{
|
||||
throw new BadRequestException($"You do not have permission to retrieve a connection of type {type}.");
|
||||
}
|
||||
|
||||
var connections = await GetConnectionsAsync(organizationId, type);
|
||||
var connection = connections.FirstOrDefault(c => c.Type == type);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case OrganizationConnectionType.CloudBillingSync:
|
||||
if (!_globalSettings.SelfHosted)
|
||||
{
|
||||
throw new BadRequestException($"Cannot get a {type} connection outside of a self-hosted instance.");
|
||||
}
|
||||
return new OrganizationConnectionResponseModel(connection, typeof(BillingSyncConfig));
|
||||
case OrganizationConnectionType.Scim:
|
||||
return new OrganizationConnectionResponseModel(connection, typeof(ScimConfig));
|
||||
default:
|
||||
throw new BadRequestException($"Unkown Organization connection Type: {type}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{organizationConnectionId}")]
|
||||
[HttpPost("{organizationConnectionId}/delete")]
|
||||
public async Task DeleteConnection(Guid organizationConnectionId)
|
||||
{
|
||||
var connection = await _organizationConnectionRepository.GetByIdAsync(organizationConnectionId);
|
||||
|
||||
if (connection == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (!await HasPermissionAsync(connection.OrganizationId, connection.Type))
|
||||
{
|
||||
throw new BadRequestException($"You do not have permission to remove this connection of type {connection.Type}.");
|
||||
}
|
||||
|
||||
await _deleteOrganizationConnectionCommand.DeleteAsync(connection);
|
||||
}
|
||||
|
||||
private async Task<ICollection<OrganizationConnection>> GetConnectionsAsync(Guid organizationId, OrganizationConnectionType type) =>
|
||||
await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organizationId, type);
|
||||
|
||||
private async Task<bool> HasConnectionTypeAsync(OrganizationConnectionRequestModel model, Guid? connectionId,
|
||||
OrganizationConnectionType type)
|
||||
{
|
||||
var existingConnections = await GetConnectionsAsync(model.OrganizationId, type);
|
||||
|
||||
return existingConnections.Any(c => c.Type == model.Type && (!connectionId.HasValue || c.Id != connectionId.Value));
|
||||
}
|
||||
|
||||
private async Task<bool> HasPermissionAsync(Guid? organizationId, OrganizationConnectionType? type = null)
|
||||
{
|
||||
if (!organizationId.HasValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return type switch
|
||||
{
|
||||
OrganizationConnectionType.Scim => await _currentContext.ManageScim(organizationId.Value),
|
||||
_ => await _currentContext.OrganizationOwner(organizationId.Value),
|
||||
};
|
||||
}
|
||||
|
||||
private async Task ValidateBillingSyncConfig(OrganizationConnectionRequestModel<BillingSyncConfig> typedModel)
|
||||
{
|
||||
if (!_globalSettings.SelfHosted)
|
||||
{
|
||||
throw new BadRequestException($"Cannot create a {typedModel.Type} connection outside of a self-hosted instance.");
|
||||
}
|
||||
var license = await _licensingService.ReadOrganizationLicenseAsync(typedModel.OrganizationId);
|
||||
if (!_licensingService.VerifyLicense(license))
|
||||
{
|
||||
throw new BadRequestException("Cannot verify license file.");
|
||||
}
|
||||
typedModel.ParsedConfig.CloudOrganizationId = license.Id;
|
||||
}
|
||||
|
||||
private async Task<OrganizationConnectionResponseModel> CreateOrUpdateOrganizationConnectionAsync<T>(
|
||||
Guid? organizationConnectionId,
|
||||
OrganizationConnectionRequestModel model,
|
||||
Func<OrganizationConnectionRequestModel<T>, Task> validateAction = null)
|
||||
where T : new()
|
||||
{
|
||||
var typedModel = new OrganizationConnectionRequestModel<T>(model);
|
||||
if (validateAction != null)
|
||||
{
|
||||
await validateAction(typedModel);
|
||||
}
|
||||
|
||||
var data = typedModel.ToData(organizationConnectionId);
|
||||
var connection = organizationConnectionId.HasValue
|
||||
? await _updateOrganizationConnectionCommand.UpdateAsync(data)
|
||||
: await _createOrganizationConnectionCommand.CreateAsync(data);
|
||||
|
||||
return new OrganizationConnectionResponseModel(connection, typeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,59 +6,58 @@ using Core.Models.Data;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("organizations/{organizationId}")]
|
||||
[Authorize("Application")]
|
||||
public class OrganizationExportController : Controller
|
||||
{
|
||||
[Route("organizations/{organizationId}")]
|
||||
[Authorize("Application")]
|
||||
public class OrganizationExportController : Controller
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICollectionService _collectionService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public OrganizationExportController(
|
||||
ICipherService cipherService,
|
||||
ICollectionService collectionService,
|
||||
IUserService userService,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICollectionService _collectionService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
_cipherService = cipherService;
|
||||
_collectionService = collectionService;
|
||||
_userService = userService;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
public OrganizationExportController(
|
||||
ICipherService cipherService,
|
||||
ICollectionService collectionService,
|
||||
IUserService userService,
|
||||
GlobalSettings globalSettings)
|
||||
[HttpGet("export")]
|
||||
public async Task<OrganizationExportResponseModel> Export(Guid organizationId)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
|
||||
IEnumerable<Collection> orgCollections = await _collectionService.GetOrganizationCollections(organizationId);
|
||||
(IEnumerable<CipherOrganizationDetails> orgCiphers, Dictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict) = await _cipherService.GetOrganizationCiphers(userId, organizationId);
|
||||
|
||||
var result = new OrganizationExportResponseModel
|
||||
{
|
||||
_cipherService = cipherService;
|
||||
_collectionService = collectionService;
|
||||
_userService = userService;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
Collections = GetOrganizationCollectionsResponse(orgCollections),
|
||||
Ciphers = GetOrganizationCiphersResponse(orgCiphers, collectionCiphersGroupDict)
|
||||
};
|
||||
|
||||
[HttpGet("export")]
|
||||
public async Task<OrganizationExportResponseModel> Export(Guid organizationId)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
return result;
|
||||
}
|
||||
|
||||
IEnumerable<Collection> orgCollections = await _collectionService.GetOrganizationCollections(organizationId);
|
||||
(IEnumerable<CipherOrganizationDetails> orgCiphers, Dictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict) = await _cipherService.GetOrganizationCiphers(userId, organizationId);
|
||||
private ListResponseModel<CollectionResponseModel> GetOrganizationCollectionsResponse(IEnumerable<Collection> orgCollections)
|
||||
{
|
||||
var collections = orgCollections.Select(c => new CollectionResponseModel(c));
|
||||
return new ListResponseModel<CollectionResponseModel>(collections);
|
||||
}
|
||||
|
||||
var result = new OrganizationExportResponseModel
|
||||
{
|
||||
Collections = GetOrganizationCollectionsResponse(orgCollections),
|
||||
Ciphers = GetOrganizationCiphersResponse(orgCiphers, collectionCiphersGroupDict)
|
||||
};
|
||||
private ListResponseModel<CipherMiniDetailsResponseModel> GetOrganizationCiphersResponse(IEnumerable<CipherOrganizationDetails> orgCiphers,
|
||||
Dictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict)
|
||||
{
|
||||
var responses = orgCiphers.Select(c => new CipherMiniDetailsResponseModel(c, _globalSettings,
|
||||
collectionCiphersGroupDict, c.OrganizationUseTotp));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private ListResponseModel<CollectionResponseModel> GetOrganizationCollectionsResponse(IEnumerable<Collection> orgCollections)
|
||||
{
|
||||
var collections = orgCollections.Select(c => new CollectionResponseModel(c));
|
||||
return new ListResponseModel<CollectionResponseModel>(collections);
|
||||
}
|
||||
|
||||
private ListResponseModel<CipherMiniDetailsResponseModel> GetOrganizationCiphersResponse(IEnumerable<CipherOrganizationDetails> orgCiphers,
|
||||
Dictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict)
|
||||
{
|
||||
var responses = orgCiphers.Select(c => new CipherMiniDetailsResponseModel(c, _globalSettings,
|
||||
collectionCiphersGroupDict, c.OrganizationUseTotp));
|
||||
|
||||
return new ListResponseModel<CipherMiniDetailsResponseModel>(responses);
|
||||
}
|
||||
return new ListResponseModel<CipherMiniDetailsResponseModel>(responses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,180 +12,179 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("organization/sponsorship")]
|
||||
public class OrganizationSponsorshipsController : Controller
|
||||
{
|
||||
[Route("organization/sponsorship")]
|
||||
public class OrganizationSponsorshipsController : Controller
|
||||
private readonly IOrganizationSponsorshipRepository _organizationSponsorshipRepository;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IValidateRedemptionTokenCommand _validateRedemptionTokenCommand;
|
||||
private readonly IValidateBillingSyncKeyCommand _validateBillingSyncKeyCommand;
|
||||
private readonly ICreateSponsorshipCommand _createSponsorshipCommand;
|
||||
private readonly ISendSponsorshipOfferCommand _sendSponsorshipOfferCommand;
|
||||
private readonly ISetUpSponsorshipCommand _setUpSponsorshipCommand;
|
||||
private readonly IRevokeSponsorshipCommand _revokeSponsorshipCommand;
|
||||
private readonly IRemoveSponsorshipCommand _removeSponsorshipCommand;
|
||||
private readonly ICloudSyncSponsorshipsCommand _syncSponsorshipsCommand;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public OrganizationSponsorshipsController(
|
||||
IOrganizationSponsorshipRepository organizationSponsorshipRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IValidateRedemptionTokenCommand validateRedemptionTokenCommand,
|
||||
IValidateBillingSyncKeyCommand validateBillingSyncKeyCommand,
|
||||
ICreateSponsorshipCommand createSponsorshipCommand,
|
||||
ISendSponsorshipOfferCommand sendSponsorshipOfferCommand,
|
||||
ISetUpSponsorshipCommand setUpSponsorshipCommand,
|
||||
IRevokeSponsorshipCommand revokeSponsorshipCommand,
|
||||
IRemoveSponsorshipCommand removeSponsorshipCommand,
|
||||
ICloudSyncSponsorshipsCommand syncSponsorshipsCommand,
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
private readonly IOrganizationSponsorshipRepository _organizationSponsorshipRepository;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IValidateRedemptionTokenCommand _validateRedemptionTokenCommand;
|
||||
private readonly IValidateBillingSyncKeyCommand _validateBillingSyncKeyCommand;
|
||||
private readonly ICreateSponsorshipCommand _createSponsorshipCommand;
|
||||
private readonly ISendSponsorshipOfferCommand _sendSponsorshipOfferCommand;
|
||||
private readonly ISetUpSponsorshipCommand _setUpSponsorshipCommand;
|
||||
private readonly IRevokeSponsorshipCommand _revokeSponsorshipCommand;
|
||||
private readonly IRemoveSponsorshipCommand _removeSponsorshipCommand;
|
||||
private readonly ICloudSyncSponsorshipsCommand _syncSponsorshipsCommand;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public OrganizationSponsorshipsController(
|
||||
IOrganizationSponsorshipRepository organizationSponsorshipRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IValidateRedemptionTokenCommand validateRedemptionTokenCommand,
|
||||
IValidateBillingSyncKeyCommand validateBillingSyncKeyCommand,
|
||||
ICreateSponsorshipCommand createSponsorshipCommand,
|
||||
ISendSponsorshipOfferCommand sendSponsorshipOfferCommand,
|
||||
ISetUpSponsorshipCommand setUpSponsorshipCommand,
|
||||
IRevokeSponsorshipCommand revokeSponsorshipCommand,
|
||||
IRemoveSponsorshipCommand removeSponsorshipCommand,
|
||||
ICloudSyncSponsorshipsCommand syncSponsorshipsCommand,
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
_organizationSponsorshipRepository = organizationSponsorshipRepository;
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_validateRedemptionTokenCommand = validateRedemptionTokenCommand;
|
||||
_validateBillingSyncKeyCommand = validateBillingSyncKeyCommand;
|
||||
_createSponsorshipCommand = createSponsorshipCommand;
|
||||
_sendSponsorshipOfferCommand = sendSponsorshipOfferCommand;
|
||||
_setUpSponsorshipCommand = setUpSponsorshipCommand;
|
||||
_revokeSponsorshipCommand = revokeSponsorshipCommand;
|
||||
_removeSponsorshipCommand = removeSponsorshipCommand;
|
||||
_syncSponsorshipsCommand = syncSponsorshipsCommand;
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
[Authorize("Application")]
|
||||
[HttpPost("{sponsoringOrgId}/families-for-enterprise")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task CreateSponsorship(Guid sponsoringOrgId, [FromBody] OrganizationSponsorshipCreateRequestModel model)
|
||||
{
|
||||
var sponsoringOrg = await _organizationRepository.GetByIdAsync(sponsoringOrgId);
|
||||
|
||||
var sponsorship = await _createSponsorshipCommand.CreateSponsorshipAsync(
|
||||
sponsoringOrg,
|
||||
await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default),
|
||||
model.PlanSponsorshipType, model.SponsoredEmail, model.FriendlyName);
|
||||
await _sendSponsorshipOfferCommand.SendSponsorshipOfferAsync(sponsorship, sponsoringOrg.Name);
|
||||
}
|
||||
|
||||
[Authorize("Application")]
|
||||
[HttpPost("{sponsoringOrgId}/families-for-enterprise/resend")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task ResendSponsorshipOffer(Guid sponsoringOrgId)
|
||||
{
|
||||
var sponsoringOrgUser = await _organizationUserRepository
|
||||
.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default);
|
||||
|
||||
await _sendSponsorshipOfferCommand.SendSponsorshipOfferAsync(
|
||||
await _organizationRepository.GetByIdAsync(sponsoringOrgId),
|
||||
sponsoringOrgUser,
|
||||
await _organizationSponsorshipRepository
|
||||
.GetBySponsoringOrganizationUserIdAsync(sponsoringOrgUser.Id));
|
||||
}
|
||||
|
||||
[Authorize("Application")]
|
||||
[HttpPost("validate-token")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<bool> PreValidateSponsorshipToken([FromQuery] string sponsorshipToken)
|
||||
{
|
||||
return (await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email)).valid;
|
||||
}
|
||||
|
||||
[Authorize("Application")]
|
||||
[HttpPost("redeem")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task RedeemSponsorship([FromQuery] string sponsorshipToken, [FromBody] OrganizationSponsorshipRedeemRequestModel model)
|
||||
{
|
||||
var (valid, sponsorship) = await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email);
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
throw new BadRequestException("Failed to parse sponsorship token.");
|
||||
}
|
||||
|
||||
if (!await _currentContext.OrganizationOwner(model.SponsoredOrganizationId))
|
||||
{
|
||||
throw new BadRequestException("Can only redeem sponsorship for an organization you own.");
|
||||
}
|
||||
|
||||
await _setUpSponsorshipCommand.SetUpSponsorshipAsync(
|
||||
sponsorship,
|
||||
await _organizationRepository.GetByIdAsync(model.SponsoredOrganizationId));
|
||||
}
|
||||
|
||||
[Authorize("Installation")]
|
||||
[HttpPost("sync")]
|
||||
public async Task<OrganizationSponsorshipSyncResponseModel> Sync([FromBody] OrganizationSponsorshipSyncRequestModel model)
|
||||
{
|
||||
var sponsoringOrg = await _organizationRepository.GetByIdAsync(model.SponsoringOrganizationCloudId);
|
||||
if (!await _validateBillingSyncKeyCommand.ValidateBillingSyncKeyAsync(sponsoringOrg, model.BillingSyncKey))
|
||||
{
|
||||
throw new BadRequestException("Invalid Billing Sync Key");
|
||||
}
|
||||
|
||||
var (syncResponseData, offersToSend) = await _syncSponsorshipsCommand.SyncOrganization(sponsoringOrg, model.ToOrganizationSponsorshipSync().SponsorshipsBatch);
|
||||
await _sendSponsorshipOfferCommand.BulkSendSponsorshipOfferAsync(sponsoringOrg.Name, offersToSend);
|
||||
return new OrganizationSponsorshipSyncResponseModel(syncResponseData);
|
||||
}
|
||||
|
||||
[Authorize("Application")]
|
||||
[HttpDelete("{sponsoringOrganizationId}")]
|
||||
[HttpPost("{sponsoringOrganizationId}/delete")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task RevokeSponsorship(Guid sponsoringOrganizationId)
|
||||
{
|
||||
|
||||
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrganizationId, _currentContext.UserId ?? default);
|
||||
if (_currentContext.UserId != orgUser?.UserId)
|
||||
{
|
||||
throw new BadRequestException("Can only revoke a sponsorship you granted.");
|
||||
}
|
||||
|
||||
var existingOrgSponsorship = await _organizationSponsorshipRepository
|
||||
.GetBySponsoringOrganizationUserIdAsync(orgUser.Id);
|
||||
|
||||
await _revokeSponsorshipCommand.RevokeSponsorshipAsync(existingOrgSponsorship);
|
||||
}
|
||||
|
||||
[Authorize("Application")]
|
||||
[HttpDelete("sponsored/{sponsoredOrgId}")]
|
||||
[HttpPost("sponsored/{sponsoredOrgId}/remove")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task RemoveSponsorship(Guid sponsoredOrgId)
|
||||
{
|
||||
|
||||
if (!await _currentContext.OrganizationOwner(sponsoredOrgId))
|
||||
{
|
||||
throw new BadRequestException("Only the owner of an organization can remove sponsorship.");
|
||||
}
|
||||
|
||||
var existingOrgSponsorship = await _organizationSponsorshipRepository
|
||||
.GetBySponsoredOrganizationIdAsync(sponsoredOrgId);
|
||||
|
||||
await _removeSponsorshipCommand.RemoveSponsorshipAsync(existingOrgSponsorship);
|
||||
}
|
||||
|
||||
[HttpGet("{sponsoringOrgId}/sync-status")]
|
||||
public async Task<object> GetSyncStatus(Guid sponsoringOrgId)
|
||||
{
|
||||
var sponsoringOrg = await _organizationRepository.GetByIdAsync(sponsoringOrgId);
|
||||
|
||||
if (!await _currentContext.OrganizationOwner(sponsoringOrg.Id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var lastSyncDate = await _organizationSponsorshipRepository.GetLatestSyncDateBySponsoringOrganizationIdAsync(sponsoringOrg.Id);
|
||||
return new OrganizationSponsorshipSyncStatusResponseModel(lastSyncDate);
|
||||
}
|
||||
|
||||
private Task<User> CurrentUser => _userService.GetUserByIdAsync(_currentContext.UserId.Value);
|
||||
_organizationSponsorshipRepository = organizationSponsorshipRepository;
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_validateRedemptionTokenCommand = validateRedemptionTokenCommand;
|
||||
_validateBillingSyncKeyCommand = validateBillingSyncKeyCommand;
|
||||
_createSponsorshipCommand = createSponsorshipCommand;
|
||||
_sendSponsorshipOfferCommand = sendSponsorshipOfferCommand;
|
||||
_setUpSponsorshipCommand = setUpSponsorshipCommand;
|
||||
_revokeSponsorshipCommand = revokeSponsorshipCommand;
|
||||
_removeSponsorshipCommand = removeSponsorshipCommand;
|
||||
_syncSponsorshipsCommand = syncSponsorshipsCommand;
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
[Authorize("Application")]
|
||||
[HttpPost("{sponsoringOrgId}/families-for-enterprise")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task CreateSponsorship(Guid sponsoringOrgId, [FromBody] OrganizationSponsorshipCreateRequestModel model)
|
||||
{
|
||||
var sponsoringOrg = await _organizationRepository.GetByIdAsync(sponsoringOrgId);
|
||||
|
||||
var sponsorship = await _createSponsorshipCommand.CreateSponsorshipAsync(
|
||||
sponsoringOrg,
|
||||
await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default),
|
||||
model.PlanSponsorshipType, model.SponsoredEmail, model.FriendlyName);
|
||||
await _sendSponsorshipOfferCommand.SendSponsorshipOfferAsync(sponsorship, sponsoringOrg.Name);
|
||||
}
|
||||
|
||||
[Authorize("Application")]
|
||||
[HttpPost("{sponsoringOrgId}/families-for-enterprise/resend")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task ResendSponsorshipOffer(Guid sponsoringOrgId)
|
||||
{
|
||||
var sponsoringOrgUser = await _organizationUserRepository
|
||||
.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default);
|
||||
|
||||
await _sendSponsorshipOfferCommand.SendSponsorshipOfferAsync(
|
||||
await _organizationRepository.GetByIdAsync(sponsoringOrgId),
|
||||
sponsoringOrgUser,
|
||||
await _organizationSponsorshipRepository
|
||||
.GetBySponsoringOrganizationUserIdAsync(sponsoringOrgUser.Id));
|
||||
}
|
||||
|
||||
[Authorize("Application")]
|
||||
[HttpPost("validate-token")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<bool> PreValidateSponsorshipToken([FromQuery] string sponsorshipToken)
|
||||
{
|
||||
return (await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email)).valid;
|
||||
}
|
||||
|
||||
[Authorize("Application")]
|
||||
[HttpPost("redeem")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task RedeemSponsorship([FromQuery] string sponsorshipToken, [FromBody] OrganizationSponsorshipRedeemRequestModel model)
|
||||
{
|
||||
var (valid, sponsorship) = await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email);
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
throw new BadRequestException("Failed to parse sponsorship token.");
|
||||
}
|
||||
|
||||
if (!await _currentContext.OrganizationOwner(model.SponsoredOrganizationId))
|
||||
{
|
||||
throw new BadRequestException("Can only redeem sponsorship for an organization you own.");
|
||||
}
|
||||
|
||||
await _setUpSponsorshipCommand.SetUpSponsorshipAsync(
|
||||
sponsorship,
|
||||
await _organizationRepository.GetByIdAsync(model.SponsoredOrganizationId));
|
||||
}
|
||||
|
||||
[Authorize("Installation")]
|
||||
[HttpPost("sync")]
|
||||
public async Task<OrganizationSponsorshipSyncResponseModel> Sync([FromBody] OrganizationSponsorshipSyncRequestModel model)
|
||||
{
|
||||
var sponsoringOrg = await _organizationRepository.GetByIdAsync(model.SponsoringOrganizationCloudId);
|
||||
if (!await _validateBillingSyncKeyCommand.ValidateBillingSyncKeyAsync(sponsoringOrg, model.BillingSyncKey))
|
||||
{
|
||||
throw new BadRequestException("Invalid Billing Sync Key");
|
||||
}
|
||||
|
||||
var (syncResponseData, offersToSend) = await _syncSponsorshipsCommand.SyncOrganization(sponsoringOrg, model.ToOrganizationSponsorshipSync().SponsorshipsBatch);
|
||||
await _sendSponsorshipOfferCommand.BulkSendSponsorshipOfferAsync(sponsoringOrg.Name, offersToSend);
|
||||
return new OrganizationSponsorshipSyncResponseModel(syncResponseData);
|
||||
}
|
||||
|
||||
[Authorize("Application")]
|
||||
[HttpDelete("{sponsoringOrganizationId}")]
|
||||
[HttpPost("{sponsoringOrganizationId}/delete")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task RevokeSponsorship(Guid sponsoringOrganizationId)
|
||||
{
|
||||
|
||||
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrganizationId, _currentContext.UserId ?? default);
|
||||
if (_currentContext.UserId != orgUser?.UserId)
|
||||
{
|
||||
throw new BadRequestException("Can only revoke a sponsorship you granted.");
|
||||
}
|
||||
|
||||
var existingOrgSponsorship = await _organizationSponsorshipRepository
|
||||
.GetBySponsoringOrganizationUserIdAsync(orgUser.Id);
|
||||
|
||||
await _revokeSponsorshipCommand.RevokeSponsorshipAsync(existingOrgSponsorship);
|
||||
}
|
||||
|
||||
[Authorize("Application")]
|
||||
[HttpDelete("sponsored/{sponsoredOrgId}")]
|
||||
[HttpPost("sponsored/{sponsoredOrgId}/remove")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task RemoveSponsorship(Guid sponsoredOrgId)
|
||||
{
|
||||
|
||||
if (!await _currentContext.OrganizationOwner(sponsoredOrgId))
|
||||
{
|
||||
throw new BadRequestException("Only the owner of an organization can remove sponsorship.");
|
||||
}
|
||||
|
||||
var existingOrgSponsorship = await _organizationSponsorshipRepository
|
||||
.GetBySponsoredOrganizationIdAsync(sponsoredOrgId);
|
||||
|
||||
await _removeSponsorshipCommand.RemoveSponsorshipAsync(existingOrgSponsorship);
|
||||
}
|
||||
|
||||
[HttpGet("{sponsoringOrgId}/sync-status")]
|
||||
public async Task<object> GetSyncStatus(Guid sponsoringOrgId)
|
||||
{
|
||||
var sponsoringOrg = await _organizationRepository.GetByIdAsync(sponsoringOrgId);
|
||||
|
||||
if (!await _currentContext.OrganizationOwner(sponsoringOrg.Id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var lastSyncDate = await _organizationSponsorshipRepository.GetLatestSyncDateBySponsoringOrganizationIdAsync(sponsoringOrg.Id);
|
||||
return new OrganizationSponsorshipSyncStatusResponseModel(lastSyncDate);
|
||||
}
|
||||
|
||||
private Task<User> CurrentUser => _userService.GetUserByIdAsync(_currentContext.UserId.Value);
|
||||
}
|
||||
|
||||
@@ -13,460 +13,459 @@ using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("organizations/{orgId}/users")]
|
||||
[Authorize("Application")]
|
||||
public class OrganizationUsersController : Controller
|
||||
{
|
||||
[Route("organizations/{orgId}/users")]
|
||||
[Authorize("Application")]
|
||||
public class OrganizationUsersController : Controller
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
private readonly IGroupRepository _groupRepository;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IPolicyRepository _policyRepository;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public OrganizationUsersController(
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IOrganizationService organizationService,
|
||||
ICollectionRepository collectionRepository,
|
||||
IGroupRepository groupRepository,
|
||||
IUserService userService,
|
||||
IPolicyRepository policyRepository,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
private readonly IGroupRepository _groupRepository;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IPolicyRepository _policyRepository;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_organizationService = organizationService;
|
||||
_collectionRepository = collectionRepository;
|
||||
_groupRepository = groupRepository;
|
||||
_userService = userService;
|
||||
_policyRepository = policyRepository;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
public OrganizationUsersController(
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IOrganizationService organizationService,
|
||||
ICollectionRepository collectionRepository,
|
||||
IGroupRepository groupRepository,
|
||||
IUserService userService,
|
||||
IPolicyRepository policyRepository,
|
||||
ICurrentContext currentContext)
|
||||
[HttpGet("{id}")]
|
||||
public async Task<OrganizationUserDetailsResponseModel> Get(string orgId, string id)
|
||||
{
|
||||
var organizationUser = await _organizationUserRepository.GetByIdWithCollectionsAsync(new Guid(id));
|
||||
if (organizationUser == null || !await _currentContext.ManageUsers(organizationUser.Item1.OrganizationId))
|
||||
{
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_organizationService = organizationService;
|
||||
_collectionRepository = collectionRepository;
|
||||
_groupRepository = groupRepository;
|
||||
_userService = userService;
|
||||
_policyRepository = policyRepository;
|
||||
_currentContext = currentContext;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<OrganizationUserDetailsResponseModel> Get(string orgId, string id)
|
||||
{
|
||||
var organizationUser = await _organizationUserRepository.GetByIdWithCollectionsAsync(new Guid(id));
|
||||
if (organizationUser == null || !await _currentContext.ManageUsers(organizationUser.Item1.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return new OrganizationUserDetailsResponseModel(organizationUser.Item1, organizationUser.Item2);
|
||||
}
|
||||
|
||||
return new OrganizationUserDetailsResponseModel(organizationUser.Item1, organizationUser.Item2);
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<OrganizationUserUserDetailsResponseModel>> Get(string orgId)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ViewAllCollections(orgGuidId) &&
|
||||
!await _currentContext.ViewAssignedCollections(orgGuidId) &&
|
||||
!await _currentContext.ManageGroups(orgGuidId) &&
|
||||
!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<OrganizationUserUserDetailsResponseModel>> Get(string orgId)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ViewAllCollections(orgGuidId) &&
|
||||
!await _currentContext.ViewAssignedCollections(orgGuidId) &&
|
||||
!await _currentContext.ManageGroups(orgGuidId) &&
|
||||
!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var organizationUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(orgGuidId);
|
||||
var responseTasks = organizationUsers.Select(async o => new OrganizationUserUserDetailsResponseModel(o,
|
||||
await _userService.TwoFactorIsEnabledAsync(o)));
|
||||
var responses = await Task.WhenAll(responseTasks);
|
||||
return new ListResponseModel<OrganizationUserUserDetailsResponseModel>(responses);
|
||||
}
|
||||
|
||||
var organizationUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(orgGuidId);
|
||||
var responseTasks = organizationUsers.Select(async o => new OrganizationUserUserDetailsResponseModel(o,
|
||||
await _userService.TwoFactorIsEnabledAsync(o)));
|
||||
var responses = await Task.WhenAll(responseTasks);
|
||||
return new ListResponseModel<OrganizationUserUserDetailsResponseModel>(responses);
|
||||
[HttpGet("{id}/groups")]
|
||||
public async Task<IEnumerable<string>> GetGroups(string orgId, string id)
|
||||
{
|
||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
||||
if (organizationUser == null || (!await _currentContext.ManageGroups(organizationUser.OrganizationId) &&
|
||||
!await _currentContext.ManageUsers(organizationUser.OrganizationId)))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{id}/groups")]
|
||||
public async Task<IEnumerable<string>> GetGroups(string orgId, string id)
|
||||
{
|
||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
||||
if (organizationUser == null || (!await _currentContext.ManageGroups(organizationUser.OrganizationId) &&
|
||||
!await _currentContext.ManageUsers(organizationUser.OrganizationId)))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var groupIds = await _groupRepository.GetManyIdsByUserIdAsync(organizationUser.Id);
|
||||
var responses = groupIds.Select(g => g.ToString());
|
||||
return responses;
|
||||
}
|
||||
|
||||
var groupIds = await _groupRepository.GetManyIdsByUserIdAsync(organizationUser.Id);
|
||||
var responses = groupIds.Select(g => g.ToString());
|
||||
return responses;
|
||||
[HttpGet("{id}/reset-password-details")]
|
||||
public async Task<OrganizationUserResetPasswordDetailsResponseModel> GetResetPasswordDetails(string orgId, string id)
|
||||
{
|
||||
// Make sure the calling user can reset passwords for this org
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageResetPassword(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{id}/reset-password-details")]
|
||||
public async Task<OrganizationUserResetPasswordDetailsResponseModel> GetResetPasswordDetails(string orgId, string id)
|
||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
||||
if (organizationUser == null || !organizationUser.UserId.HasValue)
|
||||
{
|
||||
// Make sure the calling user can reset passwords for this org
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageResetPassword(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
||||
if (organizationUser == null || !organizationUser.UserId.HasValue)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
// Retrieve data necessary for response (KDF, KDF Iterations, ResetPasswordKey)
|
||||
// TODO Reset Password - Revisit this and create SPROC to reduce DB calls
|
||||
var user = await _userService.GetUserByIdAsync(organizationUser.UserId.Value);
|
||||
if (user == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
// Retrieve Encrypted Private Key from organization
|
||||
var org = await _organizationRepository.GetByIdAsync(orgGuidId);
|
||||
if (org == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return new OrganizationUserResetPasswordDetailsResponseModel(new OrganizationUserResetPasswordDetails(organizationUser, user, org));
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("invite")]
|
||||
public async Task Invite(string orgId, [FromBody] OrganizationUserInviteRequestModel model)
|
||||
// Retrieve data necessary for response (KDF, KDF Iterations, ResetPasswordKey)
|
||||
// TODO Reset Password - Revisit this and create SPROC to reduce DB calls
|
||||
var user = await _userService.GetUserByIdAsync(organizationUser.UserId.Value);
|
||||
if (user == null)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _organizationService.InviteUsersAsync(orgGuidId, userId.Value,
|
||||
new (OrganizationUserInvite, string)[] { (new OrganizationUserInvite(model.ToData()), null) });
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("reinvite")]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkReinvite(string orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||
// Retrieve Encrypted Private Key from organization
|
||||
var org = await _organizationRepository.GetByIdAsync(orgGuidId);
|
||||
if (org == null)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _organizationService.ResendInvitesAsync(orgGuidId, userId.Value, model.Ids);
|
||||
return new ListResponseModel<OrganizationUserBulkResponseModel>(
|
||||
result.Select(t => new OrganizationUserBulkResponseModel(t.Item1.Id, t.Item2)));
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("{id}/reinvite")]
|
||||
public async Task Reinvite(string orgId, string id)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return new OrganizationUserResetPasswordDetailsResponseModel(new OrganizationUserResetPasswordDetails(organizationUser, user, org));
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _organizationService.ResendInviteAsync(orgGuidId, userId.Value, new Guid(id));
|
||||
[HttpPost("invite")]
|
||||
public async Task Invite(string orgId, [FromBody] OrganizationUserInviteRequestModel model)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("{organizationUserId}/accept")]
|
||||
public async Task Accept(Guid orgId, Guid organizationUserId, [FromBody] OrganizationUserAcceptRequestModel model)
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _organizationService.InviteUsersAsync(orgGuidId, userId.Value,
|
||||
new (OrganizationUserInvite, string)[] { (new OrganizationUserInvite(model.ToData()), null) });
|
||||
}
|
||||
|
||||
[HttpPost("reinvite")]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkReinvite(string orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
var masterPasswordPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword);
|
||||
var useMasterPasswordPolicy = masterPasswordPolicy != null &&
|
||||
masterPasswordPolicy.Enabled &&
|
||||
masterPasswordPolicy.GetDataModel<ResetPasswordDataModel>().AutoEnrollEnabled;
|
||||
|
||||
if (useMasterPasswordPolicy &&
|
||||
string.IsNullOrWhiteSpace(model.ResetPasswordKey))
|
||||
{
|
||||
throw new BadRequestException(string.Empty, "Master Password reset is required, but not provided.");
|
||||
}
|
||||
|
||||
await _organizationService.AcceptUserAsync(organizationUserId, user, model.Token, _userService);
|
||||
|
||||
if (useMasterPasswordPolicy)
|
||||
{
|
||||
await _organizationService.UpdateUserResetPasswordEnrollmentAsync(orgId, user.Id, model.ResetPasswordKey, user.Id);
|
||||
}
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("{id}/confirm")]
|
||||
public async Task Confirm(string orgId, string id, [FromBody] OrganizationUserConfirmRequestModel model)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _organizationService.ResendInvitesAsync(orgGuidId, userId.Value, model.Ids);
|
||||
return new ListResponseModel<OrganizationUserBulkResponseModel>(
|
||||
result.Select(t => new OrganizationUserBulkResponseModel(t.Item1.Id, t.Item2)));
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _organizationService.ConfirmUserAsync(orgGuidId, new Guid(id), model.Key, userId.Value,
|
||||
_userService);
|
||||
[HttpPost("{id}/reinvite")]
|
||||
public async Task Reinvite(string orgId, string id)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("confirm")]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkConfirm(string orgId,
|
||||
[FromBody] OrganizationUserBulkConfirmRequestModel model)
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _organizationService.ResendInviteAsync(orgGuidId, userId.Value, new Guid(id));
|
||||
}
|
||||
|
||||
[HttpPost("{organizationUserId}/accept")]
|
||||
public async Task Accept(Guid orgId, Guid organizationUserId, [FromBody] OrganizationUserAcceptRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var results = await _organizationService.ConfirmUsersAsync(orgGuidId, model.ToDictionary(), userId.Value,
|
||||
_userService);
|
||||
|
||||
return new ListResponseModel<OrganizationUserBulkResponseModel>(results.Select(r =>
|
||||
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
[HttpPost("public-keys")]
|
||||
public async Task<ListResponseModel<OrganizationUserPublicKeyResponseModel>> UserPublicKeys(string orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var masterPasswordPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword);
|
||||
var useMasterPasswordPolicy = masterPasswordPolicy != null &&
|
||||
masterPasswordPolicy.Enabled &&
|
||||
masterPasswordPolicy.GetDataModel<ResetPasswordDataModel>().AutoEnrollEnabled;
|
||||
|
||||
var result = await _organizationUserRepository.GetManyPublicKeysByOrganizationUserAsync(orgGuidId, model.Ids);
|
||||
var responses = result.Select(r => new OrganizationUserPublicKeyResponseModel(r.Id, r.UserId, r.PublicKey)).ToList();
|
||||
return new ListResponseModel<OrganizationUserPublicKeyResponseModel>(responses);
|
||||
if (useMasterPasswordPolicy &&
|
||||
string.IsNullOrWhiteSpace(model.ResetPasswordKey))
|
||||
{
|
||||
throw new BadRequestException(string.Empty, "Master Password reset is required, but not provided.");
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
[HttpPost("{id}")]
|
||||
public async Task Put(string orgId, string id, [FromBody] OrganizationUserUpdateRequestModel model)
|
||||
await _organizationService.AcceptUserAsync(organizationUserId, user, model.Token, _userService);
|
||||
|
||||
if (useMasterPasswordPolicy)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
||||
if (organizationUser == null || organizationUser.OrganizationId != orgGuidId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _organizationService.SaveUserAsync(model.ToOrganizationUser(organizationUser), userId.Value,
|
||||
model.Collections?.Select(c => c.ToSelectionReadOnly()));
|
||||
}
|
||||
|
||||
[HttpPut("{id}/groups")]
|
||||
[HttpPost("{id}/groups")]
|
||||
public async Task PutGroups(string orgId, string id, [FromBody] OrganizationUserUpdateGroupsRequestModel model)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
||||
if (organizationUser == null || organizationUser.OrganizationId != orgGuidId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var loggedInUserId = _userService.GetProperUserId(User);
|
||||
await _organizationService.UpdateUserGroupsAsync(organizationUser, model.GroupIds.Select(g => new Guid(g)), loggedInUserId);
|
||||
}
|
||||
|
||||
[HttpPut("{userId}/reset-password-enrollment")]
|
||||
public async Task PutResetPasswordEnrollment(string orgId, string userId, [FromBody] OrganizationUserResetPasswordEnrollmentRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (model.ResetPasswordKey != null && !await _userService.VerifySecretAsync(user, model.Secret))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var callingUserId = user.Id;
|
||||
await _organizationService.UpdateUserResetPasswordEnrollmentAsync(
|
||||
new Guid(orgId), new Guid(userId), model.ResetPasswordKey, callingUserId);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{id}/reset-password")]
|
||||
public async Task PutResetPassword(string orgId, string id, [FromBody] OrganizationUserResetPasswordRequestModel model)
|
||||
{
|
||||
|
||||
var orgGuidId = new Guid(orgId);
|
||||
|
||||
// Calling user must have Manage Reset Password permission
|
||||
if (!await _currentContext.ManageResetPassword(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
// Get the users role, since provider users aren't a member of the organization we use the owner check
|
||||
var orgUserType = await _currentContext.OrganizationOwner(orgGuidId)
|
||||
? OrganizationUserType.Owner
|
||||
: _currentContext.Organizations?.FirstOrDefault(o => o.Id == orgGuidId)?.Type;
|
||||
if (orgUserType == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var result = await _userService.AdminResetPasswordAsync(orgUserType.Value, orgGuidId, new Guid(id), model.NewMasterPasswordHash, model.Key);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, error.Description);
|
||||
}
|
||||
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException(ModelState);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[HttpPost("{id}/delete")]
|
||||
public async Task Delete(string orgId, string id)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _organizationService.DeleteUserAsync(orgGuidId, new Guid(id), userId.Value);
|
||||
}
|
||||
|
||||
[HttpDelete("")]
|
||||
[HttpPost("delete")]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkDelete(string orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _organizationService.DeleteUsersAsync(orgGuidId, model.Ids, userId.Value);
|
||||
return new ListResponseModel<OrganizationUserBulkResponseModel>(result.Select(r =>
|
||||
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
|
||||
}
|
||||
|
||||
[Obsolete("2022-07-22 Moved to {id}/revoke endpoint")]
|
||||
[HttpPatch("{id}/deactivate")]
|
||||
[HttpPut("{id}/deactivate")]
|
||||
public async Task Deactivate(Guid orgId, Guid id)
|
||||
{
|
||||
await RevokeAsync(orgId, id);
|
||||
}
|
||||
|
||||
[Obsolete("2022-07-22 Moved to /revoke endpoint")]
|
||||
[HttpPatch("deactivate")]
|
||||
[HttpPut("deactivate")]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkDeactivate(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||
{
|
||||
return await BulkRevokeAsync(orgId, model);
|
||||
}
|
||||
|
||||
[Obsolete("2022-07-22 Moved to {id}/restore endpoint")]
|
||||
[HttpPatch("{id}/activate")]
|
||||
[HttpPut("{id}/activate")]
|
||||
public async Task Activate(Guid orgId, Guid id)
|
||||
{
|
||||
await RestoreAsync(orgId, id);
|
||||
}
|
||||
|
||||
[Obsolete("2022-07-22 Moved to /restore endpoint")]
|
||||
[HttpPatch("activate")]
|
||||
[HttpPut("activate")]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkActivate(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||
{
|
||||
return await BulkRestoreAsync(orgId, model);
|
||||
}
|
||||
|
||||
[HttpPatch("{id}/revoke")]
|
||||
[HttpPut("{id}/revoke")]
|
||||
public async Task RevokeAsync(Guid orgId, Guid id)
|
||||
{
|
||||
await RestoreOrRevokeUserAsync(orgId, id, _organizationService.RevokeUserAsync);
|
||||
}
|
||||
|
||||
[HttpPatch("revoke")]
|
||||
[HttpPut("revoke")]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkRevokeAsync(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||
{
|
||||
return await RestoreOrRevokeUsersAsync(orgId, model, _organizationService.RevokeUsersAsync);
|
||||
}
|
||||
|
||||
[HttpPatch("{id}/restore")]
|
||||
[HttpPut("{id}/restore")]
|
||||
public async Task RestoreAsync(Guid orgId, Guid id)
|
||||
{
|
||||
await RestoreOrRevokeUserAsync(orgId, id, (orgUser, userId) => _organizationService.RestoreUserAsync(orgUser, userId, _userService));
|
||||
}
|
||||
|
||||
[HttpPatch("restore")]
|
||||
[HttpPut("restore")]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkRestoreAsync(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||
{
|
||||
return await RestoreOrRevokeUsersAsync(orgId, model, (orgId, orgUserIds, restoringUserId) => _organizationService.RestoreUsersAsync(orgId, orgUserIds, restoringUserId, _userService));
|
||||
}
|
||||
|
||||
private async Task RestoreOrRevokeUserAsync(
|
||||
Guid orgId,
|
||||
Guid id,
|
||||
Func<OrganizationUser, Guid?, Task> statusAction)
|
||||
{
|
||||
if (!await _currentContext.ManageUsers(orgId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var orgUser = await _organizationUserRepository.GetByIdAsync(id);
|
||||
if (orgUser == null || orgUser.OrganizationId != orgId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await statusAction(orgUser, userId);
|
||||
}
|
||||
|
||||
private async Task<ListResponseModel<OrganizationUserBulkResponseModel>> RestoreOrRevokeUsersAsync(
|
||||
Guid orgId,
|
||||
OrganizationUserBulkRequestModel model,
|
||||
Func<Guid, IEnumerable<Guid>, Guid?, Task<List<Tuple<OrganizationUser, string>>>> statusAction)
|
||||
{
|
||||
if (!await _currentContext.ManageUsers(orgId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await statusAction(orgId, model.Ids, userId.Value);
|
||||
return new ListResponseModel<OrganizationUserBulkResponseModel>(result.Select(r =>
|
||||
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
|
||||
await _organizationService.UpdateUserResetPasswordEnrollmentAsync(orgId, user.Id, model.ResetPasswordKey, user.Id);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("{id}/confirm")]
|
||||
public async Task Confirm(string orgId, string id, [FromBody] OrganizationUserConfirmRequestModel model)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _organizationService.ConfirmUserAsync(orgGuidId, new Guid(id), model.Key, userId.Value,
|
||||
_userService);
|
||||
}
|
||||
|
||||
[HttpPost("confirm")]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkConfirm(string orgId,
|
||||
[FromBody] OrganizationUserBulkConfirmRequestModel model)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var results = await _organizationService.ConfirmUsersAsync(orgGuidId, model.ToDictionary(), userId.Value,
|
||||
_userService);
|
||||
|
||||
return new ListResponseModel<OrganizationUserBulkResponseModel>(results.Select(r =>
|
||||
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
|
||||
}
|
||||
|
||||
[HttpPost("public-keys")]
|
||||
public async Task<ListResponseModel<OrganizationUserPublicKeyResponseModel>> UserPublicKeys(string orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var result = await _organizationUserRepository.GetManyPublicKeysByOrganizationUserAsync(orgGuidId, model.Ids);
|
||||
var responses = result.Select(r => new OrganizationUserPublicKeyResponseModel(r.Id, r.UserId, r.PublicKey)).ToList();
|
||||
return new ListResponseModel<OrganizationUserPublicKeyResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
[HttpPost("{id}")]
|
||||
public async Task Put(string orgId, string id, [FromBody] OrganizationUserUpdateRequestModel model)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
||||
if (organizationUser == null || organizationUser.OrganizationId != orgGuidId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _organizationService.SaveUserAsync(model.ToOrganizationUser(organizationUser), userId.Value,
|
||||
model.Collections?.Select(c => c.ToSelectionReadOnly()));
|
||||
}
|
||||
|
||||
[HttpPut("{id}/groups")]
|
||||
[HttpPost("{id}/groups")]
|
||||
public async Task PutGroups(string orgId, string id, [FromBody] OrganizationUserUpdateGroupsRequestModel model)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
||||
if (organizationUser == null || organizationUser.OrganizationId != orgGuidId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var loggedInUserId = _userService.GetProperUserId(User);
|
||||
await _organizationService.UpdateUserGroupsAsync(organizationUser, model.GroupIds.Select(g => new Guid(g)), loggedInUserId);
|
||||
}
|
||||
|
||||
[HttpPut("{userId}/reset-password-enrollment")]
|
||||
public async Task PutResetPasswordEnrollment(string orgId, string userId, [FromBody] OrganizationUserResetPasswordEnrollmentRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (model.ResetPasswordKey != null && !await _userService.VerifySecretAsync(user, model.Secret))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var callingUserId = user.Id;
|
||||
await _organizationService.UpdateUserResetPasswordEnrollmentAsync(
|
||||
new Guid(orgId), new Guid(userId), model.ResetPasswordKey, callingUserId);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{id}/reset-password")]
|
||||
public async Task PutResetPassword(string orgId, string id, [FromBody] OrganizationUserResetPasswordRequestModel model)
|
||||
{
|
||||
|
||||
var orgGuidId = new Guid(orgId);
|
||||
|
||||
// Calling user must have Manage Reset Password permission
|
||||
if (!await _currentContext.ManageResetPassword(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
// Get the users role, since provider users aren't a member of the organization we use the owner check
|
||||
var orgUserType = await _currentContext.OrganizationOwner(orgGuidId)
|
||||
? OrganizationUserType.Owner
|
||||
: _currentContext.Organizations?.FirstOrDefault(o => o.Id == orgGuidId)?.Type;
|
||||
if (orgUserType == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var result = await _userService.AdminResetPasswordAsync(orgUserType.Value, orgGuidId, new Guid(id), model.NewMasterPasswordHash, model.Key);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, error.Description);
|
||||
}
|
||||
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException(ModelState);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[HttpPost("{id}/delete")]
|
||||
public async Task Delete(string orgId, string id)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _organizationService.DeleteUserAsync(orgGuidId, new Guid(id), userId.Value);
|
||||
}
|
||||
|
||||
[HttpDelete("")]
|
||||
[HttpPost("delete")]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkDelete(string orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _organizationService.DeleteUsersAsync(orgGuidId, model.Ids, userId.Value);
|
||||
return new ListResponseModel<OrganizationUserBulkResponseModel>(result.Select(r =>
|
||||
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
|
||||
}
|
||||
|
||||
[Obsolete("2022-07-22 Moved to {id}/revoke endpoint")]
|
||||
[HttpPatch("{id}/deactivate")]
|
||||
[HttpPut("{id}/deactivate")]
|
||||
public async Task Deactivate(Guid orgId, Guid id)
|
||||
{
|
||||
await RevokeAsync(orgId, id);
|
||||
}
|
||||
|
||||
[Obsolete("2022-07-22 Moved to /revoke endpoint")]
|
||||
[HttpPatch("deactivate")]
|
||||
[HttpPut("deactivate")]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkDeactivate(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||
{
|
||||
return await BulkRevokeAsync(orgId, model);
|
||||
}
|
||||
|
||||
[Obsolete("2022-07-22 Moved to {id}/restore endpoint")]
|
||||
[HttpPatch("{id}/activate")]
|
||||
[HttpPut("{id}/activate")]
|
||||
public async Task Activate(Guid orgId, Guid id)
|
||||
{
|
||||
await RestoreAsync(orgId, id);
|
||||
}
|
||||
|
||||
[Obsolete("2022-07-22 Moved to /restore endpoint")]
|
||||
[HttpPatch("activate")]
|
||||
[HttpPut("activate")]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkActivate(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||
{
|
||||
return await BulkRestoreAsync(orgId, model);
|
||||
}
|
||||
|
||||
[HttpPatch("{id}/revoke")]
|
||||
[HttpPut("{id}/revoke")]
|
||||
public async Task RevokeAsync(Guid orgId, Guid id)
|
||||
{
|
||||
await RestoreOrRevokeUserAsync(orgId, id, _organizationService.RevokeUserAsync);
|
||||
}
|
||||
|
||||
[HttpPatch("revoke")]
|
||||
[HttpPut("revoke")]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkRevokeAsync(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||
{
|
||||
return await RestoreOrRevokeUsersAsync(orgId, model, _organizationService.RevokeUsersAsync);
|
||||
}
|
||||
|
||||
[HttpPatch("{id}/restore")]
|
||||
[HttpPut("{id}/restore")]
|
||||
public async Task RestoreAsync(Guid orgId, Guid id)
|
||||
{
|
||||
await RestoreOrRevokeUserAsync(orgId, id, (orgUser, userId) => _organizationService.RestoreUserAsync(orgUser, userId, _userService));
|
||||
}
|
||||
|
||||
[HttpPatch("restore")]
|
||||
[HttpPut("restore")]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkRestoreAsync(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||
{
|
||||
return await RestoreOrRevokeUsersAsync(orgId, model, (orgId, orgUserIds, restoringUserId) => _organizationService.RestoreUsersAsync(orgId, orgUserIds, restoringUserId, _userService));
|
||||
}
|
||||
|
||||
private async Task RestoreOrRevokeUserAsync(
|
||||
Guid orgId,
|
||||
Guid id,
|
||||
Func<OrganizationUser, Guid?, Task> statusAction)
|
||||
{
|
||||
if (!await _currentContext.ManageUsers(orgId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var orgUser = await _organizationUserRepository.GetByIdAsync(id);
|
||||
if (orgUser == null || orgUser.OrganizationId != orgId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await statusAction(orgUser, userId);
|
||||
}
|
||||
|
||||
private async Task<ListResponseModel<OrganizationUserBulkResponseModel>> RestoreOrRevokeUsersAsync(
|
||||
Guid orgId,
|
||||
OrganizationUserBulkRequestModel model,
|
||||
Func<Guid, IEnumerable<Guid>, Guid?, Task<List<Tuple<OrganizationUser, string>>>> statusAction)
|
||||
{
|
||||
if (!await _currentContext.ManageUsers(orgId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await statusAction(orgId, model.Ids, userId.Value);
|
||||
return new ListResponseModel<OrganizationUserBulkResponseModel>(result.Select(r =>
|
||||
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,33 +4,32 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("plans")]
|
||||
[Authorize("Web")]
|
||||
public class PlansController : Controller
|
||||
{
|
||||
[Route("plans")]
|
||||
[Authorize("Web")]
|
||||
public class PlansController : Controller
|
||||
private readonly ITaxRateRepository _taxRateRepository;
|
||||
public PlansController(ITaxRateRepository taxRateRepository)
|
||||
{
|
||||
private readonly ITaxRateRepository _taxRateRepository;
|
||||
public PlansController(ITaxRateRepository taxRateRepository)
|
||||
{
|
||||
_taxRateRepository = taxRateRepository;
|
||||
}
|
||||
_taxRateRepository = taxRateRepository;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
[AllowAnonymous]
|
||||
public ListResponseModel<PlanResponseModel> Get()
|
||||
{
|
||||
var data = StaticStore.Plans;
|
||||
var responses = data.Select(plan => new PlanResponseModel(plan));
|
||||
return new ListResponseModel<PlanResponseModel>(responses);
|
||||
}
|
||||
[HttpGet("")]
|
||||
[AllowAnonymous]
|
||||
public ListResponseModel<PlanResponseModel> Get()
|
||||
{
|
||||
var data = StaticStore.Plans;
|
||||
var responses = data.Select(plan => new PlanResponseModel(plan));
|
||||
return new ListResponseModel<PlanResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpGet("sales-tax-rates")]
|
||||
public async Task<ListResponseModel<TaxRateResponseModel>> GetTaxRates()
|
||||
{
|
||||
var data = await _taxRateRepository.GetAllActiveAsync();
|
||||
var responses = data.Select(x => new TaxRateResponseModel(x));
|
||||
return new ListResponseModel<TaxRateResponseModel>(responses);
|
||||
}
|
||||
[HttpGet("sales-tax-rates")]
|
||||
public async Task<ListResponseModel<TaxRateResponseModel>> GetTaxRates()
|
||||
{
|
||||
var data = await _taxRateRepository.GetAllActiveAsync();
|
||||
var responses = data.Select(x => new TaxRateResponseModel(x));
|
||||
return new ListResponseModel<TaxRateResponseModel>(responses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,145 +11,144 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("organizations/{orgId}/policies")]
|
||||
[Authorize("Application")]
|
||||
public class PoliciesController : Controller
|
||||
{
|
||||
[Route("organizations/{orgId}/policies")]
|
||||
[Authorize("Application")]
|
||||
public class PoliciesController : Controller
|
||||
private readonly IPolicyRepository _policyRepository;
|
||||
private readonly IPolicyService _policyService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IDataProtector _organizationServiceDataProtector;
|
||||
|
||||
public PoliciesController(
|
||||
IPolicyRepository policyRepository,
|
||||
IPolicyService policyService,
|
||||
IOrganizationService organizationService,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext,
|
||||
GlobalSettings globalSettings,
|
||||
IDataProtectionProvider dataProtectionProvider)
|
||||
{
|
||||
private readonly IPolicyRepository _policyRepository;
|
||||
private readonly IPolicyService _policyService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IDataProtector _organizationServiceDataProtector;
|
||||
_policyRepository = policyRepository;
|
||||
_policyService = policyService;
|
||||
_organizationService = organizationService;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
_globalSettings = globalSettings;
|
||||
_organizationServiceDataProtector = dataProtectionProvider.CreateProtector(
|
||||
"OrganizationServiceDataProtector");
|
||||
}
|
||||
|
||||
public PoliciesController(
|
||||
IPolicyRepository policyRepository,
|
||||
IPolicyService policyService,
|
||||
IOrganizationService organizationService,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext,
|
||||
GlobalSettings globalSettings,
|
||||
IDataProtectionProvider dataProtectionProvider)
|
||||
[HttpGet("{type}")]
|
||||
public async Task<PolicyResponseModel> Get(string orgId, int type)
|
||||
{
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
if (!await _currentContext.ManagePolicies(orgIdGuid))
|
||||
{
|
||||
_policyRepository = policyRepository;
|
||||
_policyService = policyService;
|
||||
_organizationService = organizationService;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
_globalSettings = globalSettings;
|
||||
_organizationServiceDataProtector = dataProtectionProvider.CreateProtector(
|
||||
"OrganizationServiceDataProtector");
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(orgIdGuid, (PolicyType)type);
|
||||
if (policy == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{type}")]
|
||||
public async Task<PolicyResponseModel> Get(string orgId, int type)
|
||||
{
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
if (!await _currentContext.ManagePolicies(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(orgIdGuid, (PolicyType)type);
|
||||
if (policy == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return new PolicyResponseModel(policy);
|
||||
}
|
||||
|
||||
return new PolicyResponseModel(policy);
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<PolicyResponseModel>> Get(string orgId)
|
||||
{
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
if (!await _currentContext.ManagePolicies(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<PolicyResponseModel>> Get(string orgId)
|
||||
{
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
if (!await _currentContext.ManagePolicies(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var policies = await _policyRepository.GetManyByOrganizationIdAsync(orgIdGuid);
|
||||
var responses = policies.Select(p => new PolicyResponseModel(p));
|
||||
return new ListResponseModel<PolicyResponseModel>(responses);
|
||||
}
|
||||
|
||||
var policies = await _policyRepository.GetManyByOrganizationIdAsync(orgIdGuid);
|
||||
var responses = policies.Select(p => new PolicyResponseModel(p));
|
||||
return new ListResponseModel<PolicyResponseModel>(responses);
|
||||
[AllowAnonymous]
|
||||
[HttpGet("token")]
|
||||
public async Task<ListResponseModel<PolicyResponseModel>> GetByToken(string orgId, [FromQuery] string email,
|
||||
[FromQuery] string token, [FromQuery] string organizationUserId)
|
||||
{
|
||||
var orgUserId = new Guid(organizationUserId);
|
||||
var tokenValid = CoreHelpers.UserInviteTokenIsValid(_organizationServiceDataProtector, token,
|
||||
email, orgUserId, _globalSettings);
|
||||
if (!tokenValid)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet("token")]
|
||||
public async Task<ListResponseModel<PolicyResponseModel>> GetByToken(string orgId, [FromQuery] string email,
|
||||
[FromQuery] string token, [FromQuery] string organizationUserId)
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
var orgUser = await _organizationUserRepository.GetByIdAsync(orgUserId);
|
||||
if (orgUser == null || orgUser.OrganizationId != orgIdGuid)
|
||||
{
|
||||
var orgUserId = new Guid(organizationUserId);
|
||||
var tokenValid = CoreHelpers.UserInviteTokenIsValid(_organizationServiceDataProtector, token,
|
||||
email, orgUserId, _globalSettings);
|
||||
if (!tokenValid)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
var orgUser = await _organizationUserRepository.GetByIdAsync(orgUserId);
|
||||
if (orgUser == null || orgUser.OrganizationId != orgIdGuid)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var policies = await _policyRepository.GetManyByOrganizationIdAsync(orgIdGuid);
|
||||
var responses = policies.Where(p => p.Enabled).Select(p => new PolicyResponseModel(p));
|
||||
return new ListResponseModel<PolicyResponseModel>(responses);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet("invited-user")]
|
||||
public async Task<ListResponseModel<PolicyResponseModel>> GetByInvitedUser(string orgId, [FromQuery] string userId)
|
||||
{
|
||||
var user = await _userService.GetUserByIdAsync(new Guid(userId));
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
var orgUsersByUserId = await _organizationUserRepository.GetManyByUserAsync(user.Id);
|
||||
var orgUser = orgUsersByUserId.SingleOrDefault(u => u.OrganizationId == orgIdGuid);
|
||||
if (orgUser == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
if (orgUser.Status != OrganizationUserStatusType.Invited)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
var policies = await _policyRepository.GetManyByOrganizationIdAsync(orgIdGuid);
|
||||
var responses = policies.Where(p => p.Enabled).Select(p => new PolicyResponseModel(p));
|
||||
return new ListResponseModel<PolicyResponseModel>(responses);
|
||||
}
|
||||
|
||||
var policies = await _policyRepository.GetManyByOrganizationIdAsync(orgIdGuid);
|
||||
var responses = policies.Where(p => p.Enabled).Select(p => new PolicyResponseModel(p));
|
||||
return new ListResponseModel<PolicyResponseModel>(responses);
|
||||
[AllowAnonymous]
|
||||
[HttpGet("invited-user")]
|
||||
public async Task<ListResponseModel<PolicyResponseModel>> GetByInvitedUser(string orgId, [FromQuery] string userId)
|
||||
{
|
||||
var user = await _userService.GetUserByIdAsync(new Guid(userId));
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
var orgUsersByUserId = await _organizationUserRepository.GetManyByUserAsync(user.Id);
|
||||
var orgUser = orgUsersByUserId.SingleOrDefault(u => u.OrganizationId == orgIdGuid);
|
||||
if (orgUser == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
if (orgUser.Status != OrganizationUserStatusType.Invited)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
[HttpPut("{type}")]
|
||||
public async Task<PolicyResponseModel> Put(string orgId, int type, [FromBody] PolicyRequestModel model)
|
||||
{
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
if (!await _currentContext.ManagePolicies(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(new Guid(orgId), (PolicyType)type);
|
||||
if (policy == null)
|
||||
{
|
||||
policy = model.ToPolicy(orgIdGuid);
|
||||
}
|
||||
else
|
||||
{
|
||||
policy = model.ToPolicy(policy);
|
||||
}
|
||||
var policies = await _policyRepository.GetManyByOrganizationIdAsync(orgIdGuid);
|
||||
var responses = policies.Where(p => p.Enabled).Select(p => new PolicyResponseModel(p));
|
||||
return new ListResponseModel<PolicyResponseModel>(responses);
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _policyService.SaveAsync(policy, _userService, _organizationService, userId);
|
||||
return new PolicyResponseModel(policy);
|
||||
[HttpPut("{type}")]
|
||||
public async Task<PolicyResponseModel> Put(string orgId, int type, [FromBody] PolicyRequestModel model)
|
||||
{
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
if (!await _currentContext.ManagePolicies(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(new Guid(orgId), (PolicyType)type);
|
||||
if (policy == null)
|
||||
{
|
||||
policy = model.ToPolicy(orgIdGuid);
|
||||
}
|
||||
else
|
||||
{
|
||||
policy = model.ToPolicy(policy);
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _policyService.SaveAsync(policy, _userService, _organizationService, userId);
|
||||
return new PolicyResponseModel(policy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,87 +9,86 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("providers/{providerId:guid}/organizations")]
|
||||
[Authorize("Application")]
|
||||
public class ProviderOrganizationsController : Controller
|
||||
{
|
||||
[Route("providers/{providerId:guid}/organizations")]
|
||||
[Authorize("Application")]
|
||||
public class ProviderOrganizationsController : Controller
|
||||
|
||||
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
|
||||
private readonly IProviderService _providerService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public ProviderOrganizationsController(
|
||||
IProviderOrganizationRepository providerOrganizationRepository,
|
||||
IProviderService providerService,
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
_providerOrganizationRepository = providerOrganizationRepository;
|
||||
_providerService = providerService;
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
|
||||
private readonly IProviderService _providerService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public ProviderOrganizationsController(
|
||||
IProviderOrganizationRepository providerOrganizationRepository,
|
||||
IProviderService providerService,
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext)
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<ProviderOrganizationOrganizationDetailsResponseModel>> Get(Guid providerId)
|
||||
{
|
||||
if (!_currentContext.AccessProviderOrganizations(providerId))
|
||||
{
|
||||
_providerOrganizationRepository = providerOrganizationRepository;
|
||||
_providerService = providerService;
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<ProviderOrganizationOrganizationDetailsResponseModel>> Get(Guid providerId)
|
||||
{
|
||||
if (!_currentContext.AccessProviderOrganizations(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var providerOrganizations = await _providerOrganizationRepository.GetManyDetailsByProviderAsync(providerId);
|
||||
var responses = providerOrganizations.Select(o => new ProviderOrganizationOrganizationDetailsResponseModel(o));
|
||||
return new ListResponseModel<ProviderOrganizationOrganizationDetailsResponseModel>(responses);
|
||||
}
|
||||
|
||||
var providerOrganizations = await _providerOrganizationRepository.GetManyDetailsByProviderAsync(providerId);
|
||||
var responses = providerOrganizations.Select(o => new ProviderOrganizationOrganizationDetailsResponseModel(o));
|
||||
return new ListResponseModel<ProviderOrganizationOrganizationDetailsResponseModel>(responses);
|
||||
[HttpPost("add")]
|
||||
public async Task Add(Guid providerId, [FromBody] ProviderOrganizationAddRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ManageProviderOrganizations(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("add")]
|
||||
public async Task Add(Guid providerId, [FromBody] ProviderOrganizationAddRequestModel model)
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
|
||||
await _providerService.AddOrganization(providerId, model.OrganizationId, userId, model.Key);
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<ProviderOrganizationResponseModel> Post(Guid providerId, [FromBody] ProviderOrganizationCreateRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
if (!_currentContext.ManageProviderOrganizations(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
|
||||
await _providerService.AddOrganization(providerId, model.OrganizationId, userId, model.Key);
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<ProviderOrganizationResponseModel> Post(Guid providerId, [FromBody] ProviderOrganizationCreateRequestModel model)
|
||||
if (!_currentContext.ManageProviderOrganizations(providerId))
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (!_currentContext.ManageProviderOrganizations(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var organizationSignup = model.OrganizationCreateRequest.ToOrganizationSignup(user);
|
||||
var result = await _providerService.CreateOrganizationAsync(providerId, organizationSignup, model.ClientOwnerEmail, user);
|
||||
return new ProviderOrganizationResponseModel(result);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpDelete("{id:guid}")]
|
||||
[HttpPost("{id:guid}/delete")]
|
||||
public async Task Delete(Guid providerId, Guid id)
|
||||
{
|
||||
if (!_currentContext.ManageProviderOrganizations(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var organizationSignup = model.OrganizationCreateRequest.ToOrganizationSignup(user);
|
||||
var result = await _providerService.CreateOrganizationAsync(providerId, organizationSignup, model.ClientOwnerEmail, user);
|
||||
return new ProviderOrganizationResponseModel(result);
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.RemoveOrganizationAsync(providerId, id, userId.Value);
|
||||
[HttpDelete("{id:guid}")]
|
||||
[HttpPost("{id:guid}/delete")]
|
||||
public async Task Delete(Guid providerId, Guid id)
|
||||
{
|
||||
if (!_currentContext.ManageProviderOrganizations(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.RemoveOrganizationAsync(providerId, id, userId.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,192 +9,191 @@ using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("providers/{providerId:guid}/users")]
|
||||
[Authorize("Application")]
|
||||
public class ProviderUsersController : Controller
|
||||
{
|
||||
[Route("providers/{providerId:guid}/users")]
|
||||
[Authorize("Application")]
|
||||
public class ProviderUsersController : Controller
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IProviderService _providerService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public ProviderUsersController(
|
||||
IProviderUserRepository providerUserRepository,
|
||||
IProviderService providerService,
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IProviderService _providerService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_providerService = providerService;
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
public ProviderUsersController(
|
||||
IProviderUserRepository providerUserRepository,
|
||||
IProviderService providerService,
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext)
|
||||
[HttpGet("{id:guid}")]
|
||||
public async Task<ProviderUserResponseModel> Get(Guid providerId, Guid id)
|
||||
{
|
||||
var providerUser = await _providerUserRepository.GetByIdAsync(id);
|
||||
if (providerUser == null || !_currentContext.ProviderManageUsers(providerUser.ProviderId))
|
||||
{
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_providerService = providerService;
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{id:guid}")]
|
||||
public async Task<ProviderUserResponseModel> Get(Guid providerId, Guid id)
|
||||
{
|
||||
var providerUser = await _providerUserRepository.GetByIdAsync(id);
|
||||
if (providerUser == null || !_currentContext.ProviderManageUsers(providerUser.ProviderId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return new ProviderUserResponseModel(providerUser);
|
||||
}
|
||||
|
||||
return new ProviderUserResponseModel(providerUser);
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<ProviderUserUserDetailsResponseModel>> Get(Guid providerId)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<ProviderUserUserDetailsResponseModel>> Get(Guid providerId)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var providerUsers = await _providerUserRepository.GetManyDetailsByProviderAsync(providerId);
|
||||
var responses = providerUsers.Select(o => new ProviderUserUserDetailsResponseModel(o));
|
||||
return new ListResponseModel<ProviderUserUserDetailsResponseModel>(responses);
|
||||
}
|
||||
|
||||
var providerUsers = await _providerUserRepository.GetManyDetailsByProviderAsync(providerId);
|
||||
var responses = providerUsers.Select(o => new ProviderUserUserDetailsResponseModel(o));
|
||||
return new ListResponseModel<ProviderUserUserDetailsResponseModel>(responses);
|
||||
[HttpPost("invite")]
|
||||
public async Task Invite(Guid providerId, [FromBody] ProviderUserInviteRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("invite")]
|
||||
public async Task Invite(Guid providerId, [FromBody] ProviderUserInviteRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var invite = ProviderUserInviteFactory.CreateIntialInvite(model.Emails, model.Type.Value,
|
||||
_userService.GetProperUserId(User).Value, providerId);
|
||||
await _providerService.InviteUserAsync(invite);
|
||||
}
|
||||
|
||||
var invite = ProviderUserInviteFactory.CreateIntialInvite(model.Emails, model.Type.Value,
|
||||
_userService.GetProperUserId(User).Value, providerId);
|
||||
await _providerService.InviteUserAsync(invite);
|
||||
[HttpPost("reinvite")]
|
||||
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkReinvite(Guid providerId, [FromBody] ProviderUserBulkRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("reinvite")]
|
||||
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkReinvite(Guid providerId, [FromBody] ProviderUserBulkRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var invite = ProviderUserInviteFactory.CreateReinvite(model.Ids, _userService.GetProperUserId(User).Value, providerId);
|
||||
var result = await _providerService.ResendInvitesAsync(invite);
|
||||
return new ListResponseModel<ProviderUserBulkResponseModel>(
|
||||
result.Select(t => new ProviderUserBulkResponseModel(t.Item1.Id, t.Item2)));
|
||||
}
|
||||
|
||||
var invite = ProviderUserInviteFactory.CreateReinvite(model.Ids, _userService.GetProperUserId(User).Value, providerId);
|
||||
var result = await _providerService.ResendInvitesAsync(invite);
|
||||
return new ListResponseModel<ProviderUserBulkResponseModel>(
|
||||
result.Select(t => new ProviderUserBulkResponseModel(t.Item1.Id, t.Item2)));
|
||||
[HttpPost("{id:guid}/reinvite")]
|
||||
public async Task Reinvite(Guid providerId, Guid id)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/reinvite")]
|
||||
public async Task Reinvite(Guid providerId, Guid id)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var invite = ProviderUserInviteFactory.CreateReinvite(new[] { id },
|
||||
_userService.GetProperUserId(User).Value, providerId);
|
||||
await _providerService.ResendInvitesAsync(invite);
|
||||
}
|
||||
|
||||
var invite = ProviderUserInviteFactory.CreateReinvite(new[] { id },
|
||||
_userService.GetProperUserId(User).Value, providerId);
|
||||
await _providerService.ResendInvitesAsync(invite);
|
||||
[HttpPost("{id:guid}/accept")]
|
||||
public async Task Accept(Guid providerId, Guid id, [FromBody] ProviderUserAcceptRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/accept")]
|
||||
public async Task Accept(Guid providerId, Guid id, [FromBody] ProviderUserAcceptRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
await _providerService.AcceptUserAsync(id, user, model.Token);
|
||||
}
|
||||
|
||||
await _providerService.AcceptUserAsync(id, user, model.Token);
|
||||
[HttpPost("{id:guid}/confirm")]
|
||||
public async Task Confirm(Guid providerId, Guid id, [FromBody] ProviderUserConfirmRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/confirm")]
|
||||
public async Task Confirm(Guid providerId, Guid id, [FromBody] ProviderUserConfirmRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.ConfirmUsersAsync(providerId, new Dictionary<Guid, string> { [id] = model.Key }, userId.Value);
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.ConfirmUsersAsync(providerId, new Dictionary<Guid, string> { [id] = model.Key }, userId.Value);
|
||||
[HttpPost("confirm")]
|
||||
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkConfirm(Guid providerId,
|
||||
[FromBody] ProviderUserBulkConfirmRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("confirm")]
|
||||
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkConfirm(Guid providerId,
|
||||
[FromBody] ProviderUserBulkConfirmRequestModel model)
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var results = await _providerService.ConfirmUsersAsync(providerId, model.ToDictionary(), userId.Value);
|
||||
|
||||
return new ListResponseModel<ProviderUserBulkResponseModel>(results.Select(r =>
|
||||
new ProviderUserBulkResponseModel(r.Item1.Id, r.Item2)));
|
||||
}
|
||||
|
||||
[HttpPost("public-keys")]
|
||||
public async Task<ListResponseModel<ProviderUserPublicKeyResponseModel>> UserPublicKeys(Guid providerId, [FromBody] ProviderUserBulkRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var results = await _providerService.ConfirmUsersAsync(providerId, model.ToDictionary(), userId.Value);
|
||||
|
||||
return new ListResponseModel<ProviderUserBulkResponseModel>(results.Select(r =>
|
||||
new ProviderUserBulkResponseModel(r.Item1.Id, r.Item2)));
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("public-keys")]
|
||||
public async Task<ListResponseModel<ProviderUserPublicKeyResponseModel>> UserPublicKeys(Guid providerId, [FromBody] ProviderUserBulkRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var result = await _providerUserRepository.GetManyPublicKeysByProviderUserAsync(providerId, model.Ids);
|
||||
var responses = result.Select(r => new ProviderUserPublicKeyResponseModel(r.Id, r.UserId, r.PublicKey)).ToList();
|
||||
return new ListResponseModel<ProviderUserPublicKeyResponseModel>(responses);
|
||||
}
|
||||
|
||||
var result = await _providerUserRepository.GetManyPublicKeysByProviderUserAsync(providerId, model.Ids);
|
||||
var responses = result.Select(r => new ProviderUserPublicKeyResponseModel(r.Id, r.UserId, r.PublicKey)).ToList();
|
||||
return new ListResponseModel<ProviderUserPublicKeyResponseModel>(responses);
|
||||
[HttpPut("{id:guid}")]
|
||||
[HttpPost("{id:guid}")]
|
||||
public async Task Put(Guid providerId, Guid id, [FromBody] ProviderUserUpdateRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPut("{id:guid}")]
|
||||
[HttpPost("{id:guid}")]
|
||||
public async Task Put(Guid providerId, Guid id, [FromBody] ProviderUserUpdateRequestModel model)
|
||||
var providerUser = await _providerUserRepository.GetByIdAsync(id);
|
||||
if (providerUser == null || providerUser.ProviderId != providerId)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var providerUser = await _providerUserRepository.GetByIdAsync(id);
|
||||
if (providerUser == null || providerUser.ProviderId != providerId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.SaveUserAsync(model.ToProviderUser(providerUser), userId.Value);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpDelete("{id:guid}")]
|
||||
[HttpPost("{id:guid}/delete")]
|
||||
public async Task Delete(Guid providerId, Guid id)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.SaveUserAsync(model.ToProviderUser(providerUser), userId.Value);
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.DeleteUsersAsync(providerId, new[] { id }, userId.Value);
|
||||
[HttpDelete("{id:guid}")]
|
||||
[HttpPost("{id:guid}/delete")]
|
||||
public async Task Delete(Guid providerId, Guid id)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpDelete("")]
|
||||
[HttpPost("delete")]
|
||||
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkDelete(Guid providerId, [FromBody] ProviderUserBulkRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.DeleteUsersAsync(providerId, new[] { id }, userId.Value);
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _providerService.DeleteUsersAsync(providerId, model.Ids, userId.Value);
|
||||
return new ListResponseModel<ProviderUserBulkResponseModel>(result.Select(r =>
|
||||
new ProviderUserBulkResponseModel(r.Item1.Id, r.Item2)));
|
||||
[HttpDelete("")]
|
||||
[HttpPost("delete")]
|
||||
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkDelete(Guid providerId, [FromBody] ProviderUserBulkRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderManageUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _providerService.DeleteUsersAsync(providerId, model.Ids, userId.Value);
|
||||
return new ListResponseModel<ProviderUserBulkResponseModel>(result.Select(r =>
|
||||
new ProviderUserBulkResponseModel(r.Item1.Id, r.Item2)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,84 +8,83 @@ using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("providers")]
|
||||
[Authorize("Application")]
|
||||
public class ProvidersController : Controller
|
||||
{
|
||||
[Route("providers")]
|
||||
[Authorize("Application")]
|
||||
public class ProvidersController : Controller
|
||||
private readonly IUserService _userService;
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
private readonly IProviderService _providerService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public ProvidersController(IUserService userService, IProviderRepository providerRepository,
|
||||
IProviderService providerService, ICurrentContext currentContext, GlobalSettings globalSettings)
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
private readonly IProviderService _providerService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
_userService = userService;
|
||||
_providerRepository = providerRepository;
|
||||
_providerService = providerService;
|
||||
_currentContext = currentContext;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
public ProvidersController(IUserService userService, IProviderRepository providerRepository,
|
||||
IProviderService providerService, ICurrentContext currentContext, GlobalSettings globalSettings)
|
||||
[HttpGet("{id:guid}")]
|
||||
public async Task<ProviderResponseModel> Get(Guid id)
|
||||
{
|
||||
if (!_currentContext.ProviderUser(id))
|
||||
{
|
||||
_userService = userService;
|
||||
_providerRepository = providerRepository;
|
||||
_providerService = providerService;
|
||||
_currentContext = currentContext;
|
||||
_globalSettings = globalSettings;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{id:guid}")]
|
||||
public async Task<ProviderResponseModel> Get(Guid id)
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider == null)
|
||||
{
|
||||
if (!_currentContext.ProviderUser(id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return new ProviderResponseModel(provider);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPut("{id:guid}")]
|
||||
[HttpPost("{id:guid}")]
|
||||
public async Task<ProviderResponseModel> Put(Guid id, [FromBody] ProviderUpdateRequestModel model)
|
||||
return new ProviderResponseModel(provider);
|
||||
}
|
||||
|
||||
[HttpPut("{id:guid}")]
|
||||
[HttpPost("{id:guid}")]
|
||||
public async Task<ProviderResponseModel> Put(Guid id, [FromBody] ProviderUpdateRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderProviderAdmin(id))
|
||||
{
|
||||
if (!_currentContext.ProviderProviderAdmin(id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _providerService.UpdateAsync(model.ToProvider(provider, _globalSettings));
|
||||
return new ProviderResponseModel(provider);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/setup")]
|
||||
public async Task<ProviderResponseModel> Setup(Guid id, [FromBody] ProviderSetupRequestModel model)
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider == null)
|
||||
{
|
||||
if (!_currentContext.ProviderProviderAdmin(id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
|
||||
var response =
|
||||
await _providerService.CompleteSetupAsync(model.ToProvider(provider), userId, model.Token, model.Key);
|
||||
|
||||
return new ProviderResponseModel(response);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _providerService.UpdateAsync(model.ToProvider(provider, _globalSettings));
|
||||
return new ProviderResponseModel(provider);
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/setup")]
|
||||
public async Task<ProviderResponseModel> Setup(Guid id, [FromBody] ProviderSetupRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderProviderAdmin(id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
|
||||
var response =
|
||||
await _providerService.CompleteSetupAsync(model.ToProvider(provider), userId, model.Token, model.Key);
|
||||
|
||||
return new ProviderResponseModel(response);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,109 +7,108 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("push")]
|
||||
[Authorize("Push")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class PushController : Controller
|
||||
{
|
||||
[Route("push")]
|
||||
[Authorize("Push")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class PushController : Controller
|
||||
private readonly IPushRegistrationService _pushRegistrationService;
|
||||
private readonly IPushNotificationService _pushNotificationService;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public PushController(
|
||||
IPushRegistrationService pushRegistrationService,
|
||||
IPushNotificationService pushNotificationService,
|
||||
IWebHostEnvironment environment,
|
||||
ICurrentContext currentContext,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
private readonly IPushRegistrationService _pushRegistrationService;
|
||||
private readonly IPushNotificationService _pushNotificationService;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
_currentContext = currentContext;
|
||||
_environment = environment;
|
||||
_pushRegistrationService = pushRegistrationService;
|
||||
_pushNotificationService = pushNotificationService;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
public PushController(
|
||||
IPushRegistrationService pushRegistrationService,
|
||||
IPushNotificationService pushNotificationService,
|
||||
IWebHostEnvironment environment,
|
||||
ICurrentContext currentContext,
|
||||
GlobalSettings globalSettings)
|
||||
[HttpPost("register")]
|
||||
public async Task PostRegister([FromBody] PushRegistrationRequestModel model)
|
||||
{
|
||||
CheckUsage();
|
||||
await _pushRegistrationService.CreateOrUpdateRegistrationAsync(model.PushToken, Prefix(model.DeviceId),
|
||||
Prefix(model.UserId), Prefix(model.Identifier), model.Type);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task Delete(string id)
|
||||
{
|
||||
CheckUsage();
|
||||
await _pushRegistrationService.DeleteRegistrationAsync(Prefix(id));
|
||||
}
|
||||
|
||||
[HttpPut("add-organization")]
|
||||
public async Task PutAddOrganization([FromBody] PushUpdateRequestModel model)
|
||||
{
|
||||
CheckUsage();
|
||||
await _pushRegistrationService.AddUserRegistrationOrganizationAsync(
|
||||
model.DeviceIds.Select(d => Prefix(d)), Prefix(model.OrganizationId));
|
||||
}
|
||||
|
||||
[HttpPut("delete-organization")]
|
||||
public async Task PutDeleteOrganization([FromBody] PushUpdateRequestModel model)
|
||||
{
|
||||
CheckUsage();
|
||||
await _pushRegistrationService.DeleteUserRegistrationOrganizationAsync(
|
||||
model.DeviceIds.Select(d => Prefix(d)), Prefix(model.OrganizationId));
|
||||
}
|
||||
|
||||
[HttpPost("send")]
|
||||
public async Task PostSend([FromBody] PushSendRequestModel model)
|
||||
{
|
||||
CheckUsage();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(model.UserId))
|
||||
{
|
||||
_currentContext = currentContext;
|
||||
_environment = environment;
|
||||
_pushRegistrationService = pushRegistrationService;
|
||||
_pushNotificationService = pushNotificationService;
|
||||
_globalSettings = globalSettings;
|
||||
await _pushNotificationService.SendPayloadToUserAsync(Prefix(model.UserId),
|
||||
model.Type.Value, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId));
|
||||
}
|
||||
|
||||
[HttpPost("register")]
|
||||
public async Task PostRegister([FromBody] PushRegistrationRequestModel model)
|
||||
else if (!string.IsNullOrWhiteSpace(model.OrganizationId))
|
||||
{
|
||||
CheckUsage();
|
||||
await _pushRegistrationService.CreateOrUpdateRegistrationAsync(model.PushToken, Prefix(model.DeviceId),
|
||||
Prefix(model.UserId), Prefix(model.Identifier), model.Type);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task Delete(string id)
|
||||
{
|
||||
CheckUsage();
|
||||
await _pushRegistrationService.DeleteRegistrationAsync(Prefix(id));
|
||||
}
|
||||
|
||||
[HttpPut("add-organization")]
|
||||
public async Task PutAddOrganization([FromBody] PushUpdateRequestModel model)
|
||||
{
|
||||
CheckUsage();
|
||||
await _pushRegistrationService.AddUserRegistrationOrganizationAsync(
|
||||
model.DeviceIds.Select(d => Prefix(d)), Prefix(model.OrganizationId));
|
||||
}
|
||||
|
||||
[HttpPut("delete-organization")]
|
||||
public async Task PutDeleteOrganization([FromBody] PushUpdateRequestModel model)
|
||||
{
|
||||
CheckUsage();
|
||||
await _pushRegistrationService.DeleteUserRegistrationOrganizationAsync(
|
||||
model.DeviceIds.Select(d => Prefix(d)), Prefix(model.OrganizationId));
|
||||
}
|
||||
|
||||
[HttpPost("send")]
|
||||
public async Task PostSend([FromBody] PushSendRequestModel model)
|
||||
{
|
||||
CheckUsage();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(model.UserId))
|
||||
{
|
||||
await _pushNotificationService.SendPayloadToUserAsync(Prefix(model.UserId),
|
||||
model.Type.Value, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId));
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(model.OrganizationId))
|
||||
{
|
||||
await _pushNotificationService.SendPayloadToOrganizationAsync(Prefix(model.OrganizationId),
|
||||
model.Type.Value, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId));
|
||||
}
|
||||
}
|
||||
|
||||
private string Prefix(string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return $"{_currentContext.InstallationId.Value}_{value}";
|
||||
}
|
||||
|
||||
private void CheckUsage()
|
||||
{
|
||||
if (CanUse())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw new BadRequestException("Not correctly configured for push relays.");
|
||||
}
|
||||
|
||||
private bool CanUse()
|
||||
{
|
||||
if (_environment.IsDevelopment())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _currentContext.InstallationId.HasValue && !_globalSettings.SelfHosted;
|
||||
await _pushNotificationService.SendPayloadToOrganizationAsync(Prefix(model.OrganizationId),
|
||||
model.Type.Value, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId));
|
||||
}
|
||||
}
|
||||
|
||||
private string Prefix(string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return $"{_currentContext.InstallationId.Value}_{value}";
|
||||
}
|
||||
|
||||
private void CheckUsage()
|
||||
{
|
||||
if (CanUse())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw new BadRequestException("Not correctly configured for push relays.");
|
||||
}
|
||||
|
||||
private bool CanUse()
|
||||
{
|
||||
if (_environment.IsDevelopment())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _currentContext.InstallationId.HasValue && !_globalSettings.SelfHosted;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,61 +7,60 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers.SelfHosted
|
||||
namespace Bit.Api.Controllers.SelfHosted;
|
||||
|
||||
[Route("organization/sponsorship/self-hosted")]
|
||||
[Authorize("Application")]
|
||||
[SelfHosted(SelfHostedOnly = true)]
|
||||
public class SelfHostedOrganizationSponsorshipsController : Controller
|
||||
{
|
||||
[Route("organization/sponsorship/self-hosted")]
|
||||
[Authorize("Application")]
|
||||
[SelfHosted(SelfHostedOnly = true)]
|
||||
public class SelfHostedOrganizationSponsorshipsController : Controller
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IOrganizationSponsorshipRepository _organizationSponsorshipRepository;
|
||||
private readonly ICreateSponsorshipCommand _offerSponsorshipCommand;
|
||||
private readonly IRevokeSponsorshipCommand _revokeSponsorshipCommand;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public SelfHostedOrganizationSponsorshipsController(
|
||||
ICreateSponsorshipCommand offerSponsorshipCommand,
|
||||
IRevokeSponsorshipCommand revokeSponsorshipCommand,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationSponsorshipRepository organizationSponsorshipRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
ICurrentContext currentContext
|
||||
)
|
||||
{
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IOrganizationSponsorshipRepository _organizationSponsorshipRepository;
|
||||
private readonly ICreateSponsorshipCommand _offerSponsorshipCommand;
|
||||
private readonly IRevokeSponsorshipCommand _revokeSponsorshipCommand;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
_offerSponsorshipCommand = offerSponsorshipCommand;
|
||||
_revokeSponsorshipCommand = revokeSponsorshipCommand;
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationSponsorshipRepository = organizationSponsorshipRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
public SelfHostedOrganizationSponsorshipsController(
|
||||
ICreateSponsorshipCommand offerSponsorshipCommand,
|
||||
IRevokeSponsorshipCommand revokeSponsorshipCommand,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationSponsorshipRepository organizationSponsorshipRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
ICurrentContext currentContext
|
||||
)
|
||||
[HttpPost("{sponsoringOrgId}/families-for-enterprise")]
|
||||
public async Task CreateSponsorship(Guid sponsoringOrgId, [FromBody] OrganizationSponsorshipCreateRequestModel model)
|
||||
{
|
||||
await _offerSponsorshipCommand.CreateSponsorshipAsync(
|
||||
await _organizationRepository.GetByIdAsync(sponsoringOrgId),
|
||||
await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default),
|
||||
model.PlanSponsorshipType, model.SponsoredEmail, model.FriendlyName);
|
||||
}
|
||||
|
||||
[HttpDelete("{sponsoringOrgId}")]
|
||||
[HttpPost("{sponsoringOrgId}/delete")]
|
||||
public async Task RevokeSponsorship(Guid sponsoringOrgId)
|
||||
{
|
||||
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default);
|
||||
|
||||
if (orgUser == null)
|
||||
{
|
||||
_offerSponsorshipCommand = offerSponsorshipCommand;
|
||||
_revokeSponsorshipCommand = revokeSponsorshipCommand;
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationSponsorshipRepository = organizationSponsorshipRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_currentContext = currentContext;
|
||||
throw new BadRequestException("Unknown Organization User");
|
||||
}
|
||||
|
||||
[HttpPost("{sponsoringOrgId}/families-for-enterprise")]
|
||||
public async Task CreateSponsorship(Guid sponsoringOrgId, [FromBody] OrganizationSponsorshipCreateRequestModel model)
|
||||
{
|
||||
await _offerSponsorshipCommand.CreateSponsorshipAsync(
|
||||
await _organizationRepository.GetByIdAsync(sponsoringOrgId),
|
||||
await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default),
|
||||
model.PlanSponsorshipType, model.SponsoredEmail, model.FriendlyName);
|
||||
}
|
||||
var existingOrgSponsorship = await _organizationSponsorshipRepository
|
||||
.GetBySponsoringOrganizationUserIdAsync(orgUser.Id);
|
||||
|
||||
[HttpDelete("{sponsoringOrgId}")]
|
||||
[HttpPost("{sponsoringOrgId}/delete")]
|
||||
public async Task RevokeSponsorship(Guid sponsoringOrgId)
|
||||
{
|
||||
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default);
|
||||
|
||||
if (orgUser == null)
|
||||
{
|
||||
throw new BadRequestException("Unknown Organization User");
|
||||
}
|
||||
|
||||
var existingOrgSponsorship = await _organizationSponsorshipRepository
|
||||
.GetBySponsoringOrganizationUserIdAsync(orgUser.Id);
|
||||
|
||||
await _revokeSponsorshipCommand.RevokeSponsorshipAsync(existingOrgSponsorship);
|
||||
}
|
||||
await _revokeSponsorshipCommand.RevokeSponsorshipAsync(existingOrgSponsorship);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,323 +16,322 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("sends")]
|
||||
[Authorize("Application")]
|
||||
public class SendsController : Controller
|
||||
{
|
||||
[Route("sends")]
|
||||
[Authorize("Application")]
|
||||
public class SendsController : Controller
|
||||
private readonly ISendRepository _sendRepository;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ISendService _sendService;
|
||||
private readonly ISendFileStorageService _sendFileStorageService;
|
||||
private readonly ILogger<SendsController> _logger;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public SendsController(
|
||||
ISendRepository sendRepository,
|
||||
IUserService userService,
|
||||
ISendService sendService,
|
||||
ISendFileStorageService sendFileStorageService,
|
||||
ILogger<SendsController> logger,
|
||||
GlobalSettings globalSettings,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
private readonly ISendRepository _sendRepository;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ISendService _sendService;
|
||||
private readonly ISendFileStorageService _sendFileStorageService;
|
||||
private readonly ILogger<SendsController> _logger;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
_sendRepository = sendRepository;
|
||||
_userService = userService;
|
||||
_sendService = sendService;
|
||||
_sendFileStorageService = sendFileStorageService;
|
||||
_logger = logger;
|
||||
_globalSettings = globalSettings;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
public SendsController(
|
||||
ISendRepository sendRepository,
|
||||
IUserService userService,
|
||||
ISendService sendService,
|
||||
ISendFileStorageService sendFileStorageService,
|
||||
ILogger<SendsController> logger,
|
||||
GlobalSettings globalSettings,
|
||||
ICurrentContext currentContext)
|
||||
[AllowAnonymous]
|
||||
[HttpPost("access/{id}")]
|
||||
public async Task<IActionResult> Access(string id, [FromBody] SendAccessRequestModel model)
|
||||
{
|
||||
// Uncomment whenever we want to require the `send-id` header
|
||||
//if (!_currentContext.HttpContext.Request.Headers.ContainsKey("Send-Id") ||
|
||||
// _currentContext.HttpContext.Request.Headers["Send-Id"] != id)
|
||||
//{
|
||||
// throw new BadRequestException("Invalid Send-Id header.");
|
||||
//}
|
||||
|
||||
var guid = new Guid(CoreHelpers.Base64UrlDecode(id));
|
||||
var (send, passwordRequired, passwordInvalid) =
|
||||
await _sendService.AccessAsync(guid, model.Password);
|
||||
if (passwordRequired)
|
||||
{
|
||||
_sendRepository = sendRepository;
|
||||
_userService = userService;
|
||||
_sendService = sendService;
|
||||
_sendFileStorageService = sendFileStorageService;
|
||||
_logger = logger;
|
||||
_globalSettings = globalSettings;
|
||||
_currentContext = currentContext;
|
||||
return new UnauthorizedResult();
|
||||
}
|
||||
if (passwordInvalid)
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("Invalid password.");
|
||||
}
|
||||
if (send == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("access/{id}")]
|
||||
public async Task<IActionResult> Access(string id, [FromBody] SendAccessRequestModel model)
|
||||
var sendResponse = new SendAccessResponseModel(send, _globalSettings);
|
||||
if (send.UserId.HasValue && !send.HideEmail.GetValueOrDefault())
|
||||
{
|
||||
// Uncomment whenever we want to require the `send-id` header
|
||||
//if (!_currentContext.HttpContext.Request.Headers.ContainsKey("Send-Id") ||
|
||||
// _currentContext.HttpContext.Request.Headers["Send-Id"] != id)
|
||||
//{
|
||||
// throw new BadRequestException("Invalid Send-Id header.");
|
||||
//}
|
||||
var creator = await _userService.GetUserByIdAsync(send.UserId.Value);
|
||||
sendResponse.CreatorIdentifier = creator.Email;
|
||||
}
|
||||
return new ObjectResult(sendResponse);
|
||||
}
|
||||
|
||||
var guid = new Guid(CoreHelpers.Base64UrlDecode(id));
|
||||
var (send, passwordRequired, passwordInvalid) =
|
||||
await _sendService.AccessAsync(guid, model.Password);
|
||||
if (passwordRequired)
|
||||
{
|
||||
return new UnauthorizedResult();
|
||||
}
|
||||
if (passwordInvalid)
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("Invalid password.");
|
||||
}
|
||||
if (send == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
[AllowAnonymous]
|
||||
[HttpPost("{encodedSendId}/access/file/{fileId}")]
|
||||
public async Task<IActionResult> GetSendFileDownloadData(string encodedSendId,
|
||||
string fileId, [FromBody] SendAccessRequestModel model)
|
||||
{
|
||||
// Uncomment whenever we want to require the `send-id` header
|
||||
//if (!_currentContext.HttpContext.Request.Headers.ContainsKey("Send-Id") ||
|
||||
// _currentContext.HttpContext.Request.Headers["Send-Id"] != encodedSendId)
|
||||
//{
|
||||
// throw new BadRequestException("Invalid Send-Id header.");
|
||||
//}
|
||||
|
||||
var sendResponse = new SendAccessResponseModel(send, _globalSettings);
|
||||
if (send.UserId.HasValue && !send.HideEmail.GetValueOrDefault())
|
||||
{
|
||||
var creator = await _userService.GetUserByIdAsync(send.UserId.Value);
|
||||
sendResponse.CreatorIdentifier = creator.Email;
|
||||
}
|
||||
return new ObjectResult(sendResponse);
|
||||
var sendId = new Guid(CoreHelpers.Base64UrlDecode(encodedSendId));
|
||||
var send = await _sendRepository.GetByIdAsync(sendId);
|
||||
|
||||
if (send == null)
|
||||
{
|
||||
throw new BadRequestException("Could not locate send");
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("{encodedSendId}/access/file/{fileId}")]
|
||||
public async Task<IActionResult> GetSendFileDownloadData(string encodedSendId,
|
||||
string fileId, [FromBody] SendAccessRequestModel model)
|
||||
var (url, passwordRequired, passwordInvalid) = await _sendService.GetSendFileDownloadUrlAsync(send, fileId,
|
||||
model.Password);
|
||||
|
||||
if (passwordRequired)
|
||||
{
|
||||
// Uncomment whenever we want to require the `send-id` header
|
||||
//if (!_currentContext.HttpContext.Request.Headers.ContainsKey("Send-Id") ||
|
||||
// _currentContext.HttpContext.Request.Headers["Send-Id"] != encodedSendId)
|
||||
//{
|
||||
// throw new BadRequestException("Invalid Send-Id header.");
|
||||
//}
|
||||
|
||||
var sendId = new Guid(CoreHelpers.Base64UrlDecode(encodedSendId));
|
||||
var send = await _sendRepository.GetByIdAsync(sendId);
|
||||
|
||||
if (send == null)
|
||||
{
|
||||
throw new BadRequestException("Could not locate send");
|
||||
}
|
||||
|
||||
var (url, passwordRequired, passwordInvalid) = await _sendService.GetSendFileDownloadUrlAsync(send, fileId,
|
||||
model.Password);
|
||||
|
||||
if (passwordRequired)
|
||||
{
|
||||
return new UnauthorizedResult();
|
||||
}
|
||||
if (passwordInvalid)
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("Invalid password.");
|
||||
}
|
||||
if (send == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return new ObjectResult(new SendFileDownloadDataResponseModel()
|
||||
{
|
||||
Id = fileId,
|
||||
Url = url,
|
||||
});
|
||||
return new UnauthorizedResult();
|
||||
}
|
||||
if (passwordInvalid)
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("Invalid password.");
|
||||
}
|
||||
if (send == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<SendResponseModel> Get(string id)
|
||||
return new ObjectResult(new SendFileDownloadDataResponseModel()
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var send = await _sendRepository.GetByIdAsync(new Guid(id));
|
||||
if (send == null || send.UserId != userId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
Id = fileId,
|
||||
Url = url,
|
||||
});
|
||||
}
|
||||
|
||||
return new SendResponseModel(send, _globalSettings);
|
||||
[HttpGet("{id}")]
|
||||
public async Task<SendResponseModel> Get(string id)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var send = await _sendRepository.GetByIdAsync(new Guid(id));
|
||||
if (send == null || send.UserId != userId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<SendResponseModel>> Get()
|
||||
return new SendResponseModel(send, _globalSettings);
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<SendResponseModel>> Get()
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var sends = await _sendRepository.GetManyByUserIdAsync(userId);
|
||||
var responses = sends.Select(s => new SendResponseModel(s, _globalSettings));
|
||||
return new ListResponseModel<SendResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
public async Task<SendResponseModel> Post([FromBody] SendRequestModel model)
|
||||
{
|
||||
model.ValidateCreation();
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var send = model.ToSend(userId, _sendService);
|
||||
await _sendService.SaveSendAsync(send);
|
||||
return new SendResponseModel(send, _globalSettings);
|
||||
}
|
||||
|
||||
[HttpPost("file")]
|
||||
[Obsolete("Deprecated File Send API", false)]
|
||||
[RequestSizeLimit(Constants.FileSize101mb)]
|
||||
[DisableFormValueModelBinding]
|
||||
public async Task<SendResponseModel> PostFile()
|
||||
{
|
||||
if (!Request?.ContentType.Contains("multipart/") ?? true)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var sends = await _sendRepository.GetManyByUserIdAsync(userId);
|
||||
var responses = sends.Select(s => new SendResponseModel(s, _globalSettings));
|
||||
return new ListResponseModel<SendResponseModel>(responses);
|
||||
throw new BadRequestException("Invalid content.");
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
public async Task<SendResponseModel> Post([FromBody] SendRequestModel model)
|
||||
Send send = null;
|
||||
await Request.GetSendFileAsync(async (stream, fileName, model) =>
|
||||
{
|
||||
model.ValidateCreation();
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var send = model.ToSend(userId, _sendService);
|
||||
await _sendService.SaveSendAsync(send);
|
||||
return new SendResponseModel(send, _globalSettings);
|
||||
var (madeSend, madeData) = model.ToSend(userId, fileName, _sendService);
|
||||
send = madeSend;
|
||||
await _sendService.SaveFileSendAsync(send, madeData, model.FileLength.GetValueOrDefault(0));
|
||||
await _sendService.UploadFileToExistingSendAsync(stream, send);
|
||||
});
|
||||
|
||||
return new SendResponseModel(send, _globalSettings);
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("file/v2")]
|
||||
public async Task<SendFileUploadDataResponseModel> PostFile([FromBody] SendRequestModel model)
|
||||
{
|
||||
if (model.Type != SendType.File)
|
||||
{
|
||||
throw new BadRequestException("Invalid content.");
|
||||
}
|
||||
|
||||
[HttpPost("file")]
|
||||
[Obsolete("Deprecated File Send API", false)]
|
||||
[RequestSizeLimit(Constants.FileSize101mb)]
|
||||
[DisableFormValueModelBinding]
|
||||
public async Task<SendResponseModel> PostFile()
|
||||
if (!model.FileLength.HasValue)
|
||||
{
|
||||
if (!Request?.ContentType.Contains("multipart/") ?? true)
|
||||
{
|
||||
throw new BadRequestException("Invalid content.");
|
||||
}
|
||||
|
||||
Send send = null;
|
||||
await Request.GetSendFileAsync(async (stream, fileName, model) =>
|
||||
{
|
||||
model.ValidateCreation();
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var (madeSend, madeData) = model.ToSend(userId, fileName, _sendService);
|
||||
send = madeSend;
|
||||
await _sendService.SaveFileSendAsync(send, madeData, model.FileLength.GetValueOrDefault(0));
|
||||
await _sendService.UploadFileToExistingSendAsync(stream, send);
|
||||
});
|
||||
|
||||
return new SendResponseModel(send, _globalSettings);
|
||||
throw new BadRequestException("Invalid content. File size hint is required.");
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("file/v2")]
|
||||
public async Task<SendFileUploadDataResponseModel> PostFile([FromBody] SendRequestModel model)
|
||||
if (model.FileLength.Value > SendService.MAX_FILE_SIZE)
|
||||
{
|
||||
if (model.Type != SendType.File)
|
||||
{
|
||||
throw new BadRequestException("Invalid content.");
|
||||
}
|
||||
|
||||
if (!model.FileLength.HasValue)
|
||||
{
|
||||
throw new BadRequestException("Invalid content. File size hint is required.");
|
||||
}
|
||||
|
||||
if (model.FileLength.Value > SendService.MAX_FILE_SIZE)
|
||||
{
|
||||
throw new BadRequestException($"Max file size is {SendService.MAX_FILE_SIZE_READABLE}.");
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var (send, data) = model.ToSend(userId, model.File.FileName, _sendService);
|
||||
var uploadUrl = await _sendService.SaveFileSendAsync(send, data, model.FileLength.Value);
|
||||
return new SendFileUploadDataResponseModel
|
||||
{
|
||||
Url = uploadUrl,
|
||||
FileUploadType = _sendFileStorageService.FileUploadType,
|
||||
SendResponse = new SendResponseModel(send, _globalSettings)
|
||||
};
|
||||
throw new BadRequestException($"Max file size is {SendService.MAX_FILE_SIZE_READABLE}.");
|
||||
}
|
||||
|
||||
[HttpGet("{id}/file/{fileId}")]
|
||||
public async Task<SendFileUploadDataResponseModel> RenewFileUpload(string id, string fileId)
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var (send, data) = model.ToSend(userId, model.File.FileName, _sendService);
|
||||
var uploadUrl = await _sendService.SaveFileSendAsync(send, data, model.FileLength.Value);
|
||||
return new SendFileUploadDataResponseModel
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var sendId = new Guid(id);
|
||||
var send = await _sendRepository.GetByIdAsync(sendId);
|
||||
var fileData = JsonSerializer.Deserialize<SendFileData>(send?.Data);
|
||||
Url = uploadUrl,
|
||||
FileUploadType = _sendFileStorageService.FileUploadType,
|
||||
SendResponse = new SendResponseModel(send, _globalSettings)
|
||||
};
|
||||
}
|
||||
|
||||
if (send == null || send.Type != SendType.File || (send.UserId.HasValue && send.UserId.Value != userId) ||
|
||||
!send.UserId.HasValue || fileData.Id != fileId || fileData.Validated)
|
||||
{
|
||||
// Not found if Send isn't found, user doesn't have access, request is faulty,
|
||||
// or we've already validated the file. This last is to emulate create-only blob permissions for Azure
|
||||
throw new NotFoundException();
|
||||
}
|
||||
[HttpGet("{id}/file/{fileId}")]
|
||||
public async Task<SendFileUploadDataResponseModel> RenewFileUpload(string id, string fileId)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var sendId = new Guid(id);
|
||||
var send = await _sendRepository.GetByIdAsync(sendId);
|
||||
var fileData = JsonSerializer.Deserialize<SendFileData>(send?.Data);
|
||||
|
||||
return new SendFileUploadDataResponseModel
|
||||
{
|
||||
Url = await _sendFileStorageService.GetSendFileUploadUrlAsync(send, fileId),
|
||||
FileUploadType = _sendFileStorageService.FileUploadType,
|
||||
SendResponse = new SendResponseModel(send, _globalSettings),
|
||||
};
|
||||
if (send == null || send.Type != SendType.File || (send.UserId.HasValue && send.UserId.Value != userId) ||
|
||||
!send.UserId.HasValue || fileData.Id != fileId || fileData.Validated)
|
||||
{
|
||||
// Not found if Send isn't found, user doesn't have access, request is faulty,
|
||||
// or we've already validated the file. This last is to emulate create-only blob permissions for Azure
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("{id}/file/{fileId}")]
|
||||
[SelfHosted(SelfHostedOnly = true)]
|
||||
[RequestSizeLimit(Constants.FileSize501mb)]
|
||||
[DisableFormValueModelBinding]
|
||||
public async Task PostFileForExistingSend(string id, string fileId)
|
||||
return new SendFileUploadDataResponseModel
|
||||
{
|
||||
if (!Request?.ContentType.Contains("multipart/") ?? true)
|
||||
{
|
||||
throw new BadRequestException("Invalid content.");
|
||||
}
|
||||
Url = await _sendFileStorageService.GetSendFileUploadUrlAsync(send, fileId),
|
||||
FileUploadType = _sendFileStorageService.FileUploadType,
|
||||
SendResponse = new SendResponseModel(send, _globalSettings),
|
||||
};
|
||||
}
|
||||
|
||||
var send = await _sendRepository.GetByIdAsync(new Guid(id));
|
||||
await Request.GetFileAsync(async (stream) =>
|
||||
{
|
||||
await _sendService.UploadFileToExistingSendAsync(stream, send);
|
||||
});
|
||||
[HttpPost("{id}/file/{fileId}")]
|
||||
[SelfHosted(SelfHostedOnly = true)]
|
||||
[RequestSizeLimit(Constants.FileSize501mb)]
|
||||
[DisableFormValueModelBinding]
|
||||
public async Task PostFileForExistingSend(string id, string fileId)
|
||||
{
|
||||
if (!Request?.ContentType.Contains("multipart/") ?? true)
|
||||
{
|
||||
throw new BadRequestException("Invalid content.");
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("file/validate/azure")]
|
||||
public async Task<ObjectResult> AzureValidateFile()
|
||||
var send = await _sendRepository.GetByIdAsync(new Guid(id));
|
||||
await Request.GetFileAsync(async (stream) =>
|
||||
{
|
||||
await _sendService.UploadFileToExistingSendAsync(stream, send);
|
||||
});
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("file/validate/azure")]
|
||||
public async Task<ObjectResult> AzureValidateFile()
|
||||
{
|
||||
return await ApiHelpers.HandleAzureEvents(Request, new Dictionary<string, Func<EventGridEvent, Task>>
|
||||
{
|
||||
return await ApiHelpers.HandleAzureEvents(Request, new Dictionary<string, Func<EventGridEvent, Task>>
|
||||
{
|
||||
"Microsoft.Storage.BlobCreated", async (eventGridEvent) =>
|
||||
{
|
||||
"Microsoft.Storage.BlobCreated", async (eventGridEvent) =>
|
||||
try
|
||||
{
|
||||
try
|
||||
var blobName = eventGridEvent.Subject.Split($"{AzureSendFileStorageService.FilesContainerName}/blobs/")[1];
|
||||
var sendId = AzureSendFileStorageService.SendIdFromBlobName(blobName);
|
||||
var send = await _sendRepository.GetByIdAsync(new Guid(sendId));
|
||||
if (send == null)
|
||||
{
|
||||
var blobName = eventGridEvent.Subject.Split($"{AzureSendFileStorageService.FilesContainerName}/blobs/")[1];
|
||||
var sendId = AzureSendFileStorageService.SendIdFromBlobName(blobName);
|
||||
var send = await _sendRepository.GetByIdAsync(new Guid(sendId));
|
||||
if (send == null)
|
||||
if (_sendFileStorageService is AzureSendFileStorageService azureSendFileStorageService)
|
||||
{
|
||||
if (_sendFileStorageService is AzureSendFileStorageService azureSendFileStorageService)
|
||||
{
|
||||
await azureSendFileStorageService.DeleteBlobAsync(blobName);
|
||||
}
|
||||
return;
|
||||
await azureSendFileStorageService.DeleteBlobAsync(blobName);
|
||||
}
|
||||
await _sendService.ValidateSendFile(send);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, $"Uncaught exception occurred while handling event grid event: {JsonSerializer.Serialize(eventGridEvent)}");
|
||||
return;
|
||||
}
|
||||
await _sendService.ValidateSendFile(send);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, $"Uncaught exception occurred while handling event grid event: {JsonSerializer.Serialize(eventGridEvent)}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<SendResponseModel> Put(string id, [FromBody] SendRequestModel model)
|
||||
{
|
||||
model.ValidateEdit();
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var send = await _sendRepository.GetByIdAsync(new Guid(id));
|
||||
if (send == null || send.UserId != userId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await _sendService.SaveSendAsync(model.ToSend(send, _sendService));
|
||||
return new SendResponseModel(send, _globalSettings);
|
||||
}
|
||||
|
||||
[HttpPut("{id}/remove-password")]
|
||||
public async Task<SendResponseModel> PutRemovePassword(string id)
|
||||
[HttpPut("{id}")]
|
||||
public async Task<SendResponseModel> Put(string id, [FromBody] SendRequestModel model)
|
||||
{
|
||||
model.ValidateEdit();
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var send = await _sendRepository.GetByIdAsync(new Guid(id));
|
||||
if (send == null || send.UserId != userId)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var send = await _sendRepository.GetByIdAsync(new Guid(id));
|
||||
if (send == null || send.UserId != userId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
send.Password = null;
|
||||
await _sendService.SaveSendAsync(send);
|
||||
return new SendResponseModel(send, _globalSettings);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task Delete(string id)
|
||||
await _sendService.SaveSendAsync(model.ToSend(send, _sendService));
|
||||
return new SendResponseModel(send, _globalSettings);
|
||||
}
|
||||
|
||||
[HttpPut("{id}/remove-password")]
|
||||
public async Task<SendResponseModel> PutRemovePassword(string id)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var send = await _sendRepository.GetByIdAsync(new Guid(id));
|
||||
if (send == null || send.UserId != userId)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var send = await _sendRepository.GetByIdAsync(new Guid(id));
|
||||
if (send == null || send.UserId != userId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _sendService.DeleteSendAsync(send);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
send.Password = null;
|
||||
await _sendService.SaveSendAsync(send);
|
||||
return new SendResponseModel(send, _globalSettings);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task Delete(string id)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var send = await _sendRepository.GetByIdAsync(new Guid(id));
|
||||
if (send == null || send.UserId != userId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _sendService.DeleteSendAsync(send);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,47 +4,46 @@ using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("settings")]
|
||||
[Authorize("Application")]
|
||||
public class SettingsController : Controller
|
||||
{
|
||||
[Route("settings")]
|
||||
[Authorize("Application")]
|
||||
public class SettingsController : Controller
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public SettingsController(
|
||||
IUserService userService)
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public SettingsController(
|
||||
IUserService userService)
|
||||
[HttpGet("domains")]
|
||||
public async Task<DomainsResponseModel> GetDomains(bool excluded = true)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
_userService = userService;
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
[HttpGet("domains")]
|
||||
public async Task<DomainsResponseModel> GetDomains(bool excluded = true)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
var response = new DomainsResponseModel(user, excluded);
|
||||
return response;
|
||||
}
|
||||
|
||||
var response = new DomainsResponseModel(user, excluded);
|
||||
return response;
|
||||
[HttpPut("domains")]
|
||||
[HttpPost("domains")]
|
||||
public async Task<DomainsResponseModel> PutDomains([FromBody] UpdateDomainsRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
[HttpPut("domains")]
|
||||
[HttpPost("domains")]
|
||||
public async Task<DomainsResponseModel> PutDomains([FromBody] UpdateDomainsRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
await _userService.SaveUserAsync(model.ToUser(user), true);
|
||||
|
||||
await _userService.SaveUserAsync(model.ToUser(user), true);
|
||||
|
||||
var response = new DomainsResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
var response = new DomainsResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,85 +10,84 @@ using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("sync")]
|
||||
[Authorize("Application")]
|
||||
public class SyncController : Controller
|
||||
{
|
||||
[Route("sync")]
|
||||
[Authorize("Application")]
|
||||
public class SyncController : Controller
|
||||
private readonly IUserService _userService;
|
||||
private readonly IFolderRepository _folderRepository;
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
private readonly ICollectionCipherRepository _collectionCipherRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IPolicyRepository _policyRepository;
|
||||
private readonly ISendRepository _sendRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public SyncController(
|
||||
IUserService userService,
|
||||
IFolderRepository folderRepository,
|
||||
ICipherRepository cipherRepository,
|
||||
ICollectionRepository collectionRepository,
|
||||
ICollectionCipherRepository collectionCipherRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IProviderUserRepository providerUserRepository,
|
||||
IPolicyRepository policyRepository,
|
||||
ISendRepository sendRepository,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IFolderRepository _folderRepository;
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
private readonly ICollectionCipherRepository _collectionCipherRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IPolicyRepository _policyRepository;
|
||||
private readonly ISendRepository _sendRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
_userService = userService;
|
||||
_folderRepository = folderRepository;
|
||||
_cipherRepository = cipherRepository;
|
||||
_collectionRepository = collectionRepository;
|
||||
_collectionCipherRepository = collectionCipherRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_policyRepository = policyRepository;
|
||||
_sendRepository = sendRepository;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
public SyncController(
|
||||
IUserService userService,
|
||||
IFolderRepository folderRepository,
|
||||
ICipherRepository cipherRepository,
|
||||
ICollectionRepository collectionRepository,
|
||||
ICollectionCipherRepository collectionCipherRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IProviderUserRepository providerUserRepository,
|
||||
IPolicyRepository policyRepository,
|
||||
ISendRepository sendRepository,
|
||||
GlobalSettings globalSettings)
|
||||
[HttpGet("")]
|
||||
public async Task<SyncResponseModel> Get([FromQuery] bool excludeDomains = false)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
_userService = userService;
|
||||
_folderRepository = folderRepository;
|
||||
_cipherRepository = cipherRepository;
|
||||
_collectionRepository = collectionRepository;
|
||||
_collectionCipherRepository = collectionCipherRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_policyRepository = policyRepository;
|
||||
_sendRepository = sendRepository;
|
||||
_globalSettings = globalSettings;
|
||||
throw new BadRequestException("User not found.");
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<SyncResponseModel> Get([FromQuery] bool excludeDomains = false)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new BadRequestException("User not found.");
|
||||
}
|
||||
|
||||
var organizationUserDetails = await _organizationUserRepository.GetManyDetailsByUserAsync(user.Id,
|
||||
OrganizationUserStatusType.Confirmed);
|
||||
var providerUserDetails = await _providerUserRepository.GetManyDetailsByUserAsync(user.Id,
|
||||
var organizationUserDetails = await _organizationUserRepository.GetManyDetailsByUserAsync(user.Id,
|
||||
OrganizationUserStatusType.Confirmed);
|
||||
var providerUserDetails = await _providerUserRepository.GetManyDetailsByUserAsync(user.Id,
|
||||
ProviderUserStatusType.Confirmed);
|
||||
var providerUserOrganizationDetails =
|
||||
await _providerUserRepository.GetManyOrganizationDetailsByUserAsync(user.Id,
|
||||
ProviderUserStatusType.Confirmed);
|
||||
var providerUserOrganizationDetails =
|
||||
await _providerUserRepository.GetManyOrganizationDetailsByUserAsync(user.Id,
|
||||
ProviderUserStatusType.Confirmed);
|
||||
var hasEnabledOrgs = organizationUserDetails.Any(o => o.Enabled);
|
||||
var folders = await _folderRepository.GetManyByUserIdAsync(user.Id);
|
||||
var ciphers = await _cipherRepository.GetManyByUserIdAsync(user.Id, hasEnabledOrgs);
|
||||
var sends = await _sendRepository.GetManyByUserIdAsync(user.Id);
|
||||
var hasEnabledOrgs = organizationUserDetails.Any(o => o.Enabled);
|
||||
var folders = await _folderRepository.GetManyByUserIdAsync(user.Id);
|
||||
var ciphers = await _cipherRepository.GetManyByUserIdAsync(user.Id, hasEnabledOrgs);
|
||||
var sends = await _sendRepository.GetManyByUserIdAsync(user.Id);
|
||||
|
||||
IEnumerable<CollectionDetails> collections = null;
|
||||
IDictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict = null;
|
||||
IEnumerable<Policy> policies = null;
|
||||
if (hasEnabledOrgs)
|
||||
{
|
||||
collections = await _collectionRepository.GetManyByUserIdAsync(user.Id);
|
||||
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdAsync(user.Id);
|
||||
collectionCiphersGroupDict = collectionCiphers.GroupBy(c => c.CipherId).ToDictionary(s => s.Key);
|
||||
policies = await _policyRepository.GetManyByUserIdAsync(user.Id);
|
||||
}
|
||||
|
||||
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
||||
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
|
||||
var response = new SyncResponseModel(_globalSettings, user, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationUserDetails,
|
||||
providerUserDetails, providerUserOrganizationDetails, folders, collections, ciphers,
|
||||
collectionCiphersGroupDict, excludeDomains, policies, sends);
|
||||
return response;
|
||||
IEnumerable<CollectionDetails> collections = null;
|
||||
IDictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict = null;
|
||||
IEnumerable<Policy> policies = null;
|
||||
if (hasEnabledOrgs)
|
||||
{
|
||||
collections = await _collectionRepository.GetManyByUserIdAsync(user.Id);
|
||||
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdAsync(user.Id);
|
||||
collectionCiphersGroupDict = collectionCiphers.GroupBy(c => c.CipherId).ToDictionary(s => s.Key);
|
||||
policies = await _policyRepository.GetManyByUserIdAsync(user.Id);
|
||||
}
|
||||
|
||||
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
||||
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
|
||||
var response = new SyncResponseModel(_globalSettings, user, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationUserDetails,
|
||||
providerUserDetails, providerUserOrganizationDetails, folders, collections, ciphers,
|
||||
collectionCiphersGroupDict, excludeDomains, policies, sends);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,443 +16,442 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("two-factor")]
|
||||
[Authorize("Web")]
|
||||
public class TwoFactorController : Controller
|
||||
{
|
||||
[Route("two-factor")]
|
||||
[Authorize("Web")]
|
||||
public class TwoFactorController : Controller
|
||||
private readonly IUserService _userService;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly UserManager<User> _userManager;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public TwoFactorController(
|
||||
IUserService userService,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationService organizationService,
|
||||
GlobalSettings globalSettings,
|
||||
UserManager<User> userManager,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly UserManager<User> _userManager;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
_userService = userService;
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationService = organizationService;
|
||||
_globalSettings = globalSettings;
|
||||
_userManager = userManager;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
public TwoFactorController(
|
||||
IUserService userService,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationService organizationService,
|
||||
GlobalSettings globalSettings,
|
||||
UserManager<User> userManager,
|
||||
ICurrentContext currentContext)
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<TwoFactorProviderResponseModel>> Get()
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
_userService = userService;
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationService = organizationService;
|
||||
_globalSettings = globalSettings;
|
||||
_userManager = userManager;
|
||||
_currentContext = currentContext;
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<TwoFactorProviderResponseModel>> Get()
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
var providers = user.GetTwoFactorProviders()?.Select(
|
||||
p => new TwoFactorProviderResponseModel(p.Key, p.Value));
|
||||
return new ListResponseModel<TwoFactorProviderResponseModel>(providers);
|
||||
}
|
||||
|
||||
var providers = user.GetTwoFactorProviders()?.Select(
|
||||
p => new TwoFactorProviderResponseModel(p.Key, p.Value));
|
||||
return new ListResponseModel<TwoFactorProviderResponseModel>(providers);
|
||||
[HttpGet("~/organizations/{id}/two-factor")]
|
||||
public async Task<ListResponseModel<TwoFactorProviderResponseModel>> GetOrganization(string id)
|
||||
{
|
||||
var orgIdGuid = new Guid(id);
|
||||
if (!await _currentContext.OrganizationAdmin(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("~/organizations/{id}/two-factor")]
|
||||
public async Task<ListResponseModel<TwoFactorProviderResponseModel>> GetOrganization(string id)
|
||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||
if (organization == null)
|
||||
{
|
||||
var orgIdGuid = new Guid(id);
|
||||
if (!await _currentContext.OrganizationAdmin(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||
if (organization == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var providers = organization.GetTwoFactorProviders()?.Select(
|
||||
p => new TwoFactorProviderResponseModel(p.Key, p.Value));
|
||||
return new ListResponseModel<TwoFactorProviderResponseModel>(providers);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("get-authenticator")]
|
||||
public async Task<TwoFactorAuthenticatorResponseModel> GetAuthenticator([FromBody] SecretVerificationRequestModel model)
|
||||
var providers = organization.GetTwoFactorProviders()?.Select(
|
||||
p => new TwoFactorProviderResponseModel(p.Key, p.Value));
|
||||
return new ListResponseModel<TwoFactorProviderResponseModel>(providers);
|
||||
}
|
||||
|
||||
[HttpPost("get-authenticator")]
|
||||
public async Task<TwoFactorAuthenticatorResponseModel> GetAuthenticator([FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
var response = new TwoFactorAuthenticatorResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPut("authenticator")]
|
||||
[HttpPost("authenticator")]
|
||||
public async Task<TwoFactorAuthenticatorResponseModel> PutAuthenticator(
|
||||
[FromBody] UpdateTwoFactorAuthenticatorRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
model.ToUser(user);
|
||||
|
||||
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
|
||||
CoreHelpers.CustomProviderName(TwoFactorProviderType.Authenticator), model.Token))
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
var response = new TwoFactorAuthenticatorResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPut("authenticator")]
|
||||
[HttpPost("authenticator")]
|
||||
public async Task<TwoFactorAuthenticatorResponseModel> PutAuthenticator(
|
||||
[FromBody] UpdateTwoFactorAuthenticatorRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
model.ToUser(user);
|
||||
|
||||
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
|
||||
CoreHelpers.CustomProviderName(TwoFactorProviderType.Authenticator), model.Token))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("Token", "Invalid token.");
|
||||
}
|
||||
|
||||
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Authenticator);
|
||||
var response = new TwoFactorAuthenticatorResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("get-yubikey")]
|
||||
public async Task<TwoFactorYubiKeyResponseModel> GetYubiKey([FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
var response = new TwoFactorYubiKeyResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPut("yubikey")]
|
||||
[HttpPost("yubikey")]
|
||||
public async Task<TwoFactorYubiKeyResponseModel> PutYubiKey([FromBody] UpdateTwoFactorYubicoOtpRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
model.ToUser(user);
|
||||
|
||||
await ValidateYubiKeyAsync(user, nameof(model.Key1), model.Key1);
|
||||
await ValidateYubiKeyAsync(user, nameof(model.Key2), model.Key2);
|
||||
await ValidateYubiKeyAsync(user, nameof(model.Key3), model.Key3);
|
||||
await ValidateYubiKeyAsync(user, nameof(model.Key4), model.Key4);
|
||||
await ValidateYubiKeyAsync(user, nameof(model.Key5), model.Key5);
|
||||
|
||||
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.YubiKey);
|
||||
var response = new TwoFactorYubiKeyResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("get-duo")]
|
||||
public async Task<TwoFactorDuoResponseModel> GetDuo([FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
var response = new TwoFactorDuoResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPut("duo")]
|
||||
[HttpPost("duo")]
|
||||
public async Task<TwoFactorDuoResponseModel> PutDuo([FromBody] UpdateTwoFactorDuoRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
try
|
||||
{
|
||||
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
||||
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
|
||||
}
|
||||
catch (DuoException)
|
||||
{
|
||||
throw new BadRequestException("Duo configuration settings are not valid. Please re-check the Duo Admin panel.");
|
||||
}
|
||||
|
||||
model.ToUser(user);
|
||||
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Duo);
|
||||
var response = new TwoFactorDuoResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("~/organizations/{id}/two-factor/get-duo")]
|
||||
public async Task<TwoFactorDuoResponseModel> GetOrganizationDuo(string id,
|
||||
[FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
|
||||
var orgIdGuid = new Guid(id);
|
||||
if (!await _currentContext.ManagePolicies(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||
if (organization == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var response = new TwoFactorDuoResponseModel(organization);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPut("~/organizations/{id}/two-factor/duo")]
|
||||
[HttpPost("~/organizations/{id}/two-factor/duo")]
|
||||
public async Task<TwoFactorDuoResponseModel> PutOrganizationDuo(string id,
|
||||
[FromBody] UpdateTwoFactorDuoRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
|
||||
var orgIdGuid = new Guid(id);
|
||||
if (!await _currentContext.ManagePolicies(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||
if (organization == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
||||
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
|
||||
}
|
||||
catch (DuoException)
|
||||
{
|
||||
throw new BadRequestException("Duo configuration settings are not valid. Please re-check the Duo Admin panel.");
|
||||
}
|
||||
|
||||
model.ToOrganization(organization);
|
||||
await _organizationService.UpdateTwoFactorProviderAsync(organization,
|
||||
TwoFactorProviderType.OrganizationDuo);
|
||||
var response = new TwoFactorDuoResponseModel(organization);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("get-webauthn")]
|
||||
public async Task<TwoFactorWebAuthnResponseModel> GetWebAuthn([FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
var response = new TwoFactorWebAuthnResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("get-webauthn-challenge")]
|
||||
public async Task<CredentialCreateOptions> GetWebAuthnChallenge([FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
var reg = await _userService.StartWebAuthnRegistrationAsync(user);
|
||||
return reg;
|
||||
}
|
||||
|
||||
[HttpPut("webauthn")]
|
||||
[HttpPost("webauthn")]
|
||||
public async Task<TwoFactorWebAuthnResponseModel> PutWebAuthn([FromBody] TwoFactorWebAuthnRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
|
||||
var success = await _userService.CompleteWebAuthRegistrationAsync(
|
||||
user, model.Id.Value, model.Name, model.DeviceResponse);
|
||||
if (!success)
|
||||
{
|
||||
throw new BadRequestException("Unable to complete WebAuthn registration.");
|
||||
}
|
||||
var response = new TwoFactorWebAuthnResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpDelete("webauthn")]
|
||||
public async Task<TwoFactorWebAuthnResponseModel> DeleteWebAuthn([FromBody] TwoFactorWebAuthnDeleteRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
await _userService.DeleteWebAuthnKeyAsync(user, model.Id.Value);
|
||||
var response = new TwoFactorWebAuthnResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("get-email")]
|
||||
public async Task<TwoFactorEmailResponseModel> GetEmail([FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
var response = new TwoFactorEmailResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("send-email")]
|
||||
public async Task SendEmail([FromBody] TwoFactorEmailRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
model.ToUser(user);
|
||||
await _userService.SendTwoFactorEmailAsync(user);
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("send-email-login")]
|
||||
public async Task SendEmailLogin([FromBody] TwoFactorEmailRequestModel model)
|
||||
{
|
||||
var user = await _userManager.FindByEmailAsync(model.Email.ToLowerInvariant());
|
||||
if (user != null)
|
||||
{
|
||||
if (await _userService.VerifySecretAsync(user, model.Secret))
|
||||
{
|
||||
var isBecauseNewDeviceLogin = false;
|
||||
if (user.GetTwoFactorProvider(TwoFactorProviderType.Email) is null
|
||||
&&
|
||||
await _userService.Needs2FABecauseNewDeviceAsync(user, model.DeviceIdentifier, null))
|
||||
{
|
||||
model.ToUser(user);
|
||||
isBecauseNewDeviceLogin = true;
|
||||
}
|
||||
|
||||
await _userService.SendTwoFactorEmailAsync(user, isBecauseNewDeviceLogin);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("Cannot send two-factor email.");
|
||||
throw new BadRequestException("Token", "Invalid token.");
|
||||
}
|
||||
|
||||
[HttpPut("email")]
|
||||
[HttpPost("email")]
|
||||
public async Task<TwoFactorEmailResponseModel> PutEmail([FromBody] UpdateTwoFactorEmailRequestModel model)
|
||||
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Authenticator);
|
||||
var response = new TwoFactorAuthenticatorResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("get-yubikey")]
|
||||
public async Task<TwoFactorYubiKeyResponseModel> GetYubiKey([FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
var response = new TwoFactorYubiKeyResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPut("yubikey")]
|
||||
[HttpPost("yubikey")]
|
||||
public async Task<TwoFactorYubiKeyResponseModel> PutYubiKey([FromBody] UpdateTwoFactorYubicoOtpRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
model.ToUser(user);
|
||||
|
||||
await ValidateYubiKeyAsync(user, nameof(model.Key1), model.Key1);
|
||||
await ValidateYubiKeyAsync(user, nameof(model.Key2), model.Key2);
|
||||
await ValidateYubiKeyAsync(user, nameof(model.Key3), model.Key3);
|
||||
await ValidateYubiKeyAsync(user, nameof(model.Key4), model.Key4);
|
||||
await ValidateYubiKeyAsync(user, nameof(model.Key5), model.Key5);
|
||||
|
||||
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.YubiKey);
|
||||
var response = new TwoFactorYubiKeyResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("get-duo")]
|
||||
public async Task<TwoFactorDuoResponseModel> GetDuo([FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
var response = new TwoFactorDuoResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPut("duo")]
|
||||
[HttpPost("duo")]
|
||||
public async Task<TwoFactorDuoResponseModel> PutDuo([FromBody] UpdateTwoFactorDuoRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
try
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
model.ToUser(user);
|
||||
|
||||
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
|
||||
CoreHelpers.CustomProviderName(TwoFactorProviderType.Email), model.Token))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("Token", "Invalid token.");
|
||||
}
|
||||
|
||||
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Email);
|
||||
var response = new TwoFactorEmailResponseModel(user);
|
||||
return response;
|
||||
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
||||
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
|
||||
}
|
||||
catch (DuoException)
|
||||
{
|
||||
throw new BadRequestException("Duo configuration settings are not valid. Please re-check the Duo Admin panel.");
|
||||
}
|
||||
|
||||
[HttpPut("disable")]
|
||||
[HttpPost("disable")]
|
||||
public async Task<TwoFactorProviderResponseModel> PutDisable([FromBody] TwoFactorProviderRequestModel model)
|
||||
model.ToUser(user);
|
||||
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Duo);
|
||||
var response = new TwoFactorDuoResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("~/organizations/{id}/two-factor/get-duo")]
|
||||
public async Task<TwoFactorDuoResponseModel> GetOrganizationDuo(string id,
|
||||
[FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
|
||||
var orgIdGuid = new Guid(id);
|
||||
if (!await _currentContext.ManagePolicies(orgIdGuid))
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
await _userService.DisableTwoFactorProviderAsync(user, model.Type.Value, _organizationService);
|
||||
var response = new TwoFactorProviderResponseModel(model.Type.Value, user);
|
||||
return response;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPut("~/organizations/{id}/two-factor/disable")]
|
||||
[HttpPost("~/organizations/{id}/two-factor/disable")]
|
||||
public async Task<TwoFactorProviderResponseModel> PutOrganizationDisable(string id,
|
||||
[FromBody] TwoFactorProviderRequestModel model)
|
||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||
if (organization == null)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
|
||||
var orgIdGuid = new Guid(id);
|
||||
if (!await _currentContext.ManagePolicies(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||
if (organization == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _organizationService.DisableTwoFactorProviderAsync(organization, model.Type.Value);
|
||||
var response = new TwoFactorProviderResponseModel(model.Type.Value, organization);
|
||||
return response;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("get-recover")]
|
||||
public async Task<TwoFactorRecoverResponseModel> GetRecover([FromBody] SecretVerificationRequestModel model)
|
||||
var response = new TwoFactorDuoResponseModel(organization);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPut("~/organizations/{id}/two-factor/duo")]
|
||||
[HttpPost("~/organizations/{id}/two-factor/duo")]
|
||||
public async Task<TwoFactorDuoResponseModel> PutOrganizationDuo(string id,
|
||||
[FromBody] UpdateTwoFactorDuoRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
|
||||
var orgIdGuid = new Guid(id);
|
||||
if (!await _currentContext.ManagePolicies(orgIdGuid))
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
var response = new TwoFactorRecoverResponseModel(user);
|
||||
return response;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpPost("recover")]
|
||||
[AllowAnonymous]
|
||||
public async Task PostRecover([FromBody] TwoFactorRecoveryRequestModel model)
|
||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||
if (organization == null)
|
||||
{
|
||||
if (!await _userService.RecoverTwoFactorAsync(model.Email, model.MasterPasswordHash, model.RecoveryCode,
|
||||
_organizationService))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException(string.Empty, "Invalid information. Try again.");
|
||||
}
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("get-device-verification-settings")]
|
||||
public async Task<DeviceVerificationResponseModel> GetDeviceVerificationSettings()
|
||||
try
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (User.Claims.HasSsoIdP())
|
||||
{
|
||||
return new DeviceVerificationResponseModel(false, false);
|
||||
}
|
||||
|
||||
var canUserEditDeviceVerificationSettings = _userService.CanEditDeviceVerificationSettings(user);
|
||||
return new DeviceVerificationResponseModel(canUserEditDeviceVerificationSettings, canUserEditDeviceVerificationSettings && user.UnknownDeviceVerificationEnabled);
|
||||
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
||||
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
|
||||
}
|
||||
catch (DuoException)
|
||||
{
|
||||
throw new BadRequestException("Duo configuration settings are not valid. Please re-check the Duo Admin panel.");
|
||||
}
|
||||
|
||||
[HttpPut("device-verification-settings")]
|
||||
public async Task<DeviceVerificationResponseModel> PutDeviceVerificationSettings([FromBody] DeviceVerificationRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
if (!_userService.CanEditDeviceVerificationSettings(user)
|
||||
|| User.Claims.HasSsoIdP())
|
||||
{
|
||||
throw new InvalidOperationException("Can't update device verification settings");
|
||||
}
|
||||
model.ToOrganization(organization);
|
||||
await _organizationService.UpdateTwoFactorProviderAsync(organization,
|
||||
TwoFactorProviderType.OrganizationDuo);
|
||||
var response = new TwoFactorDuoResponseModel(organization);
|
||||
return response;
|
||||
}
|
||||
|
||||
model.ToUser(user);
|
||||
await _userService.SaveUserAsync(user);
|
||||
return new DeviceVerificationResponseModel(true, user.UnknownDeviceVerificationEnabled);
|
||||
[HttpPost("get-webauthn")]
|
||||
public async Task<TwoFactorWebAuthnResponseModel> GetWebAuthn([FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
var response = new TwoFactorWebAuthnResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("get-webauthn-challenge")]
|
||||
public async Task<CredentialCreateOptions> GetWebAuthnChallenge([FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
var reg = await _userService.StartWebAuthnRegistrationAsync(user);
|
||||
return reg;
|
||||
}
|
||||
|
||||
[HttpPut("webauthn")]
|
||||
[HttpPost("webauthn")]
|
||||
public async Task<TwoFactorWebAuthnResponseModel> PutWebAuthn([FromBody] TwoFactorWebAuthnRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
|
||||
var success = await _userService.CompleteWebAuthRegistrationAsync(
|
||||
user, model.Id.Value, model.Name, model.DeviceResponse);
|
||||
if (!success)
|
||||
{
|
||||
throw new BadRequestException("Unable to complete WebAuthn registration.");
|
||||
}
|
||||
var response = new TwoFactorWebAuthnResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
private async Task<User> CheckAsync(SecretVerificationRequestModel model, bool premium)
|
||||
[HttpDelete("webauthn")]
|
||||
public async Task<TwoFactorWebAuthnResponseModel> DeleteWebAuthn([FromBody] TwoFactorWebAuthnDeleteRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
await _userService.DeleteWebAuthnKeyAsync(user, model.Id.Value);
|
||||
var response = new TwoFactorWebAuthnResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("get-email")]
|
||||
public async Task<TwoFactorEmailResponseModel> GetEmail([FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
var response = new TwoFactorEmailResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("send-email")]
|
||||
public async Task SendEmail([FromBody] TwoFactorEmailRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
model.ToUser(user);
|
||||
await _userService.SendTwoFactorEmailAsync(user);
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("send-email-login")]
|
||||
public async Task SendEmailLogin([FromBody] TwoFactorEmailRequestModel model)
|
||||
{
|
||||
var user = await _userManager.FindByEmailAsync(model.Email.ToLowerInvariant());
|
||||
if (user != null)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
if (await _userService.VerifySecretAsync(user, model.Secret))
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
var isBecauseNewDeviceLogin = false;
|
||||
if (user.GetTwoFactorProvider(TwoFactorProviderType.Email) is null
|
||||
&&
|
||||
await _userService.Needs2FABecauseNewDeviceAsync(user, model.DeviceIdentifier, null))
|
||||
{
|
||||
model.ToUser(user);
|
||||
isBecauseNewDeviceLogin = true;
|
||||
}
|
||||
|
||||
if (!await _userService.VerifySecretAsync(user, model.Secret))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException(string.Empty, "User verification failed.");
|
||||
}
|
||||
|
||||
if (premium && !(await _userService.CanAccessPremium(user)))
|
||||
{
|
||||
throw new BadRequestException("Premium status is required.");
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
private async Task ValidateYubiKeyAsync(User user, string name, string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value) || value.Length == 12)
|
||||
{
|
||||
await _userService.SendTwoFactorEmailAsync(user, isBecauseNewDeviceLogin);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
|
||||
CoreHelpers.CustomProviderName(TwoFactorProviderType.YubiKey), value))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException(name, $"{name} is invalid.");
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Delay(500);
|
||||
}
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("Cannot send two-factor email.");
|
||||
}
|
||||
|
||||
[HttpPut("email")]
|
||||
[HttpPost("email")]
|
||||
public async Task<TwoFactorEmailResponseModel> PutEmail([FromBody] UpdateTwoFactorEmailRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
model.ToUser(user);
|
||||
|
||||
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
|
||||
CoreHelpers.CustomProviderName(TwoFactorProviderType.Email), model.Token))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("Token", "Invalid token.");
|
||||
}
|
||||
|
||||
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Email);
|
||||
var response = new TwoFactorEmailResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPut("disable")]
|
||||
[HttpPost("disable")]
|
||||
public async Task<TwoFactorProviderResponseModel> PutDisable([FromBody] TwoFactorProviderRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
await _userService.DisableTwoFactorProviderAsync(user, model.Type.Value, _organizationService);
|
||||
var response = new TwoFactorProviderResponseModel(model.Type.Value, user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPut("~/organizations/{id}/two-factor/disable")]
|
||||
[HttpPost("~/organizations/{id}/two-factor/disable")]
|
||||
public async Task<TwoFactorProviderResponseModel> PutOrganizationDisable(string id,
|
||||
[FromBody] TwoFactorProviderRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
|
||||
var orgIdGuid = new Guid(id);
|
||||
if (!await _currentContext.ManagePolicies(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||
if (organization == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _organizationService.DisableTwoFactorProviderAsync(organization, model.Type.Value);
|
||||
var response = new TwoFactorProviderResponseModel(model.Type.Value, organization);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("get-recover")]
|
||||
public async Task<TwoFactorRecoverResponseModel> GetRecover([FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, false);
|
||||
var response = new TwoFactorRecoverResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("recover")]
|
||||
[AllowAnonymous]
|
||||
public async Task PostRecover([FromBody] TwoFactorRecoveryRequestModel model)
|
||||
{
|
||||
if (!await _userService.RecoverTwoFactorAsync(model.Email, model.MasterPasswordHash, model.RecoveryCode,
|
||||
_organizationService))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException(string.Empty, "Invalid information. Try again.");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("get-device-verification-settings")]
|
||||
public async Task<DeviceVerificationResponseModel> GetDeviceVerificationSettings()
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (User.Claims.HasSsoIdP())
|
||||
{
|
||||
return new DeviceVerificationResponseModel(false, false);
|
||||
}
|
||||
|
||||
var canUserEditDeviceVerificationSettings = _userService.CanEditDeviceVerificationSettings(user);
|
||||
return new DeviceVerificationResponseModel(canUserEditDeviceVerificationSettings, canUserEditDeviceVerificationSettings && user.UnknownDeviceVerificationEnabled);
|
||||
}
|
||||
|
||||
[HttpPut("device-verification-settings")]
|
||||
public async Task<DeviceVerificationResponseModel> PutDeviceVerificationSettings([FromBody] DeviceVerificationRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
if (!_userService.CanEditDeviceVerificationSettings(user)
|
||||
|| User.Claims.HasSsoIdP())
|
||||
{
|
||||
throw new InvalidOperationException("Can't update device verification settings");
|
||||
}
|
||||
|
||||
model.ToUser(user);
|
||||
await _userService.SaveUserAsync(user);
|
||||
return new DeviceVerificationResponseModel(true, user.UnknownDeviceVerificationEnabled);
|
||||
}
|
||||
|
||||
private async Task<User> CheckAsync(SecretVerificationRequestModel model, bool premium)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (!await _userService.VerifySecretAsync(user, model.Secret))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException(string.Empty, "User verification failed.");
|
||||
}
|
||||
|
||||
if (premium && !(await _userService.CanAccessPremium(user)))
|
||||
{
|
||||
throw new BadRequestException("Premium status is required.");
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
private async Task ValidateYubiKeyAsync(User user, string name, string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value) || value.Length == 12)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
|
||||
CoreHelpers.CustomProviderName(TwoFactorProviderType.YubiKey), value))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException(name, $"{name} is invalid.");
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Delay(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,31 +4,30 @@ using Bit.Core.Repositories;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("users")]
|
||||
[Authorize("Application")]
|
||||
public class UsersController : Controller
|
||||
{
|
||||
[Route("users")]
|
||||
[Authorize("Application")]
|
||||
public class UsersController : Controller
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public UsersController(
|
||||
IUserRepository userRepository)
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
_userRepository = userRepository;
|
||||
}
|
||||
|
||||
public UsersController(
|
||||
IUserRepository userRepository)
|
||||
[HttpGet("{id}/public-key")]
|
||||
public async Task<UserKeyResponseModel> Get(string id)
|
||||
{
|
||||
var guidId = new Guid(id);
|
||||
var key = await _userRepository.GetPublicKeyAsync(guidId);
|
||||
if (key == null)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
[HttpGet("{id}/public-key")]
|
||||
public async Task<UserKeyResponseModel> Get(string id)
|
||||
{
|
||||
var guidId = new Guid(id);
|
||||
var key = await _userRepository.GetPublicKeyAsync(guidId);
|
||||
if (key == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return new UserKeyResponseModel(guidId, key);
|
||||
}
|
||||
return new UserKeyResponseModel(guidId, key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,16 @@
|
||||
using Bit.Core.Jobs;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Api.Jobs
|
||||
{
|
||||
public class AliveJob : BaseJob
|
||||
{
|
||||
public AliveJob(ILogger<AliveJob> logger)
|
||||
: base(logger) { }
|
||||
namespace Bit.Api.Jobs;
|
||||
|
||||
protected override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, null, "It's alive!");
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public class AliveJob : BaseJob
|
||||
{
|
||||
public AliveJob(ILogger<AliveJob> logger)
|
||||
: base(logger) { }
|
||||
|
||||
protected override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, null, "It's alive!");
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,22 @@
|
||||
using Bit.Core.Services;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Api.Jobs
|
||||
namespace Bit.Api.Jobs;
|
||||
|
||||
public class EmergencyAccessNotificationJob : BaseJob
|
||||
{
|
||||
public class EmergencyAccessNotificationJob : BaseJob
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
|
||||
public EmergencyAccessNotificationJob(IServiceScopeFactory serviceScopeFactory, ILogger<EmergencyAccessNotificationJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
}
|
||||
|
||||
public EmergencyAccessNotificationJob(IServiceScopeFactory serviceScopeFactory, ILogger<EmergencyAccessNotificationJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var emergencyAccessService = scope.ServiceProvider.GetService(typeof(IEmergencyAccessService)) as IEmergencyAccessService;
|
||||
await emergencyAccessService.SendNotificationsAsync();
|
||||
}
|
||||
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var emergencyAccessService = scope.ServiceProvider.GetService(typeof(IEmergencyAccessService)) as IEmergencyAccessService;
|
||||
await emergencyAccessService.SendNotificationsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,22 @@
|
||||
using Bit.Core.Services;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Api.Jobs
|
||||
namespace Bit.Api.Jobs;
|
||||
|
||||
public class EmergencyAccessTimeoutJob : BaseJob
|
||||
{
|
||||
public class EmergencyAccessTimeoutJob : BaseJob
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
|
||||
public EmergencyAccessTimeoutJob(IServiceScopeFactory serviceScopeFactory, ILogger<EmergencyAccessNotificationJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
}
|
||||
|
||||
public EmergencyAccessTimeoutJob(IServiceScopeFactory serviceScopeFactory, ILogger<EmergencyAccessNotificationJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var emergencyAccessService = scope.ServiceProvider.GetService(typeof(IEmergencyAccessService)) as IEmergencyAccessService;
|
||||
await emergencyAccessService.HandleTimedOutRequestsAsync();
|
||||
}
|
||||
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var emergencyAccessService = scope.ServiceProvider.GetService(typeof(IEmergencyAccessService)) as IEmergencyAccessService;
|
||||
await emergencyAccessService.HandleTimedOutRequestsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,82 +2,81 @@
|
||||
using Bit.Core.Settings;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Api.Jobs
|
||||
namespace Bit.Api.Jobs;
|
||||
|
||||
public class JobsHostedService : BaseJobsHostedService
|
||||
{
|
||||
public class JobsHostedService : BaseJobsHostedService
|
||||
public JobsHostedService(
|
||||
GlobalSettings globalSettings,
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<JobsHostedService> logger,
|
||||
ILogger<JobListener> listenerLogger)
|
||||
: base(globalSettings, serviceProvider, logger, listenerLogger) { }
|
||||
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
public JobsHostedService(
|
||||
GlobalSettings globalSettings,
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<JobsHostedService> logger,
|
||||
ILogger<JobListener> listenerLogger)
|
||||
: base(globalSettings, serviceProvider, logger, listenerLogger) { }
|
||||
var everyTopOfTheHourTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryTopOfTheHourTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 * * * ?")
|
||||
.Build();
|
||||
var emergencyAccessNotificationTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EmergencyAccessNotificationTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 * * * ?")
|
||||
.Build();
|
||||
var emergencyAccessTimeoutTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EmergencyAccessTimeoutTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 * * * ?")
|
||||
.Build();
|
||||
var everyTopOfTheSixthHourTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryTopOfTheSixthHourTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 */6 * * ?")
|
||||
.Build();
|
||||
var everyTwelfthHourAndThirtyMinutesTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryTwelfthHourAndThirtyMinutesTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 30 */12 * * ?")
|
||||
.Build();
|
||||
var randomDailySponsorshipSyncTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("RandomDailySponsorshipSyncTrigger")
|
||||
.StartAt(DateBuilder.FutureDate(new Random().Next(24), IntervalUnit.Hour))
|
||||
.WithSimpleSchedule(x => x
|
||||
.WithIntervalInHours(24)
|
||||
.RepeatForever())
|
||||
.Build();
|
||||
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
var jobs = new List<Tuple<Type, ITrigger>>
|
||||
{
|
||||
var everyTopOfTheHourTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryTopOfTheHourTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 * * * ?")
|
||||
.Build();
|
||||
var emergencyAccessNotificationTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EmergencyAccessNotificationTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 * * * ?")
|
||||
.Build();
|
||||
var emergencyAccessTimeoutTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EmergencyAccessTimeoutTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 * * * ?")
|
||||
.Build();
|
||||
var everyTopOfTheSixthHourTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryTopOfTheSixthHourTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 0 */6 * * ?")
|
||||
.Build();
|
||||
var everyTwelfthHourAndThirtyMinutesTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("EveryTwelfthHourAndThirtyMinutesTrigger")
|
||||
.StartNow()
|
||||
.WithCronSchedule("0 30 */12 * * ?")
|
||||
.Build();
|
||||
var randomDailySponsorshipSyncTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity("RandomDailySponsorshipSyncTrigger")
|
||||
.StartAt(DateBuilder.FutureDate(new Random().Next(24), IntervalUnit.Hour))
|
||||
.WithSimpleSchedule(x => x
|
||||
.WithIntervalInHours(24)
|
||||
.RepeatForever())
|
||||
.Build();
|
||||
new Tuple<Type, ITrigger>(typeof(AliveJob), everyTopOfTheHourTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(EmergencyAccessNotificationJob), emergencyAccessNotificationTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(EmergencyAccessTimeoutJob), emergencyAccessTimeoutTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(ValidateUsersJob), everyTopOfTheSixthHourTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(ValidateOrganizationsJob), everyTwelfthHourAndThirtyMinutesTrigger)
|
||||
};
|
||||
|
||||
var jobs = new List<Tuple<Type, ITrigger>>
|
||||
{
|
||||
new Tuple<Type, ITrigger>(typeof(AliveJob), everyTopOfTheHourTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(EmergencyAccessNotificationJob), emergencyAccessNotificationTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(EmergencyAccessTimeoutJob), emergencyAccessTimeoutTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(ValidateUsersJob), everyTopOfTheSixthHourTrigger),
|
||||
new Tuple<Type, ITrigger>(typeof(ValidateOrganizationsJob), everyTwelfthHourAndThirtyMinutesTrigger)
|
||||
};
|
||||
|
||||
if (_globalSettings.SelfHosted && _globalSettings.EnableCloudCommunication)
|
||||
{
|
||||
jobs.Add(new Tuple<Type, ITrigger>(typeof(SelfHostedSponsorshipSyncJob), randomDailySponsorshipSyncTrigger));
|
||||
}
|
||||
|
||||
Jobs = jobs;
|
||||
|
||||
await base.StartAsync(cancellationToken);
|
||||
if (_globalSettings.SelfHosted && _globalSettings.EnableCloudCommunication)
|
||||
{
|
||||
jobs.Add(new Tuple<Type, ITrigger>(typeof(SelfHostedSponsorshipSyncJob), randomDailySponsorshipSyncTrigger));
|
||||
}
|
||||
|
||||
public static void AddJobsServices(IServiceCollection services, bool selfHosted)
|
||||
Jobs = jobs;
|
||||
|
||||
await base.StartAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public static void AddJobsServices(IServiceCollection services, bool selfHosted)
|
||||
{
|
||||
if (selfHosted)
|
||||
{
|
||||
if (selfHosted)
|
||||
{
|
||||
services.AddTransient<SelfHostedSponsorshipSyncJob>();
|
||||
}
|
||||
services.AddTransient<AliveJob>();
|
||||
services.AddTransient<EmergencyAccessNotificationJob>();
|
||||
services.AddTransient<EmergencyAccessTimeoutJob>();
|
||||
services.AddTransient<ValidateUsersJob>();
|
||||
services.AddTransient<ValidateOrganizationsJob>();
|
||||
services.AddTransient<SelfHostedSponsorshipSyncJob>();
|
||||
}
|
||||
services.AddTransient<AliveJob>();
|
||||
services.AddTransient<EmergencyAccessNotificationJob>();
|
||||
services.AddTransient<EmergencyAccessTimeoutJob>();
|
||||
services.AddTransient<ValidateUsersJob>();
|
||||
services.AddTransient<ValidateOrganizationsJob>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,59 +7,58 @@ using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Api.Jobs
|
||||
{
|
||||
public class SelfHostedSponsorshipSyncJob : BaseJob
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private IOrganizationRepository _organizationRepository;
|
||||
private IOrganizationConnectionRepository _organizationConnectionRepository;
|
||||
private readonly ILicensingService _licensingService;
|
||||
private GlobalSettings _globalSettings;
|
||||
namespace Bit.Api.Jobs;
|
||||
|
||||
public SelfHostedSponsorshipSyncJob(
|
||||
IServiceProvider serviceProvider,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationConnectionRepository organizationConnectionRepository,
|
||||
ILicensingService licensingService,
|
||||
ILogger<SelfHostedSponsorshipSyncJob> logger,
|
||||
GlobalSettings globalSettings)
|
||||
: base(logger)
|
||||
public class SelfHostedSponsorshipSyncJob : BaseJob
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private IOrganizationRepository _organizationRepository;
|
||||
private IOrganizationConnectionRepository _organizationConnectionRepository;
|
||||
private readonly ILicensingService _licensingService;
|
||||
private GlobalSettings _globalSettings;
|
||||
|
||||
public SelfHostedSponsorshipSyncJob(
|
||||
IServiceProvider serviceProvider,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationConnectionRepository organizationConnectionRepository,
|
||||
ILicensingService licensingService,
|
||||
ILogger<SelfHostedSponsorshipSyncJob> logger,
|
||||
GlobalSettings globalSettings)
|
||||
: base(logger)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationConnectionRepository = organizationConnectionRepository;
|
||||
_licensingService = licensingService;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
if (!_globalSettings.EnableCloudCommunication)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationConnectionRepository = organizationConnectionRepository;
|
||||
_licensingService = licensingService;
|
||||
_globalSettings = globalSettings;
|
||||
_logger.LogInformation("Skipping Organization sync with cloud - Cloud communication is disabled in global settings");
|
||||
return;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
var organizations = await _organizationRepository.GetManyByEnabledAsync();
|
||||
|
||||
using (var scope = _serviceProvider.CreateScope())
|
||||
{
|
||||
if (!_globalSettings.EnableCloudCommunication)
|
||||
var syncCommand = scope.ServiceProvider.GetRequiredService<ISelfHostedSyncSponsorshipsCommand>();
|
||||
foreach (var org in organizations)
|
||||
{
|
||||
_logger.LogInformation("Skipping Organization sync with cloud - Cloud communication is disabled in global settings");
|
||||
return;
|
||||
}
|
||||
|
||||
var organizations = await _organizationRepository.GetManyByEnabledAsync();
|
||||
|
||||
using (var scope = _serviceProvider.CreateScope())
|
||||
{
|
||||
var syncCommand = scope.ServiceProvider.GetRequiredService<ISelfHostedSyncSponsorshipsCommand>();
|
||||
foreach (var org in organizations)
|
||||
var connection = (await _organizationConnectionRepository.GetEnabledByOrganizationIdTypeAsync(org.Id, OrganizationConnectionType.CloudBillingSync)).FirstOrDefault();
|
||||
if (connection != null)
|
||||
{
|
||||
var connection = (await _organizationConnectionRepository.GetEnabledByOrganizationIdTypeAsync(org.Id, OrganizationConnectionType.CloudBillingSync)).FirstOrDefault();
|
||||
if (connection != null)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = connection.GetConfig<BillingSyncConfig>();
|
||||
await syncCommand.SyncOrganization(org.Id, config.CloudOrganizationId, connection);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Sponsorship sync for organization {org.Name} Failed");
|
||||
}
|
||||
var config = connection.GetConfig<BillingSyncConfig>();
|
||||
await syncCommand.SyncOrganization(org.Id, config.CloudOrganizationId, connection);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Sponsorship sync for organization {org.Name} Failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,22 @@
|
||||
using Bit.Core.Services;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Api.Jobs
|
||||
namespace Bit.Api.Jobs;
|
||||
|
||||
public class ValidateOrganizationsJob : BaseJob
|
||||
{
|
||||
public class ValidateOrganizationsJob : BaseJob
|
||||
private readonly ILicensingService _licensingService;
|
||||
|
||||
public ValidateOrganizationsJob(
|
||||
ILicensingService licensingService,
|
||||
ILogger<ValidateOrganizationsJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
private readonly ILicensingService _licensingService;
|
||||
_licensingService = licensingService;
|
||||
}
|
||||
|
||||
public ValidateOrganizationsJob(
|
||||
ILicensingService licensingService,
|
||||
ILogger<ValidateOrganizationsJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
_licensingService = licensingService;
|
||||
}
|
||||
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
await _licensingService.ValidateOrganizationsAsync();
|
||||
}
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
await _licensingService.ValidateOrganizationsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,22 @@
|
||||
using Bit.Core.Services;
|
||||
using Quartz;
|
||||
|
||||
namespace Bit.Api.Jobs
|
||||
namespace Bit.Api.Jobs;
|
||||
|
||||
public class ValidateUsersJob : BaseJob
|
||||
{
|
||||
public class ValidateUsersJob : BaseJob
|
||||
private readonly ILicensingService _licensingService;
|
||||
|
||||
public ValidateUsersJob(
|
||||
ILicensingService licensingService,
|
||||
ILogger<ValidateUsersJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
private readonly ILicensingService _licensingService;
|
||||
_licensingService = licensingService;
|
||||
}
|
||||
|
||||
public ValidateUsersJob(
|
||||
ILicensingService licensingService,
|
||||
ILogger<ValidateUsersJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
_licensingService = licensingService;
|
||||
}
|
||||
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
await _licensingService.ValidateUsersAsync();
|
||||
}
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
{
|
||||
await _licensingService.ValidateUsersAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.Models
|
||||
namespace Bit.Api.Models;
|
||||
|
||||
public class CipherAttachmentModel
|
||||
{
|
||||
public class CipherAttachmentModel
|
||||
public CipherAttachmentModel() { }
|
||||
|
||||
public CipherAttachmentModel(CipherAttachment.MetaData data)
|
||||
{
|
||||
public CipherAttachmentModel() { }
|
||||
|
||||
public CipherAttachmentModel(CipherAttachment.MetaData data)
|
||||
{
|
||||
FileName = data.FileName;
|
||||
Key = data.Key;
|
||||
}
|
||||
|
||||
[EncryptedStringLength(1000)]
|
||||
public string FileName { get; set; }
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Key { get; set; }
|
||||
FileName = data.FileName;
|
||||
Key = data.Key;
|
||||
}
|
||||
|
||||
[EncryptedStringLength(1000)]
|
||||
public string FileName { get; set; }
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Key { get; set; }
|
||||
}
|
||||
|
||||
@@ -2,39 +2,38 @@
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.Models
|
||||
namespace Bit.Api.Models;
|
||||
|
||||
public class CipherCardModel
|
||||
{
|
||||
public class CipherCardModel
|
||||
public CipherCardModel() { }
|
||||
|
||||
public CipherCardModel(CipherCardData data)
|
||||
{
|
||||
public CipherCardModel() { }
|
||||
|
||||
public CipherCardModel(CipherCardData data)
|
||||
{
|
||||
CardholderName = data.CardholderName;
|
||||
Brand = data.Brand;
|
||||
Number = data.Number;
|
||||
ExpMonth = data.ExpMonth;
|
||||
ExpYear = data.ExpYear;
|
||||
Code = data.Code;
|
||||
}
|
||||
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string CardholderName { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Brand { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Number { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string ExpMonth { get; set; }
|
||||
[EncryptedString]
|
||||
[StringLength(1000)]
|
||||
public string ExpYear { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Code { get; set; }
|
||||
CardholderName = data.CardholderName;
|
||||
Brand = data.Brand;
|
||||
Number = data.Number;
|
||||
ExpMonth = data.ExpMonth;
|
||||
ExpYear = data.ExpYear;
|
||||
Code = data.Code;
|
||||
}
|
||||
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string CardholderName { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Brand { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Number { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string ExpMonth { get; set; }
|
||||
[EncryptedString]
|
||||
[StringLength(1000)]
|
||||
public string ExpYear { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Code { get; set; }
|
||||
}
|
||||
|
||||
@@ -2,36 +2,35 @@
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.Models
|
||||
namespace Bit.Api.Models;
|
||||
|
||||
public class CipherFieldModel
|
||||
{
|
||||
public class CipherFieldModel
|
||||
public CipherFieldModel() { }
|
||||
|
||||
public CipherFieldModel(CipherFieldData data)
|
||||
{
|
||||
public CipherFieldModel() { }
|
||||
Type = data.Type;
|
||||
Name = data.Name;
|
||||
Value = data.Value;
|
||||
LinkedId = data.LinkedId ?? null;
|
||||
}
|
||||
|
||||
public CipherFieldModel(CipherFieldData data)
|
||||
public FieldType Type { get; set; }
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Name { get; set; }
|
||||
[EncryptedStringLength(5000)]
|
||||
public string Value { get; set; }
|
||||
public int? LinkedId { get; set; }
|
||||
|
||||
public CipherFieldData ToCipherFieldData()
|
||||
{
|
||||
return new CipherFieldData
|
||||
{
|
||||
Type = data.Type;
|
||||
Name = data.Name;
|
||||
Value = data.Value;
|
||||
LinkedId = data.LinkedId ?? null;
|
||||
}
|
||||
|
||||
public FieldType Type { get; set; }
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Name { get; set; }
|
||||
[EncryptedStringLength(5000)]
|
||||
public string Value { get; set; }
|
||||
public int? LinkedId { get; set; }
|
||||
|
||||
public CipherFieldData ToCipherFieldData()
|
||||
{
|
||||
return new CipherFieldData
|
||||
{
|
||||
Type = Type,
|
||||
Name = Name,
|
||||
Value = Value,
|
||||
LinkedId = LinkedId ?? null,
|
||||
};
|
||||
}
|
||||
Type = Type,
|
||||
Name = Name,
|
||||
Value = Value,
|
||||
LinkedId = LinkedId ?? null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,87 +2,86 @@
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.Models
|
||||
namespace Bit.Api.Models;
|
||||
|
||||
public class CipherIdentityModel
|
||||
{
|
||||
public class CipherIdentityModel
|
||||
public CipherIdentityModel() { }
|
||||
|
||||
public CipherIdentityModel(CipherIdentityData data)
|
||||
{
|
||||
public CipherIdentityModel() { }
|
||||
|
||||
public CipherIdentityModel(CipherIdentityData data)
|
||||
{
|
||||
Title = data.Title;
|
||||
FirstName = data.FirstName;
|
||||
MiddleName = data.MiddleName;
|
||||
LastName = data.LastName;
|
||||
Address1 = data.Address1;
|
||||
Address2 = data.Address2;
|
||||
Address3 = data.Address3;
|
||||
City = data.City;
|
||||
State = data.State;
|
||||
PostalCode = data.PostalCode;
|
||||
Country = data.Country;
|
||||
Company = data.Company;
|
||||
Email = data.Email;
|
||||
Phone = data.Phone;
|
||||
SSN = data.SSN;
|
||||
Username = data.Username;
|
||||
PassportNumber = data.PassportNumber;
|
||||
LicenseNumber = data.LicenseNumber;
|
||||
}
|
||||
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Title { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string FirstName { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string MiddleName { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string LastName { get; set; }
|
||||
[EncryptedString]
|
||||
[StringLength(1000)]
|
||||
public string Address1 { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Address2 { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Address3 { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string City { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string State { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string PostalCode { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Country { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Company { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Email { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Phone { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string SSN { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Username { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string PassportNumber { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string LicenseNumber { get; set; }
|
||||
Title = data.Title;
|
||||
FirstName = data.FirstName;
|
||||
MiddleName = data.MiddleName;
|
||||
LastName = data.LastName;
|
||||
Address1 = data.Address1;
|
||||
Address2 = data.Address2;
|
||||
Address3 = data.Address3;
|
||||
City = data.City;
|
||||
State = data.State;
|
||||
PostalCode = data.PostalCode;
|
||||
Country = data.Country;
|
||||
Company = data.Company;
|
||||
Email = data.Email;
|
||||
Phone = data.Phone;
|
||||
SSN = data.SSN;
|
||||
Username = data.Username;
|
||||
PassportNumber = data.PassportNumber;
|
||||
LicenseNumber = data.LicenseNumber;
|
||||
}
|
||||
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Title { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string FirstName { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string MiddleName { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string LastName { get; set; }
|
||||
[EncryptedString]
|
||||
[StringLength(1000)]
|
||||
public string Address1 { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Address2 { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Address3 { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string City { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string State { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string PostalCode { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Country { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Company { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Email { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Phone { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string SSN { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Username { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string PassportNumber { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string LicenseNumber { get; set; }
|
||||
}
|
||||
|
||||
@@ -2,84 +2,83 @@
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.Models
|
||||
{
|
||||
public class CipherLoginModel
|
||||
{
|
||||
public CipherLoginModel() { }
|
||||
namespace Bit.Api.Models;
|
||||
|
||||
public CipherLoginModel(CipherLoginData data)
|
||||
public class CipherLoginModel
|
||||
{
|
||||
public CipherLoginModel() { }
|
||||
|
||||
public CipherLoginModel(CipherLoginData data)
|
||||
{
|
||||
Uris = data.Uris?.Select(u => new CipherLoginUriModel(u))?.ToList();
|
||||
if (!Uris?.Any() ?? true)
|
||||
{
|
||||
Uris = data.Uris?.Select(u => new CipherLoginUriModel(u))?.ToList();
|
||||
if (!Uris?.Any() ?? true)
|
||||
Uri = data.Uri;
|
||||
}
|
||||
|
||||
Username = data.Username;
|
||||
Password = data.Password;
|
||||
PasswordRevisionDate = data.PasswordRevisionDate;
|
||||
Totp = data.Totp;
|
||||
AutofillOnPageLoad = data.AutofillOnPageLoad;
|
||||
}
|
||||
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(10000)]
|
||||
public string Uri
|
||||
{
|
||||
get => Uris?.FirstOrDefault()?.Uri;
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
Uri = data.Uri;
|
||||
return;
|
||||
}
|
||||
|
||||
Username = data.Username;
|
||||
Password = data.Password;
|
||||
PasswordRevisionDate = data.PasswordRevisionDate;
|
||||
Totp = data.Totp;
|
||||
AutofillOnPageLoad = data.AutofillOnPageLoad;
|
||||
if (Uris == null)
|
||||
{
|
||||
Uris = new List<CipherLoginUriModel>();
|
||||
}
|
||||
|
||||
Uris.Add(new CipherLoginUriModel(value));
|
||||
}
|
||||
}
|
||||
public List<CipherLoginUriModel> Uris { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Username { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(5000)]
|
||||
public string Password { get; set; }
|
||||
public DateTime? PasswordRevisionDate { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Totp { get; set; }
|
||||
public bool? AutofillOnPageLoad { get; set; }
|
||||
|
||||
public class CipherLoginUriModel
|
||||
{
|
||||
public CipherLoginUriModel() { }
|
||||
|
||||
public CipherLoginUriModel(string uri)
|
||||
{
|
||||
Uri = uri;
|
||||
}
|
||||
|
||||
public CipherLoginUriModel(CipherLoginData.CipherLoginUriData uri)
|
||||
{
|
||||
Uri = uri.Uri;
|
||||
Match = uri.Match;
|
||||
}
|
||||
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(10000)]
|
||||
public string Uri
|
||||
public string Uri { get; set; }
|
||||
public UriMatchType? Match { get; set; } = null;
|
||||
|
||||
public CipherLoginData.CipherLoginUriData ToCipherLoginUriData()
|
||||
{
|
||||
get => Uris?.FirstOrDefault()?.Uri;
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Uris == null)
|
||||
{
|
||||
Uris = new List<CipherLoginUriModel>();
|
||||
}
|
||||
|
||||
Uris.Add(new CipherLoginUriModel(value));
|
||||
}
|
||||
}
|
||||
public List<CipherLoginUriModel> Uris { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Username { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(5000)]
|
||||
public string Password { get; set; }
|
||||
public DateTime? PasswordRevisionDate { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Totp { get; set; }
|
||||
public bool? AutofillOnPageLoad { get; set; }
|
||||
|
||||
public class CipherLoginUriModel
|
||||
{
|
||||
public CipherLoginUriModel() { }
|
||||
|
||||
public CipherLoginUriModel(string uri)
|
||||
{
|
||||
Uri = uri;
|
||||
}
|
||||
|
||||
public CipherLoginUriModel(CipherLoginData.CipherLoginUriData uri)
|
||||
{
|
||||
Uri = uri.Uri;
|
||||
Match = uri.Match;
|
||||
}
|
||||
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(10000)]
|
||||
public string Uri { get; set; }
|
||||
public UriMatchType? Match { get; set; } = null;
|
||||
|
||||
public CipherLoginData.CipherLoginUriData ToCipherLoginUriData()
|
||||
{
|
||||
return new CipherLoginData.CipherLoginUriData { Uri = Uri, Match = Match, };
|
||||
}
|
||||
return new CipherLoginData.CipherLoginUriData { Uri = Uri, Match = Match, };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,28 +2,27 @@
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.Models
|
||||
namespace Bit.Api.Models;
|
||||
|
||||
public class CipherPasswordHistoryModel
|
||||
{
|
||||
public class CipherPasswordHistoryModel
|
||||
public CipherPasswordHistoryModel() { }
|
||||
|
||||
public CipherPasswordHistoryModel(CipherPasswordHistoryData data)
|
||||
{
|
||||
public CipherPasswordHistoryModel() { }
|
||||
Password = data.Password;
|
||||
LastUsedDate = data.LastUsedDate;
|
||||
}
|
||||
|
||||
public CipherPasswordHistoryModel(CipherPasswordHistoryData data)
|
||||
{
|
||||
Password = data.Password;
|
||||
LastUsedDate = data.LastUsedDate;
|
||||
}
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(5000)]
|
||||
[Required]
|
||||
public string Password { get; set; }
|
||||
[Required]
|
||||
public DateTime? LastUsedDate { get; set; }
|
||||
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(5000)]
|
||||
[Required]
|
||||
public string Password { get; set; }
|
||||
[Required]
|
||||
public DateTime? LastUsedDate { get; set; }
|
||||
|
||||
public CipherPasswordHistoryData ToCipherPasswordHistoryData()
|
||||
{
|
||||
return new CipherPasswordHistoryData { Password = Password, LastUsedDate = LastUsedDate.Value, };
|
||||
}
|
||||
public CipherPasswordHistoryData ToCipherPasswordHistoryData()
|
||||
{
|
||||
return new CipherPasswordHistoryData { Password = Password, LastUsedDate = LastUsedDate.Value, };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Api.Models
|
||||
namespace Bit.Api.Models;
|
||||
|
||||
public class CipherSecureNoteModel
|
||||
{
|
||||
public class CipherSecureNoteModel
|
||||
public CipherSecureNoteModel() { }
|
||||
|
||||
public CipherSecureNoteModel(CipherSecureNoteData data)
|
||||
{
|
||||
public CipherSecureNoteModel() { }
|
||||
|
||||
public CipherSecureNoteModel(CipherSecureNoteData data)
|
||||
{
|
||||
Type = data.Type;
|
||||
}
|
||||
|
||||
public SecureNoteType Type { get; set; }
|
||||
Type = data.Type;
|
||||
}
|
||||
|
||||
public SecureNoteType Type { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Api.Models.Public
|
||||
namespace Bit.Api.Models.Public;
|
||||
|
||||
public abstract class AssociationWithPermissionsBaseModel
|
||||
{
|
||||
public abstract class AssociationWithPermissionsBaseModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The associated object's unique identifier.
|
||||
/// </summary>
|
||||
/// <example>bfbc8338-e329-4dc0-b0c9-317c2ebf1a09</example>
|
||||
[Required]
|
||||
public Guid? Id { get; set; }
|
||||
/// <summary>
|
||||
/// When true, the read only permission will not allow the user or group to make changes to items.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool? ReadOnly { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// The associated object's unique identifier.
|
||||
/// </summary>
|
||||
/// <example>bfbc8338-e329-4dc0-b0c9-317c2ebf1a09</example>
|
||||
[Required]
|
||||
public Guid? Id { get; set; }
|
||||
/// <summary>
|
||||
/// When true, the read only permission will not allow the user or group to make changes to items.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool? ReadOnly { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Api.Models.Public
|
||||
namespace Bit.Api.Models.Public;
|
||||
|
||||
public abstract class CollectionBaseModel
|
||||
{
|
||||
public abstract class CollectionBaseModel
|
||||
{
|
||||
/// <summary>
|
||||
/// External identifier for reference or linking this collection to another system.
|
||||
/// </summary>
|
||||
/// <example>external_id_123456</example>
|
||||
[StringLength(300)]
|
||||
public string ExternalId { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// External identifier for reference or linking this collection to another system.
|
||||
/// </summary>
|
||||
/// <example>external_id_123456</example>
|
||||
[StringLength(300)]
|
||||
public string ExternalId { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Api.Models.Public
|
||||
namespace Bit.Api.Models.Public;
|
||||
|
||||
public abstract class GroupBaseModel
|
||||
{
|
||||
public abstract class GroupBaseModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the group.
|
||||
/// </summary>
|
||||
/// <example>Development Team</example>
|
||||
[Required]
|
||||
[StringLength(100)]
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// Determines if this group can access all collections within the organization, or only the associated
|
||||
/// collections. If set to <c>true</c>, this option overrides any collection assignments.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool? AccessAll { get; set; }
|
||||
/// <summary>
|
||||
/// External identifier for reference or linking this group to another system, such as a user directory.
|
||||
/// </summary>
|
||||
/// <example>external_id_123456</example>
|
||||
[StringLength(300)]
|
||||
public string ExternalId { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// The name of the group.
|
||||
/// </summary>
|
||||
/// <example>Development Team</example>
|
||||
[Required]
|
||||
[StringLength(100)]
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// Determines if this group can access all collections within the organization, or only the associated
|
||||
/// collections. If set to <c>true</c>, this option overrides any collection assignments.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool? AccessAll { get; set; }
|
||||
/// <summary>
|
||||
/// External identifier for reference or linking this group to another system, such as a user directory.
|
||||
/// </summary>
|
||||
/// <example>external_id_123456</example>
|
||||
[StringLength(300)]
|
||||
public string ExternalId { get; set; }
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user