mirror of
https://github.com/bitwarden/server.git
synced 2025-05-23 04:21:05 -05:00
Changed all C# control flow block statements to include space between keyword and open paren
This commit is contained in:
parent
943aea9a12
commit
9800b752c0
@ -44,22 +44,22 @@ namespace Bit.Admin.Controllers
|
|||||||
{
|
{
|
||||||
var response = await _httpClient.GetAsync(
|
var response = await _httpClient.GetAsync(
|
||||||
$"https://hub.docker.com/v2/repositories/bitwarden/{repository}/tags/");
|
$"https://hub.docker.com/v2/repositories/bitwarden/{repository}/tags/");
|
||||||
if(response.IsSuccessStatusCode)
|
if (response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
var json = await response.Content.ReadAsStringAsync();
|
var json = await response.Content.ReadAsStringAsync();
|
||||||
var data = JObject.Parse(json);
|
var data = JObject.Parse(json);
|
||||||
var results = data["results"] as JArray;
|
var results = data["results"] as JArray;
|
||||||
foreach(var result in results)
|
foreach (var result in results)
|
||||||
{
|
{
|
||||||
var name = result["name"].ToString();
|
var name = result["name"].ToString();
|
||||||
if(!string.IsNullOrWhiteSpace(name) && name.Length > 0 && char.IsNumber(name[0]))
|
if (!string.IsNullOrWhiteSpace(name) && name.Length > 0 && char.IsNumber(name[0]))
|
||||||
{
|
{
|
||||||
return new JsonResult(name);
|
return new JsonResult(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(HttpRequestException) { }
|
catch (HttpRequestException) { }
|
||||||
|
|
||||||
return new JsonResult("-");
|
return new JsonResult("-");
|
||||||
}
|
}
|
||||||
@ -70,14 +70,14 @@ namespace Bit.Admin.Controllers
|
|||||||
{
|
{
|
||||||
var response = await _httpClient.GetAsync(
|
var response = await _httpClient.GetAsync(
|
||||||
$"{_globalSettings.BaseServiceUri.InternalVault}/version.json");
|
$"{_globalSettings.BaseServiceUri.InternalVault}/version.json");
|
||||||
if(response.IsSuccessStatusCode)
|
if (response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
var json = await response.Content.ReadAsStringAsync();
|
var json = await response.Content.ReadAsStringAsync();
|
||||||
var data = JObject.Parse(json);
|
var data = JObject.Parse(json);
|
||||||
return new JsonResult(data["version"].ToString());
|
return new JsonResult(data["version"].ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(HttpRequestException) { }
|
catch (HttpRequestException) { }
|
||||||
|
|
||||||
return new JsonResult("-");
|
return new JsonResult("-");
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ namespace Bit.Admin.Controllers
|
|||||||
public IActionResult Index(string returnUrl = null, string error = null, string success = null,
|
public IActionResult Index(string returnUrl = null, string error = null, string success = null,
|
||||||
bool accessDenied = false)
|
bool accessDenied = false)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(error) && accessDenied)
|
if (string.IsNullOrWhiteSpace(error) && accessDenied)
|
||||||
{
|
{
|
||||||
error = "Access denied. Please log in.";
|
error = "Access denied. Please log in.";
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ namespace Bit.Admin.Controllers
|
|||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> Index(LoginModel model)
|
public async Task<IActionResult> Index(LoginModel model)
|
||||||
{
|
{
|
||||||
if(ModelState.IsValid)
|
if (ModelState.IsValid)
|
||||||
{
|
{
|
||||||
await _signInManager.PasswordlessSignInAsync(model.Email, model.ReturnUrl);
|
await _signInManager.PasswordlessSignInAsync(model.Email, model.ReturnUrl);
|
||||||
return RedirectToAction("Index", new
|
return RedirectToAction("Index", new
|
||||||
@ -52,7 +52,7 @@ namespace Bit.Admin.Controllers
|
|||||||
public async Task<IActionResult> Confirm(string email, string token, string returnUrl)
|
public async Task<IActionResult> Confirm(string email, string token, string returnUrl)
|
||||||
{
|
{
|
||||||
var result = await _signInManager.PasswordlessSignInAsync(email, token, true);
|
var result = await _signInManager.PasswordlessSignInAsync(email, token, true);
|
||||||
if(!result.Succeeded)
|
if (!result.Succeeded)
|
||||||
{
|
{
|
||||||
return RedirectToAction("Index", new
|
return RedirectToAction("Index", new
|
||||||
{
|
{
|
||||||
@ -60,7 +60,7 @@ namespace Bit.Admin.Controllers
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(returnUrl) && Url.IsLocalUrl(returnUrl))
|
if (!string.IsNullOrWhiteSpace(returnUrl) && Url.IsLocalUrl(returnUrl))
|
||||||
{
|
{
|
||||||
return Redirect(returnUrl);
|
return Redirect(returnUrl);
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ namespace Bit.Admin.Controllers
|
|||||||
LogEventLevel? level = null, string project = null, DateTime? start = null, DateTime? end = null)
|
LogEventLevel? level = null, string project = null, DateTime? start = null, DateTime? end = null)
|
||||||
{
|
{
|
||||||
var collectionLink = UriFactory.CreateDocumentCollectionUri(Database, Collection);
|
var collectionLink = UriFactory.CreateDocumentCollectionUri(Database, Collection);
|
||||||
using(var client = new DocumentClient(new Uri(_globalSettings.DocumentDb.Uri),
|
using (var client = new DocumentClient(new Uri(_globalSettings.DocumentDb.Uri),
|
||||||
_globalSettings.DocumentDb.Key))
|
_globalSettings.DocumentDb.Key))
|
||||||
{
|
{
|
||||||
var options = new FeedOptions
|
var options = new FeedOptions
|
||||||
@ -40,19 +40,19 @@ namespace Bit.Admin.Controllers
|
|||||||
};
|
};
|
||||||
|
|
||||||
var query = client.CreateDocumentQuery<LogModel>(collectionLink, options).AsQueryable();
|
var query = client.CreateDocumentQuery<LogModel>(collectionLink, options).AsQueryable();
|
||||||
if(level.HasValue)
|
if (level.HasValue)
|
||||||
{
|
{
|
||||||
query = query.Where(l => l.Level == level.Value.ToString());
|
query = query.Where(l => l.Level == level.Value.ToString());
|
||||||
}
|
}
|
||||||
if(!string.IsNullOrWhiteSpace(project))
|
if (!string.IsNullOrWhiteSpace(project))
|
||||||
{
|
{
|
||||||
query = query.Where(l => l.Properties != null && l.Properties["Project"] == (object)project);
|
query = query.Where(l => l.Properties != null && l.Properties["Project"] == (object)project);
|
||||||
}
|
}
|
||||||
if(start.HasValue)
|
if (start.HasValue)
|
||||||
{
|
{
|
||||||
query = query.Where(l => l.Timestamp >= start.Value);
|
query = query.Where(l => l.Timestamp >= start.Value);
|
||||||
}
|
}
|
||||||
if(end.HasValue)
|
if (end.HasValue)
|
||||||
{
|
{
|
||||||
query = query.Where(l => l.Timestamp <= end.Value);
|
query = query.Where(l => l.Timestamp <= end.Value);
|
||||||
}
|
}
|
||||||
@ -76,12 +76,12 @@ namespace Bit.Admin.Controllers
|
|||||||
|
|
||||||
public async Task<IActionResult> View(Guid id)
|
public async Task<IActionResult> View(Guid id)
|
||||||
{
|
{
|
||||||
using(var client = new DocumentClient(new Uri(_globalSettings.DocumentDb.Uri),
|
using (var client = new DocumentClient(new Uri(_globalSettings.DocumentDb.Uri),
|
||||||
_globalSettings.DocumentDb.Key))
|
_globalSettings.DocumentDb.Key))
|
||||||
{
|
{
|
||||||
var uri = UriFactory.CreateDocumentUri(Database, Collection, id.ToString());
|
var uri = UriFactory.CreateDocumentUri(Database, Collection, id.ToString());
|
||||||
var response = await client.ReadDocumentAsync<LogDetailsModel>(uri);
|
var response = await client.ReadDocumentAsync<LogDetailsModel>(uri);
|
||||||
if(response?.Document == null)
|
if (response?.Document == null)
|
||||||
{
|
{
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
|
@ -50,12 +50,12 @@ namespace Bit.Admin.Controllers
|
|||||||
public async Task<IActionResult> Index(string name = null, string userEmail = null, bool? paid = null,
|
public async Task<IActionResult> Index(string name = null, string userEmail = null, bool? paid = null,
|
||||||
int page = 1, int count = 25)
|
int page = 1, int count = 25)
|
||||||
{
|
{
|
||||||
if(page < 1)
|
if (page < 1)
|
||||||
{
|
{
|
||||||
page = 1;
|
page = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(count < 1)
|
if (count < 1)
|
||||||
{
|
{
|
||||||
count = 1;
|
count = 1;
|
||||||
}
|
}
|
||||||
@ -78,7 +78,7 @@ namespace Bit.Admin.Controllers
|
|||||||
public async Task<IActionResult> View(Guid id)
|
public async Task<IActionResult> View(Guid id)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||||
if(organization == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
@ -86,12 +86,12 @@ namespace Bit.Admin.Controllers
|
|||||||
var ciphers = await _cipherRepository.GetManyByOrganizationIdAsync(id);
|
var ciphers = await _cipherRepository.GetManyByOrganizationIdAsync(id);
|
||||||
var collections = await _collectionRepository.GetManyByOrganizationIdAsync(id);
|
var collections = await _collectionRepository.GetManyByOrganizationIdAsync(id);
|
||||||
IEnumerable<Group> groups = null;
|
IEnumerable<Group> groups = null;
|
||||||
if(organization.UseGroups)
|
if (organization.UseGroups)
|
||||||
{
|
{
|
||||||
groups = await _groupRepository.GetManyByOrganizationIdAsync(id);
|
groups = await _groupRepository.GetManyByOrganizationIdAsync(id);
|
||||||
}
|
}
|
||||||
IEnumerable<Policy> policies = null;
|
IEnumerable<Policy> policies = null;
|
||||||
if(organization.UsePolicies)
|
if (organization.UsePolicies)
|
||||||
{
|
{
|
||||||
policies = await _policyRepository.GetManyByOrganizationIdAsync(id);
|
policies = await _policyRepository.GetManyByOrganizationIdAsync(id);
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ namespace Bit.Admin.Controllers
|
|||||||
public async Task<IActionResult> Edit(Guid id)
|
public async Task<IActionResult> Edit(Guid id)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||||
if(organization == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
@ -111,12 +111,12 @@ namespace Bit.Admin.Controllers
|
|||||||
var ciphers = await _cipherRepository.GetManyByOrganizationIdAsync(id);
|
var ciphers = await _cipherRepository.GetManyByOrganizationIdAsync(id);
|
||||||
var collections = await _collectionRepository.GetManyByOrganizationIdAsync(id);
|
var collections = await _collectionRepository.GetManyByOrganizationIdAsync(id);
|
||||||
IEnumerable<Group> groups = null;
|
IEnumerable<Group> groups = null;
|
||||||
if(organization.UseGroups)
|
if (organization.UseGroups)
|
||||||
{
|
{
|
||||||
groups = await _groupRepository.GetManyByOrganizationIdAsync(id);
|
groups = await _groupRepository.GetManyByOrganizationIdAsync(id);
|
||||||
}
|
}
|
||||||
IEnumerable<Policy> policies = null;
|
IEnumerable<Policy> policies = null;
|
||||||
if(organization.UsePolicies)
|
if (organization.UsePolicies)
|
||||||
{
|
{
|
||||||
policies = await _policyRepository.GetManyByOrganizationIdAsync(id);
|
policies = await _policyRepository.GetManyByOrganizationIdAsync(id);
|
||||||
}
|
}
|
||||||
@ -132,7 +132,7 @@ namespace Bit.Admin.Controllers
|
|||||||
public async Task<IActionResult> Edit(Guid id, OrganizationEditModel model)
|
public async Task<IActionResult> Edit(Guid id, OrganizationEditModel model)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||||
if(organization == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
@ -148,7 +148,7 @@ namespace Bit.Admin.Controllers
|
|||||||
public async Task<IActionResult> Delete(Guid id)
|
public async Task<IActionResult> Delete(Guid id)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||||
if(organization != null)
|
if (organization != null)
|
||||||
{
|
{
|
||||||
await _organizationRepository.DeleteAsync(organization);
|
await _organizationRepository.DeleteAsync(organization);
|
||||||
await _applicationCacheService.DeleteOrganizationAbilityAsync(organization.Id);
|
await _applicationCacheService.DeleteOrganizationAbilityAsync(organization.Id);
|
||||||
|
@ -37,7 +37,7 @@ namespace Bit.Admin.Controllers
|
|||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> ChargeBraintree(ChargeBraintreeModel model)
|
public async Task<IActionResult> ChargeBraintree(ChargeBraintreeModel model)
|
||||||
{
|
{
|
||||||
if(!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ namespace Bit.Admin.Controllers
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!transactionResult.IsSuccess())
|
if (!transactionResult.IsSuccess())
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(string.Empty, "Charge failed. " +
|
ModelState.AddModelError(string.Empty, "Charge failed. " +
|
||||||
"Refer to Braintree admin portal for more information.");
|
"Refer to Braintree admin portal for more information.");
|
||||||
@ -98,13 +98,13 @@ namespace Bit.Admin.Controllers
|
|||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> CreateTransaction(CreateUpdateTransactionModel model)
|
public async Task<IActionResult> CreateTransaction(CreateUpdateTransactionModel model)
|
||||||
{
|
{
|
||||||
if(!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
return View("CreateUpdateTransaction", model);
|
return View("CreateUpdateTransaction", model);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _transactionRepository.CreateAsync(model.ToTransaction());
|
await _transactionRepository.CreateAsync(model.ToTransaction());
|
||||||
if(model.UserId.HasValue)
|
if (model.UserId.HasValue)
|
||||||
{
|
{
|
||||||
return RedirectToAction("Edit", "Users", new { id = model.UserId });
|
return RedirectToAction("Edit", "Users", new { id = model.UserId });
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ namespace Bit.Admin.Controllers
|
|||||||
public async Task<IActionResult> EditTransaction(Guid id)
|
public async Task<IActionResult> EditTransaction(Guid id)
|
||||||
{
|
{
|
||||||
var transaction = await _transactionRepository.GetByIdAsync(id);
|
var transaction = await _transactionRepository.GetByIdAsync(id);
|
||||||
if(transaction == null)
|
if (transaction == null)
|
||||||
{
|
{
|
||||||
return RedirectToAction("Index", "Home");
|
return RedirectToAction("Index", "Home");
|
||||||
}
|
}
|
||||||
@ -127,12 +127,12 @@ namespace Bit.Admin.Controllers
|
|||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> EditTransaction(Guid id, CreateUpdateTransactionModel model)
|
public async Task<IActionResult> EditTransaction(Guid id, CreateUpdateTransactionModel model)
|
||||||
{
|
{
|
||||||
if(!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
return View("CreateUpdateTransaction", model);
|
return View("CreateUpdateTransaction", model);
|
||||||
}
|
}
|
||||||
await _transactionRepository.ReplaceAsync(model.ToTransaction(id));
|
await _transactionRepository.ReplaceAsync(model.ToTransaction(id));
|
||||||
if(model.UserId.HasValue)
|
if (model.UserId.HasValue)
|
||||||
{
|
{
|
||||||
return RedirectToAction("Edit", "Users", new { id = model.UserId });
|
return RedirectToAction("Edit", "Users", new { id = model.UserId });
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ namespace Bit.Admin.Controllers
|
|||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> PromoteAdmin(PromoteAdminModel model)
|
public async Task<IActionResult> PromoteAdmin(PromoteAdminModel model)
|
||||||
{
|
{
|
||||||
if(!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
return View("PromoteAdmin", model);
|
return View("PromoteAdmin", model);
|
||||||
}
|
}
|
||||||
@ -158,16 +158,16 @@ namespace Bit.Admin.Controllers
|
|||||||
var orgUsers = await _organizationUserRepository.GetManyByOrganizationAsync(
|
var orgUsers = await _organizationUserRepository.GetManyByOrganizationAsync(
|
||||||
model.OrganizationId.Value, null);
|
model.OrganizationId.Value, null);
|
||||||
var user = orgUsers.FirstOrDefault(u => u.UserId == model.UserId.Value);
|
var user = orgUsers.FirstOrDefault(u => u.UserId == model.UserId.Value);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(nameof(model.UserId), "User Id not found in this organization.");
|
ModelState.AddModelError(nameof(model.UserId), "User Id not found in this organization.");
|
||||||
}
|
}
|
||||||
else if(user.Type != Core.Enums.OrganizationUserType.Admin)
|
else if (user.Type != Core.Enums.OrganizationUserType.Admin)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(nameof(model.UserId), "User is not an admin of this organization.");
|
ModelState.AddModelError(nameof(model.UserId), "User is not an admin of this organization.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
return View("PromoteAdmin", model);
|
return View("PromoteAdmin", model);
|
||||||
}
|
}
|
||||||
|
@ -34,12 +34,12 @@ namespace Bit.Admin.Controllers
|
|||||||
|
|
||||||
public async Task<IActionResult> Index(string email, int page = 1, int count = 25)
|
public async Task<IActionResult> Index(string email, int page = 1, int count = 25)
|
||||||
{
|
{
|
||||||
if(page < 1)
|
if (page < 1)
|
||||||
{
|
{
|
||||||
page = 1;
|
page = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(count < 1)
|
if (count < 1)
|
||||||
{
|
{
|
||||||
count = 1;
|
count = 1;
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ namespace Bit.Admin.Controllers
|
|||||||
public async Task<IActionResult> View(Guid id)
|
public async Task<IActionResult> View(Guid id)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(id);
|
var user = await _userRepository.GetByIdAsync(id);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
@ -72,7 +72,7 @@ namespace Bit.Admin.Controllers
|
|||||||
public async Task<IActionResult> Edit(Guid id)
|
public async Task<IActionResult> Edit(Guid id)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(id);
|
var user = await _userRepository.GetByIdAsync(id);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ namespace Bit.Admin.Controllers
|
|||||||
public async Task<IActionResult> Edit(Guid id, UserEditModel model)
|
public async Task<IActionResult> Edit(Guid id, UserEditModel model)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(id);
|
var user = await _userRepository.GetByIdAsync(id);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ namespace Bit.Admin.Controllers
|
|||||||
public async Task<IActionResult> Delete(Guid id)
|
public async Task<IActionResult> Delete(Guid id)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(id);
|
var user = await _userRepository.GetByIdAsync(id);
|
||||||
if(user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
await _userRepository.DeleteAsync(user);
|
await _userRepository.DeleteAsync(user);
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ namespace Bit.Admin.HostedServices
|
|||||||
var unblockIpQueue = await _client.GetQueueUrlAsync("unblock-ip", cancellationToken);
|
var unblockIpQueue = await _client.GetQueueUrlAsync("unblock-ip", cancellationToken);
|
||||||
var unblockIpQueueUrl = unblockIpQueue.QueueUrl;
|
var unblockIpQueueUrl = unblockIpQueue.QueueUrl;
|
||||||
|
|
||||||
while(!cancellationToken.IsCancellationRequested)
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
var blockMessageResponse = await _client.ReceiveMessageAsync(new ReceiveMessageRequest
|
var blockMessageResponse = await _client.ReceiveMessageAsync(new ReceiveMessageRequest
|
||||||
{
|
{
|
||||||
@ -44,15 +44,15 @@ namespace Bit.Admin.HostedServices
|
|||||||
MaxNumberOfMessages = 10,
|
MaxNumberOfMessages = 10,
|
||||||
WaitTimeSeconds = 15
|
WaitTimeSeconds = 15
|
||||||
}, cancellationToken);
|
}, cancellationToken);
|
||||||
if(blockMessageResponse.Messages.Any())
|
if (blockMessageResponse.Messages.Any())
|
||||||
{
|
{
|
||||||
foreach(var message in blockMessageResponse.Messages)
|
foreach (var message in blockMessageResponse.Messages)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await BlockIpAsync(message.Body, cancellationToken);
|
await BlockIpAsync(message.Body, cancellationToken);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError(e, "Failed to block IP.");
|
_logger.LogError(e, "Failed to block IP.");
|
||||||
}
|
}
|
||||||
@ -66,15 +66,15 @@ namespace Bit.Admin.HostedServices
|
|||||||
MaxNumberOfMessages = 10,
|
MaxNumberOfMessages = 10,
|
||||||
WaitTimeSeconds = 15
|
WaitTimeSeconds = 15
|
||||||
}, cancellationToken);
|
}, cancellationToken);
|
||||||
if(unblockMessageResponse.Messages.Any())
|
if (unblockMessageResponse.Messages.Any())
|
||||||
{
|
{
|
||||||
foreach(var message in unblockMessageResponse.Messages)
|
foreach (var message in unblockMessageResponse.Messages)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await UnblockIpAsync(message.Body, cancellationToken);
|
await UnblockIpAsync(message.Body, cancellationToken);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError(e, "Failed to unblock IP.");
|
_logger.LogError(e, "Failed to unblock IP.");
|
||||||
}
|
}
|
||||||
|
@ -26,18 +26,18 @@ namespace Bit.Admin.HostedServices
|
|||||||
_blockIpQueueClient = new QueueClient(_globalSettings.Storage.ConnectionString, "blockip");
|
_blockIpQueueClient = new QueueClient(_globalSettings.Storage.ConnectionString, "blockip");
|
||||||
_unblockIpQueueClient = new QueueClient(_globalSettings.Storage.ConnectionString, "unblockip");
|
_unblockIpQueueClient = new QueueClient(_globalSettings.Storage.ConnectionString, "unblockip");
|
||||||
|
|
||||||
while(!cancellationToken.IsCancellationRequested)
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
var blockMessages = await _blockIpQueueClient.ReceiveMessagesAsync(maxMessages: 32);
|
var blockMessages = await _blockIpQueueClient.ReceiveMessagesAsync(maxMessages: 32);
|
||||||
if(blockMessages.Value?.Any() ?? false)
|
if (blockMessages.Value?.Any() ?? false)
|
||||||
{
|
{
|
||||||
foreach(var message in blockMessages.Value)
|
foreach (var message in blockMessages.Value)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await BlockIpAsync(message.MessageText, cancellationToken);
|
await BlockIpAsync(message.MessageText, cancellationToken);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError(e, "Failed to block IP.");
|
_logger.LogError(e, "Failed to block IP.");
|
||||||
}
|
}
|
||||||
@ -46,15 +46,15 @@ namespace Bit.Admin.HostedServices
|
|||||||
}
|
}
|
||||||
|
|
||||||
var unblockMessages = await _unblockIpQueueClient.ReceiveMessagesAsync(maxMessages: 32);
|
var unblockMessages = await _unblockIpQueueClient.ReceiveMessagesAsync(maxMessages: 32);
|
||||||
if(unblockMessages.Value?.Any() ?? false)
|
if (unblockMessages.Value?.Any() ?? false)
|
||||||
{
|
{
|
||||||
foreach(var message in unblockMessages.Value)
|
foreach (var message in unblockMessages.Value)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await UnblockIpAsync(message.MessageText, cancellationToken);
|
await UnblockIpAsync(message.MessageText, cancellationToken);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError(e, "Failed to unblock IP.");
|
_logger.LogError(e, "Failed to unblock IP.");
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ namespace Bit.Admin.HostedServices
|
|||||||
|
|
||||||
public async Task StopAsync(CancellationToken cancellationToken)
|
public async Task StopAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if(_executingTask == null)
|
if (_executingTask == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -78,14 +78,14 @@ namespace Bit.Admin.HostedServices
|
|||||||
request.Content = new StringContent(bodyContent, Encoding.UTF8, "application/json");
|
request.Content = new StringContent(bodyContent, Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
var response = await _httpClient.SendAsync(request, cancellationToken);
|
var response = await _httpClient.SendAsync(request, cancellationToken);
|
||||||
if(!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var responseString = await response.Content.ReadAsStringAsync();
|
var responseString = await response.Content.ReadAsStringAsync();
|
||||||
var accessRuleResponse = JsonConvert.DeserializeObject<AccessRuleResponse>(responseString);
|
var accessRuleResponse = JsonConvert.DeserializeObject<AccessRuleResponse>(responseString);
|
||||||
if(!accessRuleResponse.Success)
|
if (!accessRuleResponse.Success)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -95,12 +95,12 @@ namespace Bit.Admin.HostedServices
|
|||||||
|
|
||||||
protected async Task UnblockIpAsync(string message, CancellationToken cancellationToken)
|
protected async Task UnblockIpAsync(string message, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(message))
|
if (string.IsNullOrWhiteSpace(message))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(message.Contains(".") || message.Contains(":"))
|
if (message.Contains(".") || message.Contains(":"))
|
||||||
{
|
{
|
||||||
// IP address messages
|
// IP address messages
|
||||||
var request = new HttpRequestMessage();
|
var request = new HttpRequestMessage();
|
||||||
@ -113,19 +113,19 @@ namespace Bit.Admin.HostedServices
|
|||||||
$"configuration_target=ip&configuration_value={message}");
|
$"configuration_target=ip&configuration_value={message}");
|
||||||
|
|
||||||
var response = await _httpClient.SendAsync(request, cancellationToken);
|
var response = await _httpClient.SendAsync(request, cancellationToken);
|
||||||
if(!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var responseString = await response.Content.ReadAsStringAsync();
|
var responseString = await response.Content.ReadAsStringAsync();
|
||||||
var listResponse = JsonConvert.DeserializeObject<ListResponse>(responseString);
|
var listResponse = JsonConvert.DeserializeObject<ListResponse>(responseString);
|
||||||
if(!listResponse.Success)
|
if (!listResponse.Success)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var rule in listResponse.Result)
|
foreach (var rule in listResponse.Result)
|
||||||
{
|
{
|
||||||
await DeleteAccessRuleAsync(rule.Id, cancellationToken);
|
await DeleteAccessRuleAsync(rule.Id, cancellationToken);
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ namespace Bit.Admin.HostedServices
|
|||||||
await Task.Delay(20000);
|
await Task.Delay(20000);
|
||||||
|
|
||||||
var maxMigrationAttempts = 10;
|
var maxMigrationAttempts = 10;
|
||||||
for(var i = 1; i <= maxMigrationAttempts; i++)
|
for (var i = 1; i <= maxMigrationAttempts; i++)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -41,9 +41,9 @@ namespace Bit.Admin.HostedServices
|
|||||||
// TODO: Maybe flip a flag somewhere to indicate migration is complete??
|
// TODO: Maybe flip a flag somewhere to indicate migration is complete??
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
catch(SqlException e)
|
catch (SqlException e)
|
||||||
{
|
{
|
||||||
if(i >= maxMigrationAttempts)
|
if (i >= maxMigrationAttempts)
|
||||||
{
|
{
|
||||||
_logger.LogError(e, "Database failed to migrate.");
|
_logger.LogError(e, "Database failed to migrate.");
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -30,7 +30,7 @@ namespace Bit.Admin.Jobs
|
|||||||
var timeZone = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
var timeZone = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
||||||
TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time") :
|
TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time") :
|
||||||
TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
|
TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
|
||||||
if(_globalSettings.SelfHosted)
|
if (_globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
timeZone = TimeZoneInfo.Utc;
|
timeZone = TimeZoneInfo.Utc;
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ namespace Bit.Admin.Jobs
|
|||||||
new Tuple<Type, ITrigger>(typeof(DatabaseRebuildlIndexesJob), everySundayAtMidnightTrigger)
|
new Tuple<Type, ITrigger>(typeof(DatabaseRebuildlIndexesJob), everySundayAtMidnightTrigger)
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!_globalSettings.SelfHosted)
|
if (!_globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
jobs.Add(new Tuple<Type, ITrigger>(typeof(AliveJob), everyTopOfTheHourTrigger));
|
jobs.Add(new Tuple<Type, ITrigger>(typeof(AliveJob), everyTopOfTheHourTrigger));
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ namespace Bit.Admin.Jobs
|
|||||||
|
|
||||||
public static void AddJobsServices(IServiceCollection services, bool selfHosted)
|
public static void AddJobsServices(IServiceCollection services, bool selfHosted)
|
||||||
{
|
{
|
||||||
if(!selfHosted)
|
if (!selfHosted)
|
||||||
{
|
{
|
||||||
services.AddTransient<AliveJob>();
|
services.AddTransient<AliveJob>();
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,9 @@ namespace Bit.Admin.Models
|
|||||||
|
|
||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
if(Id != null)
|
if (Id != null)
|
||||||
{
|
{
|
||||||
if(Id.Length != 36 || (Id[0] != 'o' && Id[0] != 'u') ||
|
if (Id.Length != 36 || (Id[0] != 'o' && Id[0] != 'u') ||
|
||||||
!Guid.TryParse(Id.Substring(1, 32), out var guid))
|
!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.");
|
||||||
|
@ -52,7 +52,7 @@ namespace Bit.Admin.Models
|
|||||||
|
|
||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
if((!UserId.HasValue && !OrganizationId.HasValue) || (UserId.HasValue && OrganizationId.HasValue))
|
if ((!UserId.HasValue && !OrganizationId.HasValue) || (UserId.HasValue && OrganizationId.HasValue))
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("Must provide either User Id, or Organization Id.");
|
yield return new ValidationResult("Must provide either User Id, or Organization Id.");
|
||||||
}
|
}
|
||||||
|
@ -21,30 +21,30 @@ namespace Bit.Admin.Models
|
|||||||
|
|
||||||
public string ExceptionToString(JObject e)
|
public string ExceptionToString(JObject e)
|
||||||
{
|
{
|
||||||
if(e == null)
|
if (e == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var val = string.Empty;
|
var val = string.Empty;
|
||||||
if(e["Message"] != null && e["Message"].ToObject<string>() != null)
|
if (e["Message"] != null && e["Message"].ToObject<string>() != null)
|
||||||
{
|
{
|
||||||
val += "Message:\n";
|
val += "Message:\n";
|
||||||
val += e["Message"] + "\n";
|
val += e["Message"] + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(e["StackTrace"] != null && e["StackTrace"].ToObject<string>() != null)
|
if (e["StackTrace"] != null && e["StackTrace"].ToObject<string>() != null)
|
||||||
{
|
{
|
||||||
val += "\nStack Trace:\n";
|
val += "\nStack Trace:\n";
|
||||||
val += e["StackTrace"];
|
val += e["StackTrace"];
|
||||||
}
|
}
|
||||||
else if(e["StackTraceString"] != null && e["StackTraceString"].ToObject<string>() != null)
|
else if (e["StackTraceString"] != null && e["StackTraceString"].ToObject<string>() != null)
|
||||||
{
|
{
|
||||||
val += "\nStack Trace String:\n";
|
val += "\nStack Trace String:\n";
|
||||||
val += e["StackTraceString"];
|
val += e["StackTraceString"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(e["InnerException"] != null && e["InnerException"].ToObject<JObject>() != null)
|
if (e["InnerException"] != null && e["InnerException"].ToObject<JObject>() != null)
|
||||||
{
|
{
|
||||||
val += "\n\n=== Inner Exception ===\n\n";
|
val += "\n\n=== Inner Exception ===\n\n";
|
||||||
val += ExceptionToString(e["InnerException"].ToObject<JObject>());
|
val += ExceptionToString(e["InnerException"].ToObject<JObject>());
|
||||||
|
@ -22,7 +22,7 @@ namespace Bit.Admin
|
|||||||
logging.AddSerilog(hostingContext, e =>
|
logging.AddSerilog(hostingContext, e =>
|
||||||
{
|
{
|
||||||
var context = e.Properties["SourceContext"].ToString();
|
var context = e.Properties["SourceContext"].ToString();
|
||||||
if(e.Properties.ContainsKey("RequestPath") &&
|
if (e.Properties.ContainsKey("RequestPath") &&
|
||||||
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
|
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
|
||||||
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
||||||
{
|
{
|
||||||
|
@ -53,7 +53,7 @@ namespace Bit.Admin
|
|||||||
{
|
{
|
||||||
options.ValidationInterval = TimeSpan.FromMinutes(5);
|
options.ValidationInterval = TimeSpan.FromMinutes(5);
|
||||||
});
|
});
|
||||||
if(globalSettings.SelfHosted)
|
if (globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
services.ConfigureApplicationCookie(options =>
|
services.ConfigureApplicationCookie(options =>
|
||||||
{
|
{
|
||||||
@ -75,17 +75,17 @@ namespace Bit.Admin
|
|||||||
// Jobs service
|
// Jobs service
|
||||||
Jobs.JobsHostedService.AddJobsServices(services, globalSettings.SelfHosted);
|
Jobs.JobsHostedService.AddJobsServices(services, globalSettings.SelfHosted);
|
||||||
services.AddHostedService<Jobs.JobsHostedService>();
|
services.AddHostedService<Jobs.JobsHostedService>();
|
||||||
if(globalSettings.SelfHosted)
|
if (globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
services.AddHostedService<HostedServices.DatabaseMigrationHostedService>();
|
services.AddHostedService<HostedServices.DatabaseMigrationHostedService>();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(CoreHelpers.SettingHasValue(globalSettings.Storage.ConnectionString))
|
if (CoreHelpers.SettingHasValue(globalSettings.Storage.ConnectionString))
|
||||||
{
|
{
|
||||||
services.AddHostedService<HostedServices.AzureQueueBlockIpHostedService>();
|
services.AddHostedService<HostedServices.AzureQueueBlockIpHostedService>();
|
||||||
}
|
}
|
||||||
else if(CoreHelpers.SettingHasValue(globalSettings.Amazon?.AccessKeySecret))
|
else if (CoreHelpers.SettingHasValue(globalSettings.Amazon?.AccessKeySecret))
|
||||||
{
|
{
|
||||||
services.AddHostedService<HostedServices.AmazonSqsBlockIpHostedService>();
|
services.AddHostedService<HostedServices.AmazonSqsBlockIpHostedService>();
|
||||||
}
|
}
|
||||||
@ -100,13 +100,13 @@ namespace Bit.Admin
|
|||||||
{
|
{
|
||||||
app.UseSerilog(env, appLifetime, globalSettings);
|
app.UseSerilog(env, appLifetime, globalSettings);
|
||||||
|
|
||||||
if(globalSettings.SelfHosted)
|
if (globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
app.UsePathBase("/admin");
|
app.UsePathBase("/admin");
|
||||||
app.UseForwardedHeaders(globalSettings);
|
app.UseForwardedHeaders(globalSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(env.IsDevelopment())
|
if (env.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.UseDeveloperExceptionPage();
|
app.UseDeveloperExceptionPage();
|
||||||
}
|
}
|
||||||
|
@ -31,33 +31,33 @@ namespace Bit.Admin.TagHelpers
|
|||||||
|
|
||||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||||
{
|
{
|
||||||
if(context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(output == null)
|
if (output == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(output));
|
throw new ArgumentNullException(nameof(output));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ActiveAction == null && ActiveController == null)
|
if (ActiveAction == null && ActiveController == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var descriptor = ViewContext.ActionDescriptor as ControllerActionDescriptor;
|
var descriptor = ViewContext.ActionDescriptor as ControllerActionDescriptor;
|
||||||
if(descriptor == null)
|
if (descriptor == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var controllerMatch = ActiveMatch(ActiveController, descriptor.ControllerName);
|
var controllerMatch = ActiveMatch(ActiveController, descriptor.ControllerName);
|
||||||
var actionMatch = ActiveMatch(ActiveAction, descriptor.ActionName);
|
var actionMatch = ActiveMatch(ActiveAction, descriptor.ActionName);
|
||||||
if(controllerMatch && actionMatch)
|
if (controllerMatch && actionMatch)
|
||||||
{
|
{
|
||||||
var classValue = "active";
|
var classValue = "active";
|
||||||
if(output.Attributes["class"] != null)
|
if (output.Attributes["class"] != null)
|
||||||
{
|
{
|
||||||
classValue += " " + output.Attributes["class"].Value;
|
classValue += " " + output.Attributes["class"].Value;
|
||||||
output.Attributes.Remove(output.Attributes["class"]);
|
output.Attributes.Remove(output.Attributes["class"]);
|
||||||
|
@ -21,17 +21,17 @@ namespace Bit.Admin.TagHelpers
|
|||||||
|
|
||||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||||
{
|
{
|
||||||
if(context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(output == null)
|
if (output == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(output));
|
throw new ArgumentNullException(nameof(output));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Selected)
|
if (Selected)
|
||||||
{
|
{
|
||||||
output.Attributes.Add("selected", "selected");
|
output.Attributes.Add("selected", "selected");
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<PreloginResponseModel> PostPrelogin([FromBody]PreloginRequestModel model)
|
public async Task<PreloginResponseModel> PostPrelogin([FromBody]PreloginRequestModel model)
|
||||||
{
|
{
|
||||||
var kdfInformation = await _userRepository.GetKdfInformationByEmailAsync(model.Email);
|
var kdfInformation = await _userRepository.GetKdfInformationByEmailAsync(model.Email);
|
||||||
if(kdfInformation == null)
|
if (kdfInformation == null)
|
||||||
{
|
{
|
||||||
kdfInformation = new UserKdfInformation
|
kdfInformation = new UserKdfInformation
|
||||||
{
|
{
|
||||||
@ -70,12 +70,12 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var result = await _userService.RegisterUserAsync(model.ToUser(), model.MasterPasswordHash,
|
var result = await _userService.RegisterUserAsync(model.ToUser(), model.MasterPasswordHash,
|
||||||
model.Token, model.OrganizationUserId);
|
model.Token, model.OrganizationUserId);
|
||||||
if(result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var error in result.Errors.Where(e => e.Code != "DuplicateUserName"))
|
foreach (var error in result.Errors.Where(e => e.Code != "DuplicateUserName"))
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(string.Empty, error.Description);
|
ModelState.AddModelError(string.Empty, error.Description);
|
||||||
}
|
}
|
||||||
@ -95,12 +95,12 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostEmailToken([FromBody]EmailTokenRequestModel model)
|
public async Task PostEmailToken([FromBody]EmailTokenRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
if (!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
||||||
{
|
{
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
|
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
|
||||||
@ -113,19 +113,19 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostEmail([FromBody]EmailRequestModel model)
|
public async Task PostEmail([FromBody]EmailRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await _userService.ChangeEmailAsync(user, model.MasterPasswordHash, model.NewEmail,
|
var result = await _userService.ChangeEmailAsync(user, model.MasterPasswordHash, model.NewEmail,
|
||||||
model.NewMasterPasswordHash, model.Token, model.Key);
|
model.NewMasterPasswordHash, model.Token, model.Key);
|
||||||
if(result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var error in result.Errors)
|
foreach (var error in result.Errors)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(string.Empty, error.Description);
|
ModelState.AddModelError(string.Empty, error.Description);
|
||||||
}
|
}
|
||||||
@ -138,7 +138,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostVerifyEmail()
|
public async Task PostVerifyEmail()
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -151,17 +151,17 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostVerifyEmailToken([FromBody]VerifyEmailRequestModel model)
|
public async Task PostVerifyEmailToken([FromBody]VerifyEmailRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByIdAsync(new Guid(model.UserId));
|
var user = await _userService.GetUserByIdAsync(new Guid(model.UserId));
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
var result = await _userService.ConfirmEmailAsync(user, model.Token);
|
var result = await _userService.ConfirmEmailAsync(user, model.Token);
|
||||||
if(result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var error in result.Errors)
|
foreach (var error in result.Errors)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(string.Empty, error.Description);
|
ModelState.AddModelError(string.Empty, error.Description);
|
||||||
}
|
}
|
||||||
@ -174,19 +174,19 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostPassword([FromBody]PasswordRequestModel model)
|
public async Task PostPassword([FromBody]PasswordRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await _userService.ChangePasswordAsync(user, model.MasterPasswordHash,
|
var result = await _userService.ChangePasswordAsync(user, model.MasterPasswordHash,
|
||||||
model.NewMasterPasswordHash, model.Key);
|
model.NewMasterPasswordHash, model.Key);
|
||||||
if(result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var error in result.Errors)
|
foreach (var error in result.Errors)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(string.Empty, error.Description);
|
ModelState.AddModelError(string.Empty, error.Description);
|
||||||
}
|
}
|
||||||
@ -199,19 +199,19 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostKdf([FromBody]KdfRequestModel model)
|
public async Task PostKdf([FromBody]KdfRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await _userService.ChangeKdfAsync(user, model.MasterPasswordHash,
|
var result = await _userService.ChangeKdfAsync(user, model.MasterPasswordHash,
|
||||||
model.NewMasterPasswordHash, model.Key, model.Kdf.Value, model.KdfIterations.Value);
|
model.NewMasterPasswordHash, model.Key, model.Kdf.Value, model.KdfIterations.Value);
|
||||||
if(result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var error in result.Errors)
|
foreach (var error in result.Errors)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(string.Empty, error.Description);
|
ModelState.AddModelError(string.Empty, error.Description);
|
||||||
}
|
}
|
||||||
@ -224,7 +224,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostKey([FromBody]UpdateKeyRequestModel model)
|
public async Task PostKey([FromBody]UpdateKeyRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -232,9 +232,9 @@ namespace Bit.Api.Controllers
|
|||||||
var existingCiphers = await _cipherRepository.GetManyByUserIdAsync(user.Id);
|
var existingCiphers = await _cipherRepository.GetManyByUserIdAsync(user.Id);
|
||||||
var ciphersDict = model.Ciphers?.ToDictionary(c => c.Id.Value);
|
var ciphersDict = model.Ciphers?.ToDictionary(c => c.Id.Value);
|
||||||
var ciphers = new List<Cipher>();
|
var ciphers = new List<Cipher>();
|
||||||
if(existingCiphers.Any() && ciphersDict != null)
|
if (existingCiphers.Any() && ciphersDict != null)
|
||||||
{
|
{
|
||||||
foreach(var cipher in existingCiphers.Where(c => ciphersDict.ContainsKey(c.Id)))
|
foreach (var cipher in existingCiphers.Where(c => ciphersDict.ContainsKey(c.Id)))
|
||||||
{
|
{
|
||||||
ciphers.Add(ciphersDict[cipher.Id].ToCipher(cipher));
|
ciphers.Add(ciphersDict[cipher.Id].ToCipher(cipher));
|
||||||
}
|
}
|
||||||
@ -243,9 +243,9 @@ namespace Bit.Api.Controllers
|
|||||||
var existingFolders = await _folderRepository.GetManyByUserIdAsync(user.Id);
|
var existingFolders = await _folderRepository.GetManyByUserIdAsync(user.Id);
|
||||||
var foldersDict = model.Folders?.ToDictionary(f => f.Id);
|
var foldersDict = model.Folders?.ToDictionary(f => f.Id);
|
||||||
var folders = new List<Folder>();
|
var folders = new List<Folder>();
|
||||||
if(existingFolders.Any() && foldersDict != null)
|
if (existingFolders.Any() && foldersDict != null)
|
||||||
{
|
{
|
||||||
foreach(var folder in existingFolders.Where(f => foldersDict.ContainsKey(f.Id)))
|
foreach (var folder in existingFolders.Where(f => foldersDict.ContainsKey(f.Id)))
|
||||||
{
|
{
|
||||||
folders.Add(foldersDict[folder.Id].ToFolder(folder));
|
folders.Add(foldersDict[folder.Id].ToFolder(folder));
|
||||||
}
|
}
|
||||||
@ -259,12 +259,12 @@ namespace Bit.Api.Controllers
|
|||||||
ciphers,
|
ciphers,
|
||||||
folders);
|
folders);
|
||||||
|
|
||||||
if(result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var error in result.Errors)
|
foreach (var error in result.Errors)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(string.Empty, error.Description);
|
ModelState.AddModelError(string.Empty, error.Description);
|
||||||
}
|
}
|
||||||
@ -277,18 +277,18 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostSecurityStamp([FromBody]SecurityStampRequestModel model)
|
public async Task PostSecurityStamp([FromBody]SecurityStampRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await _userService.RefreshSecurityStampAsync(user, model.MasterPasswordHash);
|
var result = await _userService.RefreshSecurityStampAsync(user, model.MasterPasswordHash);
|
||||||
if(result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var error in result.Errors)
|
foreach (var error in result.Errors)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(string.Empty, error.Description);
|
ModelState.AddModelError(string.Empty, error.Description);
|
||||||
}
|
}
|
||||||
@ -301,7 +301,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<ProfileResponseModel> GetProfile()
|
public async Task<ProfileResponseModel> GetProfile()
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -328,7 +328,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<ProfileResponseModel> PutProfile([FromBody]UpdateProfileRequestModel model)
|
public async Task<ProfileResponseModel> PutProfile([FromBody]UpdateProfileRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -343,7 +343,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User);
|
var userId = _userService.GetProperUserId(User);
|
||||||
long? revisionDate = null;
|
long? revisionDate = null;
|
||||||
if(userId.HasValue)
|
if (userId.HasValue)
|
||||||
{
|
{
|
||||||
var date = await _userService.GetAccountRevisionDateByIdAsync(userId.Value);
|
var date = await _userService.GetAccountRevisionDateByIdAsync(userId.Value);
|
||||||
revisionDate = CoreHelpers.ToEpocMilliseconds(date);
|
revisionDate = CoreHelpers.ToEpocMilliseconds(date);
|
||||||
@ -356,7 +356,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<KeysResponseModel> PostKeys([FromBody]KeysRequestModel model)
|
public async Task<KeysResponseModel> PostKeys([FromBody]KeysRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -369,7 +369,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<KeysResponseModel> GetKeys()
|
public async Task<KeysResponseModel> GetKeys()
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -382,12 +382,12 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task Delete([FromBody]DeleteAccountRequestModel model)
|
public async Task Delete([FromBody]DeleteAccountRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
if (!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
||||||
{
|
{
|
||||||
ModelState.AddModelError("MasterPasswordHash", "Invalid password.");
|
ModelState.AddModelError("MasterPasswordHash", "Invalid password.");
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
@ -395,12 +395,12 @@ namespace Bit.Api.Controllers
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var result = await _userService.DeleteAsync(user);
|
var result = await _userService.DeleteAsync(user);
|
||||||
if(result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var error in result.Errors)
|
foreach (var error in result.Errors)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(string.Empty, error.Description);
|
ModelState.AddModelError(string.Empty, error.Description);
|
||||||
}
|
}
|
||||||
@ -421,18 +421,18 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostDeleteRecoverToken([FromBody]VerifyDeleteRecoverRequestModel model)
|
public async Task PostDeleteRecoverToken([FromBody]VerifyDeleteRecoverRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByIdAsync(new Guid(model.UserId));
|
var user = await _userService.GetUserByIdAsync(new Guid(model.UserId));
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await _userService.DeleteAsync(user, model.Token);
|
var result = await _userService.DeleteAsync(user, model.Token);
|
||||||
if(result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var error in result.Errors)
|
foreach (var error in result.Errors)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(string.Empty, error.Description);
|
ModelState.AddModelError(string.Empty, error.Description);
|
||||||
}
|
}
|
||||||
@ -445,7 +445,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostIapCheck([FromBody]IapCheckRequestModel model)
|
public async Task PostIapCheck([FromBody]IapCheckRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -456,19 +456,19 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<PaymentResponseModel> PostPremium(PremiumRequestModel model)
|
public async Task<PaymentResponseModel> PostPremium(PremiumRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var valid = model.Validate(_globalSettings);
|
var valid = model.Validate(_globalSettings);
|
||||||
UserLicense license = null;
|
UserLicense license = null;
|
||||||
if(valid && _globalSettings.SelfHosted)
|
if (valid && _globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
license = await ApiHelpers.ReadJsonFileFromBody<UserLicense>(HttpContext, model.License);
|
license = await ApiHelpers.ReadJsonFileFromBody<UserLicense>(HttpContext, model.License);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!valid || (_globalSettings.SelfHosted && license == null))
|
if (!valid || (_globalSettings.SelfHosted && license == null))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Invalid license.");
|
throw new BadRequestException("Invalid license.");
|
||||||
}
|
}
|
||||||
@ -489,7 +489,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<BillingResponseModel> GetBilling()
|
public async Task<BillingResponseModel> GetBilling()
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -502,18 +502,18 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<SubscriptionResponseModel> GetSubscription()
|
public async Task<SubscriptionResponseModel> GetSubscription()
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!_globalSettings.SelfHosted && user.Gateway != null)
|
if (!_globalSettings.SelfHosted && user.Gateway != null)
|
||||||
{
|
{
|
||||||
var subscriptionInfo = await _paymentService.GetSubscriptionAsync(user);
|
var subscriptionInfo = await _paymentService.GetSubscriptionAsync(user);
|
||||||
var license = await _userService.GenerateLicenseAsync(user, subscriptionInfo);
|
var license = await _userService.GenerateLicenseAsync(user, subscriptionInfo);
|
||||||
return new SubscriptionResponseModel(user, subscriptionInfo, license);
|
return new SubscriptionResponseModel(user, subscriptionInfo, license);
|
||||||
}
|
}
|
||||||
else if(!_globalSettings.SelfHosted)
|
else if (!_globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
var license = await _userService.GenerateLicenseAsync(user);
|
var license = await _userService.GenerateLicenseAsync(user);
|
||||||
return new SubscriptionResponseModel(user, license);
|
return new SubscriptionResponseModel(user, license);
|
||||||
@ -529,7 +529,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostPayment([FromBody]PaymentRequestModel model)
|
public async Task PostPayment([FromBody]PaymentRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -542,7 +542,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<PaymentResponseModel> PostStorage([FromBody]StorageRequestModel model)
|
public async Task<PaymentResponseModel> PostStorage([FromBody]StorageRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -560,13 +560,13 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostLicense(LicenseRequestModel model)
|
public async Task PostLicense(LicenseRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var license = await ApiHelpers.ReadJsonFileFromBody<UserLicense>(HttpContext, model.License);
|
var license = await ApiHelpers.ReadJsonFileFromBody<UserLicense>(HttpContext, model.License);
|
||||||
if(license == null)
|
if (license == null)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Invalid license");
|
throw new BadRequestException("Invalid license");
|
||||||
}
|
}
|
||||||
@ -579,7 +579,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostCancel()
|
public async Task PostCancel()
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -592,7 +592,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostReinstate()
|
public async Task PostReinstate()
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id), userId);
|
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id), userId);
|
||||||
if(cipher == null)
|
if (cipher == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<CipherMiniResponseModel> GetAdmin(string id)
|
public async Task<CipherMiniResponseModel> GetAdmin(string id)
|
||||||
{
|
{
|
||||||
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(new Guid(id));
|
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(new Guid(id));
|
||||||
if(cipher == null || !cipher.OrganizationId.HasValue ||
|
if (cipher == null || !cipher.OrganizationId.HasValue ||
|
||||||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
|
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -75,7 +75,7 @@ namespace Bit.Api.Controllers
|
|||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipherId = new Guid(id);
|
var cipherId = new Guid(id);
|
||||||
var cipher = await _cipherRepository.GetByIdAsync(cipherId, userId);
|
var cipher = await _cipherRepository.GetByIdAsync(cipherId, userId);
|
||||||
if(cipher == null)
|
if (cipher == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -92,7 +92,7 @@ namespace Bit.Api.Controllers
|
|||||||
// TODO: Use hasOrgs proper for cipher listing here?
|
// TODO: Use hasOrgs proper for cipher listing here?
|
||||||
var ciphers = await _cipherRepository.GetManyByUserIdAsync(userId, true || hasOrgs);
|
var ciphers = await _cipherRepository.GetManyByUserIdAsync(userId, true || hasOrgs);
|
||||||
Dictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict = null;
|
Dictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict = null;
|
||||||
if(hasOrgs)
|
if (hasOrgs)
|
||||||
{
|
{
|
||||||
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdAsync(userId);
|
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdAsync(userId);
|
||||||
collectionCiphersGroupDict = collectionCiphers.GroupBy(c => c.CipherId).ToDictionary(s => s.Key);
|
collectionCiphersGroupDict = collectionCiphers.GroupBy(c => c.CipherId).ToDictionary(s => s.Key);
|
||||||
@ -108,7 +108,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipher = model.ToCipherDetails(userId);
|
var cipher = model.ToCipherDetails(userId);
|
||||||
if(cipher.OrganizationId.HasValue && !_currentContext.OrganizationUser(cipher.OrganizationId.Value))
|
if (cipher.OrganizationId.HasValue && !_currentContext.OrganizationUser(cipher.OrganizationId.Value))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipher = model.Cipher.ToCipherDetails(userId);
|
var cipher = model.Cipher.ToCipherDetails(userId);
|
||||||
if(cipher.OrganizationId.HasValue && !_currentContext.OrganizationUser(cipher.OrganizationId.Value))
|
if (cipher.OrganizationId.HasValue && !_currentContext.OrganizationUser(cipher.OrganizationId.Value))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -137,7 +137,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<CipherMiniResponseModel> PostAdmin([FromBody]CipherCreateRequestModel model)
|
public async Task<CipherMiniResponseModel> PostAdmin([FromBody]CipherCreateRequestModel model)
|
||||||
{
|
{
|
||||||
var cipher = model.Cipher.ToOrganizationCipher();
|
var cipher = model.Cipher.ToOrganizationCipher();
|
||||||
if(!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
|
if (!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -155,14 +155,14 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id), userId);
|
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id), userId);
|
||||||
if(cipher == null)
|
if (cipher == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var modelOrgId = string.IsNullOrWhiteSpace(model.OrganizationId) ?
|
var modelOrgId = string.IsNullOrWhiteSpace(model.OrganizationId) ?
|
||||||
(Guid?)null : new Guid(model.OrganizationId);
|
(Guid?)null : new Guid(model.OrganizationId);
|
||||||
if(cipher.OrganizationId != modelOrgId)
|
if (cipher.OrganizationId != modelOrgId)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Organization mismatch. Re-sync if you recently shared this item, " +
|
throw new BadRequestException("Organization mismatch. Re-sync if you recently shared this item, " +
|
||||||
"then try again.");
|
"then try again.");
|
||||||
@ -180,7 +180,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(new Guid(id));
|
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(new Guid(id));
|
||||||
if(cipher == null || !cipher.OrganizationId.HasValue ||
|
if (cipher == null || !cipher.OrganizationId.HasValue ||
|
||||||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
|
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -200,7 +200,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var orgIdGuid = new Guid(organizationId);
|
var orgIdGuid = new Guid(organizationId);
|
||||||
if(!_currentContext.OrganizationAdmin(orgIdGuid))
|
if (!_currentContext.OrganizationAdmin(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -218,7 +218,7 @@ namespace Bit.Api.Controllers
|
|||||||
[HttpPost("import")]
|
[HttpPost("import")]
|
||||||
public async Task PostImport([FromBody]ImportCiphersRequestModel model)
|
public async Task PostImport([FromBody]ImportCiphersRequestModel model)
|
||||||
{
|
{
|
||||||
if(!_globalSettings.SelfHosted &&
|
if (!_globalSettings.SelfHosted &&
|
||||||
(model.Ciphers.Count() > 6000 || model.FolderRelationships.Count() > 6000 ||
|
(model.Ciphers.Count() > 6000 || model.FolderRelationships.Count() > 6000 ||
|
||||||
model.Folders.Count() > 1000))
|
model.Folders.Count() > 1000))
|
||||||
{
|
{
|
||||||
@ -235,7 +235,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostImport([FromQuery]string organizationId,
|
public async Task PostImport([FromQuery]string organizationId,
|
||||||
[FromBody]ImportOrganizationCiphersRequestModel model)
|
[FromBody]ImportOrganizationCiphersRequestModel model)
|
||||||
{
|
{
|
||||||
if(!_globalSettings.SelfHosted &&
|
if (!_globalSettings.SelfHosted &&
|
||||||
(model.Ciphers.Count() > 6000 || model.CollectionRelationships.Count() > 12000 ||
|
(model.Ciphers.Count() > 6000 || model.CollectionRelationships.Count() > 12000 ||
|
||||||
model.Collections.Count() > 1000))
|
model.Collections.Count() > 1000))
|
||||||
{
|
{
|
||||||
@ -243,7 +243,7 @@ namespace Bit.Api.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
var orgId = new Guid(organizationId);
|
var orgId = new Guid(organizationId);
|
||||||
if(!_currentContext.OrganizationAdmin(orgId))
|
if (!_currentContext.OrganizationAdmin(orgId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -270,7 +270,7 @@ namespace Bit.Api.Controllers
|
|||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipherId = new Guid(id);
|
var cipherId = new Guid(id);
|
||||||
var cipher = await _cipherRepository.GetByIdAsync(cipherId);
|
var cipher = await _cipherRepository.GetByIdAsync(cipherId);
|
||||||
if(cipher == null || cipher.UserId != userId ||
|
if (cipher == null || cipher.UserId != userId ||
|
||||||
!_currentContext.OrganizationUser(new Guid(model.Cipher.OrganizationId)))
|
!_currentContext.OrganizationUser(new Guid(model.Cipher.OrganizationId)))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -291,7 +291,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id), userId);
|
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id), userId);
|
||||||
if(cipher == null || !cipher.OrganizationId.HasValue ||
|
if (cipher == null || !cipher.OrganizationId.HasValue ||
|
||||||
!_currentContext.OrganizationUser(cipher.OrganizationId.Value))
|
!_currentContext.OrganizationUser(cipher.OrganizationId.Value))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -307,7 +307,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
|
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
|
||||||
if(cipher == null || !cipher.OrganizationId.HasValue ||
|
if (cipher == null || !cipher.OrganizationId.HasValue ||
|
||||||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
|
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -323,7 +323,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id), userId);
|
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id), userId);
|
||||||
if(cipher == null)
|
if (cipher == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -337,7 +337,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
|
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
|
||||||
if(cipher == null || !cipher.OrganizationId.HasValue ||
|
if (cipher == null || !cipher.OrganizationId.HasValue ||
|
||||||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
|
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -350,7 +350,7 @@ namespace Bit.Api.Controllers
|
|||||||
[HttpPost("delete")]
|
[HttpPost("delete")]
|
||||||
public async Task DeleteMany([FromBody]CipherBulkDeleteRequestModel model)
|
public async Task DeleteMany([FromBody]CipherBulkDeleteRequestModel model)
|
||||||
{
|
{
|
||||||
if(!_globalSettings.SelfHosted && model.Ids.Count() > 500)
|
if (!_globalSettings.SelfHosted && model.Ids.Count() > 500)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("You can only delete up to 500 items at a time. " +
|
throw new BadRequestException("You can only delete up to 500 items at a time. " +
|
||||||
"Consider using the \"Purge Vault\" option instead.");
|
"Consider using the \"Purge Vault\" option instead.");
|
||||||
@ -364,7 +364,7 @@ namespace Bit.Api.Controllers
|
|||||||
[HttpPost("move")]
|
[HttpPost("move")]
|
||||||
public async Task MoveMany([FromBody]CipherBulkMoveRequestModel model)
|
public async Task MoveMany([FromBody]CipherBulkMoveRequestModel model)
|
||||||
{
|
{
|
||||||
if(!_globalSettings.SelfHosted && model.Ids.Count() > 500)
|
if (!_globalSettings.SelfHosted && model.Ids.Count() > 500)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("You can only move up to 500 items at a time.");
|
throw new BadRequestException("You can only move up to 500 items at a time.");
|
||||||
}
|
}
|
||||||
@ -379,7 +379,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PutShareMany([FromBody]CipherBulkShareRequestModel model)
|
public async Task PutShareMany([FromBody]CipherBulkShareRequestModel model)
|
||||||
{
|
{
|
||||||
var organizationId = new Guid(model.Ciphers.First().OrganizationId);
|
var organizationId = new Guid(model.Ciphers.First().OrganizationId);
|
||||||
if(!_currentContext.OrganizationUser(organizationId))
|
if (!_currentContext.OrganizationUser(organizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -389,9 +389,9 @@ namespace Bit.Api.Controllers
|
|||||||
var ciphersDict = ciphers.ToDictionary(c => c.Id);
|
var ciphersDict = ciphers.ToDictionary(c => c.Id);
|
||||||
|
|
||||||
var shareCiphers = new List<Cipher>();
|
var shareCiphers = new List<Cipher>();
|
||||||
foreach(var cipher in model.Ciphers)
|
foreach (var cipher in model.Ciphers)
|
||||||
{
|
{
|
||||||
if(!ciphersDict.ContainsKey(cipher.Id.Value))
|
if (!ciphersDict.ContainsKey(cipher.Id.Value))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Trying to share ciphers that you do not own.");
|
throw new BadRequestException("Trying to share ciphers that you do not own.");
|
||||||
}
|
}
|
||||||
@ -407,26 +407,26 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostPurge([FromBody]CipherPurgeRequestModel model, string organizationId = null)
|
public async Task PostPurge([FromBody]CipherPurgeRequestModel model, string organizationId = null)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
if (!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
||||||
{
|
{
|
||||||
ModelState.AddModelError("MasterPasswordHash", "Invalid password.");
|
ModelState.AddModelError("MasterPasswordHash", "Invalid password.");
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
throw new BadRequestException(ModelState);
|
throw new BadRequestException(ModelState);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(organizationId))
|
if (string.IsNullOrWhiteSpace(organizationId))
|
||||||
{
|
{
|
||||||
await _cipherRepository.DeleteByUserIdAsync(user.Id);
|
await _cipherRepository.DeleteByUserIdAsync(user.Id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var orgId = new Guid(organizationId);
|
var orgId = new Guid(organizationId);
|
||||||
if(!_currentContext.OrganizationAdmin(orgId))
|
if (!_currentContext.OrganizationAdmin(orgId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -444,7 +444,7 @@ namespace Bit.Api.Controllers
|
|||||||
var idGuid = new Guid(id);
|
var idGuid = new Guid(id);
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipher = await _cipherRepository.GetByIdAsync(idGuid, userId);
|
var cipher = await _cipherRepository.GetByIdAsync(idGuid, userId);
|
||||||
if(cipher == null)
|
if (cipher == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -468,7 +468,7 @@ namespace Bit.Api.Controllers
|
|||||||
var idGuid = new Guid(id);
|
var idGuid = new Guid(id);
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(idGuid);
|
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(idGuid);
|
||||||
if(cipher == null || !cipher.OrganizationId.HasValue ||
|
if (cipher == null || !cipher.OrganizationId.HasValue ||
|
||||||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
|
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -492,7 +492,7 @@ namespace Bit.Api.Controllers
|
|||||||
|
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
|
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
|
||||||
if(cipher == null || cipher.UserId != userId || !_currentContext.OrganizationUser(organizationId))
|
if (cipher == null || cipher.UserId != userId || !_currentContext.OrganizationUser(organizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -511,7 +511,7 @@ namespace Bit.Api.Controllers
|
|||||||
var idGuid = new Guid(id);
|
var idGuid = new Guid(id);
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipher = await _cipherRepository.GetByIdAsync(idGuid, userId);
|
var cipher = await _cipherRepository.GetByIdAsync(idGuid, userId);
|
||||||
if(cipher == null)
|
if (cipher == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -526,7 +526,7 @@ namespace Bit.Api.Controllers
|
|||||||
var idGuid = new Guid(id);
|
var idGuid = new Guid(id);
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var cipher = await _cipherRepository.GetByIdAsync(idGuid);
|
var cipher = await _cipherRepository.GetByIdAsync(idGuid);
|
||||||
if(cipher == null || !cipher.OrganizationId.HasValue ||
|
if (cipher == null || !cipher.OrganizationId.HasValue ||
|
||||||
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
|
!_currentContext.OrganizationAdmin(cipher.OrganizationId.Value))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -537,12 +537,12 @@ namespace Bit.Api.Controllers
|
|||||||
|
|
||||||
private void ValidateAttachment()
|
private void ValidateAttachment()
|
||||||
{
|
{
|
||||||
if(!Request?.ContentType.Contains("multipart/") ?? true)
|
if (!Request?.ContentType.Contains("multipart/") ?? true)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Invalid content.");
|
throw new BadRequestException("Invalid content.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Request.ContentLength > 105906176) // 101 MB, give em' 1 extra MB for cushion
|
if (Request.ContentLength > 105906176) // 101 MB, give em' 1 extra MB for cushion
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Max file size is 100 MB.");
|
throw new BadRequestException("Max file size is 100 MB.");
|
||||||
}
|
}
|
||||||
|
@ -45,16 +45,16 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<CollectionGroupDetailsResponseModel> GetDetails(string orgId, string id)
|
public async Task<CollectionGroupDetailsResponseModel> GetDetails(string orgId, string id)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(orgId);
|
var orgIdGuid = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationManager(orgIdGuid))
|
if (!_currentContext.OrganizationManager(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var idGuid = new Guid(id);
|
var idGuid = new Guid(id);
|
||||||
if(_currentContext.OrganizationAdmin(orgIdGuid))
|
if (_currentContext.OrganizationAdmin(orgIdGuid))
|
||||||
{
|
{
|
||||||
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(idGuid);
|
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(idGuid);
|
||||||
if(collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgIdGuid)
|
if (collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgIdGuid)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -64,7 +64,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(idGuid,
|
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(idGuid,
|
||||||
_currentContext.UserId.Value);
|
_currentContext.UserId.Value);
|
||||||
if(collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgIdGuid)
|
if (collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgIdGuid)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -76,7 +76,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<ListResponseModel<CollectionResponseModel>> Get(string orgId)
|
public async Task<ListResponseModel<CollectionResponseModel>> Get(string orgId)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(orgId);
|
var orgIdGuid = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationAdmin(orgIdGuid))
|
if (!_currentContext.OrganizationAdmin(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<CollectionResponseModel> Post(string orgId, [FromBody]CollectionRequestModel model)
|
public async Task<CollectionResponseModel> Post(string orgId, [FromBody]CollectionRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(orgId);
|
var orgIdGuid = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationManager(orgIdGuid))
|
if (!_currentContext.OrganizationManager(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -154,7 +154,7 @@ namespace Bit.Api.Controllers
|
|||||||
|
|
||||||
private async Task<Collection> GetCollectionAsync(Guid id, Guid orgId)
|
private async Task<Collection> GetCollectionAsync(Guid id, Guid orgId)
|
||||||
{
|
{
|
||||||
if(!_currentContext.OrganizationManager(orgId))
|
if (!_currentContext.OrganizationManager(orgId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -162,7 +162,7 @@ namespace Bit.Api.Controllers
|
|||||||
var collection = _currentContext.OrganizationAdmin(orgId) ?
|
var collection = _currentContext.OrganizationAdmin(orgId) ?
|
||||||
await _collectionRepository.GetByIdAsync(id) :
|
await _collectionRepository.GetByIdAsync(id) :
|
||||||
await _collectionRepository.GetByIdAsync(id, _currentContext.UserId.Value);
|
await _collectionRepository.GetByIdAsync(id, _currentContext.UserId.Value);
|
||||||
if(collection == null || collection.OrganizationId != orgId)
|
if (collection == null || collection.OrganizationId != orgId)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<DeviceResponseModel> Get(string id)
|
public async Task<DeviceResponseModel> Get(string id)
|
||||||
{
|
{
|
||||||
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
|
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
|
||||||
if(device == null)
|
if (device == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -47,7 +47,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<DeviceResponseModel> GetByIdentifier(string identifier)
|
public async Task<DeviceResponseModel> GetByIdentifier(string identifier)
|
||||||
{
|
{
|
||||||
var device = await _deviceRepository.GetByIdentifierAsync(identifier, _userService.GetProperUserId(User).Value);
|
var device = await _deviceRepository.GetByIdentifierAsync(identifier, _userService.GetProperUserId(User).Value);
|
||||||
if(device == null)
|
if (device == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<DeviceResponseModel> Put(string id, [FromBody]DeviceRequestModel model)
|
public async Task<DeviceResponseModel> Put(string id, [FromBody]DeviceRequestModel model)
|
||||||
{
|
{
|
||||||
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
|
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
|
||||||
if(device == null)
|
if (device == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -95,7 +95,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PutToken(string identifier, [FromBody]DeviceTokenRequestModel model)
|
public async Task PutToken(string identifier, [FromBody]DeviceTokenRequestModel model)
|
||||||
{
|
{
|
||||||
var device = await _deviceRepository.GetByIdentifierAsync(identifier, _userService.GetProperUserId(User).Value);
|
var device = await _deviceRepository.GetByIdentifierAsync(identifier, _userService.GetProperUserId(User).Value);
|
||||||
if(device == null)
|
if (device == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -109,7 +109,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PutClearToken(string identifier)
|
public async Task PutClearToken(string identifier)
|
||||||
{
|
{
|
||||||
var device = await _deviceRepository.GetByIdentifierAsync(identifier);
|
var device = await _deviceRepository.GetByIdentifierAsync(identifier);
|
||||||
if(device == null)
|
if (device == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -122,7 +122,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task Delete(string id)
|
public async Task Delete(string id)
|
||||||
{
|
{
|
||||||
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
|
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
|
||||||
if(device == null)
|
if (device == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
@ -53,23 +53,23 @@ namespace Bit.Api.Controllers
|
|||||||
[FromQuery]DateTime? start = null, [FromQuery]DateTime? end = null, [FromQuery]string continuationToken = null)
|
[FromQuery]DateTime? start = null, [FromQuery]DateTime? end = null, [FromQuery]string continuationToken = null)
|
||||||
{
|
{
|
||||||
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
|
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
|
||||||
if(cipher == null)
|
if (cipher == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var canView = false;
|
var canView = false;
|
||||||
if(cipher.OrganizationId.HasValue)
|
if (cipher.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
canView = _currentContext.OrganizationAdmin(cipher.OrganizationId.Value);
|
canView = _currentContext.OrganizationAdmin(cipher.OrganizationId.Value);
|
||||||
}
|
}
|
||||||
else if(cipher.UserId.HasValue)
|
else if (cipher.UserId.HasValue)
|
||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
canView = userId == cipher.UserId.Value;
|
canView = userId == cipher.UserId.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!canView)
|
if (!canView)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@ namespace Bit.Api.Controllers
|
|||||||
[FromQuery]DateTime? start = null, [FromQuery]DateTime? end = null, [FromQuery]string continuationToken = null)
|
[FromQuery]DateTime? start = null, [FromQuery]DateTime? end = null, [FromQuery]string continuationToken = null)
|
||||||
{
|
{
|
||||||
var orgId = new Guid(id);
|
var orgId = new Guid(id);
|
||||||
if(!_currentContext.OrganizationAdmin(orgId))
|
if (!_currentContext.OrganizationAdmin(orgId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ namespace Bit.Api.Controllers
|
|||||||
[FromQuery]DateTime? start = null, [FromQuery]DateTime? end = null, [FromQuery]string continuationToken = null)
|
[FromQuery]DateTime? start = null, [FromQuery]DateTime? end = null, [FromQuery]string continuationToken = null)
|
||||||
{
|
{
|
||||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
||||||
if(organizationUser == null || !organizationUser.UserId.HasValue ||
|
if (organizationUser == null || !organizationUser.UserId.HasValue ||
|
||||||
!_currentContext.OrganizationAdmin(organizationUser.OrganizationId))
|
!_currentContext.OrganizationAdmin(organizationUser.OrganizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -119,19 +119,19 @@ namespace Bit.Api.Controllers
|
|||||||
|
|
||||||
private Tuple<DateTime, DateTime> GetDateRange(DateTime? start, DateTime? end)
|
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);
|
end = DateTime.UtcNow.Date.AddDays(1).AddMilliseconds(-1);
|
||||||
start = DateTime.UtcNow.Date.AddDays(-30);
|
start = DateTime.UtcNow.Date.AddDays(-30);
|
||||||
}
|
}
|
||||||
else if(start.Value > end.Value)
|
else if (start.Value > end.Value)
|
||||||
{
|
{
|
||||||
var newEnd = start;
|
var newEnd = start;
|
||||||
start = end;
|
start = end;
|
||||||
end = newEnd;
|
end = newEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((end.Value - start.Value) > TimeSpan.FromDays(367))
|
if ((end.Value - start.Value) > TimeSpan.FromDays(367))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Range too large.");
|
throw new BadRequestException("Range too large.");
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
|
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
|
||||||
if(folder == null)
|
if (folder == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
|
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
|
||||||
if(folder == null)
|
if (folder == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
|
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
|
||||||
if(folder == null)
|
if (folder == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<GroupResponseModel> Get(string orgId, string id)
|
public async Task<GroupResponseModel> Get(string orgId, string id)
|
||||||
{
|
{
|
||||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||||
if(group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
|
if (group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -46,7 +46,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<GroupDetailsResponseModel> GetDetails(string orgId, string id)
|
public async Task<GroupDetailsResponseModel> GetDetails(string orgId, string id)
|
||||||
{
|
{
|
||||||
var groupDetails = await _groupRepository.GetByIdWithCollectionsAsync(new Guid(id));
|
var groupDetails = await _groupRepository.GetByIdWithCollectionsAsync(new Guid(id));
|
||||||
if(groupDetails?.Item1 == null || !_currentContext.OrganizationAdmin(groupDetails.Item1.OrganizationId))
|
if (groupDetails?.Item1 == null || !_currentContext.OrganizationAdmin(groupDetails.Item1.OrganizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -58,7 +58,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<ListResponseModel<GroupResponseModel>> Get(string orgId)
|
public async Task<ListResponseModel<GroupResponseModel>> Get(string orgId)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(orgId);
|
var orgIdGuid = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationManager(orgIdGuid))
|
if (!_currentContext.OrganizationManager(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var idGuid = new Guid(id);
|
var idGuid = new Guid(id);
|
||||||
var group = await _groupRepository.GetByIdAsync(idGuid);
|
var group = await _groupRepository.GetByIdAsync(idGuid);
|
||||||
if(group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
|
if (group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<GroupResponseModel> Post(string orgId, [FromBody]GroupRequestModel model)
|
public async Task<GroupResponseModel> Post(string orgId, [FromBody]GroupRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(orgId);
|
var orgIdGuid = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationAdmin(orgIdGuid))
|
if (!_currentContext.OrganizationAdmin(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -101,7 +101,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<GroupResponseModel> Put(string orgId, string id, [FromBody]GroupRequestModel model)
|
public async Task<GroupResponseModel> Put(string orgId, string id, [FromBody]GroupRequestModel model)
|
||||||
{
|
{
|
||||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||||
if(group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
|
if (group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PutUsers(string orgId, string id, [FromBody]IEnumerable<Guid> model)
|
public async Task PutUsers(string orgId, string id, [FromBody]IEnumerable<Guid> model)
|
||||||
{
|
{
|
||||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||||
if(group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
|
if (group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task Delete(string orgId, string id)
|
public async Task Delete(string orgId, string id)
|
||||||
{
|
{
|
||||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||||
if(group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
|
if (group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -139,7 +139,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task Delete(string orgId, string id, string orgUserId)
|
public async Task Delete(string orgId, string id, string orgUserId)
|
||||||
{
|
{
|
||||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
||||||
if(group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
|
if (group == null || !_currentContext.OrganizationAdmin(group.OrganizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ namespace Bit.Api.Controllers
|
|||||||
|
|
||||||
private async Task<IActionResult> SendAsync(string username, bool retry)
|
private async Task<IActionResult> SendAsync(string username, bool retry)
|
||||||
{
|
{
|
||||||
if(!CoreHelpers.SettingHasValue(_globalSettings.HibpApiKey))
|
if (!CoreHelpers.SettingHasValue(_globalSettings.HibpApiKey))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("HaveIBeenPwned API key not set.");
|
throw new BadRequestException("HaveIBeenPwned API key not set.");
|
||||||
}
|
}
|
||||||
@ -59,22 +59,22 @@ namespace Bit.Api.Controllers
|
|||||||
request.Headers.Add("hibp-client-id", GetClientId());
|
request.Headers.Add("hibp-client-id", GetClientId());
|
||||||
request.Headers.Add("User-Agent", _userAgent);
|
request.Headers.Add("User-Agent", _userAgent);
|
||||||
var response = await _httpClient.SendAsync(request);
|
var response = await _httpClient.SendAsync(request);
|
||||||
if(response.IsSuccessStatusCode)
|
if (response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
var data = await response.Content.ReadAsStringAsync();
|
var data = await response.Content.ReadAsStringAsync();
|
||||||
return Content(data, "application/json");
|
return Content(data, "application/json");
|
||||||
}
|
}
|
||||||
else if(response.StatusCode == HttpStatusCode.NotFound)
|
else if (response.StatusCode == HttpStatusCode.NotFound)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
else if(response.StatusCode == HttpStatusCode.TooManyRequests && retry)
|
else if (response.StatusCode == HttpStatusCode.TooManyRequests && retry)
|
||||||
{
|
{
|
||||||
var delay = 2000;
|
var delay = 2000;
|
||||||
if(response.Headers.Contains("retry-after"))
|
if (response.Headers.Contains("retry-after"))
|
||||||
{
|
{
|
||||||
var vals = response.Headers.GetValues("retry-after");
|
var vals = response.Headers.GetValues("retry-after");
|
||||||
if(vals.Any() && int.TryParse(vals.FirstOrDefault(), out var secDelay))
|
if (vals.Any() && int.TryParse(vals.FirstOrDefault(), out var secDelay))
|
||||||
{
|
{
|
||||||
delay = (secDelay * 1000) + 200;
|
delay = (secDelay * 1000) + 200;
|
||||||
}
|
}
|
||||||
@ -91,7 +91,7 @@ namespace Bit.Api.Controllers
|
|||||||
private string GetClientId()
|
private string GetClientId()
|
||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
using(var sha256 = SHA256.Create())
|
using (var sha256 = SHA256.Create())
|
||||||
{
|
{
|
||||||
var hash = sha256.ComputeHash(userId.ToByteArray());
|
var hash = sha256.ComputeHash(userId.ToByteArray());
|
||||||
return Convert.ToBase64String(hash);
|
return Convert.ToBase64String(hash);
|
||||||
|
@ -26,7 +26,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<InstallationResponseModel> Get(Guid id)
|
public async Task<InstallationResponseModel> Get(Guid id)
|
||||||
{
|
{
|
||||||
var installation = await _installationRepository.GetByIdAsync(id);
|
var installation = await _installationRepository.GetByIdAsync(id);
|
||||||
if(installation == null)
|
if (installation == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
@ -43,11 +43,11 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<UserLicense> GetUser(string id, [FromQuery]string key)
|
public async Task<UserLicense> GetUser(string id, [FromQuery]string key)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(new Guid(id));
|
var user = await _userRepository.GetByIdAsync(new Guid(id));
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else if(!user.LicenseKey.Equals(key))
|
else if (!user.LicenseKey.Equals(key))
|
||||||
{
|
{
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
throw new BadRequestException("Invalid license key.");
|
throw new BadRequestException("Invalid license key.");
|
||||||
@ -61,11 +61,11 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<OrganizationLicense> GetOrganization(string id, [FromQuery]string key)
|
public async Task<OrganizationLicense> GetOrganization(string id, [FromQuery]string key)
|
||||||
{
|
{
|
||||||
var org = await _organizationRepository.GetByIdAsync(new Guid(id));
|
var org = await _organizationRepository.GetByIdAsync(new Guid(id));
|
||||||
if(org == null)
|
if (org == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else if(!org.LicenseKey.Equals(key))
|
else if (!org.LicenseKey.Equals(key))
|
||||||
{
|
{
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
throw new BadRequestException("Invalid license key.");
|
throw new BadRequestException("Invalid license key.");
|
||||||
|
@ -46,7 +46,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<OrganizationUserDetailsResponseModel> Get(string orgId, string id)
|
public async Task<OrganizationUserDetailsResponseModel> Get(string orgId, string id)
|
||||||
{
|
{
|
||||||
var organizationUser = await _organizationUserRepository.GetByIdWithCollectionsAsync(new Guid(id));
|
var organizationUser = await _organizationUserRepository.GetByIdWithCollectionsAsync(new Guid(id));
|
||||||
if(organizationUser == null || !_currentContext.OrganizationAdmin(organizationUser.Item1.OrganizationId))
|
if (organizationUser == null || !_currentContext.OrganizationAdmin(organizationUser.Item1.OrganizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -58,7 +58,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<ListResponseModel<OrganizationUserUserDetailsResponseModel>> Get(string orgId)
|
public async Task<ListResponseModel<OrganizationUserUserDetailsResponseModel>> Get(string orgId)
|
||||||
{
|
{
|
||||||
var orgGuidId = new Guid(orgId);
|
var orgGuidId = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationManager(orgGuidId))
|
if (!_currentContext.OrganizationManager(orgGuidId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<IEnumerable<string>> GetGroups(string orgId, string id)
|
public async Task<IEnumerable<string>> GetGroups(string orgId, string id)
|
||||||
{
|
{
|
||||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
||||||
if(organizationUser == null || !_currentContext.OrganizationAdmin(organizationUser.OrganizationId))
|
if (organizationUser == null || !_currentContext.OrganizationAdmin(organizationUser.OrganizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task Invite(string orgId, [FromBody]OrganizationUserInviteRequestModel model)
|
public async Task Invite(string orgId, [FromBody]OrganizationUserInviteRequestModel model)
|
||||||
{
|
{
|
||||||
var orgGuidId = new Guid(orgId);
|
var orgGuidId = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationAdmin(orgGuidId))
|
if (!_currentContext.OrganizationAdmin(orgGuidId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -102,7 +102,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task Reinvite(string orgId, string id)
|
public async Task Reinvite(string orgId, string id)
|
||||||
{
|
{
|
||||||
var orgGuidId = new Guid(orgId);
|
var orgGuidId = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationAdmin(orgGuidId))
|
if (!_currentContext.OrganizationAdmin(orgGuidId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -115,7 +115,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task Accept(string orgId, string id, [FromBody]OrganizationUserAcceptRequestModel model)
|
public async Task Accept(string orgId, string id, [FromBody]OrganizationUserAcceptRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -127,7 +127,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task Confirm(string orgId, string id, [FromBody]OrganizationUserConfirmRequestModel model)
|
public async Task Confirm(string orgId, string id, [FromBody]OrganizationUserConfirmRequestModel model)
|
||||||
{
|
{
|
||||||
var orgGuidId = new Guid(orgId);
|
var orgGuidId = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationAdmin(orgGuidId))
|
if (!_currentContext.OrganizationAdmin(orgGuidId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -142,13 +142,13 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task Put(string orgId, string id, [FromBody]OrganizationUserUpdateRequestModel model)
|
public async Task Put(string orgId, string id, [FromBody]OrganizationUserUpdateRequestModel model)
|
||||||
{
|
{
|
||||||
var orgGuidId = new Guid(orgId);
|
var orgGuidId = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationAdmin(orgGuidId))
|
if (!_currentContext.OrganizationAdmin(orgGuidId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
||||||
if(organizationUser == null || organizationUser.OrganizationId != orgGuidId)
|
if (organizationUser == null || organizationUser.OrganizationId != orgGuidId)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -163,13 +163,13 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PutGroups(string orgId, string id, [FromBody]OrganizationUserUpdateGroupsRequestModel model)
|
public async Task PutGroups(string orgId, string id, [FromBody]OrganizationUserUpdateGroupsRequestModel model)
|
||||||
{
|
{
|
||||||
var orgGuidId = new Guid(orgId);
|
var orgGuidId = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationAdmin(orgGuidId))
|
if (!_currentContext.OrganizationAdmin(orgGuidId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
|
||||||
if(organizationUser == null || organizationUser.OrganizationId != orgGuidId)
|
if (organizationUser == null || organizationUser.OrganizationId != orgGuidId)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -182,7 +182,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task Delete(string orgId, string id)
|
public async Task Delete(string orgId, string id)
|
||||||
{
|
{
|
||||||
var orgGuidId = new Guid(orgId);
|
var orgGuidId = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationAdmin(orgGuidId))
|
if (!_currentContext.OrganizationAdmin(orgGuidId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
@ -48,13 +48,13 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<OrganizationResponseModel> Get(string id)
|
public async Task<OrganizationResponseModel> Get(string id)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||||
if(organization == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -67,13 +67,13 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<BillingResponseModel> GetBilling(string id)
|
public async Task<BillingResponseModel> GetBilling(string id)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||||
if(organization == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -86,21 +86,21 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<OrganizationSubscriptionResponseModel> GetSubscription(string id)
|
public async Task<OrganizationSubscriptionResponseModel> GetSubscription(string id)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||||
if(organization == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!_globalSettings.SelfHosted && organization.Gateway != null)
|
if (!_globalSettings.SelfHosted && organization.Gateway != null)
|
||||||
{
|
{
|
||||||
var subscriptionInfo = await _paymentService.GetSubscriptionAsync(organization);
|
var subscriptionInfo = await _paymentService.GetSubscriptionAsync(organization);
|
||||||
if(subscriptionInfo == null)
|
if (subscriptionInfo == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -117,13 +117,13 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<OrganizationLicense> GetLicense(string id, [FromQuery]Guid installationId)
|
public async Task<OrganizationLicense> GetLicense(string id, [FromQuery]Guid installationId)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var license = await _organizationService.GenerateLicenseAsync(orgIdGuid, installationId);
|
var license = await _organizationService.GenerateLicenseAsync(orgIdGuid, installationId);
|
||||||
if(license == null)
|
if (license == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -145,7 +145,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<OrganizationResponseModel> Post([FromBody]OrganizationCreateRequestModel model)
|
public async Task<OrganizationResponseModel> Post([FromBody]OrganizationCreateRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -160,13 +160,13 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<OrganizationResponseModel> PostLicense(OrganizationCreateLicenseRequestModel model)
|
public async Task<OrganizationResponseModel> PostLicense(OrganizationCreateLicenseRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var license = await ApiHelpers.ReadJsonFileFromBody<OrganizationLicense>(HttpContext, model.License);
|
var license = await ApiHelpers.ReadJsonFileFromBody<OrganizationLicense>(HttpContext, model.License);
|
||||||
if(license == null)
|
if (license == null)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Invalid license");
|
throw new BadRequestException("Invalid license");
|
||||||
}
|
}
|
||||||
@ -181,13 +181,13 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<OrganizationResponseModel> Put(string id, [FromBody]OrganizationUpdateRequestModel model)
|
public async Task<OrganizationResponseModel> Put(string id, [FromBody]OrganizationUpdateRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||||
if(organization == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -204,7 +204,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostPayment(string id, [FromBody]PaymentRequestModel model)
|
public async Task PostPayment(string id, [FromBody]PaymentRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -218,7 +218,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<PaymentResponseModel> PostUpgrade(string id, [FromBody]OrganizationUpgradeRequestModel model)
|
public async Task<PaymentResponseModel> PostUpgrade(string id, [FromBody]OrganizationUpgradeRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -236,7 +236,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<PaymentResponseModel> PostSeat(string id, [FromBody]OrganizationSeatRequestModel model)
|
public async Task<PaymentResponseModel> PostSeat(string id, [FromBody]OrganizationSeatRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -254,7 +254,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<PaymentResponseModel> PostStorage(string id, [FromBody]StorageRequestModel model)
|
public async Task<PaymentResponseModel> PostStorage(string id, [FromBody]StorageRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -272,7 +272,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostVerifyBank(string id, [FromBody]OrganizationVerifyBankRequestModel model)
|
public async Task PostVerifyBank(string id, [FromBody]OrganizationVerifyBankRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -285,7 +285,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostCancel(string id)
|
public async Task PostCancel(string id)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -298,7 +298,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostReinstate(string id)
|
public async Task PostReinstate(string id)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -310,7 +310,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task Leave(string id)
|
public async Task Leave(string id)
|
||||||
{
|
{
|
||||||
var orgGuidId = new Guid(id);
|
var orgGuidId = new Guid(id);
|
||||||
if(!_currentContext.OrganizationUser(orgGuidId))
|
if (!_currentContext.OrganizationUser(orgGuidId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -324,24 +324,24 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task Delete(string id, [FromBody]OrganizationDeleteRequestModel model)
|
public async Task Delete(string id, [FromBody]OrganizationDeleteRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||||
if(organization == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
if (!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
||||||
{
|
{
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
|
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
|
||||||
@ -357,13 +357,13 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task PostLicense(string id, LicenseRequestModel model)
|
public async Task PostLicense(string id, LicenseRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var license = await ApiHelpers.ReadJsonFileFromBody<OrganizationLicense>(HttpContext, model.License);
|
var license = await ApiHelpers.ReadJsonFileFromBody<OrganizationLicense>(HttpContext, model.License);
|
||||||
if(license == null)
|
if (license == null)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Invalid license");
|
throw new BadRequestException("Invalid license");
|
||||||
}
|
}
|
||||||
@ -374,13 +374,13 @@ namespace Bit.Api.Controllers
|
|||||||
[HttpPost("{id}/import")]
|
[HttpPost("{id}/import")]
|
||||||
public async Task Import(string id, [FromBody]ImportOrganizationUsersRequestModel model)
|
public async Task Import(string id, [FromBody]ImportOrganizationUsersRequestModel model)
|
||||||
{
|
{
|
||||||
if(!_globalSettings.SelfHosted && (model.Groups.Count() > 200 || model.Users.Count() > 1000))
|
if (!_globalSettings.SelfHosted && (model.Groups.Count() > 200 || model.Users.Count() > 1000))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("You cannot import this much data at once.");
|
throw new BadRequestException("You cannot import this much data at once.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationAdmin(orgIdGuid))
|
if (!_currentContext.OrganizationAdmin(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -399,24 +399,24 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<ApiKeyResponseModel> ApiKey(string id, [FromBody]ApiKeyRequestModel model)
|
public async Task<ApiKeyResponseModel> ApiKey(string id, [FromBody]ApiKeyRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||||
if(organization == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
if (!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
||||||
{
|
{
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
|
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
|
||||||
@ -432,24 +432,24 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<ApiKeyResponseModel> RotateApiKey(string id, [FromBody]ApiKeyRequestModel model)
|
public async Task<ApiKeyResponseModel> RotateApiKey(string id, [FromBody]ApiKeyRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if (!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||||
if(organization == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
if (!await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
||||||
{
|
{
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
|
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
|
||||||
|
@ -52,12 +52,12 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<PolicyResponseModel> Get(string orgId, int type)
|
public async Task<PolicyResponseModel> Get(string orgId, int type)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(orgId);
|
var orgIdGuid = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationAdmin(orgIdGuid))
|
if (!_currentContext.OrganizationAdmin(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(orgIdGuid, (PolicyType)type);
|
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(orgIdGuid, (PolicyType)type);
|
||||||
if(policy == null)
|
if (policy == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<ListResponseModel<PolicyResponseModel>> Get(string orgId)
|
public async Task<ListResponseModel<PolicyResponseModel>> Get(string orgId)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(orgId);
|
var orgIdGuid = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationManager(orgIdGuid))
|
if (!_currentContext.OrganizationManager(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -87,14 +87,14 @@ namespace Bit.Api.Controllers
|
|||||||
var orgUserId = new Guid(organizationUserId);
|
var orgUserId = new Guid(organizationUserId);
|
||||||
var tokenValid = CoreHelpers.UserInviteTokenIsValid(_organizationServiceDataProtector, token,
|
var tokenValid = CoreHelpers.UserInviteTokenIsValid(_organizationServiceDataProtector, token,
|
||||||
email, orgUserId, _globalSettings);
|
email, orgUserId, _globalSettings);
|
||||||
if(!tokenValid)
|
if (!tokenValid)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var orgIdGuid = new Guid(orgId);
|
var orgIdGuid = new Guid(orgId);
|
||||||
var orgUser = await _organizationUserRepository.GetByIdAsync(orgUserId);
|
var orgUser = await _organizationUserRepository.GetByIdAsync(orgUserId);
|
||||||
if(orgUser == null || orgUser.OrganizationId != orgIdGuid)
|
if (orgUser == null || orgUser.OrganizationId != orgIdGuid)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -108,12 +108,12 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<PolicyResponseModel> Put(string orgId, int type, [FromBody]PolicyRequestModel model)
|
public async Task<PolicyResponseModel> Put(string orgId, int type, [FromBody]PolicyRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(orgId);
|
var orgIdGuid = new Guid(orgId);
|
||||||
if(!_currentContext.OrganizationAdmin(orgIdGuid))
|
if (!_currentContext.OrganizationAdmin(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(new Guid(orgId), (PolicyType)type);
|
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(new Guid(orgId), (PolicyType)type);
|
||||||
if(policy == null)
|
if (policy == null)
|
||||||
{
|
{
|
||||||
policy = model.ToPolicy(orgIdGuid);
|
policy = model.ToPolicy(orgIdGuid);
|
||||||
}
|
}
|
||||||
|
@ -74,12 +74,12 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
CheckUsage();
|
CheckUsage();
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(model.UserId))
|
if (!string.IsNullOrWhiteSpace(model.UserId))
|
||||||
{
|
{
|
||||||
await _pushNotificationService.SendPayloadToUserAsync(Prefix(model.UserId),
|
await _pushNotificationService.SendPayloadToUserAsync(Prefix(model.UserId),
|
||||||
model.Type.Value, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId));
|
model.Type.Value, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId));
|
||||||
}
|
}
|
||||||
else if(!string.IsNullOrWhiteSpace(model.OrganizationId))
|
else if (!string.IsNullOrWhiteSpace(model.OrganizationId))
|
||||||
{
|
{
|
||||||
await _pushNotificationService.SendPayloadToOrganizationAsync(Prefix(model.OrganizationId),
|
await _pushNotificationService.SendPayloadToOrganizationAsync(Prefix(model.OrganizationId),
|
||||||
model.Type.Value, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId));
|
model.Type.Value, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId));
|
||||||
@ -88,7 +88,7 @@ namespace Bit.Api.Controllers
|
|||||||
|
|
||||||
private string Prefix(string value)
|
private string Prefix(string value)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(value))
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -98,7 +98,7 @@ namespace Bit.Api.Controllers
|
|||||||
|
|
||||||
private void CheckUsage()
|
private void CheckUsage()
|
||||||
{
|
{
|
||||||
if(CanUse())
|
if (CanUse())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@ namespace Bit.Api.Controllers
|
|||||||
|
|
||||||
private bool CanUse()
|
private bool CanUse()
|
||||||
{
|
{
|
||||||
if(_environment.IsDevelopment())
|
if (_environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<DomainsResponseModel> GetDomains(bool excluded = true)
|
public async Task<DomainsResponseModel> GetDomains(bool excluded = true)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<DomainsResponseModel> PutDomains([FromBody]UpdateDomainsRequestModel model)
|
public async Task<DomainsResponseModel> PutDomains([FromBody]UpdateDomainsRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<SyncResponseModel> Get([FromQuery]bool excludeDomains = false)
|
public async Task<SyncResponseModel> Get([FromQuery]bool excludeDomains = false)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("User not found.");
|
throw new BadRequestException("User not found.");
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ namespace Bit.Api.Controllers
|
|||||||
IEnumerable<CollectionDetails> collections = null;
|
IEnumerable<CollectionDetails> collections = null;
|
||||||
IDictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict = null;
|
IDictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict = null;
|
||||||
IEnumerable<Policy> policies = null;
|
IEnumerable<Policy> policies = null;
|
||||||
if(hasEnabledOrgs)
|
if (hasEnabledOrgs)
|
||||||
{
|
{
|
||||||
collections = await _collectionRepository.GetManyByUserIdAsync(user.Id);
|
collections = await _collectionRepository.GetManyByUserIdAsync(user.Id);
|
||||||
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdAsync(user.Id);
|
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdAsync(user.Id);
|
||||||
|
@ -47,7 +47,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<ListResponseModel<TwoFactorProviderResponseModel>> Get()
|
public async Task<ListResponseModel<TwoFactorProviderResponseModel>> Get()
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@ -61,13 +61,13 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<ListResponseModel<TwoFactorProviderResponseModel>> GetOrganization(string id)
|
public async Task<ListResponseModel<TwoFactorProviderResponseModel>> GetOrganization(string id)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationAdmin(orgIdGuid))
|
if (!_currentContext.OrganizationAdmin(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||||
if(organization == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -93,7 +93,7 @@ namespace Bit.Api.Controllers
|
|||||||
var user = await CheckAsync(model.MasterPasswordHash, false);
|
var user = await CheckAsync(model.MasterPasswordHash, false);
|
||||||
model.ToUser(user);
|
model.ToUser(user);
|
||||||
|
|
||||||
if(!await _userManager.VerifyTwoFactorTokenAsync(user,
|
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
|
||||||
CoreHelpers.CustomProviderName(TwoFactorProviderType.Authenticator), model.Token))
|
CoreHelpers.CustomProviderName(TwoFactorProviderType.Authenticator), model.Token))
|
||||||
{
|
{
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
@ -149,7 +149,7 @@ namespace Bit.Api.Controllers
|
|||||||
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
||||||
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
|
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
|
||||||
}
|
}
|
||||||
catch(DuoException)
|
catch (DuoException)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Duo configuration settings are not valid. Please re-check the Duo Admin panel.");
|
throw new BadRequestException("Duo configuration settings are not valid. Please re-check the Duo Admin panel.");
|
||||||
}
|
}
|
||||||
@ -167,13 +167,13 @@ namespace Bit.Api.Controllers
|
|||||||
var user = await CheckAsync(model.MasterPasswordHash, false);
|
var user = await CheckAsync(model.MasterPasswordHash, false);
|
||||||
|
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationAdmin(orgIdGuid))
|
if (!_currentContext.OrganizationAdmin(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||||
if(organization == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -190,13 +190,13 @@ namespace Bit.Api.Controllers
|
|||||||
var user = await CheckAsync(model.MasterPasswordHash, false);
|
var user = await CheckAsync(model.MasterPasswordHash, false);
|
||||||
|
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationAdmin(orgIdGuid))
|
if (!_currentContext.OrganizationAdmin(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||||
if(organization == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -206,7 +206,7 @@ namespace Bit.Api.Controllers
|
|||||||
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
||||||
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
|
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
|
||||||
}
|
}
|
||||||
catch(DuoException)
|
catch (DuoException)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Duo configuration settings are not valid. Please re-check the Duo Admin panel.");
|
throw new BadRequestException("Duo configuration settings are not valid. Please re-check the Duo Admin panel.");
|
||||||
}
|
}
|
||||||
@ -243,7 +243,7 @@ namespace Bit.Api.Controllers
|
|||||||
var user = await CheckAsync(model.MasterPasswordHash, true);
|
var user = await CheckAsync(model.MasterPasswordHash, true);
|
||||||
var success = await _userService.CompleteU2fRegistrationAsync(
|
var success = await _userService.CompleteU2fRegistrationAsync(
|
||||||
user, model.Id.Value, model.Name, model.DeviceResponse);
|
user, model.Id.Value, model.Name, model.DeviceResponse);
|
||||||
if(!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Unable to complete U2F key registration.");
|
throw new BadRequestException("Unable to complete U2F key registration.");
|
||||||
}
|
}
|
||||||
@ -281,9 +281,9 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task SendEmailLogin([FromBody]TwoFactorEmailRequestModel model)
|
public async Task SendEmailLogin([FromBody]TwoFactorEmailRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userManager.FindByEmailAsync(model.Email.ToLowerInvariant());
|
var user = await _userManager.FindByEmailAsync(model.Email.ToLowerInvariant());
|
||||||
if(user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
if(await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
if (await _userService.CheckPasswordAsync(user, model.MasterPasswordHash))
|
||||||
{
|
{
|
||||||
await _userService.SendTwoFactorEmailAsync(user);
|
await _userService.SendTwoFactorEmailAsync(user);
|
||||||
return;
|
return;
|
||||||
@ -301,7 +301,7 @@ namespace Bit.Api.Controllers
|
|||||||
var user = await CheckAsync(model.MasterPasswordHash, false);
|
var user = await CheckAsync(model.MasterPasswordHash, false);
|
||||||
model.ToUser(user);
|
model.ToUser(user);
|
||||||
|
|
||||||
if(!await _userManager.VerifyTwoFactorTokenAsync(user,
|
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
|
||||||
CoreHelpers.CustomProviderName(TwoFactorProviderType.Email), model.Token))
|
CoreHelpers.CustomProviderName(TwoFactorProviderType.Email), model.Token))
|
||||||
{
|
{
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
@ -331,13 +331,13 @@ namespace Bit.Api.Controllers
|
|||||||
var user = await CheckAsync(model.MasterPasswordHash, false);
|
var user = await CheckAsync(model.MasterPasswordHash, false);
|
||||||
|
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationAdmin(orgIdGuid))
|
if (!_currentContext.OrganizationAdmin(orgIdGuid))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||||
if(organization == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -359,7 +359,7 @@ namespace Bit.Api.Controllers
|
|||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task PostRecover([FromBody]TwoFactorRecoveryRequestModel model)
|
public async Task PostRecover([FromBody]TwoFactorRecoveryRequestModel model)
|
||||||
{
|
{
|
||||||
if(!await _userService.RecoverTwoFactorAsync(model.Email, model.MasterPasswordHash, model.RecoveryCode,
|
if (!await _userService.RecoverTwoFactorAsync(model.Email, model.MasterPasswordHash, model.RecoveryCode,
|
||||||
_organizationService))
|
_organizationService))
|
||||||
{
|
{
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
@ -370,18 +370,18 @@ namespace Bit.Api.Controllers
|
|||||||
private async Task<User> CheckAsync(string masterPasswordHash, bool premium)
|
private async Task<User> CheckAsync(string masterPasswordHash, bool premium)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!await _userService.CheckPasswordAsync(user, masterPasswordHash))
|
if (!await _userService.CheckPasswordAsync(user, masterPasswordHash))
|
||||||
{
|
{
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
|
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(premium && !(await _userService.CanAccessPremium(user)))
|
if (premium && !(await _userService.CanAccessPremium(user)))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Premium status is required.");
|
throw new BadRequestException("Premium status is required.");
|
||||||
}
|
}
|
||||||
@ -391,12 +391,12 @@ namespace Bit.Api.Controllers
|
|||||||
|
|
||||||
private async Task ValidateYubiKeyAsync(User user, string name, string value)
|
private async Task ValidateYubiKeyAsync(User user, string name, string value)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(value) || value.Length == 12)
|
if (string.IsNullOrWhiteSpace(value) || value.Length == 12)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!await _userManager.VerifyTwoFactorTokenAsync(user,
|
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
|
||||||
CoreHelpers.CustomProviderName(TwoFactorProviderType.YubiKey), value))
|
CoreHelpers.CustomProviderName(TwoFactorProviderType.YubiKey), value))
|
||||||
{
|
{
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
|
@ -25,7 +25,7 @@ namespace Bit.Api.Controllers
|
|||||||
{
|
{
|
||||||
var guidId = new Guid(id);
|
var guidId = new Guid(id);
|
||||||
var key = await _userRepository.GetPublicKeyAsync(guidId);
|
var key = await _userRepository.GetPublicKeyAsync(guidId);
|
||||||
if(key == null)
|
if (key == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
@ -20,20 +20,20 @@ namespace Bit.Api
|
|||||||
logging.AddSerilog(hostingContext, e =>
|
logging.AddSerilog(hostingContext, e =>
|
||||||
{
|
{
|
||||||
var context = e.Properties["SourceContext"].ToString();
|
var context = e.Properties["SourceContext"].ToString();
|
||||||
if(e.Exception != null &&
|
if (e.Exception != null &&
|
||||||
(e.Exception.GetType() == typeof(SecurityTokenValidationException) ||
|
(e.Exception.GetType() == typeof(SecurityTokenValidationException) ||
|
||||||
e.Exception.Message == "Bad security stamp."))
|
e.Exception.Message == "Bad security stamp."))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(e.Level == LogEventLevel.Information &&
|
if (e.Level == LogEventLevel.Information &&
|
||||||
context.Contains(typeof(IpRateLimitMiddleware).FullName))
|
context.Contains(typeof(IpRateLimitMiddleware).FullName))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(context.Contains("IdentityServer4.Validation.TokenValidator") ||
|
if (context.Contains("IdentityServer4.Validation.TokenValidator") ||
|
||||||
context.Contains("IdentityServer4.Validation.TokenRequestValidator"))
|
context.Contains("IdentityServer4.Validation.TokenRequestValidator"))
|
||||||
{
|
{
|
||||||
return e.Level > LogEventLevel.Error;
|
return e.Level > LogEventLevel.Error;
|
||||||
|
@ -44,7 +44,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
{
|
{
|
||||||
var collectionWithGroups = await _collectionRepository.GetByIdWithGroupsAsync(id);
|
var collectionWithGroups = await _collectionRepository.GetByIdWithGroupsAsync(id);
|
||||||
var collection = collectionWithGroups?.Item1;
|
var collection = collectionWithGroups?.Item1;
|
||||||
if(collection == null || collection.OrganizationId != _currentContext.OrganizationId)
|
if (collection == null || collection.OrganizationId != _currentContext.OrganizationId)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
public async Task<IActionResult> Put(Guid id, [FromBody]CollectionUpdateRequestModel model)
|
public async Task<IActionResult> Put(Guid id, [FromBody]CollectionUpdateRequestModel model)
|
||||||
{
|
{
|
||||||
var existingCollection = await _collectionRepository.GetByIdAsync(id);
|
var existingCollection = await _collectionRepository.GetByIdAsync(id);
|
||||||
if(existingCollection == null || existingCollection.OrganizationId != _currentContext.OrganizationId)
|
if (existingCollection == null || existingCollection.OrganizationId != _currentContext.OrganizationId)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
@ -111,7 +111,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
public async Task<IActionResult> Delete(Guid id)
|
public async Task<IActionResult> Delete(Guid id)
|
||||||
{
|
{
|
||||||
var collection = await _collectionRepository.GetByIdAsync(id);
|
var collection = await _collectionRepository.GetByIdAsync(id);
|
||||||
if(collection == null || collection.OrganizationId != _currentContext.OrganizationId)
|
if (collection == null || collection.OrganizationId != _currentContext.OrganizationId)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
|
@ -42,16 +42,16 @@ namespace Bit.Api.Public.Controllers
|
|||||||
{
|
{
|
||||||
var dateRange = request.ToDateRange();
|
var dateRange = request.ToDateRange();
|
||||||
var result = new PagedResult<IEvent>();
|
var result = new PagedResult<IEvent>();
|
||||||
if(request.ActingUserId.HasValue)
|
if (request.ActingUserId.HasValue)
|
||||||
{
|
{
|
||||||
result = await _eventRepository.GetManyByOrganizationActingUserAsync(
|
result = await _eventRepository.GetManyByOrganizationActingUserAsync(
|
||||||
_currentContext.OrganizationId.Value, request.ActingUserId.Value, dateRange.Item1, dateRange.Item2,
|
_currentContext.OrganizationId.Value, request.ActingUserId.Value, dateRange.Item1, dateRange.Item2,
|
||||||
new PageOptions { ContinuationToken = request.ContinuationToken });
|
new PageOptions { ContinuationToken = request.ContinuationToken });
|
||||||
}
|
}
|
||||||
else if(request.ItemId.HasValue)
|
else if (request.ItemId.HasValue)
|
||||||
{
|
{
|
||||||
var cipher = await _cipherRepository.GetByIdAsync(request.ItemId.Value);
|
var cipher = await _cipherRepository.GetByIdAsync(request.ItemId.Value);
|
||||||
if(cipher != null && cipher.OrganizationId == _currentContext.OrganizationId.Value)
|
if (cipher != null && cipher.OrganizationId == _currentContext.OrganizationId.Value)
|
||||||
{
|
{
|
||||||
result = await _eventRepository.GetManyByCipherAsync(
|
result = await _eventRepository.GetManyByCipherAsync(
|
||||||
cipher, dateRange.Item1, dateRange.Item2,
|
cipher, dateRange.Item1, dateRange.Item2,
|
||||||
|
@ -45,7 +45,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
{
|
{
|
||||||
var groupDetails = await _groupRepository.GetByIdWithCollectionsAsync(id);
|
var groupDetails = await _groupRepository.GetByIdWithCollectionsAsync(id);
|
||||||
var group = groupDetails?.Item1;
|
var group = groupDetails?.Item1;
|
||||||
if(group == null || group.OrganizationId != _currentContext.OrganizationId)
|
if (group == null || group.OrganizationId != _currentContext.OrganizationId)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
public async Task<IActionResult> GetMemberIds(Guid id)
|
public async Task<IActionResult> GetMemberIds(Guid id)
|
||||||
{
|
{
|
||||||
var group = await _groupRepository.GetByIdAsync(id);
|
var group = await _groupRepository.GetByIdAsync(id);
|
||||||
if(group == null || group.OrganizationId != _currentContext.OrganizationId)
|
if (group == null || group.OrganizationId != _currentContext.OrganizationId)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
@ -128,7 +128,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
public async Task<IActionResult> Put(Guid id, [FromBody]GroupCreateUpdateRequestModel model)
|
public async Task<IActionResult> Put(Guid id, [FromBody]GroupCreateUpdateRequestModel model)
|
||||||
{
|
{
|
||||||
var existingGroup = await _groupRepository.GetByIdAsync(id);
|
var existingGroup = await _groupRepository.GetByIdAsync(id);
|
||||||
if(existingGroup == null || existingGroup.OrganizationId != _currentContext.OrganizationId)
|
if (existingGroup == null || existingGroup.OrganizationId != _currentContext.OrganizationId)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
@ -154,7 +154,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
public async Task<IActionResult> PutMemberIds(Guid id, [FromBody]UpdateMemberIdsRequestModel model)
|
public async Task<IActionResult> PutMemberIds(Guid id, [FromBody]UpdateMemberIdsRequestModel model)
|
||||||
{
|
{
|
||||||
var existingGroup = await _groupRepository.GetByIdAsync(id);
|
var existingGroup = await _groupRepository.GetByIdAsync(id);
|
||||||
if(existingGroup == null || existingGroup.OrganizationId != _currentContext.OrganizationId)
|
if (existingGroup == null || existingGroup.OrganizationId != _currentContext.OrganizationId)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
@ -175,7 +175,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
public async Task<IActionResult> Delete(Guid id)
|
public async Task<IActionResult> Delete(Guid id)
|
||||||
{
|
{
|
||||||
var group = await _groupRepository.GetByIdAsync(id);
|
var group = await _groupRepository.GetByIdAsync(id);
|
||||||
if(group == null || group.OrganizationId != _currentContext.OrganizationId)
|
if (group == null || group.OrganizationId != _currentContext.OrganizationId)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
{
|
{
|
||||||
var userDetails = await _organizationUserRepository.GetDetailsByIdWithCollectionsAsync(id);
|
var userDetails = await _organizationUserRepository.GetDetailsByIdWithCollectionsAsync(id);
|
||||||
var orgUser = userDetails?.Item1;
|
var orgUser = userDetails?.Item1;
|
||||||
if(orgUser == null || orgUser.OrganizationId != _currentContext.OrganizationId)
|
if (orgUser == null || orgUser.OrganizationId != _currentContext.OrganizationId)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
public async Task<IActionResult> GetGroupIds(Guid id)
|
public async Task<IActionResult> GetGroupIds(Guid id)
|
||||||
{
|
{
|
||||||
var orgUser = await _organizationUserRepository.GetByIdAsync(id);
|
var orgUser = await _organizationUserRepository.GetByIdAsync(id);
|
||||||
if(orgUser == null || orgUser.OrganizationId != _currentContext.OrganizationId)
|
if (orgUser == null || orgUser.OrganizationId != _currentContext.OrganizationId)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
@ -138,7 +138,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
public async Task<IActionResult> Put(Guid id, [FromBody]MemberUpdateRequestModel model)
|
public async Task<IActionResult> Put(Guid id, [FromBody]MemberUpdateRequestModel model)
|
||||||
{
|
{
|
||||||
var existingUser = await _organizationUserRepository.GetByIdAsync(id);
|
var existingUser = await _organizationUserRepository.GetByIdAsync(id);
|
||||||
if(existingUser == null || existingUser.OrganizationId != _currentContext.OrganizationId)
|
if (existingUser == null || existingUser.OrganizationId != _currentContext.OrganizationId)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
@ -146,7 +146,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
var associations = model.Collections?.Select(c => c.ToSelectionReadOnly());
|
var associations = model.Collections?.Select(c => c.ToSelectionReadOnly());
|
||||||
await _organizationService.SaveUserAsync(updatedUser, null, associations);
|
await _organizationService.SaveUserAsync(updatedUser, null, associations);
|
||||||
MemberResponseModel response = null;
|
MemberResponseModel response = null;
|
||||||
if(existingUser.UserId.HasValue)
|
if (existingUser.UserId.HasValue)
|
||||||
{
|
{
|
||||||
var existingUserDetails = await _organizationUserRepository.GetDetailsByIdAsync(id);
|
var existingUserDetails = await _organizationUserRepository.GetDetailsByIdAsync(id);
|
||||||
response = new MemberResponseModel(existingUserDetails,
|
response = new MemberResponseModel(existingUserDetails,
|
||||||
@ -174,7 +174,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
public async Task<IActionResult> PutGroupIds(Guid id, [FromBody]UpdateGroupIdsRequestModel model)
|
public async Task<IActionResult> PutGroupIds(Guid id, [FromBody]UpdateGroupIdsRequestModel model)
|
||||||
{
|
{
|
||||||
var existingUser = await _organizationUserRepository.GetByIdAsync(id);
|
var existingUser = await _organizationUserRepository.GetByIdAsync(id);
|
||||||
if(existingUser == null || existingUser.OrganizationId != _currentContext.OrganizationId)
|
if (existingUser == null || existingUser.OrganizationId != _currentContext.OrganizationId)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
@ -196,7 +196,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
public async Task<IActionResult> Delete(Guid id)
|
public async Task<IActionResult> Delete(Guid id)
|
||||||
{
|
{
|
||||||
var user = await _organizationUserRepository.GetByIdAsync(id);
|
var user = await _organizationUserRepository.GetByIdAsync(id);
|
||||||
if(user == null || user.OrganizationId != _currentContext.OrganizationId)
|
if (user == null || user.OrganizationId != _currentContext.OrganizationId)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
@ -218,7 +218,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
public async Task<IActionResult> PostReinvite(Guid id)
|
public async Task<IActionResult> PostReinvite(Guid id)
|
||||||
{
|
{
|
||||||
var existingUser = await _organizationUserRepository.GetByIdAsync(id);
|
var existingUser = await _organizationUserRepository.GetByIdAsync(id);
|
||||||
if(existingUser == null || existingUser.OrganizationId != _currentContext.OrganizationId)
|
if (existingUser == null || existingUser.OrganizationId != _currentContext.OrganizationId)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
{
|
{
|
||||||
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(
|
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(
|
||||||
_currentContext.OrganizationId.Value, type);
|
_currentContext.OrganizationId.Value, type);
|
||||||
if(policy == null)
|
if (policy == null)
|
||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
@ -91,7 +91,7 @@ namespace Bit.Api.Public.Controllers
|
|||||||
{
|
{
|
||||||
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(
|
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(
|
||||||
_currentContext.OrganizationId.Value, type);
|
_currentContext.OrganizationId.Value, type);
|
||||||
if(policy == null)
|
if (policy == null)
|
||||||
{
|
{
|
||||||
policy = model.ToPolicy(_currentContext.OrganizationId.Value);
|
policy = model.ToPolicy(_currentContext.OrganizationId.Value);
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ namespace Bit.Api
|
|||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
var globalSettings = services.AddGlobalSettingsServices(Configuration);
|
var globalSettings = services.AddGlobalSettingsServices(Configuration);
|
||||||
if(!globalSettings.SelfHosted)
|
if (!globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimitOptions"));
|
services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimitOptions"));
|
||||||
services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimitPolicies"));
|
services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimitPolicies"));
|
||||||
@ -64,7 +64,7 @@ namespace Bit.Api
|
|||||||
// BitPay
|
// BitPay
|
||||||
services.AddSingleton<BitPayClient>();
|
services.AddSingleton<BitPayClient>();
|
||||||
|
|
||||||
if(!globalSettings.SelfHosted)
|
if (!globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
// Rate limiting
|
// Rate limiting
|
||||||
services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
|
services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
|
||||||
@ -118,7 +118,7 @@ namespace Bit.Api
|
|||||||
config.Conventions.Add(new PublicApiControllersModelConvention());
|
config.Conventions.Add(new PublicApiControllersModelConvention());
|
||||||
}).AddNewtonsoftJson(options =>
|
}).AddNewtonsoftJson(options =>
|
||||||
{
|
{
|
||||||
if(Environment.IsProduction() && Configuration["swaggerGen"] != "true")
|
if (Environment.IsProduction() && Configuration["swaggerGen"] != "true")
|
||||||
{
|
{
|
||||||
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
|
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
|
||||||
}
|
}
|
||||||
@ -126,13 +126,13 @@ namespace Bit.Api
|
|||||||
|
|
||||||
services.AddSwagger(globalSettings);
|
services.AddSwagger(globalSettings);
|
||||||
|
|
||||||
if(globalSettings.SelfHosted)
|
if (globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
// Jobs service
|
// Jobs service
|
||||||
Jobs.JobsHostedService.AddJobsServices(services);
|
Jobs.JobsHostedService.AddJobsServices(services);
|
||||||
services.AddHostedService<Jobs.JobsHostedService>();
|
services.AddHostedService<Jobs.JobsHostedService>();
|
||||||
}
|
}
|
||||||
if(CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ConnectionString) &&
|
if (CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ConnectionString) &&
|
||||||
CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ApplicationCacheTopicName))
|
CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ApplicationCacheTopicName))
|
||||||
{
|
{
|
||||||
services.AddHostedService<Core.HostedServices.ApplicationCacheHostedService>();
|
services.AddHostedService<Core.HostedServices.ApplicationCacheHostedService>();
|
||||||
@ -152,7 +152,7 @@ namespace Bit.Api
|
|||||||
// Default Middleware
|
// Default Middleware
|
||||||
app.UseDefaultMiddleware(env, globalSettings);
|
app.UseDefaultMiddleware(env, globalSettings);
|
||||||
|
|
||||||
if(!globalSettings.SelfHosted)
|
if (!globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
// Rate limiting
|
// Rate limiting
|
||||||
app.UseMiddleware<CustomIpRateLimitMiddleware>();
|
app.UseMiddleware<CustomIpRateLimitMiddleware>();
|
||||||
@ -183,7 +183,7 @@ namespace Bit.Api
|
|||||||
app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());
|
app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());
|
||||||
|
|
||||||
// Add Swagger
|
// Add Swagger
|
||||||
if(Environment.IsDevelopment() || globalSettings.SelfHosted)
|
if (Environment.IsDevelopment() || globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
app.UseSwagger(config =>
|
app.UseSwagger(config =>
|
||||||
{
|
{
|
||||||
|
@ -10,15 +10,15 @@ namespace Bit.Api.Utilities
|
|||||||
public async static Task<T> ReadJsonFileFromBody<T>(HttpContext httpContext, IFormFile file, long maxSize = 51200)
|
public async static Task<T> ReadJsonFileFromBody<T>(HttpContext httpContext, IFormFile file, long maxSize = 51200)
|
||||||
{
|
{
|
||||||
T obj = default(T);
|
T obj = default(T);
|
||||||
if(file != null && httpContext.Request.ContentLength.HasValue && httpContext.Request.ContentLength.Value <= maxSize)
|
if (file != null && httpContext.Request.ContentLength.HasValue && httpContext.Request.ContentLength.Value <= maxSize)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using(var stream = file.OpenReadStream())
|
using (var stream = file.OpenReadStream())
|
||||||
using(var reader = new StreamReader(stream))
|
using (var reader = new StreamReader(stream))
|
||||||
{
|
{
|
||||||
var s = await reader.ReadToEndAsync();
|
var s = await reader.ReadToEndAsync();
|
||||||
if(!string.IsNullOrWhiteSpace(s))
|
if (!string.IsNullOrWhiteSpace(s))
|
||||||
{
|
{
|
||||||
obj = JsonConvert.DeserializeObject<T>(s);
|
obj = JsonConvert.DeserializeObject<T>(s);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ namespace Bit.Api.Utilities
|
|||||||
|
|
||||||
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||||
{
|
{
|
||||||
if(context.Result is JsonResult jsonResult)
|
if (context.Result is JsonResult jsonResult)
|
||||||
{
|
{
|
||||||
context.Result = new JsonResult(jsonResult.Value, _jsonSerializerSettings);
|
context.Result = new JsonResult(jsonResult.Value, _jsonSerializerSettings);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ namespace Bit.Api.Utilities
|
|||||||
var errorMessage = "An error has occurred.";
|
var errorMessage = "An error has occurred.";
|
||||||
|
|
||||||
var exception = context.Exception;
|
var exception = context.Exception;
|
||||||
if(exception == null)
|
if (exception == null)
|
||||||
{
|
{
|
||||||
// Should never happen.
|
// Should never happen.
|
||||||
return;
|
return;
|
||||||
@ -35,12 +35,12 @@ namespace Bit.Api.Utilities
|
|||||||
|
|
||||||
PublicApi.ErrorResponseModel publicErrorModel = null;
|
PublicApi.ErrorResponseModel publicErrorModel = null;
|
||||||
InternalApi.ErrorResponseModel internalErrorModel = null;
|
InternalApi.ErrorResponseModel internalErrorModel = null;
|
||||||
if(exception is BadRequestException badRequestException)
|
if (exception is BadRequestException badRequestException)
|
||||||
{
|
{
|
||||||
context.HttpContext.Response.StatusCode = 400;
|
context.HttpContext.Response.StatusCode = 400;
|
||||||
if(badRequestException.ModelState != null)
|
if (badRequestException.ModelState != null)
|
||||||
{
|
{
|
||||||
if(_publicApi)
|
if (_publicApi)
|
||||||
{
|
{
|
||||||
publicErrorModel = new PublicApi.ErrorResponseModel(badRequestException.ModelState);
|
publicErrorModel = new PublicApi.ErrorResponseModel(badRequestException.ModelState);
|
||||||
}
|
}
|
||||||
@ -54,11 +54,11 @@ namespace Bit.Api.Utilities
|
|||||||
errorMessage = badRequestException.Message;
|
errorMessage = badRequestException.Message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(exception is StripeException stripeException &&
|
else if (exception is StripeException stripeException &&
|
||||||
stripeException?.StripeError?.ErrorType == "card_error")
|
stripeException?.StripeError?.ErrorType == "card_error")
|
||||||
{
|
{
|
||||||
context.HttpContext.Response.StatusCode = 400;
|
context.HttpContext.Response.StatusCode = 400;
|
||||||
if(_publicApi)
|
if (_publicApi)
|
||||||
{
|
{
|
||||||
publicErrorModel = new PublicApi.ErrorResponseModel(stripeException.StripeError.Parameter,
|
publicErrorModel = new PublicApi.ErrorResponseModel(stripeException.StripeError.Parameter,
|
||||||
stripeException.Message);
|
stripeException.Message);
|
||||||
@ -69,31 +69,31 @@ namespace Bit.Api.Utilities
|
|||||||
stripeException.Message);
|
stripeException.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(exception is GatewayException)
|
else if (exception is GatewayException)
|
||||||
{
|
{
|
||||||
errorMessage = exception.Message;
|
errorMessage = exception.Message;
|
||||||
context.HttpContext.Response.StatusCode = 400;
|
context.HttpContext.Response.StatusCode = 400;
|
||||||
}
|
}
|
||||||
else if(exception is NotSupportedException && !string.IsNullOrWhiteSpace(exception.Message))
|
else if (exception is NotSupportedException && !string.IsNullOrWhiteSpace(exception.Message))
|
||||||
{
|
{
|
||||||
errorMessage = exception.Message;
|
errorMessage = exception.Message;
|
||||||
context.HttpContext.Response.StatusCode = 400;
|
context.HttpContext.Response.StatusCode = 400;
|
||||||
}
|
}
|
||||||
else if(exception is ApplicationException)
|
else if (exception is ApplicationException)
|
||||||
{
|
{
|
||||||
context.HttpContext.Response.StatusCode = 402;
|
context.HttpContext.Response.StatusCode = 402;
|
||||||
}
|
}
|
||||||
else if(exception is NotFoundException)
|
else if (exception is NotFoundException)
|
||||||
{
|
{
|
||||||
errorMessage = "Resource not found.";
|
errorMessage = "Resource not found.";
|
||||||
context.HttpContext.Response.StatusCode = 404;
|
context.HttpContext.Response.StatusCode = 404;
|
||||||
}
|
}
|
||||||
else if(exception is SecurityTokenValidationException)
|
else if (exception is SecurityTokenValidationException)
|
||||||
{
|
{
|
||||||
errorMessage = "Invalid token.";
|
errorMessage = "Invalid token.";
|
||||||
context.HttpContext.Response.StatusCode = 403;
|
context.HttpContext.Response.StatusCode = 403;
|
||||||
}
|
}
|
||||||
else if(exception is UnauthorizedAccessException)
|
else if (exception is UnauthorizedAccessException)
|
||||||
{
|
{
|
||||||
errorMessage = "Unauthorized.";
|
errorMessage = "Unauthorized.";
|
||||||
context.HttpContext.Response.StatusCode = 401;
|
context.HttpContext.Response.StatusCode = 401;
|
||||||
@ -106,7 +106,7 @@ namespace Bit.Api.Utilities
|
|||||||
context.HttpContext.Response.StatusCode = 500;
|
context.HttpContext.Response.StatusCode = 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_publicApi)
|
if (_publicApi)
|
||||||
{
|
{
|
||||||
var errorModel = publicErrorModel ?? new PublicApi.ErrorResponseModel(errorMessage);
|
var errorModel = publicErrorModel ?? new PublicApi.ErrorResponseModel(errorMessage);
|
||||||
context.Result = new ObjectResult(errorModel);
|
context.Result = new ObjectResult(errorModel);
|
||||||
@ -115,7 +115,7 @@ namespace Bit.Api.Utilities
|
|||||||
{
|
{
|
||||||
var errorModel = internalErrorModel ?? new InternalApi.ErrorResponseModel(errorMessage);
|
var errorModel = internalErrorModel ?? new InternalApi.ErrorResponseModel(errorMessage);
|
||||||
var env = context.HttpContext.RequestServices.GetRequiredService<IWebHostEnvironment>();
|
var env = context.HttpContext.RequestServices.GetRequiredService<IWebHostEnvironment>();
|
||||||
if(env.IsDevelopment())
|
if (env.IsDevelopment())
|
||||||
{
|
{
|
||||||
errorModel.ExceptionMessage = exception.Message;
|
errorModel.ExceptionMessage = exception.Message;
|
||||||
errorModel.ExceptionStackTrace = exception.StackTrace;
|
errorModel.ExceptionStackTrace = exception.StackTrace;
|
||||||
|
@ -18,14 +18,14 @@ namespace Bit.Api.Utilities
|
|||||||
public override void OnActionExecuting(ActionExecutingContext context)
|
public override void OnActionExecuting(ActionExecutingContext context)
|
||||||
{
|
{
|
||||||
var model = context.ActionArguments.FirstOrDefault(a => a.Key == "model");
|
var model = context.ActionArguments.FirstOrDefault(a => a.Key == "model");
|
||||||
if(model.Key == "model" && model.Value == null)
|
if (model.Key == "model" && model.Value == null)
|
||||||
{
|
{
|
||||||
context.ModelState.AddModelError(string.Empty, "Body is empty.");
|
context.ModelState.AddModelError(string.Empty, "Body is empty.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!context.ModelState.IsValid)
|
if (!context.ModelState.IsValid)
|
||||||
{
|
{
|
||||||
if(_publicApi)
|
if (_publicApi)
|
||||||
{
|
{
|
||||||
context.Result = new BadRequestObjectResult(new PublicApi.ErrorResponseModel(context.ModelState));
|
context.Result = new BadRequestObjectResult(new PublicApi.ErrorResponseModel(context.ModelState));
|
||||||
}
|
}
|
||||||
|
@ -20,36 +20,36 @@ namespace Bit.Api.Utilities
|
|||||||
var reader = new MultipartReader(boundary, request.Body);
|
var reader = new MultipartReader(boundary, request.Body);
|
||||||
|
|
||||||
var firstSection = await reader.ReadNextSectionAsync();
|
var firstSection = await reader.ReadNextSectionAsync();
|
||||||
if(firstSection != null)
|
if (firstSection != null)
|
||||||
{
|
{
|
||||||
if(ContentDispositionHeaderValue.TryParse(firstSection.ContentDisposition, out var firstContent))
|
if (ContentDispositionHeaderValue.TryParse(firstSection.ContentDisposition, out var firstContent))
|
||||||
{
|
{
|
||||||
if(HasFileContentDisposition(firstContent))
|
if (HasFileContentDisposition(firstContent))
|
||||||
{
|
{
|
||||||
// Old style with just data
|
// Old style with just data
|
||||||
var fileName = HeaderUtilities.RemoveQuotes(firstContent.FileName).ToString();
|
var fileName = HeaderUtilities.RemoveQuotes(firstContent.FileName).ToString();
|
||||||
using(firstSection.Body)
|
using (firstSection.Body)
|
||||||
{
|
{
|
||||||
await callback(firstSection.Body, fileName, null);
|
await callback(firstSection.Body, fileName, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(HasKeyDisposition(firstContent))
|
else if (HasKeyDisposition(firstContent))
|
||||||
{
|
{
|
||||||
// New style with key, then data
|
// New style with key, then data
|
||||||
string key = null;
|
string key = null;
|
||||||
using(var sr = new StreamReader(firstSection.Body))
|
using (var sr = new StreamReader(firstSection.Body))
|
||||||
{
|
{
|
||||||
key = await sr.ReadToEndAsync();
|
key = await sr.ReadToEndAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
var secondSection = await reader.ReadNextSectionAsync();
|
var secondSection = await reader.ReadNextSectionAsync();
|
||||||
if(secondSection != null)
|
if (secondSection != null)
|
||||||
{
|
{
|
||||||
if(ContentDispositionHeaderValue.TryParse(secondSection.ContentDisposition,
|
if (ContentDispositionHeaderValue.TryParse(secondSection.ContentDisposition,
|
||||||
out var secondContent) && HasFileContentDisposition(secondContent))
|
out var secondContent) && HasFileContentDisposition(secondContent))
|
||||||
{
|
{
|
||||||
var fileName = HeaderUtilities.RemoveQuotes(secondContent.FileName).ToString();
|
var fileName = HeaderUtilities.RemoveQuotes(secondContent.FileName).ToString();
|
||||||
using(secondSection.Body)
|
using (secondSection.Body)
|
||||||
{
|
{
|
||||||
await callback(secondSection.Body, fileName, key);
|
await callback(secondSection.Body, fileName, key);
|
||||||
}
|
}
|
||||||
@ -67,12 +67,12 @@ namespace Bit.Api.Utilities
|
|||||||
private static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
|
private static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
|
||||||
{
|
{
|
||||||
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary);
|
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary);
|
||||||
if(StringSegment.IsNullOrEmpty(boundary))
|
if (StringSegment.IsNullOrEmpty(boundary))
|
||||||
{
|
{
|
||||||
throw new InvalidDataException("Missing content-type boundary.");
|
throw new InvalidDataException("Missing content-type boundary.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(boundary.Length > lengthLimit)
|
if (boundary.Length > lengthLimit)
|
||||||
{
|
{
|
||||||
throw new InvalidDataException($"Multipart boundary length limit {lengthLimit} exceeded.");
|
throw new InvalidDataException($"Multipart boundary length limit {lengthLimit} exceeded.");
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ namespace Bit.Api.Utilities
|
|||||||
{
|
{
|
||||||
var controllerNamespace = controller.ControllerType.Namespace;
|
var controllerNamespace = controller.ControllerType.Namespace;
|
||||||
var publicApi = controllerNamespace.Contains(".Public.");
|
var publicApi = controllerNamespace.Contains(".Public.");
|
||||||
if(publicApi)
|
if (publicApi)
|
||||||
{
|
{
|
||||||
controller.Filters.Add(new CamelCaseJsonResultFilterAttribute());
|
controller.Filters.Add(new CamelCaseJsonResultFilterAttribute());
|
||||||
}
|
}
|
||||||
|
@ -27,25 +27,25 @@ namespace Bit.Billing.Controllers
|
|||||||
[HttpPost("iap")]
|
[HttpPost("iap")]
|
||||||
public async Task<IActionResult> PostIap()
|
public async Task<IActionResult> PostIap()
|
||||||
{
|
{
|
||||||
if(HttpContext?.Request?.Query == null)
|
if (HttpContext?.Request?.Query == null)
|
||||||
{
|
{
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = HttpContext.Request.Query.ContainsKey("key") ?
|
var key = HttpContext.Request.Query.ContainsKey("key") ?
|
||||||
HttpContext.Request.Query["key"].ToString() : null;
|
HttpContext.Request.Query["key"].ToString() : null;
|
||||||
if(key != _billingSettings.AppleWebhookKey)
|
if (key != _billingSettings.AppleWebhookKey)
|
||||||
{
|
{
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
string body = null;
|
string body = null;
|
||||||
using(var reader = new StreamReader(HttpContext.Request.Body, Encoding.UTF8))
|
using (var reader = new StreamReader(HttpContext.Request.Body, Encoding.UTF8))
|
||||||
{
|
{
|
||||||
body = await reader.ReadToEndAsync();
|
body = await reader.ReadToEndAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(body))
|
if (string.IsNullOrWhiteSpace(body))
|
||||||
{
|
{
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ namespace Bit.Billing.Controllers
|
|||||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Apple IAP Notification:\n\n{0}", json);
|
_logger.LogInformation(Constants.BypassFiltersEventId, "Apple IAP Notification:\n\n{0}", json);
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError(e, "Error processing IAP status notification.");
|
_logger.LogError(e, "Error processing IAP status notification.");
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
|
@ -49,31 +49,31 @@ namespace Bit.Billing.Controllers
|
|||||||
[HttpPost("ipn")]
|
[HttpPost("ipn")]
|
||||||
public async Task<IActionResult> PostIpn([FromBody] BitPayEventModel model, [FromQuery] string key)
|
public async Task<IActionResult> PostIpn([FromBody] BitPayEventModel model, [FromQuery] string key)
|
||||||
{
|
{
|
||||||
if(key != _billingSettings.BitPayWebhookKey)
|
if (key != _billingSettings.BitPayWebhookKey)
|
||||||
{
|
{
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
if(model == null || string.IsNullOrWhiteSpace(model.Data?.Id) ||
|
if (model == null || string.IsNullOrWhiteSpace(model.Data?.Id) ||
|
||||||
string.IsNullOrWhiteSpace(model.Event?.Name))
|
string.IsNullOrWhiteSpace(model.Event?.Name))
|
||||||
{
|
{
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(model.Event.Name != "invoice_confirmed")
|
if (model.Event.Name != "invoice_confirmed")
|
||||||
{
|
{
|
||||||
// Only processing confirmed invoice events for now.
|
// Only processing confirmed invoice events for now.
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
var invoice = await _bitPayClient.GetInvoiceAsync(model.Data.Id);
|
var invoice = await _bitPayClient.GetInvoiceAsync(model.Data.Id);
|
||||||
if(invoice == null || invoice.Status != "confirmed")
|
if (invoice == null || invoice.Status != "confirmed")
|
||||||
{
|
{
|
||||||
// Request forged...?
|
// Request forged...?
|
||||||
_logger.LogWarning("Forged invoice detected. #" + model.Data.Id);
|
_logger.LogWarning("Forged invoice detected. #" + model.Data.Id);
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(invoice.Currency != "USD")
|
if (invoice.Currency != "USD")
|
||||||
{
|
{
|
||||||
// Only process USD payments
|
// Only process USD payments
|
||||||
_logger.LogWarning("Non USD payment received. #" + invoice.Id);
|
_logger.LogWarning("Non USD payment received. #" + invoice.Id);
|
||||||
@ -81,13 +81,13 @@ namespace Bit.Billing.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ids = GetIdsFromPosData(invoice);
|
var ids = GetIdsFromPosData(invoice);
|
||||||
if(!ids.Item1.HasValue && !ids.Item2.HasValue)
|
if (!ids.Item1.HasValue && !ids.Item2.HasValue)
|
||||||
{
|
{
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
var isAccountCredit = IsAccountCredit(invoice);
|
var isAccountCredit = IsAccountCredit(invoice);
|
||||||
if(!isAccountCredit)
|
if (!isAccountCredit)
|
||||||
{
|
{
|
||||||
// Only processing credits
|
// Only processing credits
|
||||||
_logger.LogWarning("Non-credit payment received. #" + invoice.Id);
|
_logger.LogWarning("Non-credit payment received. #" + invoice.Id);
|
||||||
@ -95,7 +95,7 @@ namespace Bit.Billing.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
var transaction = await _transactionRepository.GetByGatewayIdAsync(GatewayType.BitPay, invoice.Id);
|
var transaction = await _transactionRepository.GetByGatewayIdAsync(GatewayType.BitPay, invoice.Id);
|
||||||
if(transaction != null)
|
if (transaction != null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Already processed this confirmed invoice. #" + invoice.Id);
|
_logger.LogWarning("Already processed this confirmed invoice. #" + invoice.Id);
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
@ -117,16 +117,16 @@ namespace Bit.Billing.Controllers
|
|||||||
};
|
};
|
||||||
await _transactionRepository.CreateAsync(tx);
|
await _transactionRepository.CreateAsync(tx);
|
||||||
|
|
||||||
if(isAccountCredit)
|
if (isAccountCredit)
|
||||||
{
|
{
|
||||||
string billingEmail = null;
|
string billingEmail = null;
|
||||||
if(tx.OrganizationId.HasValue)
|
if (tx.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
var org = await _organizationRepository.GetByIdAsync(tx.OrganizationId.Value);
|
var org = await _organizationRepository.GetByIdAsync(tx.OrganizationId.Value);
|
||||||
if(org != null)
|
if (org != null)
|
||||||
{
|
{
|
||||||
billingEmail = org.BillingEmailAddress();
|
billingEmail = org.BillingEmailAddress();
|
||||||
if(await _paymentService.CreditAccountAsync(org, tx.Amount))
|
if (await _paymentService.CreditAccountAsync(org, tx.Amount))
|
||||||
{
|
{
|
||||||
await _organizationRepository.ReplaceAsync(org);
|
await _organizationRepository.ReplaceAsync(org);
|
||||||
}
|
}
|
||||||
@ -135,24 +135,24 @@ namespace Bit.Billing.Controllers
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(tx.UserId.Value);
|
var user = await _userRepository.GetByIdAsync(tx.UserId.Value);
|
||||||
if(user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
billingEmail = user.BillingEmailAddress();
|
billingEmail = user.BillingEmailAddress();
|
||||||
if(await _paymentService.CreditAccountAsync(user, tx.Amount))
|
if (await _paymentService.CreditAccountAsync(user, tx.Amount))
|
||||||
{
|
{
|
||||||
await _userRepository.ReplaceAsync(user);
|
await _userRepository.ReplaceAsync(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(billingEmail))
|
if (!string.IsNullOrWhiteSpace(billingEmail))
|
||||||
{
|
{
|
||||||
await _mailService.SendAddedCreditAsync(billingEmail, tx.Amount);
|
await _mailService.SendAddedCreditAsync(billingEmail, tx.Amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Catch foreign key violations because user/org could have been deleted.
|
// Catch foreign key violations because user/org could have been deleted.
|
||||||
catch(SqlException e) when(e.Number == 547) { }
|
catch (SqlException e) when(e.Number == 547) { }
|
||||||
|
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
}
|
}
|
||||||
@ -166,7 +166,7 @@ namespace Bit.Billing.Controllers
|
|||||||
{
|
{
|
||||||
var transactions = invoice.Transactions?.Where(t => t.Type == null &&
|
var transactions = invoice.Transactions?.Where(t => t.Type == null &&
|
||||||
!string.IsNullOrWhiteSpace(t.Confirmations) && t.Confirmations != "0");
|
!string.IsNullOrWhiteSpace(t.Confirmations) && t.Confirmations != "0");
|
||||||
if(transactions != null && transactions.Count() == 1)
|
if (transactions != null && transactions.Count() == 1)
|
||||||
{
|
{
|
||||||
return DateTime.Parse(transactions.First().ReceivedTime, CultureInfo.InvariantCulture,
|
return DateTime.Parse(transactions.First().ReceivedTime, CultureInfo.InvariantCulture,
|
||||||
DateTimeStyles.RoundtripKind);
|
DateTimeStyles.RoundtripKind);
|
||||||
@ -179,19 +179,19 @@ namespace Bit.Billing.Controllers
|
|||||||
Guid? orgId = null;
|
Guid? orgId = null;
|
||||||
Guid? userId = null;
|
Guid? userId = null;
|
||||||
|
|
||||||
if(invoice != null && !string.IsNullOrWhiteSpace(invoice.PosData) && invoice.PosData.Contains(":"))
|
if (invoice != null && !string.IsNullOrWhiteSpace(invoice.PosData) && invoice.PosData.Contains(":"))
|
||||||
{
|
{
|
||||||
var mainParts = invoice.PosData.Split(',');
|
var mainParts = invoice.PosData.Split(',');
|
||||||
foreach(var mainPart in mainParts)
|
foreach (var mainPart in mainParts)
|
||||||
{
|
{
|
||||||
var parts = mainPart.Split(':');
|
var parts = mainPart.Split(':');
|
||||||
if(parts.Length > 1 && Guid.TryParse(parts[1], out var id))
|
if (parts.Length > 1 && Guid.TryParse(parts[1], out var id))
|
||||||
{
|
{
|
||||||
if(parts[0] == "userId")
|
if (parts[0] == "userId")
|
||||||
{
|
{
|
||||||
userId = id;
|
userId = id;
|
||||||
}
|
}
|
||||||
else if(parts[0] == "organizationId")
|
else if (parts[0] == "organizationId")
|
||||||
{
|
{
|
||||||
orgId = id;
|
orgId = id;
|
||||||
}
|
}
|
||||||
|
@ -49,25 +49,25 @@ namespace Bit.Billing.Controllers
|
|||||||
[HttpPost("webhook")]
|
[HttpPost("webhook")]
|
||||||
public async Task<IActionResult> PostWebhook()
|
public async Task<IActionResult> PostWebhook()
|
||||||
{
|
{
|
||||||
if(HttpContext?.Request?.Query == null)
|
if (HttpContext?.Request?.Query == null)
|
||||||
{
|
{
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = HttpContext.Request.Query.ContainsKey("key") ?
|
var key = HttpContext.Request.Query.ContainsKey("key") ?
|
||||||
HttpContext.Request.Query["key"].ToString() : null;
|
HttpContext.Request.Query["key"].ToString() : null;
|
||||||
if(key != _billingSettings.FreshdeskWebhookKey)
|
if (key != _billingSettings.FreshdeskWebhookKey)
|
||||||
{
|
{
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
string body = null;
|
string body = null;
|
||||||
using(var reader = new StreamReader(HttpContext.Request.Body, Encoding.UTF8))
|
using (var reader = new StreamReader(HttpContext.Request.Body, Encoding.UTF8))
|
||||||
{
|
{
|
||||||
body = await reader.ReadToEndAsync();
|
body = await reader.ReadToEndAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(body))
|
if (string.IsNullOrWhiteSpace(body))
|
||||||
{
|
{
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
@ -78,7 +78,7 @@ namespace Bit.Billing.Controllers
|
|||||||
string ticketId = data.ticket_id;
|
string ticketId = data.ticket_id;
|
||||||
string ticketContactEmail = data.ticket_contact_email;
|
string ticketContactEmail = data.ticket_contact_email;
|
||||||
string ticketTags = data.ticket_tags;
|
string ticketTags = data.ticket_tags;
|
||||||
if(string.IsNullOrWhiteSpace(ticketId) || string.IsNullOrWhiteSpace(ticketContactEmail))
|
if (string.IsNullOrWhiteSpace(ticketId) || string.IsNullOrWhiteSpace(ticketContactEmail))
|
||||||
{
|
{
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
@ -86,32 +86,32 @@ namespace Bit.Billing.Controllers
|
|||||||
var updateBody = new Dictionary<string, object>();
|
var updateBody = new Dictionary<string, object>();
|
||||||
var note = string.Empty;
|
var note = string.Empty;
|
||||||
var user = await _userRepository.GetByEmailAsync(ticketContactEmail);
|
var user = await _userRepository.GetByEmailAsync(ticketContactEmail);
|
||||||
if(user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
note += $"<li>User, {user.Email}: {_globalSettings.BaseServiceUri.Admin}/users/edit/{user.Id}</li>";
|
note += $"<li>User, {user.Email}: {_globalSettings.BaseServiceUri.Admin}/users/edit/{user.Id}</li>";
|
||||||
var tags = new HashSet<string>();
|
var tags = new HashSet<string>();
|
||||||
if(user.Premium)
|
if (user.Premium)
|
||||||
{
|
{
|
||||||
tags.Add("Premium");
|
tags.Add("Premium");
|
||||||
}
|
}
|
||||||
var orgs = await _organizationRepository.GetManyByUserIdAsync(user.Id);
|
var orgs = await _organizationRepository.GetManyByUserIdAsync(user.Id);
|
||||||
foreach(var org in orgs)
|
foreach (var org in orgs)
|
||||||
{
|
{
|
||||||
note += $"<li>Org, {org.Name}: " +
|
note += $"<li>Org, {org.Name}: " +
|
||||||
$"{_globalSettings.BaseServiceUri.Admin}/organizations/edit/{org.Id}</li>";
|
$"{_globalSettings.BaseServiceUri.Admin}/organizations/edit/{org.Id}</li>";
|
||||||
var planName = GetAttribute<DisplayAttribute>(org.PlanType).Name.Split(" ").FirstOrDefault();
|
var planName = GetAttribute<DisplayAttribute>(org.PlanType).Name.Split(" ").FirstOrDefault();
|
||||||
if(!string.IsNullOrWhiteSpace(planName))
|
if (!string.IsNullOrWhiteSpace(planName))
|
||||||
{
|
{
|
||||||
tags.Add(string.Format("Org: {0}", planName));
|
tags.Add(string.Format("Org: {0}", planName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(tags.Any())
|
if (tags.Any())
|
||||||
{
|
{
|
||||||
var tagsToUpdate = tags.ToList();
|
var tagsToUpdate = tags.ToList();
|
||||||
if(!string.IsNullOrWhiteSpace(ticketTags))
|
if (!string.IsNullOrWhiteSpace(ticketTags))
|
||||||
{
|
{
|
||||||
var splitTicketTags = ticketTags.Split(',');
|
var splitTicketTags = ticketTags.Split(',');
|
||||||
for(var i = 0; i < splitTicketTags.Length; i++)
|
for (var i = 0; i < splitTicketTags.Length; i++)
|
||||||
{
|
{
|
||||||
tagsToUpdate.Insert(i, splitTicketTags[i]);
|
tagsToUpdate.Insert(i, splitTicketTags[i]);
|
||||||
}
|
}
|
||||||
@ -139,7 +139,7 @@ namespace Bit.Billing.Controllers
|
|||||||
|
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError(e, "Error processing freshdesk webhook.");
|
_logger.LogError(e, "Error processing freshdesk webhook.");
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
@ -152,14 +152,14 @@ namespace Bit.Billing.Controllers
|
|||||||
{
|
{
|
||||||
request.Headers.Add("Authorization", _freshdeskAuthkey);
|
request.Headers.Add("Authorization", _freshdeskAuthkey);
|
||||||
var response = await _httpClient.SendAsync(request);
|
var response = await _httpClient.SendAsync(request);
|
||||||
if(response.StatusCode != System.Net.HttpStatusCode.TooManyRequests || retriedCount > 3)
|
if (response.StatusCode != System.Net.HttpStatusCode.TooManyRequests || retriedCount > 3)
|
||||||
{
|
{
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
if(retriedCount > 3)
|
if (retriedCount > 3)
|
||||||
{
|
{
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
@ -26,11 +26,11 @@ namespace Billing.Controllers
|
|||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> Index(LoginModel model)
|
public async Task<IActionResult> Index(LoginModel model)
|
||||||
{
|
{
|
||||||
if(ModelState.IsValid)
|
if (ModelState.IsValid)
|
||||||
{
|
{
|
||||||
var result = await _signInManager.PasswordlessSignInAsync(model.Email,
|
var result = await _signInManager.PasswordlessSignInAsync(model.Email,
|
||||||
Url.Action("Confirm", "Login", null, Request.Scheme));
|
Url.Action("Confirm", "Login", null, Request.Scheme));
|
||||||
if(result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
return RedirectToAction("Index", "Home");
|
return RedirectToAction("Index", "Home");
|
||||||
}
|
}
|
||||||
@ -46,7 +46,7 @@ namespace Billing.Controllers
|
|||||||
public async Task<IActionResult> Confirm(string email, string token)
|
public async Task<IActionResult> Confirm(string email, string token)
|
||||||
{
|
{
|
||||||
var result = await _signInManager.PasswordlessSignInAsync(email, token, false);
|
var result = await _signInManager.PasswordlessSignInAsync(email, token, false);
|
||||||
if(!result.Succeeded)
|
if (!result.Succeeded)
|
||||||
{
|
{
|
||||||
return View("Error");
|
return View("Error");
|
||||||
}
|
}
|
||||||
|
@ -48,64 +48,64 @@ namespace Bit.Billing.Controllers
|
|||||||
[HttpPost("ipn")]
|
[HttpPost("ipn")]
|
||||||
public async Task<IActionResult> PostIpn()
|
public async Task<IActionResult> PostIpn()
|
||||||
{
|
{
|
||||||
if(HttpContext?.Request?.Query == null)
|
if (HttpContext?.Request?.Query == null)
|
||||||
{
|
{
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = HttpContext.Request.Query.ContainsKey("key") ?
|
var key = HttpContext.Request.Query.ContainsKey("key") ?
|
||||||
HttpContext.Request.Query["key"].ToString() : null;
|
HttpContext.Request.Query["key"].ToString() : null;
|
||||||
if(key != _billingSettings.PayPal.WebhookKey)
|
if (key != _billingSettings.PayPal.WebhookKey)
|
||||||
{
|
{
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
string body = null;
|
string body = null;
|
||||||
using(var reader = new StreamReader(HttpContext.Request.Body, Encoding.UTF8))
|
using (var reader = new StreamReader(HttpContext.Request.Body, Encoding.UTF8))
|
||||||
{
|
{
|
||||||
body = await reader.ReadToEndAsync();
|
body = await reader.ReadToEndAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(body))
|
if (string.IsNullOrWhiteSpace(body))
|
||||||
{
|
{
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
var verified = await _paypalIpnClient.VerifyIpnAsync(body);
|
var verified = await _paypalIpnClient.VerifyIpnAsync(body);
|
||||||
if(!verified)
|
if (!verified)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Unverified IPN received.");
|
_logger.LogWarning("Unverified IPN received.");
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
var ipnTransaction = new PayPalIpnClient.IpnTransaction(body);
|
var ipnTransaction = new PayPalIpnClient.IpnTransaction(body);
|
||||||
if(ipnTransaction.TxnType != "web_accept" && ipnTransaction.TxnType != "merch_pmt" &&
|
if (ipnTransaction.TxnType != "web_accept" && ipnTransaction.TxnType != "merch_pmt" &&
|
||||||
ipnTransaction.PaymentStatus != "Refunded")
|
ipnTransaction.PaymentStatus != "Refunded")
|
||||||
{
|
{
|
||||||
// Only processing billing agreement payments, buy now button payments, and refunds for now.
|
// Only processing billing agreement payments, buy now button payments, and refunds for now.
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ipnTransaction.ReceiverId != _billingSettings.PayPal.BusinessId)
|
if (ipnTransaction.ReceiverId != _billingSettings.PayPal.BusinessId)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Receiver was not proper business id. " + ipnTransaction.ReceiverId);
|
_logger.LogWarning("Receiver was not proper business id. " + ipnTransaction.ReceiverId);
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ipnTransaction.PaymentStatus == "Refunded" && ipnTransaction.ParentTxnId == null)
|
if (ipnTransaction.PaymentStatus == "Refunded" && ipnTransaction.ParentTxnId == null)
|
||||||
{
|
{
|
||||||
// Refunds require parent transaction
|
// Refunds require parent transaction
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ipnTransaction.PaymentType == "echeck" && ipnTransaction.PaymentStatus != "Refunded")
|
if (ipnTransaction.PaymentType == "echeck" && ipnTransaction.PaymentStatus != "Refunded")
|
||||||
{
|
{
|
||||||
// Not accepting eChecks, unless it is a refund
|
// Not accepting eChecks, unless it is a refund
|
||||||
_logger.LogWarning("Got an eCheck payment. " + ipnTransaction.TxnId);
|
_logger.LogWarning("Got an eCheck payment. " + ipnTransaction.TxnId);
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ipnTransaction.McCurrency != "USD")
|
if (ipnTransaction.McCurrency != "USD")
|
||||||
{
|
{
|
||||||
// Only process USD payments
|
// Only process USD payments
|
||||||
_logger.LogWarning("Received a payment not in USD. " + ipnTransaction.TxnId);
|
_logger.LogWarning("Received a payment not in USD. " + ipnTransaction.TxnId);
|
||||||
@ -113,16 +113,16 @@ namespace Bit.Billing.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ids = ipnTransaction.GetIdsFromCustom();
|
var ids = ipnTransaction.GetIdsFromCustom();
|
||||||
if(!ids.Item1.HasValue && !ids.Item2.HasValue)
|
if (!ids.Item1.HasValue && !ids.Item2.HasValue)
|
||||||
{
|
{
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ipnTransaction.PaymentStatus == "Completed")
|
if (ipnTransaction.PaymentStatus == "Completed")
|
||||||
{
|
{
|
||||||
var transaction = await _transactionRepository.GetByGatewayIdAsync(
|
var transaction = await _transactionRepository.GetByGatewayIdAsync(
|
||||||
GatewayType.PayPal, ipnTransaction.TxnId);
|
GatewayType.PayPal, ipnTransaction.TxnId);
|
||||||
if(transaction != null)
|
if (transaction != null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Already processed this completed transaction. #" + ipnTransaction.TxnId);
|
_logger.LogWarning("Already processed this completed transaction. #" + ipnTransaction.TxnId);
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
@ -145,16 +145,16 @@ namespace Bit.Billing.Controllers
|
|||||||
};
|
};
|
||||||
await _transactionRepository.CreateAsync(tx);
|
await _transactionRepository.CreateAsync(tx);
|
||||||
|
|
||||||
if(isAccountCredit)
|
if (isAccountCredit)
|
||||||
{
|
{
|
||||||
string billingEmail = null;
|
string billingEmail = null;
|
||||||
if(tx.OrganizationId.HasValue)
|
if (tx.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
var org = await _organizationRepository.GetByIdAsync(tx.OrganizationId.Value);
|
var org = await _organizationRepository.GetByIdAsync(tx.OrganizationId.Value);
|
||||||
if(org != null)
|
if (org != null)
|
||||||
{
|
{
|
||||||
billingEmail = org.BillingEmailAddress();
|
billingEmail = org.BillingEmailAddress();
|
||||||
if(await _paymentService.CreditAccountAsync(org, tx.Amount))
|
if (await _paymentService.CreditAccountAsync(org, tx.Amount))
|
||||||
{
|
{
|
||||||
await _organizationRepository.ReplaceAsync(org);
|
await _organizationRepository.ReplaceAsync(org);
|
||||||
}
|
}
|
||||||
@ -163,30 +163,30 @@ namespace Bit.Billing.Controllers
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(tx.UserId.Value);
|
var user = await _userRepository.GetByIdAsync(tx.UserId.Value);
|
||||||
if(user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
billingEmail = user.BillingEmailAddress();
|
billingEmail = user.BillingEmailAddress();
|
||||||
if(await _paymentService.CreditAccountAsync(user, tx.Amount))
|
if (await _paymentService.CreditAccountAsync(user, tx.Amount))
|
||||||
{
|
{
|
||||||
await _userRepository.ReplaceAsync(user);
|
await _userRepository.ReplaceAsync(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(billingEmail))
|
if (!string.IsNullOrWhiteSpace(billingEmail))
|
||||||
{
|
{
|
||||||
await _mailService.SendAddedCreditAsync(billingEmail, tx.Amount);
|
await _mailService.SendAddedCreditAsync(billingEmail, tx.Amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Catch foreign key violations because user/org could have been deleted.
|
// Catch foreign key violations because user/org could have been deleted.
|
||||||
catch(SqlException e) when(e.Number == 547) { }
|
catch (SqlException e) when(e.Number == 547) { }
|
||||||
}
|
}
|
||||||
else if(ipnTransaction.PaymentStatus == "Refunded" || ipnTransaction.PaymentStatus == "Reversed")
|
else if (ipnTransaction.PaymentStatus == "Refunded" || ipnTransaction.PaymentStatus == "Reversed")
|
||||||
{
|
{
|
||||||
var refundTransaction = await _transactionRepository.GetByGatewayIdAsync(
|
var refundTransaction = await _transactionRepository.GetByGatewayIdAsync(
|
||||||
GatewayType.PayPal, ipnTransaction.TxnId);
|
GatewayType.PayPal, ipnTransaction.TxnId);
|
||||||
if(refundTransaction != null)
|
if (refundTransaction != null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Already processed this refunded transaction. #" + ipnTransaction.TxnId);
|
_logger.LogWarning("Already processed this refunded transaction. #" + ipnTransaction.TxnId);
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
@ -194,7 +194,7 @@ namespace Bit.Billing.Controllers
|
|||||||
|
|
||||||
var parentTransaction = await _transactionRepository.GetByGatewayIdAsync(
|
var parentTransaction = await _transactionRepository.GetByGatewayIdAsync(
|
||||||
GatewayType.PayPal, ipnTransaction.ParentTxnId);
|
GatewayType.PayPal, ipnTransaction.ParentTxnId);
|
||||||
if(parentTransaction == null)
|
if (parentTransaction == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Parent transaction was not found. " + ipnTransaction.TxnId);
|
_logger.LogWarning("Parent transaction was not found. " + ipnTransaction.TxnId);
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
@ -203,12 +203,12 @@ namespace Bit.Billing.Controllers
|
|||||||
var refundAmount = System.Math.Abs(ipnTransaction.McGross);
|
var refundAmount = System.Math.Abs(ipnTransaction.McGross);
|
||||||
var remainingAmount = parentTransaction.Amount -
|
var remainingAmount = parentTransaction.Amount -
|
||||||
parentTransaction.RefundedAmount.GetValueOrDefault();
|
parentTransaction.RefundedAmount.GetValueOrDefault();
|
||||||
if(refundAmount > 0 && !parentTransaction.Refunded.GetValueOrDefault() &&
|
if (refundAmount > 0 && !parentTransaction.Refunded.GetValueOrDefault() &&
|
||||||
remainingAmount >= refundAmount)
|
remainingAmount >= refundAmount)
|
||||||
{
|
{
|
||||||
parentTransaction.RefundedAmount =
|
parentTransaction.RefundedAmount =
|
||||||
parentTransaction.RefundedAmount.GetValueOrDefault() + refundAmount;
|
parentTransaction.RefundedAmount.GetValueOrDefault() + refundAmount;
|
||||||
if(parentTransaction.RefundedAmount == parentTransaction.Amount)
|
if (parentTransaction.RefundedAmount == parentTransaction.Amount)
|
||||||
{
|
{
|
||||||
parentTransaction.Refunded = true;
|
parentTransaction.Refunded = true;
|
||||||
}
|
}
|
||||||
|
@ -69,26 +69,26 @@ namespace Bit.Billing.Controllers
|
|||||||
[HttpPost("webhook")]
|
[HttpPost("webhook")]
|
||||||
public async Task<IActionResult> PostWebhook([FromQuery] string key)
|
public async Task<IActionResult> PostWebhook([FromQuery] string key)
|
||||||
{
|
{
|
||||||
if(key != _billingSettings.StripeWebhookKey)
|
if (key != _billingSettings.StripeWebhookKey)
|
||||||
{
|
{
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
Stripe.Event parsedEvent;
|
Stripe.Event parsedEvent;
|
||||||
using(var sr = new StreamReader(HttpContext.Request.Body))
|
using (var sr = new StreamReader(HttpContext.Request.Body))
|
||||||
{
|
{
|
||||||
var json = await sr.ReadToEndAsync();
|
var json = await sr.ReadToEndAsync();
|
||||||
parsedEvent = EventUtility.ConstructEvent(json, Request.Headers["Stripe-Signature"],
|
parsedEvent = EventUtility.ConstructEvent(json, Request.Headers["Stripe-Signature"],
|
||||||
_billingSettings.StripeWebhookSecret);
|
_billingSettings.StripeWebhookSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(parsedEvent?.Id))
|
if (string.IsNullOrWhiteSpace(parsedEvent?.Id))
|
||||||
{
|
{
|
||||||
_logger.LogWarning("No event id.");
|
_logger.LogWarning("No event id.");
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_hostingEnvironment.IsProduction() && !parsedEvent.Livemode)
|
if (_hostingEnvironment.IsProduction() && !parsedEvent.Livemode)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Getting test events in production.");
|
_logger.LogWarning("Getting test events in production.");
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
@ -97,7 +97,7 @@ namespace Bit.Billing.Controllers
|
|||||||
var subDeleted = parsedEvent.Type.Equals("customer.subscription.deleted");
|
var subDeleted = parsedEvent.Type.Equals("customer.subscription.deleted");
|
||||||
var subUpdated = parsedEvent.Type.Equals("customer.subscription.updated");
|
var subUpdated = parsedEvent.Type.Equals("customer.subscription.updated");
|
||||||
|
|
||||||
if(subDeleted || subUpdated)
|
if (subDeleted || subUpdated)
|
||||||
{
|
{
|
||||||
var subscription = await GetSubscriptionAsync(parsedEvent, true);
|
var subscription = await GetSubscriptionAsync(parsedEvent, true);
|
||||||
var ids = GetIdsFromMetaData(subscription.Metadata);
|
var ids = GetIdsFromMetaData(subscription.Metadata);
|
||||||
@ -106,42 +106,42 @@ namespace Bit.Billing.Controllers
|
|||||||
var subUnpaid = subUpdated && subscription.Status == "unpaid";
|
var subUnpaid = subUpdated && subscription.Status == "unpaid";
|
||||||
var subIncompleteExpired = subUpdated && subscription.Status == "incomplete_expired";
|
var subIncompleteExpired = subUpdated && subscription.Status == "incomplete_expired";
|
||||||
|
|
||||||
if(subCanceled || subUnpaid || subIncompleteExpired)
|
if (subCanceled || subUnpaid || subIncompleteExpired)
|
||||||
{
|
{
|
||||||
// org
|
// org
|
||||||
if(ids.Item1.HasValue)
|
if (ids.Item1.HasValue)
|
||||||
{
|
{
|
||||||
await _organizationService.DisableAsync(ids.Item1.Value, subscription.CurrentPeriodEnd);
|
await _organizationService.DisableAsync(ids.Item1.Value, subscription.CurrentPeriodEnd);
|
||||||
}
|
}
|
||||||
// user
|
// user
|
||||||
else if(ids.Item2.HasValue)
|
else if (ids.Item2.HasValue)
|
||||||
{
|
{
|
||||||
await _userService.DisablePremiumAsync(ids.Item2.Value, subscription.CurrentPeriodEnd);
|
await _userService.DisablePremiumAsync(ids.Item2.Value, subscription.CurrentPeriodEnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(subUpdated)
|
if (subUpdated)
|
||||||
{
|
{
|
||||||
// org
|
// org
|
||||||
if(ids.Item1.HasValue)
|
if (ids.Item1.HasValue)
|
||||||
{
|
{
|
||||||
await _organizationService.UpdateExpirationDateAsync(ids.Item1.Value,
|
await _organizationService.UpdateExpirationDateAsync(ids.Item1.Value,
|
||||||
subscription.CurrentPeriodEnd);
|
subscription.CurrentPeriodEnd);
|
||||||
}
|
}
|
||||||
// user
|
// user
|
||||||
else if(ids.Item2.HasValue)
|
else if (ids.Item2.HasValue)
|
||||||
{
|
{
|
||||||
await _userService.UpdatePremiumExpirationAsync(ids.Item2.Value,
|
await _userService.UpdatePremiumExpirationAsync(ids.Item2.Value,
|
||||||
subscription.CurrentPeriodEnd);
|
subscription.CurrentPeriodEnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(parsedEvent.Type.Equals("invoice.upcoming"))
|
else if (parsedEvent.Type.Equals("invoice.upcoming"))
|
||||||
{
|
{
|
||||||
var invoice = await GetInvoiceAsync(parsedEvent);
|
var invoice = await GetInvoiceAsync(parsedEvent);
|
||||||
var subscriptionService = new SubscriptionService();
|
var subscriptionService = new SubscriptionService();
|
||||||
var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId);
|
var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId);
|
||||||
if(subscription == null)
|
if (subscription == null)
|
||||||
{
|
{
|
||||||
throw new Exception("Invoice subscription is null. " + invoice.Id);
|
throw new Exception("Invoice subscription is null. " + invoice.Id);
|
||||||
}
|
}
|
||||||
@ -149,37 +149,37 @@ namespace Bit.Billing.Controllers
|
|||||||
string email = null;
|
string email = null;
|
||||||
var ids = GetIdsFromMetaData(subscription.Metadata);
|
var ids = GetIdsFromMetaData(subscription.Metadata);
|
||||||
// org
|
// org
|
||||||
if(ids.Item1.HasValue)
|
if (ids.Item1.HasValue)
|
||||||
{
|
{
|
||||||
var org = await _organizationRepository.GetByIdAsync(ids.Item1.Value);
|
var org = await _organizationRepository.GetByIdAsync(ids.Item1.Value);
|
||||||
if(org != null && OrgPlanForInvoiceNotifications(org))
|
if (org != null && OrgPlanForInvoiceNotifications(org))
|
||||||
{
|
{
|
||||||
email = org.BillingEmail;
|
email = org.BillingEmail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// user
|
// user
|
||||||
else if(ids.Item2.HasValue)
|
else if (ids.Item2.HasValue)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByIdAsync(ids.Item2.Value);
|
var user = await _userService.GetUserByIdAsync(ids.Item2.Value);
|
||||||
if(user.Premium)
|
if (user.Premium)
|
||||||
{
|
{
|
||||||
email = user.Email;
|
email = user.Email;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(email) && invoice.NextPaymentAttempt.HasValue)
|
if (!string.IsNullOrWhiteSpace(email) && invoice.NextPaymentAttempt.HasValue)
|
||||||
{
|
{
|
||||||
var items = invoice.Lines.Select(i => i.Description).ToList();
|
var items = invoice.Lines.Select(i => i.Description).ToList();
|
||||||
await _mailService.SendInvoiceUpcomingAsync(email, invoice.AmountDue / 100M,
|
await _mailService.SendInvoiceUpcomingAsync(email, invoice.AmountDue / 100M,
|
||||||
invoice.NextPaymentAttempt.Value, items, true);
|
invoice.NextPaymentAttempt.Value, items, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(parsedEvent.Type.Equals("charge.succeeded"))
|
else if (parsedEvent.Type.Equals("charge.succeeded"))
|
||||||
{
|
{
|
||||||
var charge = await GetChargeAsync(parsedEvent);
|
var charge = await GetChargeAsync(parsedEvent);
|
||||||
var chargeTransaction = await _transactionRepository.GetByGatewayIdAsync(
|
var chargeTransaction = await _transactionRepository.GetByGatewayIdAsync(
|
||||||
GatewayType.Stripe, charge.Id);
|
GatewayType.Stripe, charge.Id);
|
||||||
if(chargeTransaction != null)
|
if (chargeTransaction != null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Charge success already processed. " + charge.Id);
|
_logger.LogWarning("Charge success already processed. " + charge.Id);
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
@ -189,29 +189,29 @@ namespace Bit.Billing.Controllers
|
|||||||
Subscription subscription = null;
|
Subscription subscription = null;
|
||||||
var subscriptionService = new SubscriptionService();
|
var subscriptionService = new SubscriptionService();
|
||||||
|
|
||||||
if(charge.InvoiceId != null)
|
if (charge.InvoiceId != null)
|
||||||
{
|
{
|
||||||
var invoiceService = new InvoiceService();
|
var invoiceService = new InvoiceService();
|
||||||
var invoice = await invoiceService.GetAsync(charge.InvoiceId);
|
var invoice = await invoiceService.GetAsync(charge.InvoiceId);
|
||||||
if(invoice?.SubscriptionId != null)
|
if (invoice?.SubscriptionId != null)
|
||||||
{
|
{
|
||||||
subscription = await subscriptionService.GetAsync(invoice.SubscriptionId);
|
subscription = await subscriptionService.GetAsync(invoice.SubscriptionId);
|
||||||
ids = GetIdsFromMetaData(subscription?.Metadata);
|
ids = GetIdsFromMetaData(subscription?.Metadata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(subscription == null || ids == null || (ids.Item1.HasValue && ids.Item2.HasValue))
|
if (subscription == null || ids == null || (ids.Item1.HasValue && ids.Item2.HasValue))
|
||||||
{
|
{
|
||||||
var subscriptions = await subscriptionService.ListAsync(new SubscriptionListOptions
|
var subscriptions = await subscriptionService.ListAsync(new SubscriptionListOptions
|
||||||
{
|
{
|
||||||
CustomerId = charge.CustomerId
|
CustomerId = charge.CustomerId
|
||||||
});
|
});
|
||||||
foreach(var sub in subscriptions)
|
foreach (var sub in subscriptions)
|
||||||
{
|
{
|
||||||
if(sub.Status != "canceled" && sub.Status != "incomplete_expired")
|
if (sub.Status != "canceled" && sub.Status != "incomplete_expired")
|
||||||
{
|
{
|
||||||
ids = GetIdsFromMetaData(sub.Metadata);
|
ids = GetIdsFromMetaData(sub.Metadata);
|
||||||
if(ids.Item1.HasValue || ids.Item2.HasValue)
|
if (ids.Item1.HasValue || ids.Item2.HasValue)
|
||||||
{
|
{
|
||||||
subscription = sub;
|
subscription = sub;
|
||||||
break;
|
break;
|
||||||
@ -220,7 +220,7 @@ namespace Bit.Billing.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!ids.Item1.HasValue && !ids.Item2.HasValue)
|
if (!ids.Item1.HasValue && !ids.Item2.HasValue)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Charge success has no subscriber ids. " + charge.Id);
|
_logger.LogWarning("Charge success has no subscriber ids. " + charge.Id);
|
||||||
return new BadRequestResult();
|
return new BadRequestResult();
|
||||||
@ -237,50 +237,50 @@ namespace Bit.Billing.Controllers
|
|||||||
GatewayId = charge.Id
|
GatewayId = charge.Id
|
||||||
};
|
};
|
||||||
|
|
||||||
if(charge.Source != null && charge.Source is Card card)
|
if (charge.Source != null && charge.Source is Card card)
|
||||||
{
|
{
|
||||||
tx.PaymentMethodType = PaymentMethodType.Card;
|
tx.PaymentMethodType = PaymentMethodType.Card;
|
||||||
tx.Details = $"{card.Brand}, *{card.Last4}";
|
tx.Details = $"{card.Brand}, *{card.Last4}";
|
||||||
}
|
}
|
||||||
else if(charge.Source != null && charge.Source is BankAccount bankAccount)
|
else if (charge.Source != null && charge.Source is BankAccount bankAccount)
|
||||||
{
|
{
|
||||||
tx.PaymentMethodType = PaymentMethodType.BankAccount;
|
tx.PaymentMethodType = PaymentMethodType.BankAccount;
|
||||||
tx.Details = $"{bankAccount.BankName}, *{bankAccount.Last4}";
|
tx.Details = $"{bankAccount.BankName}, *{bankAccount.Last4}";
|
||||||
}
|
}
|
||||||
else if(charge.Source != null && charge.Source is Source source)
|
else if (charge.Source != null && charge.Source is Source source)
|
||||||
{
|
{
|
||||||
if(source.Card != null)
|
if (source.Card != null)
|
||||||
{
|
{
|
||||||
tx.PaymentMethodType = PaymentMethodType.Card;
|
tx.PaymentMethodType = PaymentMethodType.Card;
|
||||||
tx.Details = $"{source.Card.Brand}, *{source.Card.Last4}";
|
tx.Details = $"{source.Card.Brand}, *{source.Card.Last4}";
|
||||||
}
|
}
|
||||||
else if(source.AchDebit != null)
|
else if (source.AchDebit != null)
|
||||||
{
|
{
|
||||||
tx.PaymentMethodType = PaymentMethodType.BankAccount;
|
tx.PaymentMethodType = PaymentMethodType.BankAccount;
|
||||||
tx.Details = $"{source.AchDebit.BankName}, *{source.AchDebit.Last4}";
|
tx.Details = $"{source.AchDebit.BankName}, *{source.AchDebit.Last4}";
|
||||||
}
|
}
|
||||||
else if(source.AchCreditTransfer != null)
|
else if (source.AchCreditTransfer != null)
|
||||||
{
|
{
|
||||||
tx.PaymentMethodType = PaymentMethodType.BankAccount;
|
tx.PaymentMethodType = PaymentMethodType.BankAccount;
|
||||||
tx.Details = $"ACH => {source.AchCreditTransfer.BankName}, " +
|
tx.Details = $"ACH => {source.AchCreditTransfer.BankName}, " +
|
||||||
$"{source.AchCreditTransfer.AccountNumber}";
|
$"{source.AchCreditTransfer.AccountNumber}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(charge.PaymentMethodDetails != null)
|
else if (charge.PaymentMethodDetails != null)
|
||||||
{
|
{
|
||||||
if(charge.PaymentMethodDetails.Card != null)
|
if (charge.PaymentMethodDetails.Card != null)
|
||||||
{
|
{
|
||||||
tx.PaymentMethodType = PaymentMethodType.Card;
|
tx.PaymentMethodType = PaymentMethodType.Card;
|
||||||
tx.Details = $"{charge.PaymentMethodDetails.Card.Brand?.ToUpperInvariant()}, " +
|
tx.Details = $"{charge.PaymentMethodDetails.Card.Brand?.ToUpperInvariant()}, " +
|
||||||
$"*{charge.PaymentMethodDetails.Card.Last4}";
|
$"*{charge.PaymentMethodDetails.Card.Last4}";
|
||||||
}
|
}
|
||||||
else if(charge.PaymentMethodDetails.AchDebit != null)
|
else if (charge.PaymentMethodDetails.AchDebit != null)
|
||||||
{
|
{
|
||||||
tx.PaymentMethodType = PaymentMethodType.BankAccount;
|
tx.PaymentMethodType = PaymentMethodType.BankAccount;
|
||||||
tx.Details = $"{charge.PaymentMethodDetails.AchDebit.BankName}, " +
|
tx.Details = $"{charge.PaymentMethodDetails.AchDebit.BankName}, " +
|
||||||
$"*{charge.PaymentMethodDetails.AchDebit.Last4}";
|
$"*{charge.PaymentMethodDetails.AchDebit.Last4}";
|
||||||
}
|
}
|
||||||
else if(charge.PaymentMethodDetails.AchCreditTransfer != null)
|
else if (charge.PaymentMethodDetails.AchCreditTransfer != null)
|
||||||
{
|
{
|
||||||
tx.PaymentMethodType = PaymentMethodType.BankAccount;
|
tx.PaymentMethodType = PaymentMethodType.BankAccount;
|
||||||
tx.Details = $"ACH => {charge.PaymentMethodDetails.AchCreditTransfer.BankName}, " +
|
tx.Details = $"ACH => {charge.PaymentMethodDetails.AchCreditTransfer.BankName}, " +
|
||||||
@ -288,7 +288,7 @@ namespace Bit.Billing.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!tx.PaymentMethodType.HasValue)
|
if (!tx.PaymentMethodType.HasValue)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Charge success from unsupported source/method. " + charge.Id);
|
_logger.LogWarning("Charge success from unsupported source/method. " + charge.Id);
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
@ -299,35 +299,35 @@ namespace Bit.Billing.Controllers
|
|||||||
await _transactionRepository.CreateAsync(tx);
|
await _transactionRepository.CreateAsync(tx);
|
||||||
}
|
}
|
||||||
// Catch foreign key violations because user/org could have been deleted.
|
// Catch foreign key violations because user/org could have been deleted.
|
||||||
catch(SqlException e) when(e.Number == 547) { }
|
catch (SqlException e) when(e.Number == 547) { }
|
||||||
}
|
}
|
||||||
else if(parsedEvent.Type.Equals("charge.refunded"))
|
else if (parsedEvent.Type.Equals("charge.refunded"))
|
||||||
{
|
{
|
||||||
var charge = await GetChargeAsync(parsedEvent);
|
var charge = await GetChargeAsync(parsedEvent);
|
||||||
var chargeTransaction = await _transactionRepository.GetByGatewayIdAsync(
|
var chargeTransaction = await _transactionRepository.GetByGatewayIdAsync(
|
||||||
GatewayType.Stripe, charge.Id);
|
GatewayType.Stripe, charge.Id);
|
||||||
if(chargeTransaction == null)
|
if (chargeTransaction == null)
|
||||||
{
|
{
|
||||||
throw new Exception("Cannot find refunded charge. " + charge.Id);
|
throw new Exception("Cannot find refunded charge. " + charge.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
var amountRefunded = charge.AmountRefunded / 100M;
|
var amountRefunded = charge.AmountRefunded / 100M;
|
||||||
|
|
||||||
if(!chargeTransaction.Refunded.GetValueOrDefault() &&
|
if (!chargeTransaction.Refunded.GetValueOrDefault() &&
|
||||||
chargeTransaction.RefundedAmount.GetValueOrDefault() < amountRefunded)
|
chargeTransaction.RefundedAmount.GetValueOrDefault() < amountRefunded)
|
||||||
{
|
{
|
||||||
chargeTransaction.RefundedAmount = amountRefunded;
|
chargeTransaction.RefundedAmount = amountRefunded;
|
||||||
if(charge.Refunded)
|
if (charge.Refunded)
|
||||||
{
|
{
|
||||||
chargeTransaction.Refunded = true;
|
chargeTransaction.Refunded = true;
|
||||||
}
|
}
|
||||||
await _transactionRepository.ReplaceAsync(chargeTransaction);
|
await _transactionRepository.ReplaceAsync(chargeTransaction);
|
||||||
|
|
||||||
foreach(var refund in charge.Refunds)
|
foreach (var refund in charge.Refunds)
|
||||||
{
|
{
|
||||||
var refundTransaction = await _transactionRepository.GetByGatewayIdAsync(
|
var refundTransaction = await _transactionRepository.GetByGatewayIdAsync(
|
||||||
GatewayType.Stripe, refund.Id);
|
GatewayType.Stripe, refund.Id);
|
||||||
if(refundTransaction != null)
|
if (refundTransaction != null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -351,33 +351,33 @@ namespace Bit.Billing.Controllers
|
|||||||
_logger.LogWarning("Charge refund amount doesn't seem correct. " + charge.Id);
|
_logger.LogWarning("Charge refund amount doesn't seem correct. " + charge.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(parsedEvent.Type.Equals("invoice.payment_succeeded"))
|
else if (parsedEvent.Type.Equals("invoice.payment_succeeded"))
|
||||||
{
|
{
|
||||||
var invoice = await GetInvoiceAsync(parsedEvent, true);
|
var invoice = await GetInvoiceAsync(parsedEvent, true);
|
||||||
if(invoice.Paid && invoice.BillingReason == "subscription_create")
|
if (invoice.Paid && invoice.BillingReason == "subscription_create")
|
||||||
{
|
{
|
||||||
var subscriptionService = new SubscriptionService();
|
var subscriptionService = new SubscriptionService();
|
||||||
var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId);
|
var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId);
|
||||||
if(subscription?.Status == "active")
|
if (subscription?.Status == "active")
|
||||||
{
|
{
|
||||||
if(DateTime.UtcNow - invoice.Created < TimeSpan.FromMinutes(1))
|
if (DateTime.UtcNow - invoice.Created < TimeSpan.FromMinutes(1))
|
||||||
{
|
{
|
||||||
await Task.Delay(5000);
|
await Task.Delay(5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
var ids = GetIdsFromMetaData(subscription.Metadata);
|
var ids = GetIdsFromMetaData(subscription.Metadata);
|
||||||
// org
|
// org
|
||||||
if(ids.Item1.HasValue)
|
if (ids.Item1.HasValue)
|
||||||
{
|
{
|
||||||
if(subscription.Items.Any(i => StaticStore.Plans.Any(p => p.StripePlanId == i.Plan.Id)))
|
if (subscription.Items.Any(i => StaticStore.Plans.Any(p => p.StripePlanId == i.Plan.Id)))
|
||||||
{
|
{
|
||||||
await _organizationService.EnableAsync(ids.Item1.Value, subscription.CurrentPeriodEnd);
|
await _organizationService.EnableAsync(ids.Item1.Value, subscription.CurrentPeriodEnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// user
|
// user
|
||||||
else if(ids.Item2.HasValue)
|
else if (ids.Item2.HasValue)
|
||||||
{
|
{
|
||||||
if(subscription.Items.Any(i => i.Plan.Id == "premium-annually"))
|
if (subscription.Items.Any(i => i.Plan.Id == "premium-annually"))
|
||||||
{
|
{
|
||||||
await _userService.EnablePremiumAsync(ids.Item2.Value, subscription.CurrentPeriodEnd);
|
await _userService.EnablePremiumAsync(ids.Item2.Value, subscription.CurrentPeriodEnd);
|
||||||
}
|
}
|
||||||
@ -385,18 +385,18 @@ namespace Bit.Billing.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(parsedEvent.Type.Equals("invoice.payment_failed"))
|
else if (parsedEvent.Type.Equals("invoice.payment_failed"))
|
||||||
{
|
{
|
||||||
var invoice = await GetInvoiceAsync(parsedEvent, true);
|
var invoice = await GetInvoiceAsync(parsedEvent, true);
|
||||||
if(!invoice.Paid && invoice.AttemptCount > 1 && UnpaidAutoChargeInvoiceForSubscriptionCycle(invoice))
|
if (!invoice.Paid && invoice.AttemptCount > 1 && UnpaidAutoChargeInvoiceForSubscriptionCycle(invoice))
|
||||||
{
|
{
|
||||||
await AttemptToPayInvoiceAsync(invoice);
|
await AttemptToPayInvoiceAsync(invoice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(parsedEvent.Type.Equals("invoice.created"))
|
else if (parsedEvent.Type.Equals("invoice.created"))
|
||||||
{
|
{
|
||||||
var invoice = await GetInvoiceAsync(parsedEvent, true);
|
var invoice = await GetInvoiceAsync(parsedEvent, true);
|
||||||
if(!invoice.Paid && UnpaidAutoChargeInvoiceForSubscriptionCycle(invoice))
|
if (!invoice.Paid && UnpaidAutoChargeInvoiceForSubscriptionCycle(invoice))
|
||||||
{
|
{
|
||||||
await AttemptToPayInvoiceAsync(invoice);
|
await AttemptToPayInvoiceAsync(invoice);
|
||||||
}
|
}
|
||||||
@ -411,7 +411,7 @@ namespace Bit.Billing.Controllers
|
|||||||
|
|
||||||
private Tuple<Guid?, Guid?> GetIdsFromMetaData(IDictionary<string, string> metaData)
|
private Tuple<Guid?, Guid?> GetIdsFromMetaData(IDictionary<string, string> metaData)
|
||||||
{
|
{
|
||||||
if(metaData == null || !metaData.Any())
|
if (metaData == null || !metaData.Any())
|
||||||
{
|
{
|
||||||
return new Tuple<Guid?, Guid?>(null, null);
|
return new Tuple<Guid?, Guid?>(null, null);
|
||||||
}
|
}
|
||||||
@ -419,26 +419,26 @@ namespace Bit.Billing.Controllers
|
|||||||
Guid? orgId = null;
|
Guid? orgId = null;
|
||||||
Guid? userId = null;
|
Guid? userId = null;
|
||||||
|
|
||||||
if(metaData.ContainsKey("organizationId"))
|
if (metaData.ContainsKey("organizationId"))
|
||||||
{
|
{
|
||||||
orgId = new Guid(metaData["organizationId"]);
|
orgId = new Guid(metaData["organizationId"]);
|
||||||
}
|
}
|
||||||
else if(metaData.ContainsKey("userId"))
|
else if (metaData.ContainsKey("userId"))
|
||||||
{
|
{
|
||||||
userId = new Guid(metaData["userId"]);
|
userId = new Guid(metaData["userId"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(userId == null && orgId == null)
|
if (userId == null && orgId == null)
|
||||||
{
|
{
|
||||||
var orgIdKey = metaData.Keys.FirstOrDefault(k => k.ToLowerInvariant() == "organizationid");
|
var orgIdKey = metaData.Keys.FirstOrDefault(k => k.ToLowerInvariant() == "organizationid");
|
||||||
if(!string.IsNullOrWhiteSpace(orgIdKey))
|
if (!string.IsNullOrWhiteSpace(orgIdKey))
|
||||||
{
|
{
|
||||||
orgId = new Guid(metaData[orgIdKey]);
|
orgId = new Guid(metaData[orgIdKey]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var userIdKey = metaData.Keys.FirstOrDefault(k => k.ToLowerInvariant() == "userid");
|
var userIdKey = metaData.Keys.FirstOrDefault(k => k.ToLowerInvariant() == "userid");
|
||||||
if(!string.IsNullOrWhiteSpace(userIdKey))
|
if (!string.IsNullOrWhiteSpace(userIdKey))
|
||||||
{
|
{
|
||||||
userId = new Guid(metaData[userIdKey]);
|
userId = new Guid(metaData[userIdKey]);
|
||||||
}
|
}
|
||||||
@ -450,7 +450,7 @@ namespace Bit.Billing.Controllers
|
|||||||
|
|
||||||
private bool OrgPlanForInvoiceNotifications(Organization org)
|
private bool OrgPlanForInvoiceNotifications(Organization org)
|
||||||
{
|
{
|
||||||
switch(org.PlanType)
|
switch (org.PlanType)
|
||||||
{
|
{
|
||||||
case PlanType.FamiliesAnnually:
|
case PlanType.FamiliesAnnually:
|
||||||
case PlanType.TeamsAnnually:
|
case PlanType.TeamsAnnually:
|
||||||
@ -465,11 +465,11 @@ namespace Bit.Billing.Controllers
|
|||||||
{
|
{
|
||||||
var customerService = new CustomerService();
|
var customerService = new CustomerService();
|
||||||
var customer = await customerService.GetAsync(invoice.CustomerId);
|
var customer = await customerService.GetAsync(invoice.CustomerId);
|
||||||
if(customer?.Metadata?.ContainsKey("appleReceipt") ?? false)
|
if (customer?.Metadata?.ContainsKey("appleReceipt") ?? false)
|
||||||
{
|
{
|
||||||
return await AttemptToPayInvoiceWithAppleReceiptAsync(invoice, customer);
|
return await AttemptToPayInvoiceWithAppleReceiptAsync(invoice, customer);
|
||||||
}
|
}
|
||||||
else if(customer?.Metadata?.ContainsKey("btCustomerId") ?? false)
|
else if (customer?.Metadata?.ContainsKey("btCustomerId") ?? false)
|
||||||
{
|
{
|
||||||
return await AttemptToPayInvoiceWithBraintreeAsync(invoice, customer);
|
return await AttemptToPayInvoiceWithBraintreeAsync(invoice, customer);
|
||||||
}
|
}
|
||||||
@ -478,14 +478,14 @@ namespace Bit.Billing.Controllers
|
|||||||
|
|
||||||
private async Task<bool> AttemptToPayInvoiceWithAppleReceiptAsync(Invoice invoice, Customer customer)
|
private async Task<bool> AttemptToPayInvoiceWithAppleReceiptAsync(Invoice invoice, Customer customer)
|
||||||
{
|
{
|
||||||
if(!customer?.Metadata?.ContainsKey("appleReceipt") ?? true)
|
if (!customer?.Metadata?.ContainsKey("appleReceipt") ?? true)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var originalAppleReceiptTransactionId = customer.Metadata["appleReceipt"];
|
var originalAppleReceiptTransactionId = customer.Metadata["appleReceipt"];
|
||||||
var appleReceiptRecord = await _appleIapService.GetReceiptAsync(originalAppleReceiptTransactionId);
|
var appleReceiptRecord = await _appleIapService.GetReceiptAsync(originalAppleReceiptTransactionId);
|
||||||
if(string.IsNullOrWhiteSpace(appleReceiptRecord?.Item1) || !appleReceiptRecord.Item2.HasValue)
|
if (string.IsNullOrWhiteSpace(appleReceiptRecord?.Item1) || !appleReceiptRecord.Item2.HasValue)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -493,13 +493,13 @@ namespace Bit.Billing.Controllers
|
|||||||
var subscriptionService = new SubscriptionService();
|
var subscriptionService = new SubscriptionService();
|
||||||
var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId);
|
var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId);
|
||||||
var ids = GetIdsFromMetaData(subscription?.Metadata);
|
var ids = GetIdsFromMetaData(subscription?.Metadata);
|
||||||
if(!ids.Item2.HasValue)
|
if (!ids.Item2.HasValue)
|
||||||
{
|
{
|
||||||
// Apple receipt is only for user subscriptions
|
// Apple receipt is only for user subscriptions
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(appleReceiptRecord.Item2.Value != ids.Item2.Value)
|
if (appleReceiptRecord.Item2.Value != ids.Item2.Value)
|
||||||
{
|
{
|
||||||
_logger.LogError("User Ids for Apple Receipt and subscription do not match: {0} != {1}.",
|
_logger.LogError("User Ids for Apple Receipt and subscription do not match: {0} != {1}.",
|
||||||
appleReceiptRecord.Item2.Value, ids.Item2.Value);
|
appleReceiptRecord.Item2.Value, ids.Item2.Value);
|
||||||
@ -507,7 +507,7 @@ namespace Bit.Billing.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
var appleReceiptStatus = await _appleIapService.GetVerifiedReceiptStatusAsync(appleReceiptRecord.Item1);
|
var appleReceiptStatus = await _appleIapService.GetVerifiedReceiptStatusAsync(appleReceiptRecord.Item1);
|
||||||
if(appleReceiptStatus == null)
|
if (appleReceiptStatus == null)
|
||||||
{
|
{
|
||||||
// TODO: cancel sub if receipt is cancelled?
|
// TODO: cancel sub if receipt is cancelled?
|
||||||
return false;
|
return false;
|
||||||
@ -515,7 +515,7 @@ namespace Bit.Billing.Controllers
|
|||||||
|
|
||||||
var receiptExpiration = appleReceiptStatus.GetLastExpiresDate().GetValueOrDefault(DateTime.MinValue);
|
var receiptExpiration = appleReceiptStatus.GetLastExpiresDate().GetValueOrDefault(DateTime.MinValue);
|
||||||
var invoiceDue = invoice.DueDate.GetValueOrDefault(DateTime.MinValue);
|
var invoiceDue = invoice.DueDate.GetValueOrDefault(DateTime.MinValue);
|
||||||
if(receiptExpiration <= invoiceDue)
|
if (receiptExpiration <= invoiceDue)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Apple receipt expiration is before invoice due date. {0} <= {1}",
|
_logger.LogWarning("Apple receipt expiration is before invoice due date. {0} <= {1}",
|
||||||
receiptExpiration, invoiceDue);
|
receiptExpiration, invoiceDue);
|
||||||
@ -525,7 +525,7 @@ namespace Bit.Billing.Controllers
|
|||||||
var receiptLastTransactionId = appleReceiptStatus.GetLastTransactionId();
|
var receiptLastTransactionId = appleReceiptStatus.GetLastTransactionId();
|
||||||
var existingTransaction = await _transactionRepository.GetByGatewayIdAsync(
|
var existingTransaction = await _transactionRepository.GetByGatewayIdAsync(
|
||||||
GatewayType.AppStore, receiptLastTransactionId);
|
GatewayType.AppStore, receiptLastTransactionId);
|
||||||
if(existingTransaction != null)
|
if (existingTransaction != null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("There is already an existing transaction for this Apple receipt.",
|
_logger.LogWarning("There is already an existing transaction for this Apple receipt.",
|
||||||
receiptLastTransactionId);
|
receiptLastTransactionId);
|
||||||
@ -551,9 +551,9 @@ namespace Bit.Billing.Controllers
|
|||||||
await _transactionRepository.CreateAsync(appleTransaction);
|
await _transactionRepository.CreateAsync(appleTransaction);
|
||||||
await invoiceService.PayAsync(invoice.Id, new InvoicePayOptions { PaidOutOfBand = true });
|
await invoiceService.PayAsync(invoice.Id, new InvoicePayOptions { PaidOutOfBand = true });
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
if(e.Message.Contains("Invoice is already paid"))
|
if (e.Message.Contains("Invoice is already paid"))
|
||||||
{
|
{
|
||||||
await invoiceService.UpdateAsync(invoice.Id, new InvoiceUpdateOptions
|
await invoiceService.UpdateAsync(invoice.Id, new InvoiceUpdateOptions
|
||||||
{
|
{
|
||||||
@ -571,7 +571,7 @@ namespace Bit.Billing.Controllers
|
|||||||
|
|
||||||
private async Task<bool> AttemptToPayInvoiceWithBraintreeAsync(Invoice invoice, Customer customer)
|
private async Task<bool> AttemptToPayInvoiceWithBraintreeAsync(Invoice invoice, Customer customer)
|
||||||
{
|
{
|
||||||
if(!customer?.Metadata?.ContainsKey("btCustomerId") ?? true)
|
if (!customer?.Metadata?.ContainsKey("btCustomerId") ?? true)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -579,7 +579,7 @@ namespace Bit.Billing.Controllers
|
|||||||
var subscriptionService = new SubscriptionService();
|
var subscriptionService = new SubscriptionService();
|
||||||
var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId);
|
var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId);
|
||||||
var ids = GetIdsFromMetaData(subscription?.Metadata);
|
var ids = GetIdsFromMetaData(subscription?.Metadata);
|
||||||
if(!ids.Item1.HasValue && !ids.Item2.HasValue)
|
if (!ids.Item1.HasValue && !ids.Item2.HasValue)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -596,7 +596,7 @@ namespace Bit.Billing.Controllers
|
|||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
var duplicateTransaction = existingTransactions?
|
var duplicateTransaction = existingTransactions?
|
||||||
.FirstOrDefault(t => (now - t.CreationDate) < duplicateTimeSpan);
|
.FirstOrDefault(t => (now - t.CreationDate) < duplicateTimeSpan);
|
||||||
if(duplicateTransaction != null)
|
if (duplicateTransaction != null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("There is already a recent PayPal transaction ({0}). " +
|
_logger.LogWarning("There is already a recent PayPal transaction ({0}). " +
|
||||||
"Do not charge again to prevent possible duplicate.", duplicateTransaction.GatewayId);
|
"Do not charge again to prevent possible duplicate.", duplicateTransaction.GatewayId);
|
||||||
@ -622,9 +622,9 @@ namespace Bit.Billing.Controllers
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!transactionResult.IsSuccess())
|
if (!transactionResult.IsSuccess())
|
||||||
{
|
{
|
||||||
if(invoice.AttemptCount < 4)
|
if (invoice.AttemptCount < 4)
|
||||||
{
|
{
|
||||||
await _mailService.SendPaymentFailedAsync(customer.Email, btInvoiceAmount, true);
|
await _mailService.SendPaymentFailedAsync(customer.Email, btInvoiceAmount, true);
|
||||||
}
|
}
|
||||||
@ -645,10 +645,10 @@ namespace Bit.Billing.Controllers
|
|||||||
});
|
});
|
||||||
await invoiceService.PayAsync(invoice.Id, new InvoicePayOptions { PaidOutOfBand = true });
|
await invoiceService.PayAsync(invoice.Id, new InvoicePayOptions { PaidOutOfBand = true });
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
await _btGateway.Transaction.RefundAsync(transactionResult.Target.Id);
|
await _btGateway.Transaction.RefundAsync(transactionResult.Target.Id);
|
||||||
if(e.Message.Contains("Invoice is already paid"))
|
if (e.Message.Contains("Invoice is already paid"))
|
||||||
{
|
{
|
||||||
await invoiceService.UpdateAsync(invoice.Id, new InvoiceUpdateOptions
|
await invoiceService.UpdateAsync(invoice.Id, new InvoiceUpdateOptions
|
||||||
{
|
{
|
||||||
@ -672,17 +672,17 @@ namespace Bit.Billing.Controllers
|
|||||||
|
|
||||||
private async Task<Charge> GetChargeAsync(Stripe.Event parsedEvent, bool fresh = false)
|
private async Task<Charge> GetChargeAsync(Stripe.Event parsedEvent, bool fresh = false)
|
||||||
{
|
{
|
||||||
if(!(parsedEvent.Data.Object is Charge eventCharge))
|
if (!(parsedEvent.Data.Object is Charge eventCharge))
|
||||||
{
|
{
|
||||||
throw new Exception("Charge is null (from parsed event). " + parsedEvent.Id);
|
throw new Exception("Charge is null (from parsed event). " + parsedEvent.Id);
|
||||||
}
|
}
|
||||||
if(!fresh)
|
if (!fresh)
|
||||||
{
|
{
|
||||||
return eventCharge;
|
return eventCharge;
|
||||||
}
|
}
|
||||||
var chargeService = new ChargeService();
|
var chargeService = new ChargeService();
|
||||||
var charge = await chargeService.GetAsync(eventCharge.Id);
|
var charge = await chargeService.GetAsync(eventCharge.Id);
|
||||||
if(charge == null)
|
if (charge == null)
|
||||||
{
|
{
|
||||||
throw new Exception("Charge is null. " + eventCharge.Id);
|
throw new Exception("Charge is null. " + eventCharge.Id);
|
||||||
}
|
}
|
||||||
@ -691,17 +691,17 @@ namespace Bit.Billing.Controllers
|
|||||||
|
|
||||||
private async Task<Invoice> GetInvoiceAsync(Stripe.Event parsedEvent, bool fresh = false)
|
private async Task<Invoice> GetInvoiceAsync(Stripe.Event parsedEvent, bool fresh = false)
|
||||||
{
|
{
|
||||||
if(!(parsedEvent.Data.Object is Invoice eventInvoice))
|
if (!(parsedEvent.Data.Object is Invoice eventInvoice))
|
||||||
{
|
{
|
||||||
throw new Exception("Invoice is null (from parsed event). " + parsedEvent.Id);
|
throw new Exception("Invoice is null (from parsed event). " + parsedEvent.Id);
|
||||||
}
|
}
|
||||||
if(!fresh)
|
if (!fresh)
|
||||||
{
|
{
|
||||||
return eventInvoice;
|
return eventInvoice;
|
||||||
}
|
}
|
||||||
var invoiceService = new InvoiceService();
|
var invoiceService = new InvoiceService();
|
||||||
var invoice = await invoiceService.GetAsync(eventInvoice.Id);
|
var invoice = await invoiceService.GetAsync(eventInvoice.Id);
|
||||||
if(invoice == null)
|
if (invoice == null)
|
||||||
{
|
{
|
||||||
throw new Exception("Invoice is null. " + eventInvoice.Id);
|
throw new Exception("Invoice is null. " + eventInvoice.Id);
|
||||||
}
|
}
|
||||||
@ -710,17 +710,17 @@ namespace Bit.Billing.Controllers
|
|||||||
|
|
||||||
private async Task<Subscription> GetSubscriptionAsync(Stripe.Event parsedEvent, bool fresh = false)
|
private async Task<Subscription> GetSubscriptionAsync(Stripe.Event parsedEvent, bool fresh = false)
|
||||||
{
|
{
|
||||||
if(!(parsedEvent.Data.Object is Subscription eventSubscription))
|
if (!(parsedEvent.Data.Object is Subscription eventSubscription))
|
||||||
{
|
{
|
||||||
throw new Exception("Subscription is null (from parsed event). " + parsedEvent.Id);
|
throw new Exception("Subscription is null (from parsed event). " + parsedEvent.Id);
|
||||||
}
|
}
|
||||||
if(!fresh)
|
if (!fresh)
|
||||||
{
|
{
|
||||||
return eventSubscription;
|
return eventSubscription;
|
||||||
}
|
}
|
||||||
var subscriptionService = new SubscriptionService();
|
var subscriptionService = new SubscriptionService();
|
||||||
var subscription = await subscriptionService.GetAsync(eventSubscription.Id);
|
var subscription = await subscriptionService.GetAsync(eventSubscription.Id);
|
||||||
if(subscription == null)
|
if (subscription == null)
|
||||||
{
|
{
|
||||||
throw new Exception("Subscription is null. " + eventSubscription.Id);
|
throw new Exception("Subscription is null. " + eventSubscription.Id);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ namespace Bit.Billing.Jobs
|
|||||||
var timeZone = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
var timeZone = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
||||||
TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time") :
|
TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time") :
|
||||||
TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
|
TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
|
||||||
if(_globalSettings.SelfHosted)
|
if (_globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
timeZone = TimeZoneInfo.Utc;
|
timeZone = TimeZoneInfo.Utc;
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ namespace Bit.Billing.Models
|
|||||||
|
|
||||||
public string GetOriginalTransactionId()
|
public string GetOriginalTransactionId()
|
||||||
{
|
{
|
||||||
if(LatestReceiptInfo != null)
|
if (LatestReceiptInfo != null)
|
||||||
{
|
{
|
||||||
return LatestReceiptInfo.OriginalTransactionId;
|
return LatestReceiptInfo.OriginalTransactionId;
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ namespace Bit.Billing.Models
|
|||||||
|
|
||||||
public string GetTransactionId()
|
public string GetTransactionId()
|
||||||
{
|
{
|
||||||
if(LatestReceiptInfo != null)
|
if (LatestReceiptInfo != null)
|
||||||
{
|
{
|
||||||
return LatestReceiptInfo.TransactionId;
|
return LatestReceiptInfo.TransactionId;
|
||||||
}
|
}
|
||||||
@ -47,7 +47,7 @@ namespace Bit.Billing.Models
|
|||||||
|
|
||||||
public DateTime? GetExpiresDate()
|
public DateTime? GetExpiresDate()
|
||||||
{
|
{
|
||||||
if(LatestReceiptInfo != null)
|
if (LatestReceiptInfo != null)
|
||||||
{
|
{
|
||||||
return LatestReceiptInfo.ExpiresDate;
|
return LatestReceiptInfo.ExpiresDate;
|
||||||
}
|
}
|
||||||
|
@ -18,13 +18,13 @@ namespace Bit.Billing
|
|||||||
logging.AddSerilog(hostingContext, e =>
|
logging.AddSerilog(hostingContext, e =>
|
||||||
{
|
{
|
||||||
var context = e.Properties["SourceContext"].ToString();
|
var context = e.Properties["SourceContext"].ToString();
|
||||||
if(e.Level == LogEventLevel.Information &&
|
if (e.Level == LogEventLevel.Information &&
|
||||||
(context.StartsWith("\"Bit.Billing.Jobs") || context.StartsWith("\"Bit.Core.Jobs")))
|
(context.StartsWith("\"Bit.Billing.Jobs") || context.StartsWith("\"Bit.Core.Jobs")))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(e.Properties.ContainsKey("RequestPath") &&
|
if (e.Properties.ContainsKey("RequestPath") &&
|
||||||
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
|
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
|
||||||
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
||||||
{
|
{
|
||||||
|
@ -78,7 +78,7 @@ namespace Bit.Billing
|
|||||||
{
|
{
|
||||||
app.UseSerilog(env, appLifetime, globalSettings);
|
app.UseSerilog(env, appLifetime, globalSettings);
|
||||||
|
|
||||||
if(env.IsDevelopment())
|
if (env.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.UseDeveloperExceptionPage();
|
app.UseDeveloperExceptionPage();
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ namespace Bit.Billing.Utilities
|
|||||||
|
|
||||||
public async Task<bool> VerifyIpnAsync(string ipnBody)
|
public async Task<bool> VerifyIpnAsync(string ipnBody)
|
||||||
{
|
{
|
||||||
if(ipnBody == null)
|
if (ipnBody == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("No IPN body.");
|
throw new ArgumentException("No IPN body.");
|
||||||
}
|
}
|
||||||
@ -38,16 +38,16 @@ namespace Bit.Billing.Utilities
|
|||||||
var cmdIpnBody = string.Concat("cmd=_notify-validate&", ipnBody);
|
var cmdIpnBody = string.Concat("cmd=_notify-validate&", ipnBody);
|
||||||
request.Content = new StringContent(cmdIpnBody, Encoding.UTF8, "application/x-www-form-urlencoded");
|
request.Content = new StringContent(cmdIpnBody, Encoding.UTF8, "application/x-www-form-urlencoded");
|
||||||
var response = await _httpClient.SendAsync(request);
|
var response = await _httpClient.SendAsync(request);
|
||||||
if(!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
throw new Exception("Failed to verify IPN, status: " + response.StatusCode);
|
throw new Exception("Failed to verify IPN, status: " + response.StatusCode);
|
||||||
}
|
}
|
||||||
var responseContent = await response.Content.ReadAsStringAsync();
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
if(responseContent.Equals("VERIFIED"))
|
if (responseContent.Equals("VERIFIED"))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if(responseContent.Equals("INVALID"))
|
else if (responseContent.Equals("INVALID"))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ namespace Bit.Billing.Utilities
|
|||||||
|
|
||||||
public IpnTransaction(string ipnFormData)
|
public IpnTransaction(string ipnFormData)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(ipnFormData))
|
if (string.IsNullOrWhiteSpace(ipnFormData))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -92,12 +92,12 @@ namespace Bit.Billing.Utilities
|
|||||||
PaymentDate = ConvertDate(GetDictValue(dataDict, "payment_date"));
|
PaymentDate = ConvertDate(GetDictValue(dataDict, "payment_date"));
|
||||||
|
|
||||||
var mcGrossString = GetDictValue(dataDict, "mc_gross");
|
var mcGrossString = GetDictValue(dataDict, "mc_gross");
|
||||||
if(!string.IsNullOrWhiteSpace(mcGrossString) && decimal.TryParse(mcGrossString, out var mcGross))
|
if (!string.IsNullOrWhiteSpace(mcGrossString) && decimal.TryParse(mcGrossString, out var mcGross))
|
||||||
{
|
{
|
||||||
McGross = mcGross;
|
McGross = mcGross;
|
||||||
}
|
}
|
||||||
var mcFeeString = GetDictValue(dataDict, "mc_fee");
|
var mcFeeString = GetDictValue(dataDict, "mc_fee");
|
||||||
if(!string.IsNullOrWhiteSpace(mcFeeString) && decimal.TryParse(mcFeeString, out var mcFee))
|
if (!string.IsNullOrWhiteSpace(mcFeeString) && decimal.TryParse(mcFeeString, out var mcFee))
|
||||||
{
|
{
|
||||||
McFee = mcFee;
|
McFee = mcFee;
|
||||||
}
|
}
|
||||||
@ -125,19 +125,19 @@ namespace Bit.Billing.Utilities
|
|||||||
Guid? orgId = null;
|
Guid? orgId = null;
|
||||||
Guid? userId = null;
|
Guid? userId = null;
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(Custom) && Custom.Contains(":"))
|
if (!string.IsNullOrWhiteSpace(Custom) && Custom.Contains(":"))
|
||||||
{
|
{
|
||||||
var mainParts = Custom.Split(',');
|
var mainParts = Custom.Split(',');
|
||||||
foreach(var mainPart in mainParts)
|
foreach (var mainPart in mainParts)
|
||||||
{
|
{
|
||||||
var parts = mainPart.Split(':');
|
var parts = mainPart.Split(':');
|
||||||
if(parts.Length > 1 && Guid.TryParse(parts[1], out var id))
|
if (parts.Length > 1 && Guid.TryParse(parts[1], out var id))
|
||||||
{
|
{
|
||||||
if(parts[0] == "user_id")
|
if (parts[0] == "user_id")
|
||||||
{
|
{
|
||||||
userId = id;
|
userId = id;
|
||||||
}
|
}
|
||||||
else if(parts[0] == "organization_id")
|
else if (parts[0] == "organization_id")
|
||||||
{
|
{
|
||||||
orgId = id;
|
orgId = id;
|
||||||
}
|
}
|
||||||
@ -160,11 +160,11 @@ namespace Bit.Billing.Utilities
|
|||||||
|
|
||||||
private DateTime ConvertDate(string dateString)
|
private DateTime ConvertDate(string dateString)
|
||||||
{
|
{
|
||||||
if(!string.IsNullOrWhiteSpace(dateString))
|
if (!string.IsNullOrWhiteSpace(dateString))
|
||||||
{
|
{
|
||||||
var parsed = DateTime.TryParseExact(dateString, _dateFormats,
|
var parsed = DateTime.TryParseExact(dateString, _dateFormats,
|
||||||
CultureInfo.InvariantCulture, DateTimeStyles.None, out var paymentDate);
|
CultureInfo.InvariantCulture, DateTimeStyles.None, out var paymentDate);
|
||||||
if(parsed)
|
if (parsed)
|
||||||
{
|
{
|
||||||
var pacificTime = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
var pacificTime = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
||||||
TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time") :
|
TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time") :
|
||||||
|
@ -28,7 +28,7 @@ namespace Bit.Core
|
|||||||
|
|
||||||
public void Build(HttpContext httpContext, GlobalSettings globalSettings)
|
public void Build(HttpContext httpContext, GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
if(_builtHttpContext)
|
if (_builtHttpContext)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -37,12 +37,12 @@ namespace Bit.Core
|
|||||||
HttpContext = httpContext;
|
HttpContext = httpContext;
|
||||||
Build(httpContext.User, globalSettings);
|
Build(httpContext.User, globalSettings);
|
||||||
|
|
||||||
if(DeviceIdentifier == null && httpContext.Request.Headers.ContainsKey("Device-Identifier"))
|
if (DeviceIdentifier == null && httpContext.Request.Headers.ContainsKey("Device-Identifier"))
|
||||||
{
|
{
|
||||||
DeviceIdentifier = httpContext.Request.Headers["Device-Identifier"];
|
DeviceIdentifier = httpContext.Request.Headers["Device-Identifier"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(httpContext.Request.Headers.ContainsKey("Device-Type") &&
|
if (httpContext.Request.Headers.ContainsKey("Device-Type") &&
|
||||||
Enum.TryParse(httpContext.Request.Headers["Device-Type"].ToString(), out DeviceType dType))
|
Enum.TryParse(httpContext.Request.Headers["Device-Type"].ToString(), out DeviceType dType))
|
||||||
{
|
{
|
||||||
DeviceType = dType;
|
DeviceType = dType;
|
||||||
@ -51,14 +51,14 @@ namespace Bit.Core
|
|||||||
|
|
||||||
public void Build(ClaimsPrincipal user, GlobalSettings globalSettings)
|
public void Build(ClaimsPrincipal user, GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
if(_builtClaimsPrincipal)
|
if (_builtClaimsPrincipal)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_builtClaimsPrincipal = true;
|
_builtClaimsPrincipal = true;
|
||||||
IpAddress = HttpContext.GetIpAddress(globalSettings);
|
IpAddress = HttpContext.GetIpAddress(globalSettings);
|
||||||
if(user == null || !user.Claims.Any())
|
if (user == null || !user.Claims.Any())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ namespace Bit.Core
|
|||||||
var claimsDict = user.Claims.GroupBy(c => c.Type).ToDictionary(c => c.Key, c => c.Select(v => v));
|
var claimsDict = user.Claims.GroupBy(c => c.Type).ToDictionary(c => c.Key, c => c.Select(v => v));
|
||||||
|
|
||||||
var subject = GetClaimValue(claimsDict, "sub");
|
var subject = GetClaimValue(claimsDict, "sub");
|
||||||
if(Guid.TryParse(subject, out var subIdGuid))
|
if (Guid.TryParse(subject, out var subIdGuid))
|
||||||
{
|
{
|
||||||
UserId = subIdGuid;
|
UserId = subIdGuid;
|
||||||
}
|
}
|
||||||
@ -74,18 +74,18 @@ namespace Bit.Core
|
|||||||
var clientId = GetClaimValue(claimsDict, "client_id");
|
var clientId = GetClaimValue(claimsDict, "client_id");
|
||||||
var clientSubject = GetClaimValue(claimsDict, "client_sub");
|
var clientSubject = GetClaimValue(claimsDict, "client_sub");
|
||||||
var orgApi = false;
|
var orgApi = false;
|
||||||
if(clientSubject != null)
|
if (clientSubject != null)
|
||||||
{
|
{
|
||||||
if(clientId?.StartsWith("installation.") ?? false)
|
if (clientId?.StartsWith("installation.") ?? false)
|
||||||
{
|
{
|
||||||
if(Guid.TryParse(clientSubject, out var idGuid))
|
if (Guid.TryParse(clientSubject, out var idGuid))
|
||||||
{
|
{
|
||||||
InstallationId = idGuid;
|
InstallationId = idGuid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(clientId?.StartsWith("organization.") ?? false)
|
else if (clientId?.StartsWith("organization.") ?? false)
|
||||||
{
|
{
|
||||||
if(Guid.TryParse(clientSubject, out var idGuid))
|
if (Guid.TryParse(clientSubject, out var idGuid))
|
||||||
{
|
{
|
||||||
OrganizationId = idGuid;
|
OrganizationId = idGuid;
|
||||||
orgApi = true;
|
orgApi = true;
|
||||||
@ -96,7 +96,7 @@ namespace Bit.Core
|
|||||||
DeviceIdentifier = GetClaimValue(claimsDict, "device");
|
DeviceIdentifier = GetClaimValue(claimsDict, "device");
|
||||||
|
|
||||||
Organizations = new List<CurrentContentOrganization>();
|
Organizations = new List<CurrentContentOrganization>();
|
||||||
if(claimsDict.ContainsKey("orgowner"))
|
if (claimsDict.ContainsKey("orgowner"))
|
||||||
{
|
{
|
||||||
Organizations.AddRange(claimsDict["orgowner"].Select(c =>
|
Organizations.AddRange(claimsDict["orgowner"].Select(c =>
|
||||||
new CurrentContentOrganization
|
new CurrentContentOrganization
|
||||||
@ -105,7 +105,7 @@ namespace Bit.Core
|
|||||||
Type = OrganizationUserType.Owner
|
Type = OrganizationUserType.Owner
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
else if(orgApi && OrganizationId.HasValue)
|
else if (orgApi && OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
Organizations.Add(new CurrentContentOrganization
|
Organizations.Add(new CurrentContentOrganization
|
||||||
{
|
{
|
||||||
@ -114,7 +114,7 @@ namespace Bit.Core
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(claimsDict.ContainsKey("orgadmin"))
|
if (claimsDict.ContainsKey("orgadmin"))
|
||||||
{
|
{
|
||||||
Organizations.AddRange(claimsDict["orgadmin"].Select(c =>
|
Organizations.AddRange(claimsDict["orgadmin"].Select(c =>
|
||||||
new CurrentContentOrganization
|
new CurrentContentOrganization
|
||||||
@ -124,7 +124,7 @@ namespace Bit.Core
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(claimsDict.ContainsKey("orguser"))
|
if (claimsDict.ContainsKey("orguser"))
|
||||||
{
|
{
|
||||||
Organizations.AddRange(claimsDict["orguser"].Select(c =>
|
Organizations.AddRange(claimsDict["orguser"].Select(c =>
|
||||||
new CurrentContentOrganization
|
new CurrentContentOrganization
|
||||||
@ -134,7 +134,7 @@ namespace Bit.Core
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(claimsDict.ContainsKey("orgmanager"))
|
if (claimsDict.ContainsKey("orgmanager"))
|
||||||
{
|
{
|
||||||
Organizations.AddRange(claimsDict["orgmanager"].Select(c =>
|
Organizations.AddRange(claimsDict["orgmanager"].Select(c =>
|
||||||
new CurrentContentOrganization
|
new CurrentContentOrganization
|
||||||
@ -171,7 +171,7 @@ namespace Bit.Core
|
|||||||
public async Task<ICollection<CurrentContentOrganization>> OrganizationMembershipAsync(
|
public async Task<ICollection<CurrentContentOrganization>> OrganizationMembershipAsync(
|
||||||
IOrganizationUserRepository organizationUserRepository, Guid userId)
|
IOrganizationUserRepository organizationUserRepository, Guid userId)
|
||||||
{
|
{
|
||||||
if(Organizations == null)
|
if (Organizations == null)
|
||||||
{
|
{
|
||||||
var userOrgs = await organizationUserRepository.GetManyByUserAsync(userId);
|
var userOrgs = await organizationUserRepository.GetManyByUserAsync(userId);
|
||||||
Organizations = userOrgs.Where(ou => ou.Status == OrganizationUserStatusType.Confirmed)
|
Organizations = userOrgs.Where(ou => ou.Status == OrganizationUserStatusType.Confirmed)
|
||||||
@ -182,7 +182,7 @@ namespace Bit.Core
|
|||||||
|
|
||||||
private string GetClaimValue(Dictionary<string, IEnumerable<Claim>> claims, string type)
|
private string GetClaimValue(Dictionary<string, IEnumerable<Claim>> claims, string type)
|
||||||
{
|
{
|
||||||
if(!claims.ContainsKey(type))
|
if (!claims.ContainsKey(type))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ namespace Bit.Core.Exceptions
|
|||||||
public BadRequestException(ModelStateDictionary modelState)
|
public BadRequestException(ModelStateDictionary modelState)
|
||||||
: base("The model state is invalid.")
|
: base("The model state is invalid.")
|
||||||
{
|
{
|
||||||
if(modelState.IsValid || modelState.ErrorCount == 0)
|
if (modelState.IsValid || modelState.ErrorCount == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ namespace Bit.Core.HostedServices
|
|||||||
EnableDeadLetteringOnMessageExpiration = true,
|
EnableDeadLetteringOnMessageExpiration = true,
|
||||||
}, new RuleDescription("default", new SqlFilter($"sys.Label != '{_subName}'")));
|
}, new RuleDescription("default", new SqlFilter($"sys.Label != '{_subName}'")));
|
||||||
}
|
}
|
||||||
catch(MessagingEntityAlreadyExistsException) { }
|
catch (MessagingEntityAlreadyExistsException) { }
|
||||||
_subscriptionClient.RegisterMessageHandler(ProcessMessageAsync,
|
_subscriptionClient.RegisterMessageHandler(ProcessMessageAsync,
|
||||||
new MessageHandlerOptions(ExceptionReceivedHandlerAsync)
|
new MessageHandlerOptions(ExceptionReceivedHandlerAsync)
|
||||||
{
|
{
|
||||||
@ -74,14 +74,14 @@ namespace Bit.Core.HostedServices
|
|||||||
|
|
||||||
private async Task ProcessMessageAsync(Message message, CancellationToken cancellationToken)
|
private async Task ProcessMessageAsync(Message message, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if(message.Label != _subName && _applicationCacheService != null)
|
if (message.Label != _subName && _applicationCacheService != null)
|
||||||
{
|
{
|
||||||
switch((ApplicationCacheMessageType)message.UserProperties["type"])
|
switch ((ApplicationCacheMessageType)message.UserProperties["type"])
|
||||||
{
|
{
|
||||||
case ApplicationCacheMessageType.UpsertOrganizationAbility:
|
case ApplicationCacheMessageType.UpsertOrganizationAbility:
|
||||||
var upsertedOrgId = (Guid)message.UserProperties["id"];
|
var upsertedOrgId = (Guid)message.UserProperties["id"];
|
||||||
var upsertedOrg = await _organizationRepository.GetByIdAsync(upsertedOrgId);
|
var upsertedOrg = await _organizationRepository.GetByIdAsync(upsertedOrgId);
|
||||||
if(upsertedOrg != null)
|
if (upsertedOrg != null)
|
||||||
{
|
{
|
||||||
await _applicationCacheService.BaseUpsertOrganizationAbilityAsync(upsertedOrg);
|
await _applicationCacheService.BaseUpsertOrganizationAbilityAsync(upsertedOrg);
|
||||||
}
|
}
|
||||||
@ -94,7 +94,7 @@ namespace Bit.Core.HostedServices
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!cancellationToken.IsCancellationRequested)
|
if (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
await _subscriptionClient.CompleteAsync(message.SystemProperties.LockToken);
|
await _subscriptionClient.CompleteAsync(message.SystemProperties.LockToken);
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ namespace Bit.Core.Identity
|
|||||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||||
{
|
{
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Authenticator);
|
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Authenticator);
|
||||||
if(string.IsNullOrWhiteSpace((string)provider?.MetaData["Key"]))
|
if (string.IsNullOrWhiteSpace((string)provider?.MetaData["Key"]))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
services.TryAddScoped<SignInManager<TUser>>();
|
services.TryAddScoped<SignInManager<TUser>>();
|
||||||
services.TryAddScoped<RoleManager<TRole>>();
|
services.TryAddScoped<RoleManager<TRole>>();
|
||||||
|
|
||||||
if(setupAction != null)
|
if (setupAction != null)
|
||||||
{
|
{
|
||||||
services.Configure(setupAction);
|
services.Configure(setupAction);
|
||||||
}
|
}
|
||||||
|
@ -26,13 +26,13 @@ namespace Bit.Core.Identity
|
|||||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||||
{
|
{
|
||||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||||
if(!(await userService.CanAccessPremium(user)))
|
if (!(await userService.CanAccessPremium(user)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
|
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
|
||||||
if(!HasProperMetaData(provider))
|
if (!HasProperMetaData(provider))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -43,13 +43,13 @@ namespace Bit.Core.Identity
|
|||||||
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||||
{
|
{
|
||||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||||
if(!(await userService.CanAccessPremium(user)))
|
if (!(await userService.CanAccessPremium(user)))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
|
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
|
||||||
if(!HasProperMetaData(provider))
|
if (!HasProperMetaData(provider))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -62,13 +62,13 @@ namespace Bit.Core.Identity
|
|||||||
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||||
{
|
{
|
||||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||||
if(!(await userService.CanAccessPremium(user)))
|
if (!(await userService.CanAccessPremium(user)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
|
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
|
||||||
if(!HasProperMetaData(provider))
|
if (!HasProperMetaData(provider))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ namespace Bit.Core.Identity
|
|||||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||||
{
|
{
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
|
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
|
||||||
if(!HasProperMetaData(provider))
|
if (!HasProperMetaData(provider))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -33,7 +33,7 @@ namespace Bit.Core.Identity
|
|||||||
public Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
public Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||||
{
|
{
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
|
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
|
||||||
if(!HasProperMetaData(provider))
|
if (!HasProperMetaData(provider))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -57,11 +57,11 @@ namespace Bit.Core.Identity
|
|||||||
var emailParts = email.Split('@');
|
var emailParts = email.Split('@');
|
||||||
|
|
||||||
string shownPart = null;
|
string shownPart = null;
|
||||||
if(emailParts[0].Length > 2 && emailParts[0].Length <= 4)
|
if (emailParts[0].Length > 2 && emailParts[0].Length <= 4)
|
||||||
{
|
{
|
||||||
shownPart = emailParts[0].Substring(0, 1);
|
shownPart = emailParts[0].Substring(0, 1);
|
||||||
}
|
}
|
||||||
else if(emailParts[0].Length > 4)
|
else if (emailParts[0].Length > 4)
|
||||||
{
|
{
|
||||||
shownPart = emailParts[0].Substring(0, 2);
|
shownPart = emailParts[0].Substring(0, 2);
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ namespace Bit.Core.Identity
|
|||||||
}
|
}
|
||||||
|
|
||||||
string redactedPart = null;
|
string redactedPart = null;
|
||||||
if(emailParts[0].Length > 4)
|
if (emailParts[0].Length > 4)
|
||||||
{
|
{
|
||||||
redactedPart = new string('*', emailParts[0].Length - 2);
|
redactedPart = new string('*', emailParts[0].Length - 2);
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ namespace Bit.Core.Identity
|
|||||||
|
|
||||||
public Task<bool> CanGenerateTwoFactorTokenAsync(Organization organization)
|
public Task<bool> CanGenerateTwoFactorTokenAsync(Organization organization)
|
||||||
{
|
{
|
||||||
if(organization == null || !organization.Enabled || !organization.Use2fa)
|
if (organization == null || !organization.Enabled || !organization.Use2fa)
|
||||||
{
|
{
|
||||||
return Task.FromResult(false);
|
return Task.FromResult(false);
|
||||||
}
|
}
|
||||||
@ -32,13 +32,13 @@ namespace Bit.Core.Identity
|
|||||||
|
|
||||||
public Task<string> GenerateAsync(Organization organization, User user)
|
public Task<string> GenerateAsync(Organization organization, User user)
|
||||||
{
|
{
|
||||||
if(organization == null || !organization.Enabled || !organization.Use2fa)
|
if (organization == null || !organization.Enabled || !organization.Use2fa)
|
||||||
{
|
{
|
||||||
return Task.FromResult<string>(null);
|
return Task.FromResult<string>(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
|
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
|
||||||
if(!HasProperMetaData(provider))
|
if (!HasProperMetaData(provider))
|
||||||
{
|
{
|
||||||
return Task.FromResult<string>(null);
|
return Task.FromResult<string>(null);
|
||||||
}
|
}
|
||||||
@ -50,13 +50,13 @@ namespace Bit.Core.Identity
|
|||||||
|
|
||||||
public Task<bool> ValidateAsync(string token, Organization organization, User user)
|
public Task<bool> ValidateAsync(string token, Organization organization, User user)
|
||||||
{
|
{
|
||||||
if(organization == null || !organization.Enabled || !organization.Use2fa)
|
if (organization == null || !organization.Enabled || !organization.Use2fa)
|
||||||
{
|
{
|
||||||
return Task.FromResult(false);
|
return Task.FromResult(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
|
var provider = organization.GetTwoFactorProvider(TwoFactorProviderType.OrganizationDuo);
|
||||||
if(!HasProperMetaData(provider))
|
if (!HasProperMetaData(provider))
|
||||||
{
|
{
|
||||||
return Task.FromResult(false);
|
return Task.FromResult(false);
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ namespace Bit.Core.Identity
|
|||||||
public async Task<SignInResult> PasswordlessSignInAsync(string email, string returnUrl)
|
public async Task<SignInResult> PasswordlessSignInAsync(string email, string returnUrl)
|
||||||
{
|
{
|
||||||
var user = await UserManager.FindByEmailAsync(email);
|
var user = await UserManager.FindByEmailAsync(email);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return SignInResult.Failed;
|
return SignInResult.Failed;
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ namespace Bit.Core.Identity
|
|||||||
|
|
||||||
public async Task<SignInResult> PasswordlessSignInAsync(TUser user, string token, bool isPersistent)
|
public async Task<SignInResult> PasswordlessSignInAsync(TUser user, string token, bool isPersistent)
|
||||||
{
|
{
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(user));
|
throw new ArgumentNullException(nameof(user));
|
||||||
}
|
}
|
||||||
@ -58,7 +58,7 @@ namespace Bit.Core.Identity
|
|||||||
public async Task<SignInResult> PasswordlessSignInAsync(string email, string token, bool isPersistent)
|
public async Task<SignInResult> PasswordlessSignInAsync(string email, string token, bool isPersistent)
|
||||||
{
|
{
|
||||||
var user = await UserManager.FindByEmailAsync(email);
|
var user = await UserManager.FindByEmailAsync(email);
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return SignInResult.Failed;
|
return SignInResult.Failed;
|
||||||
}
|
}
|
||||||
@ -68,18 +68,18 @@ namespace Bit.Core.Identity
|
|||||||
|
|
||||||
public virtual async Task<SignInResult> CheckPasswordlessSignInAsync(TUser user, string token)
|
public virtual async Task<SignInResult> CheckPasswordlessSignInAsync(TUser user, string token)
|
||||||
{
|
{
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(user));
|
throw new ArgumentNullException(nameof(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
var error = await PreSignInCheck(user);
|
var error = await PreSignInCheck(user);
|
||||||
if(error != null)
|
if (error != null)
|
||||||
{
|
{
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(await UserManager.VerifyUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
|
if (await UserManager.VerifyUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
|
||||||
PasswordlessSignInPurpose, token))
|
PasswordlessSignInPurpose, token))
|
||||||
{
|
{
|
||||||
return SignInResult.Success;
|
return SignInResult.Success;
|
||||||
|
@ -30,7 +30,7 @@ namespace Bit.Core.Identity
|
|||||||
public override async Task<IdentityUser> FindByIdAsync(string userId,
|
public override async Task<IdentityUser> FindByIdAsync(string userId,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
if(!Guid.TryParse(userId, out var userIdGuid))
|
if (!Guid.TryParse(userId, out var userIdGuid))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -20,17 +20,17 @@ namespace Bit.Core.Identity
|
|||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var usersCsv = _configuration["adminSettings:admins"];
|
var usersCsv = _configuration["adminSettings:admins"];
|
||||||
if(!CoreHelpers.SettingHasValue(usersCsv))
|
if (!CoreHelpers.SettingHasValue(usersCsv))
|
||||||
{
|
{
|
||||||
return Task.FromResult<IdentityUser>(null);
|
return Task.FromResult<IdentityUser>(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var users = usersCsv.ToLowerInvariant().Split(',');
|
var users = usersCsv.ToLowerInvariant().Split(',');
|
||||||
var usersDict = new Dictionary<string, string>();
|
var usersDict = new Dictionary<string, string>();
|
||||||
foreach(var u in users)
|
foreach (var u in users)
|
||||||
{
|
{
|
||||||
var parts = u.Split(':');
|
var parts = u.Split(':');
|
||||||
if(parts.Length == 2)
|
if (parts.Length == 2)
|
||||||
{
|
{
|
||||||
var email = parts[0].Trim();
|
var email = parts[0].Trim();
|
||||||
var stamp = parts[1].Trim();
|
var stamp = parts[1].Trim();
|
||||||
@ -44,7 +44,7 @@ namespace Bit.Core.Identity
|
|||||||
}
|
}
|
||||||
|
|
||||||
var userStamp = usersDict.ContainsKey(normalizedEmail) ? usersDict[normalizedEmail] : null;
|
var userStamp = usersDict.ContainsKey(normalizedEmail) ? usersDict[normalizedEmail] : null;
|
||||||
if(userStamp == null)
|
if (userStamp == null)
|
||||||
{
|
{
|
||||||
return Task.FromResult<IdentityUser>(null);
|
return Task.FromResult<IdentityUser>(null);
|
||||||
}
|
}
|
||||||
|
@ -36,13 +36,13 @@ namespace Bit.Core.Identity
|
|||||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||||
{
|
{
|
||||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||||
if(!(await userService.CanAccessPremium(user)))
|
if (!(await userService.CanAccessPremium(user)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||||
if(!HasProperMetaData(provider))
|
if (!HasProperMetaData(provider))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -53,14 +53,14 @@ namespace Bit.Core.Identity
|
|||||||
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||||
{
|
{
|
||||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||||
if(!(await userService.CanAccessPremium(user)))
|
if (!(await userService.CanAccessPremium(user)))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||||
var keys = LoadKeys(provider);
|
var keys = LoadKeys(provider);
|
||||||
if(keys.Count == 0)
|
if (keys.Count == 0)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ namespace Bit.Core.Identity
|
|||||||
var appId = Utilities.CoreHelpers.U2fAppIdUrl(_globalSettings);
|
var appId = Utilities.CoreHelpers.U2fAppIdUrl(_globalSettings);
|
||||||
var oldChallenges = new List<object>();
|
var oldChallenges = new List<object>();
|
||||||
var challengeKeys = new List<object>();
|
var challengeKeys = new List<object>();
|
||||||
foreach(var key in keys)
|
foreach (var key in keys)
|
||||||
{
|
{
|
||||||
var registration = new DeviceRegistration(key.Item2.KeyHandleBytes, key.Item2.PublicKeyBytes,
|
var registration = new DeviceRegistration(key.Item2.KeyHandleBytes, key.Item2.PublicKeyBytes,
|
||||||
key.Item2.CertificateBytes, key.Item2.Counter);
|
key.Item2.CertificateBytes, key.Item2.Counter);
|
||||||
@ -115,7 +115,7 @@ namespace Bit.Core.Identity
|
|||||||
});
|
});
|
||||||
return $"{token}|{oldToken}";
|
return $"{token}|{oldToken}";
|
||||||
}
|
}
|
||||||
catch(U2fException)
|
catch (U2fException)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -124,14 +124,14 @@ namespace Bit.Core.Identity
|
|||||||
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||||
{
|
{
|
||||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||||
if(!(await userService.CanAccessPremium(user)) || string.IsNullOrWhiteSpace(token))
|
if (!(await userService.CanAccessPremium(user)) || string.IsNullOrWhiteSpace(token))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||||
var keys = LoadKeys(provider);
|
var keys = LoadKeys(provider);
|
||||||
if(keys.Count == 0)
|
if (keys.Count == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -139,13 +139,13 @@ namespace Bit.Core.Identity
|
|||||||
var authenticateResponse = BaseModel.FromJson<AuthenticateResponse>(token);
|
var authenticateResponse = BaseModel.FromJson<AuthenticateResponse>(token);
|
||||||
var key = keys.FirstOrDefault(f => f.Item2.KeyHandle == authenticateResponse.KeyHandle);
|
var key = keys.FirstOrDefault(f => f.Item2.KeyHandle == authenticateResponse.KeyHandle);
|
||||||
|
|
||||||
if(key == null)
|
if (key == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var challenges = await _u2fRepository.GetManyByUserIdAsync(user.Id);
|
var challenges = await _u2fRepository.GetManyByUserIdAsync(user.Id);
|
||||||
if(challenges.Count == 0)
|
if (challenges.Count == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -153,7 +153,7 @@ namespace Bit.Core.Identity
|
|||||||
// User will have a authentication request for each device they have registered so get the one that matches
|
// User will have a authentication request for each device they have registered so get the one that matches
|
||||||
// the device key handle
|
// the device key handle
|
||||||
var challenge = challenges.FirstOrDefault(c => c.KeyHandle == authenticateResponse.KeyHandle);
|
var challenge = challenges.FirstOrDefault(c => c.KeyHandle == authenticateResponse.KeyHandle);
|
||||||
if(challenge == null)
|
if (challenge == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -166,7 +166,7 @@ namespace Bit.Core.Identity
|
|||||||
var auth = new StartedAuthentication(challenge.Challenge, challenge.AppId, challenge.KeyHandle);
|
var auth = new StartedAuthentication(challenge.Challenge, challenge.AppId, challenge.KeyHandle);
|
||||||
U2fLib.FinishAuthentication(auth, authenticateResponse, registration);
|
U2fLib.FinishAuthentication(auth, authenticateResponse, registration);
|
||||||
}
|
}
|
||||||
catch(U2fException)
|
catch (U2fException)
|
||||||
{
|
{
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
@ -174,7 +174,7 @@ namespace Bit.Core.Identity
|
|||||||
// Update database
|
// Update database
|
||||||
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
|
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
|
||||||
key.Item2.Counter = registration.Counter;
|
key.Item2.Counter = registration.Counter;
|
||||||
if(key.Item2.Counter > 0)
|
if (key.Item2.Counter > 0)
|
||||||
{
|
{
|
||||||
key.Item2.Compromised = registration.IsCompromised;
|
key.Item2.Compromised = registration.IsCompromised;
|
||||||
}
|
}
|
||||||
@ -195,19 +195,19 @@ namespace Bit.Core.Identity
|
|||||||
private List<Tuple<string, TwoFactorProvider.U2fMetaData>> LoadKeys(TwoFactorProvider provider)
|
private List<Tuple<string, TwoFactorProvider.U2fMetaData>> LoadKeys(TwoFactorProvider provider)
|
||||||
{
|
{
|
||||||
var keys = new List<Tuple<string, TwoFactorProvider.U2fMetaData>>();
|
var keys = new List<Tuple<string, TwoFactorProvider.U2fMetaData>>();
|
||||||
if(!HasProperMetaData(provider))
|
if (!HasProperMetaData(provider))
|
||||||
{
|
{
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support up to 5 keys
|
// Support up to 5 keys
|
||||||
for(var i = 1; i <= 5; i++)
|
for (var i = 1; i <= 5; i++)
|
||||||
{
|
{
|
||||||
var keyName = $"Key{i}";
|
var keyName = $"Key{i}";
|
||||||
if(provider.MetaData.ContainsKey(keyName))
|
if (provider.MetaData.ContainsKey(keyName))
|
||||||
{
|
{
|
||||||
var key = new TwoFactorProvider.U2fMetaData((dynamic)provider.MetaData[keyName]);
|
var key = new TwoFactorProvider.U2fMetaData((dynamic)provider.MetaData[keyName]);
|
||||||
if(!key?.Compromised ?? false)
|
if (!key?.Compromised ?? false)
|
||||||
{
|
{
|
||||||
keys.Add(new Tuple<string, TwoFactorProvider.U2fMetaData>(keyName, key));
|
keys.Add(new Tuple<string, TwoFactorProvider.U2fMetaData>(keyName, key));
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ namespace Bit.Core.Identity
|
|||||||
|
|
||||||
public async Task<User> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
|
public async Task<User> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
if(_currentContext?.User != null && _currentContext.User.Email == normalizedEmail)
|
if (_currentContext?.User != null && _currentContext.User.Email == normalizedEmail)
|
||||||
{
|
{
|
||||||
return _currentContext.User;
|
return _currentContext.User;
|
||||||
}
|
}
|
||||||
@ -57,14 +57,14 @@ namespace Bit.Core.Identity
|
|||||||
|
|
||||||
public async Task<User> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
|
public async Task<User> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
if(_currentContext?.User != null &&
|
if (_currentContext?.User != null &&
|
||||||
string.Equals(_currentContext.User.Id.ToString(), userId, StringComparison.InvariantCultureIgnoreCase))
|
string.Equals(_currentContext.User.Id.ToString(), userId, StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
return _currentContext.User;
|
return _currentContext.User;
|
||||||
}
|
}
|
||||||
|
|
||||||
Guid userIdGuid;
|
Guid userIdGuid;
|
||||||
if(!Guid.TryParse(userId, out userIdGuid))
|
if (!Guid.TryParse(userId, out userIdGuid))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -26,13 +26,13 @@ namespace Bit.Core.Identity
|
|||||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||||
{
|
{
|
||||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||||
if(!(await userService.CanAccessPremium(user)))
|
if (!(await userService.CanAccessPremium(user)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.YubiKey);
|
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.YubiKey);
|
||||||
if(!provider?.MetaData.Values.Any(v => !string.IsNullOrWhiteSpace((string)v)) ?? true)
|
if (!provider?.MetaData.Values.Any(v => !string.IsNullOrWhiteSpace((string)v)) ?? true)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -48,12 +48,12 @@ namespace Bit.Core.Identity
|
|||||||
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||||
{
|
{
|
||||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||||
if(!(await userService.CanAccessPremium(user)))
|
if (!(await userService.CanAccessPremium(user)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(token) || token.Length < 32 || token.Length > 48)
|
if (string.IsNullOrWhiteSpace(token) || token.Length < 32 || token.Length > 48)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -61,13 +61,13 @@ namespace Bit.Core.Identity
|
|||||||
var id = token.Substring(0, 12);
|
var id = token.Substring(0, 12);
|
||||||
|
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.YubiKey);
|
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.YubiKey);
|
||||||
if(!provider.MetaData.ContainsValue(id))
|
if (!provider.MetaData.ContainsValue(id))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var client = new YubicoClient(_globalSettings.Yubico.ClientId, _globalSettings.Yubico.Key);
|
var client = new YubicoClient(_globalSettings.Yubico.ClientId, _globalSettings.Yubico.Key);
|
||||||
if(_globalSettings.Yubico.ValidationUrls != null && _globalSettings.Yubico.ValidationUrls.Length > 0)
|
if (_globalSettings.Yubico.ValidationUrls != null && _globalSettings.Yubico.ValidationUrls.Length > 0)
|
||||||
{
|
{
|
||||||
client.SetUrls(_globalSettings.Yubico.ValidationUrls);
|
client.SetUrls(_globalSettings.Yubico.ValidationUrls);
|
||||||
}
|
}
|
||||||
|
@ -30,13 +30,13 @@ namespace Bit.Core.IdentityServer
|
|||||||
|
|
||||||
public async Task<Client> FindClientByIdAsync(string clientId)
|
public async Task<Client> FindClientByIdAsync(string clientId)
|
||||||
{
|
{
|
||||||
if(!_globalSettings.SelfHosted && clientId.StartsWith("installation."))
|
if (!_globalSettings.SelfHosted && clientId.StartsWith("installation."))
|
||||||
{
|
{
|
||||||
var idParts = clientId.Split('.');
|
var idParts = clientId.Split('.');
|
||||||
if(idParts.Length > 1 && Guid.TryParse(idParts[1], out Guid id))
|
if (idParts.Length > 1 && Guid.TryParse(idParts[1], out Guid id))
|
||||||
{
|
{
|
||||||
var installation = await _installationRepository.GetByIdAsync(id);
|
var installation = await _installationRepository.GetByIdAsync(id);
|
||||||
if(installation != null)
|
if (installation != null)
|
||||||
{
|
{
|
||||||
return new Client
|
return new Client
|
||||||
{
|
{
|
||||||
@ -52,14 +52,14 @@ namespace Bit.Core.IdentityServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(_globalSettings.SelfHosted && clientId.StartsWith("internal.") &&
|
else if (_globalSettings.SelfHosted && clientId.StartsWith("internal.") &&
|
||||||
CoreHelpers.SettingHasValue(_globalSettings.InternalIdentityKey))
|
CoreHelpers.SettingHasValue(_globalSettings.InternalIdentityKey))
|
||||||
{
|
{
|
||||||
var idParts = clientId.Split('.');
|
var idParts = clientId.Split('.');
|
||||||
if(idParts.Length > 1)
|
if (idParts.Length > 1)
|
||||||
{
|
{
|
||||||
var id = idParts[1];
|
var id = idParts[1];
|
||||||
if(!string.IsNullOrWhiteSpace(id))
|
if (!string.IsNullOrWhiteSpace(id))
|
||||||
{
|
{
|
||||||
return new Client
|
return new Client
|
||||||
{
|
{
|
||||||
@ -75,13 +75,13 @@ namespace Bit.Core.IdentityServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(clientId.StartsWith("organization."))
|
else if (clientId.StartsWith("organization."))
|
||||||
{
|
{
|
||||||
var idParts = clientId.Split('.');
|
var idParts = clientId.Split('.');
|
||||||
if(idParts.Length > 1 && Guid.TryParse(idParts[1], out var id))
|
if (idParts.Length > 1 && Guid.TryParse(idParts[1], out var id))
|
||||||
{
|
{
|
||||||
var org = await _organizationRepository.GetByIdAsync(id);
|
var org = await _organizationRepository.GetByIdAsync(id);
|
||||||
if(org != null)
|
if (org != null)
|
||||||
{
|
{
|
||||||
return new Client
|
return new Client
|
||||||
{
|
{
|
||||||
|
@ -29,7 +29,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
public async Task<PersistedGrant> GetAsync(string key)
|
public async Task<PersistedGrant> GetAsync(string key)
|
||||||
{
|
{
|
||||||
var grant = await _grantRepository.GetByKeyAsync(key);
|
var grant = await _grantRepository.GetByKeyAsync(key);
|
||||||
if(grant == null)
|
if (grant == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
var newClaims = new List<Claim>();
|
var newClaims = new List<Claim>();
|
||||||
|
|
||||||
var user = await _userService.GetUserByPrincipalAsync(context.Subject);
|
var user = await _userService.GetUserByPrincipalAsync(context.Subject);
|
||||||
if(user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
var isPremium = await _licensingService.ValidateUserPremiumAsync(user);
|
var isPremium = await _licensingService.ValidateUserPremiumAsync(user);
|
||||||
newClaims.AddRange(new List<Claim>
|
newClaims.AddRange(new List<Claim>
|
||||||
@ -50,39 +50,39 @@ namespace Bit.Core.IdentityServer
|
|||||||
new Claim("sstamp", user.SecurityStamp)
|
new Claim("sstamp", user.SecurityStamp)
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(user.Name))
|
if (!string.IsNullOrWhiteSpace(user.Name))
|
||||||
{
|
{
|
||||||
newClaims.Add(new Claim(JwtClaimTypes.Name, user.Name));
|
newClaims.Add(new Claim(JwtClaimTypes.Name, user.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Orgs that this user belongs to
|
// Orgs that this user belongs to
|
||||||
var orgs = await _currentContext.OrganizationMembershipAsync(_organizationUserRepository, user.Id);
|
var orgs = await _currentContext.OrganizationMembershipAsync(_organizationUserRepository, user.Id);
|
||||||
if(orgs.Any())
|
if (orgs.Any())
|
||||||
{
|
{
|
||||||
foreach(var group in orgs.GroupBy(o => o.Type))
|
foreach (var group in orgs.GroupBy(o => o.Type))
|
||||||
{
|
{
|
||||||
switch(group.Key)
|
switch (group.Key)
|
||||||
{
|
{
|
||||||
case Enums.OrganizationUserType.Owner:
|
case Enums.OrganizationUserType.Owner:
|
||||||
foreach(var org in group)
|
foreach (var org in group)
|
||||||
{
|
{
|
||||||
newClaims.Add(new Claim("orgowner", org.Id.ToString()));
|
newClaims.Add(new Claim("orgowner", org.Id.ToString()));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Enums.OrganizationUserType.Admin:
|
case Enums.OrganizationUserType.Admin:
|
||||||
foreach(var org in group)
|
foreach (var org in group)
|
||||||
{
|
{
|
||||||
newClaims.Add(new Claim("orgadmin", org.Id.ToString()));
|
newClaims.Add(new Claim("orgadmin", org.Id.ToString()));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Enums.OrganizationUserType.Manager:
|
case Enums.OrganizationUserType.Manager:
|
||||||
foreach(var org in group)
|
foreach (var org in group)
|
||||||
{
|
{
|
||||||
newClaims.Add(new Claim("orgmanager", org.Id.ToString()));
|
newClaims.Add(new Claim("orgmanager", org.Id.ToString()));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Enums.OrganizationUserType.User:
|
case Enums.OrganizationUserType.User:
|
||||||
foreach(var org in group)
|
foreach (var org in group)
|
||||||
{
|
{
|
||||||
newClaims.Add(new Claim("orguser", org.Id.ToString()));
|
newClaims.Add(new Claim("orguser", org.Id.ToString()));
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
newClaims.AddRange(existingClaimsToKeep);
|
newClaims.AddRange(existingClaimsToKeep);
|
||||||
if(newClaims.Any())
|
if (newClaims.Any())
|
||||||
{
|
{
|
||||||
context.AddRequestedClaims(newClaims);
|
context.AddRequestedClaims(newClaims);
|
||||||
}
|
}
|
||||||
@ -111,7 +111,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
var securityTokenClaim = context.Subject?.Claims.FirstOrDefault(c => c.Type == "sstamp");
|
var securityTokenClaim = context.Subject?.Claims.FirstOrDefault(c => c.Type == "sstamp");
|
||||||
var user = await _userService.GetUserByPrincipalAsync(context.Subject);
|
var user = await _userService.GetUserByPrincipalAsync(context.Subject);
|
||||||
|
|
||||||
if(user != null && securityTokenClaim != null)
|
if (user != null && securityTokenClaim != null)
|
||||||
{
|
{
|
||||||
context.IsActive = string.Equals(user.SecurityStamp, securityTokenClaim.Value,
|
context.IsActive = string.Equals(user.SecurityStamp, securityTokenClaim.Value,
|
||||||
StringComparison.InvariantCultureIgnoreCase);
|
StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
@ -75,24 +75,24 @@ namespace Bit.Core.IdentityServer
|
|||||||
var twoFactorRequest = !string.IsNullOrWhiteSpace(twoFactorToken) &&
|
var twoFactorRequest = !string.IsNullOrWhiteSpace(twoFactorToken) &&
|
||||||
!string.IsNullOrWhiteSpace(twoFactorProvider);
|
!string.IsNullOrWhiteSpace(twoFactorProvider);
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(context.UserName))
|
if (string.IsNullOrWhiteSpace(context.UserName))
|
||||||
{
|
{
|
||||||
await BuildErrorResultAsync(false, context, null);
|
await BuildErrorResultAsync(false, context, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = await _userManager.FindByEmailAsync(context.UserName.ToLowerInvariant());
|
var user = await _userManager.FindByEmailAsync(context.UserName.ToLowerInvariant());
|
||||||
if(user == null || !await _userService.CheckPasswordAsync(user, context.Password))
|
if (user == null || !await _userService.CheckPasswordAsync(user, context.Password))
|
||||||
{
|
{
|
||||||
await BuildErrorResultAsync(false, context, user);
|
await BuildErrorResultAsync(false, context, user);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var twoFactorRequirement = await RequiresTwoFactorAsync(user);
|
var twoFactorRequirement = await RequiresTwoFactorAsync(user);
|
||||||
if(twoFactorRequirement.Item1)
|
if (twoFactorRequirement.Item1)
|
||||||
{
|
{
|
||||||
var twoFactorProviderType = TwoFactorProviderType.Authenticator; // Just defaulting it
|
var twoFactorProviderType = TwoFactorProviderType.Authenticator; // Just defaulting it
|
||||||
if(!twoFactorRequest || !Enum.TryParse(twoFactorProvider, out twoFactorProviderType))
|
if (!twoFactorRequest || !Enum.TryParse(twoFactorProvider, out twoFactorProviderType))
|
||||||
{
|
{
|
||||||
await BuildTwoFactorResultAsync(user, twoFactorRequirement.Item2, context);
|
await BuildTwoFactorResultAsync(user, twoFactorRequirement.Item2, context);
|
||||||
return;
|
return;
|
||||||
@ -100,12 +100,12 @@ namespace Bit.Core.IdentityServer
|
|||||||
|
|
||||||
var verified = await VerifyTwoFactor(user, twoFactorRequirement.Item2,
|
var verified = await VerifyTwoFactor(user, twoFactorRequirement.Item2,
|
||||||
twoFactorProviderType, twoFactorToken);
|
twoFactorProviderType, twoFactorToken);
|
||||||
if(!verified && twoFactorProviderType != TwoFactorProviderType.Remember)
|
if (!verified && twoFactorProviderType != TwoFactorProviderType.Remember)
|
||||||
{
|
{
|
||||||
await BuildErrorResultAsync(true, context, user);
|
await BuildErrorResultAsync(true, context, user);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if(!verified && twoFactorProviderType == TwoFactorProviderType.Remember)
|
else if (!verified && twoFactorProviderType == TwoFactorProviderType.Remember)
|
||||||
{
|
{
|
||||||
await Task.Delay(2000); // Delay for brute force.
|
await Task.Delay(2000); // Delay for brute force.
|
||||||
await BuildTwoFactorResultAsync(user, twoFactorRequirement.Item2, context);
|
await BuildTwoFactorResultAsync(user, twoFactorRequirement.Item2, context);
|
||||||
@ -131,23 +131,23 @@ namespace Bit.Core.IdentityServer
|
|||||||
|
|
||||||
var claims = new List<Claim>();
|
var claims = new List<Claim>();
|
||||||
|
|
||||||
if(device != null)
|
if (device != null)
|
||||||
{
|
{
|
||||||
claims.Add(new Claim("device", device.Identifier));
|
claims.Add(new Claim("device", device.Identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
var customResponse = new Dictionary<string, object>();
|
var customResponse = new Dictionary<string, object>();
|
||||||
if(!string.IsNullOrWhiteSpace(user.PrivateKey))
|
if (!string.IsNullOrWhiteSpace(user.PrivateKey))
|
||||||
{
|
{
|
||||||
customResponse.Add("PrivateKey", user.PrivateKey);
|
customResponse.Add("PrivateKey", user.PrivateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(user.Key))
|
if (!string.IsNullOrWhiteSpace(user.Key))
|
||||||
{
|
{
|
||||||
customResponse.Add("Key", user.Key);
|
customResponse.Add("Key", user.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sendRememberToken)
|
if (sendRememberToken)
|
||||||
{
|
{
|
||||||
var token = await _userManager.GenerateTwoFactorTokenAsync(user,
|
var token = await _userManager.GenerateTwoFactorTokenAsync(user,
|
||||||
CoreHelpers.CustomProviderName(TwoFactorProviderType.Remember));
|
CoreHelpers.CustomProviderName(TwoFactorProviderType.Remember));
|
||||||
@ -167,30 +167,30 @@ namespace Bit.Core.IdentityServer
|
|||||||
var providers = new Dictionary<byte, Dictionary<string, object>>();
|
var providers = new Dictionary<byte, Dictionary<string, object>>();
|
||||||
|
|
||||||
var enabledProviders = new List<KeyValuePair<TwoFactorProviderType, TwoFactorProvider>>();
|
var enabledProviders = new List<KeyValuePair<TwoFactorProviderType, TwoFactorProvider>>();
|
||||||
if(organization?.GetTwoFactorProviders() != null)
|
if (organization?.GetTwoFactorProviders() != null)
|
||||||
{
|
{
|
||||||
enabledProviders.AddRange(organization.GetTwoFactorProviders().Where(
|
enabledProviders.AddRange(organization.GetTwoFactorProviders().Where(
|
||||||
p => organization.TwoFactorProviderIsEnabled(p.Key)));
|
p => organization.TwoFactorProviderIsEnabled(p.Key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(user.GetTwoFactorProviders() != null)
|
if (user.GetTwoFactorProviders() != null)
|
||||||
{
|
{
|
||||||
foreach(var p in user.GetTwoFactorProviders())
|
foreach (var p in user.GetTwoFactorProviders())
|
||||||
{
|
{
|
||||||
if(await _userService.TwoFactorProviderIsEnabledAsync(p.Key, user))
|
if (await _userService.TwoFactorProviderIsEnabledAsync(p.Key, user))
|
||||||
{
|
{
|
||||||
enabledProviders.Add(p);
|
enabledProviders.Add(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!enabledProviders.Any())
|
if (!enabledProviders.Any())
|
||||||
{
|
{
|
||||||
await BuildErrorResultAsync(false, context, user);
|
await BuildErrorResultAsync(false, context, user);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var provider in enabledProviders)
|
foreach (var provider in enabledProviders)
|
||||||
{
|
{
|
||||||
providerKeys.Add((byte)provider.Key);
|
providerKeys.Add((byte)provider.Key);
|
||||||
var infoDict = await BuildTwoFactorParams(organization, user, provider.Key, provider.Value);
|
var infoDict = await BuildTwoFactorParams(organization, user, provider.Key, provider.Value);
|
||||||
@ -204,7 +204,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
{ "TwoFactorProviders2", providers }
|
{ "TwoFactorProviders2", providers }
|
||||||
});
|
});
|
||||||
|
|
||||||
if(enabledProviders.Count() == 1 && enabledProviders.First().Key == TwoFactorProviderType.Email)
|
if (enabledProviders.Count() == 1 && enabledProviders.First().Key == TwoFactorProviderType.Email)
|
||||||
{
|
{
|
||||||
// Send email now if this is their only 2FA method
|
// Send email now if this is their only 2FA method
|
||||||
await _userService.SendTwoFactorEmailAsync(user);
|
await _userService.SendTwoFactorEmailAsync(user);
|
||||||
@ -214,13 +214,13 @@ namespace Bit.Core.IdentityServer
|
|||||||
private async Task BuildErrorResultAsync(bool twoFactorRequest,
|
private async Task BuildErrorResultAsync(bool twoFactorRequest,
|
||||||
ResourceOwnerPasswordValidationContext context, User user)
|
ResourceOwnerPasswordValidationContext context, User user)
|
||||||
{
|
{
|
||||||
if(user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
await _eventService.LogUserEventAsync(user.Id,
|
await _eventService.LogUserEventAsync(user.Id,
|
||||||
twoFactorRequest ? EventType.User_FailedLogIn2fa : EventType.User_FailedLogIn);
|
twoFactorRequest ? EventType.User_FailedLogIn2fa : EventType.User_FailedLogIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_globalSettings.SelfHosted)
|
if (_globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(Constants.BypassFiltersEventId,
|
_logger.LogWarning(Constants.BypassFiltersEventId,
|
||||||
string.Format("Failed login attempt{0}{1}", twoFactorRequest ? ", 2FA invalid." : ".",
|
string.Format("Failed login attempt{0}{1}", twoFactorRequest ? ", 2FA invalid." : ".",
|
||||||
@ -244,11 +244,11 @@ namespace Bit.Core.IdentityServer
|
|||||||
Organization firstEnabledOrg = null;
|
Organization firstEnabledOrg = null;
|
||||||
var orgs = (await _currentContext.OrganizationMembershipAsync(_organizationUserRepository, user.Id))
|
var orgs = (await _currentContext.OrganizationMembershipAsync(_organizationUserRepository, user.Id))
|
||||||
.ToList();
|
.ToList();
|
||||||
if(orgs.Any())
|
if (orgs.Any())
|
||||||
{
|
{
|
||||||
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
||||||
var twoFactorOrgs = orgs.Where(o => OrgUsing2fa(orgAbilities, o.Id));
|
var twoFactorOrgs = orgs.Where(o => OrgUsing2fa(orgAbilities, o.Id));
|
||||||
if(twoFactorOrgs.Any())
|
if (twoFactorOrgs.Any())
|
||||||
{
|
{
|
||||||
var userOrgs = await _organizationRepository.GetManyByUserIdAsync(user.Id);
|
var userOrgs = await _organizationRepository.GetManyByUserIdAsync(user.Id);
|
||||||
firstEnabledOrg = userOrgs.FirstOrDefault(
|
firstEnabledOrg = userOrgs.FirstOrDefault(
|
||||||
@ -272,7 +272,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
var deviceName = context.Request.Raw["DeviceName"]?.ToString();
|
var deviceName = context.Request.Raw["DeviceName"]?.ToString();
|
||||||
var devicePushToken = context.Request.Raw["DevicePushToken"]?.ToString();
|
var devicePushToken = context.Request.Raw["DevicePushToken"]?.ToString();
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(deviceIdentifier) || string.IsNullOrWhiteSpace(deviceType) ||
|
if (string.IsNullOrWhiteSpace(deviceIdentifier) || string.IsNullOrWhiteSpace(deviceType) ||
|
||||||
string.IsNullOrWhiteSpace(deviceName) || !Enum.TryParse(deviceType, out DeviceType type))
|
string.IsNullOrWhiteSpace(deviceName) || !Enum.TryParse(deviceType, out DeviceType type))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@ -290,7 +290,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
private async Task<bool> VerifyTwoFactor(User user, Organization organization, TwoFactorProviderType type,
|
private async Task<bool> VerifyTwoFactor(User user, Organization organization, TwoFactorProviderType type,
|
||||||
string token)
|
string token)
|
||||||
{
|
{
|
||||||
switch(type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case TwoFactorProviderType.Authenticator:
|
case TwoFactorProviderType.Authenticator:
|
||||||
case TwoFactorProviderType.Email:
|
case TwoFactorProviderType.Email:
|
||||||
@ -298,7 +298,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
case TwoFactorProviderType.YubiKey:
|
case TwoFactorProviderType.YubiKey:
|
||||||
case TwoFactorProviderType.U2f:
|
case TwoFactorProviderType.U2f:
|
||||||
case TwoFactorProviderType.Remember:
|
case TwoFactorProviderType.Remember:
|
||||||
if(type != TwoFactorProviderType.Remember &&
|
if (type != TwoFactorProviderType.Remember &&
|
||||||
!(await _userService.TwoFactorProviderIsEnabledAsync(type, user)))
|
!(await _userService.TwoFactorProviderIsEnabledAsync(type, user)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -306,7 +306,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
return await _userManager.VerifyTwoFactorTokenAsync(user,
|
return await _userManager.VerifyTwoFactorTokenAsync(user,
|
||||||
CoreHelpers.CustomProviderName(type), token);
|
CoreHelpers.CustomProviderName(type), token);
|
||||||
case TwoFactorProviderType.OrganizationDuo:
|
case TwoFactorProviderType.OrganizationDuo:
|
||||||
if(!organization?.TwoFactorProviderIsEnabled(type) ?? true)
|
if (!organization?.TwoFactorProviderIsEnabled(type) ?? true)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -320,20 +320,20 @@ namespace Bit.Core.IdentityServer
|
|||||||
private async Task<Dictionary<string, object>> BuildTwoFactorParams(Organization organization, User user,
|
private async Task<Dictionary<string, object>> BuildTwoFactorParams(Organization organization, User user,
|
||||||
TwoFactorProviderType type, TwoFactorProvider provider)
|
TwoFactorProviderType type, TwoFactorProvider provider)
|
||||||
{
|
{
|
||||||
switch(type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case TwoFactorProviderType.Duo:
|
case TwoFactorProviderType.Duo:
|
||||||
case TwoFactorProviderType.U2f:
|
case TwoFactorProviderType.U2f:
|
||||||
case TwoFactorProviderType.Email:
|
case TwoFactorProviderType.Email:
|
||||||
case TwoFactorProviderType.YubiKey:
|
case TwoFactorProviderType.YubiKey:
|
||||||
if(!(await _userService.TwoFactorProviderIsEnabledAsync(type, user)))
|
if (!(await _userService.TwoFactorProviderIsEnabledAsync(type, user)))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var token = await _userManager.GenerateTwoFactorTokenAsync(user,
|
var token = await _userManager.GenerateTwoFactorTokenAsync(user,
|
||||||
CoreHelpers.CustomProviderName(type));
|
CoreHelpers.CustomProviderName(type));
|
||||||
if(type == TwoFactorProviderType.Duo)
|
if (type == TwoFactorProviderType.Duo)
|
||||||
{
|
{
|
||||||
return new Dictionary<string, object>
|
return new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
@ -341,7 +341,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
["Signature"] = token
|
["Signature"] = token
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if(type == TwoFactorProviderType.U2f)
|
else if (type == TwoFactorProviderType.U2f)
|
||||||
{
|
{
|
||||||
// TODO: Remove "Challenges" in a future update. Deprecated.
|
// TODO: Remove "Challenges" in a future update. Deprecated.
|
||||||
var tokens = token?.Split('|');
|
var tokens = token?.Split('|');
|
||||||
@ -351,14 +351,14 @@ namespace Bit.Core.IdentityServer
|
|||||||
["Challenges"] = tokens != null && tokens.Length > 1 ? tokens[1] : null
|
["Challenges"] = tokens != null && tokens.Length > 1 ? tokens[1] : null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if(type == TwoFactorProviderType.Email)
|
else if (type == TwoFactorProviderType.Email)
|
||||||
{
|
{
|
||||||
return new Dictionary<string, object>
|
return new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
["Email"] = token
|
["Email"] = token
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if(type == TwoFactorProviderType.YubiKey)
|
else if (type == TwoFactorProviderType.YubiKey)
|
||||||
{
|
{
|
||||||
return new Dictionary<string, object>
|
return new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
@ -367,7 +367,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
case TwoFactorProviderType.OrganizationDuo:
|
case TwoFactorProviderType.OrganizationDuo:
|
||||||
if(await _organizationDuoWebTokenProvider.CanGenerateTwoFactorTokenAsync(organization))
|
if (await _organizationDuoWebTokenProvider.CanGenerateTwoFactorTokenAsync(organization))
|
||||||
{
|
{
|
||||||
return new Dictionary<string, object>
|
return new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
@ -384,20 +384,20 @@ namespace Bit.Core.IdentityServer
|
|||||||
private async Task<Device> SaveDeviceAsync(User user, ResourceOwnerPasswordValidationContext context)
|
private async Task<Device> SaveDeviceAsync(User user, ResourceOwnerPasswordValidationContext context)
|
||||||
{
|
{
|
||||||
var device = GetDeviceFromRequest(context);
|
var device = GetDeviceFromRequest(context);
|
||||||
if(device != null)
|
if (device != null)
|
||||||
{
|
{
|
||||||
var existingDevice = await _deviceRepository.GetByIdentifierAsync(device.Identifier, user.Id);
|
var existingDevice = await _deviceRepository.GetByIdentifierAsync(device.Identifier, user.Id);
|
||||||
if(existingDevice == null)
|
if (existingDevice == null)
|
||||||
{
|
{
|
||||||
device.UserId = user.Id;
|
device.UserId = user.Id;
|
||||||
await _deviceService.SaveAsync(device);
|
await _deviceService.SaveAsync(device);
|
||||||
|
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
if(now - user.CreationDate > TimeSpan.FromMinutes(10))
|
if (now - user.CreationDate > TimeSpan.FromMinutes(10))
|
||||||
{
|
{
|
||||||
var deviceType = device.Type.GetType().GetMember(device.Type.ToString())
|
var deviceType = device.Type.GetType().GetMember(device.Type.ToString())
|
||||||
.FirstOrDefault()?.GetCustomAttribute<DisplayAttribute>()?.GetName();
|
.FirstOrDefault()?.GetCustomAttribute<DisplayAttribute>()?.GetName();
|
||||||
if(!_globalSettings.DisableEmailNewDevice)
|
if (!_globalSettings.DisableEmailNewDevice)
|
||||||
{
|
{
|
||||||
await _mailService.SendNewDeviceLoggedInEmail(user.Email, deviceType, now,
|
await _mailService.SendNewDeviceLoggedInEmail(user.Email, deviceType, now,
|
||||||
_currentContext.IpAddress);
|
_currentContext.IpAddress);
|
||||||
|
@ -38,7 +38,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
AccessTokenLifetime = 3600 * accessTokenLifetimeHours;
|
AccessTokenLifetime = 3600 * accessTokenLifetimeHours;
|
||||||
AllowOfflineAccess = true;
|
AllowOfflineAccess = true;
|
||||||
|
|
||||||
if(scopes == null)
|
if (scopes == null)
|
||||||
{
|
{
|
||||||
scopes = new string[] { "api" };
|
scopes = new string[] { "api" };
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,12 @@ namespace Bit.Core.IdentityServer
|
|||||||
return (request) =>
|
return (request) =>
|
||||||
{
|
{
|
||||||
var authorization = request.Headers[_authHeader].FirstOrDefault();
|
var authorization = request.Headers[_authHeader].FirstOrDefault();
|
||||||
if(string.IsNullOrWhiteSpace(authorization))
|
if (string.IsNullOrWhiteSpace(authorization))
|
||||||
{
|
{
|
||||||
return request.Query[_queuryScheme].FirstOrDefault();
|
return request.Query[_queuryScheme].FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(authorization.StartsWith(_headerScheme, StringComparison.OrdinalIgnoreCase))
|
if (authorization.StartsWith(_headerScheme, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return authorization.Substring(_headerScheme.Length).Trim();
|
return authorization.Substring(_headerScheme.Length).Trim();
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ namespace Bit.Core.Jobs
|
|||||||
{
|
{
|
||||||
await ExecuteJobAsync(context);
|
await ExecuteJobAsync(context);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError(2, e, "Error performing {0}.", GetType().Name);
|
_logger.LogError(2, e, "Error performing {0}.", GetType().Name);
|
||||||
}
|
}
|
||||||
|
@ -42,9 +42,9 @@ namespace Bit.Core.Jobs
|
|||||||
_scheduler.ListenerManager.AddJobListener(new JobListener(_listenerLogger),
|
_scheduler.ListenerManager.AddJobListener(new JobListener(_listenerLogger),
|
||||||
GroupMatcher<JobKey>.AnyGroup());
|
GroupMatcher<JobKey>.AnyGroup());
|
||||||
await _scheduler.Start(cancellationToken);
|
await _scheduler.Start(cancellationToken);
|
||||||
if(Jobs != null)
|
if (Jobs != null)
|
||||||
{
|
{
|
||||||
foreach(var job in Jobs)
|
foreach (var job in Jobs)
|
||||||
{
|
{
|
||||||
var builtJob = JobBuilder.Create(job.Item1).Build();
|
var builtJob = JobBuilder.Create(job.Item1).Build();
|
||||||
await _scheduler.ScheduleJob(builtJob, job.Item2);
|
await _scheduler.ScheduleJob(builtJob, job.Item2);
|
||||||
|
@ -15,7 +15,7 @@ namespace Bit.Core.Models.Api
|
|||||||
public CipherLoginModel(CipherLoginData data)
|
public CipherLoginModel(CipherLoginData data)
|
||||||
{
|
{
|
||||||
Uris = data.Uris?.Select(u => new CipherLoginUriModel(u))?.ToList();
|
Uris = data.Uris?.Select(u => new CipherLoginUriModel(u))?.ToList();
|
||||||
if(!Uris?.Any() ?? true)
|
if (!Uris?.Any() ?? true)
|
||||||
{
|
{
|
||||||
Uri = data.Uri;
|
Uri = data.Uri;
|
||||||
}
|
}
|
||||||
@ -33,12 +33,12 @@ namespace Bit.Core.Models.Api
|
|||||||
get => Uris?.FirstOrDefault()?.Uri;
|
get => Uris?.FirstOrDefault()?.Uri;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(value))
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Uris == null)
|
if (Uris == null)
|
||||||
{
|
{
|
||||||
Uris = new List<CipherLoginUriModel>();
|
Uris = new List<CipherLoginUriModel>();
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ namespace Bit.Core.Models.Api.Public
|
|||||||
|
|
||||||
public MemberBaseModel(OrganizationUser user)
|
public MemberBaseModel(OrganizationUser user)
|
||||||
{
|
{
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(user));
|
throw new ArgumentNullException(nameof(user));
|
||||||
}
|
}
|
||||||
@ -24,7 +24,7 @@ namespace Bit.Core.Models.Api.Public
|
|||||||
|
|
||||||
public MemberBaseModel(OrganizationUserUserDetails user)
|
public MemberBaseModel(OrganizationUserUserDetails user)
|
||||||
{
|
{
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(user));
|
throw new ArgumentNullException(nameof(user));
|
||||||
}
|
}
|
||||||
|
@ -28,19 +28,19 @@ namespace Bit.Core.Models.Api.Public
|
|||||||
|
|
||||||
public Tuple<DateTime, DateTime> ToDateRange()
|
public Tuple<DateTime, DateTime> ToDateRange()
|
||||||
{
|
{
|
||||||
if(!End.HasValue || !Start.HasValue)
|
if (!End.HasValue || !Start.HasValue)
|
||||||
{
|
{
|
||||||
End = DateTime.UtcNow.Date.AddDays(1).AddMilliseconds(-1);
|
End = DateTime.UtcNow.Date.AddDays(1).AddMilliseconds(-1);
|
||||||
Start = DateTime.UtcNow.Date.AddDays(-30);
|
Start = DateTime.UtcNow.Date.AddDays(-30);
|
||||||
}
|
}
|
||||||
else if(Start.Value > End.Value)
|
else if (Start.Value > End.Value)
|
||||||
{
|
{
|
||||||
var newEnd = Start;
|
var newEnd = Start;
|
||||||
Start = End;
|
Start = End;
|
||||||
End = newEnd;
|
End = newEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((End.Value - Start.Value) > TimeSpan.FromDays(367))
|
if ((End.Value - Start.Value) > TimeSpan.FromDays(367))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Date range must be < 367 days.");
|
throw new BadRequestException("Date range must be < 367 days.");
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,12 @@ namespace Bit.Core.Models.Api.Public
|
|||||||
|
|
||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
if(Email.Contains(" ") || Email.Contains("<"))
|
if (Email.Contains(" ") || Email.Contains("<"))
|
||||||
{
|
{
|
||||||
yield return new ValidationResult($"Email is not valid.",
|
yield return new ValidationResult($"Email is not valid.",
|
||||||
new string[] { nameof(Email) });
|
new string[] { nameof(Email) });
|
||||||
}
|
}
|
||||||
else if(Email.Length > 50)
|
else if (Email.Length > 50)
|
||||||
{
|
{
|
||||||
yield return new ValidationResult($"Email is longer than 50 characters.",
|
yield return new ValidationResult($"Email is longer than 50 characters.",
|
||||||
new string[] { nameof(Email) });
|
new string[] { nameof(Email) });
|
||||||
|
@ -7,7 +7,7 @@ namespace Bit.Core.Models.Api.Public
|
|||||||
{
|
{
|
||||||
public AssociationWithPermissionsResponseModel(SelectionReadOnly selection)
|
public AssociationWithPermissionsResponseModel(SelectionReadOnly selection)
|
||||||
{
|
{
|
||||||
if(selection == null)
|
if (selection == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(selection));
|
throw new ArgumentNullException(nameof(selection));
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ namespace Bit.Core.Models.Api.Public
|
|||||||
{
|
{
|
||||||
public CollectionResponseModel(Collection collection, IEnumerable<SelectionReadOnly> groups)
|
public CollectionResponseModel(Collection collection, IEnumerable<SelectionReadOnly> groups)
|
||||||
{
|
{
|
||||||
if(collection == null)
|
if (collection == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(collection));
|
throw new ArgumentNullException(nameof(collection));
|
||||||
}
|
}
|
||||||
|
@ -20,17 +20,17 @@ namespace Bit.Core.Models.Api.Public
|
|||||||
var keys = modelState.Keys.ToList();
|
var keys = modelState.Keys.ToList();
|
||||||
var values = modelState.Values.ToList();
|
var values = modelState.Values.ToList();
|
||||||
|
|
||||||
for(var i = 0; i < values.Count; i++)
|
for (var i = 0; i < values.Count; i++)
|
||||||
{
|
{
|
||||||
var value = values[i];
|
var value = values[i];
|
||||||
if(keys.Count <= i)
|
if (keys.Count <= i)
|
||||||
{
|
{
|
||||||
// Keys not available for some reason.
|
// Keys not available for some reason.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = keys[i];
|
var key = keys[i];
|
||||||
if(value.ValidationState != ModelValidationState.Invalid || value.Errors.Count == 0)
|
if (value.ValidationState != ModelValidationState.Invalid || value.Errors.Count == 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ namespace Bit.Core.Models.Api.Public
|
|||||||
{
|
{
|
||||||
public EventResponseModel(IEvent ev)
|
public EventResponseModel(IEvent ev)
|
||||||
{
|
{
|
||||||
if(ev == null)
|
if (ev == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(ev));
|
throw new ArgumentNullException(nameof(ev));
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ namespace Bit.Core.Models.Api.Public
|
|||||||
{
|
{
|
||||||
public GroupResponseModel(Group group, IEnumerable<SelectionReadOnly> collections)
|
public GroupResponseModel(Group group, IEnumerable<SelectionReadOnly> collections)
|
||||||
{
|
{
|
||||||
if(group == null)
|
if (group == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(group));
|
throw new ArgumentNullException(nameof(group));
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace Bit.Core.Models.Api.Public
|
|||||||
public MemberResponseModel(OrganizationUser user, IEnumerable<SelectionReadOnly> collections)
|
public MemberResponseModel(OrganizationUser user, IEnumerable<SelectionReadOnly> collections)
|
||||||
: base(user)
|
: base(user)
|
||||||
{
|
{
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(user));
|
throw new ArgumentNullException(nameof(user));
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ namespace Bit.Core.Models.Api.Public
|
|||||||
IEnumerable<SelectionReadOnly> collections)
|
IEnumerable<SelectionReadOnly> collections)
|
||||||
: base(user)
|
: base(user)
|
||||||
{
|
{
|
||||||
if(user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(user));
|
throw new ArgumentNullException(nameof(user));
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ namespace Bit.Core.Models.Api.Public
|
|||||||
{
|
{
|
||||||
public PolicyResponseModel(Policy policy)
|
public PolicyResponseModel(Policy policy)
|
||||||
{
|
{
|
||||||
if(policy == null)
|
if (policy == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(policy));
|
throw new ArgumentNullException(nameof(policy));
|
||||||
}
|
}
|
||||||
@ -21,7 +21,7 @@ namespace Bit.Core.Models.Api.Public
|
|||||||
Id = policy.Id;
|
Id = policy.Id;
|
||||||
Type = policy.Type;
|
Type = policy.Type;
|
||||||
Enabled = policy.Enabled;
|
Enabled = policy.Enabled;
|
||||||
if(!string.IsNullOrWhiteSpace(policy.Data))
|
if (!string.IsNullOrWhiteSpace(policy.Data))
|
||||||
{
|
{
|
||||||
Data = JsonConvert.DeserializeObject<Dictionary<string, object>>(policy.Data);
|
Data = JsonConvert.DeserializeObject<Dictionary<string, object>>(policy.Data);
|
||||||
}
|
}
|
||||||
|
@ -14,12 +14,12 @@ namespace Bit.Core.Models.Api
|
|||||||
|
|
||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
if(Kdf.HasValue && KdfIterations.HasValue)
|
if (Kdf.HasValue && KdfIterations.HasValue)
|
||||||
{
|
{
|
||||||
switch(Kdf.Value)
|
switch (Kdf.Value)
|
||||||
{
|
{
|
||||||
case KdfType.PBKDF2_SHA256:
|
case KdfType.PBKDF2_SHA256:
|
||||||
if(KdfIterations.Value < 5000 || KdfIterations.Value > 2_000_000)
|
if (KdfIterations.Value < 5000 || KdfIterations.Value > 2_000_000)
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("KDF iterations must be between 5000 and 2000000.");
|
yield return new ValidationResult("KDF iterations must be between 5000 and 2000000.");
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,12 @@ namespace Bit.Core.Models.Api
|
|||||||
|
|
||||||
public User ToUser(User existingUser)
|
public User ToUser(User existingUser)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(existingUser.PublicKey) && !string.IsNullOrWhiteSpace(PublicKey))
|
if (string.IsNullOrWhiteSpace(existingUser.PublicKey) && !string.IsNullOrWhiteSpace(PublicKey))
|
||||||
{
|
{
|
||||||
existingUser.PublicKey = PublicKey;
|
existingUser.PublicKey = PublicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(existingUser.PrivateKey))
|
if (string.IsNullOrWhiteSpace(existingUser.PrivateKey))
|
||||||
{
|
{
|
||||||
existingUser.PrivateKey = EncryptedPrivateKey;
|
existingUser.PrivateKey = EncryptedPrivateKey;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ namespace Bit.Core.Models.Api
|
|||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
var creditType = PaymentMethodType.HasValue && PaymentMethodType.Value == Enums.PaymentMethodType.Credit;
|
var creditType = PaymentMethodType.HasValue && PaymentMethodType.Value == Enums.PaymentMethodType.Credit;
|
||||||
if(string.IsNullOrWhiteSpace(PaymentToken) && !creditType && License == null)
|
if (string.IsNullOrWhiteSpace(PaymentToken) && !creditType && License == null)
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("Payment token or license is required.");
|
yield return new ValidationResult("Payment token or license is required.");
|
||||||
}
|
}
|
||||||
|
@ -37,12 +37,12 @@ namespace Bit.Core.Models.Api
|
|||||||
KdfIterations = KdfIterations.GetValueOrDefault(5000)
|
KdfIterations = KdfIterations.GetValueOrDefault(5000)
|
||||||
};
|
};
|
||||||
|
|
||||||
if(Key != null)
|
if (Key != null)
|
||||||
{
|
{
|
||||||
user.Key = Key;
|
user.Key = Key;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Keys != null)
|
if (Keys != null)
|
||||||
{
|
{
|
||||||
Keys.ToUser(user);
|
Keys.ToUser(user);
|
||||||
}
|
}
|
||||||
@ -52,12 +52,12 @@ namespace Bit.Core.Models.Api
|
|||||||
|
|
||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
if(Kdf.HasValue && KdfIterations.HasValue)
|
if (Kdf.HasValue && KdfIterations.HasValue)
|
||||||
{
|
{
|
||||||
switch(Kdf.Value)
|
switch (Kdf.Value)
|
||||||
{
|
{
|
||||||
case KdfType.PBKDF2_SHA256:
|
case KdfType.PBKDF2_SHA256:
|
||||||
if(KdfIterations.Value < 5000 || KdfIterations.Value > 1_000_000)
|
if (KdfIterations.Value < 5000 || KdfIterations.Value > 1_000_000)
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("KDF iterations must be between 5000 and 1000000.");
|
yield return new ValidationResult("KDF iterations must be between 5000 and 1000000.");
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ namespace Bit.Core.Models.Api
|
|||||||
|
|
||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
if(StorageGbAdjustment == 0)
|
if (StorageGbAdjustment == 0)
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("Storage adjustment cannot be 0.",
|
yield return new ValidationResult("Storage adjustment cannot be 0.",
|
||||||
new string[] { nameof(StorageGbAdjustment) });
|
new string[] { nameof(StorageGbAdjustment) });
|
||||||
|
@ -33,16 +33,16 @@ namespace Bit.Core.Models.Api
|
|||||||
};
|
};
|
||||||
|
|
||||||
var posData = string.Empty;
|
var posData = string.Empty;
|
||||||
if(UserId.HasValue)
|
if (UserId.HasValue)
|
||||||
{
|
{
|
||||||
posData = "userId:" + UserId.Value;
|
posData = "userId:" + UserId.Value;
|
||||||
}
|
}
|
||||||
else if(OrganizationId.HasValue)
|
else if (OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
posData = "organizationId:" + OrganizationId.Value;
|
posData = "organizationId:" + OrganizationId.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Credit)
|
if (Credit)
|
||||||
{
|
{
|
||||||
posData += ",accountCredit:1";
|
posData += ",accountCredit:1";
|
||||||
inv.ItemDesc = "Bitwarden Account Credit";
|
inv.ItemDesc = "Bitwarden Account Credit";
|
||||||
@ -58,7 +58,7 @@ namespace Bit.Core.Models.Api
|
|||||||
|
|
||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
if(!UserId.HasValue && !OrganizationId.HasValue)
|
if (!UserId.HasValue && !OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("User or Ooganization is required.");
|
yield return new ValidationResult("User or Ooganization is required.");
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ namespace Bit.Core.Models.Api
|
|||||||
|
|
||||||
public Cipher ToCipher(Cipher existingCipher)
|
public Cipher ToCipher(Cipher existingCipher)
|
||||||
{
|
{
|
||||||
switch(existingCipher.Type)
|
switch (existingCipher.Type)
|
||||||
{
|
{
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
var loginObj = JObject.FromObject(new CipherLoginData(this),
|
var loginObj = JObject.FromObject(new CipherLoginData(this),
|
||||||
@ -90,29 +90,29 @@ namespace Bit.Core.Models.Api
|
|||||||
var hasAttachments2 = (Attachments2?.Count ?? 0) > 0;
|
var hasAttachments2 = (Attachments2?.Count ?? 0) > 0;
|
||||||
var hasAttachments = (Attachments?.Count ?? 0) > 0;
|
var hasAttachments = (Attachments?.Count ?? 0) > 0;
|
||||||
|
|
||||||
if(!hasAttachments2 && !hasAttachments)
|
if (!hasAttachments2 && !hasAttachments)
|
||||||
{
|
{
|
||||||
return existingCipher;
|
return existingCipher;
|
||||||
}
|
}
|
||||||
|
|
||||||
var attachments = existingCipher.GetAttachments();
|
var attachments = existingCipher.GetAttachments();
|
||||||
if((attachments?.Count ?? 0) == 0)
|
if ((attachments?.Count ?? 0) == 0)
|
||||||
{
|
{
|
||||||
return existingCipher;
|
return existingCipher;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(hasAttachments2)
|
if (hasAttachments2)
|
||||||
{
|
{
|
||||||
foreach(var attachment in attachments.Where(a => Attachments2.ContainsKey(a.Key)))
|
foreach (var attachment in attachments.Where(a => Attachments2.ContainsKey(a.Key)))
|
||||||
{
|
{
|
||||||
var attachment2 = Attachments2[attachment.Key];
|
var attachment2 = Attachments2[attachment.Key];
|
||||||
attachment.Value.FileName = attachment2.FileName;
|
attachment.Value.FileName = attachment2.FileName;
|
||||||
attachment.Value.Key = attachment2.Key;
|
attachment.Value.Key = attachment2.Key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(hasAttachments)
|
else if (hasAttachments)
|
||||||
{
|
{
|
||||||
foreach(var attachment in attachments.Where(a => Attachments.ContainsKey(a.Key)))
|
foreach (var attachment in attachments.Where(a => Attachments.ContainsKey(a.Key)))
|
||||||
{
|
{
|
||||||
attachment.Value.FileName = Attachments[attachment.Key];
|
attachment.Value.FileName = Attachments[attachment.Key];
|
||||||
attachment.Value.Key = null;
|
attachment.Value.Key = null;
|
||||||
@ -125,7 +125,7 @@ namespace Bit.Core.Models.Api
|
|||||||
|
|
||||||
public Cipher ToOrganizationCipher()
|
public Cipher ToOrganizationCipher()
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(OrganizationId))
|
if (string.IsNullOrWhiteSpace(OrganizationId))
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(OrganizationId));
|
throw new ArgumentNullException(nameof(OrganizationId));
|
||||||
}
|
}
|
||||||
@ -162,7 +162,7 @@ namespace Bit.Core.Models.Api
|
|||||||
|
|
||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
if(!string.IsNullOrWhiteSpace(Cipher.OrganizationId) && (!CollectionIds?.Any() ?? true))
|
if (!string.IsNullOrWhiteSpace(Cipher.OrganizationId) && (!CollectionIds?.Any() ?? true))
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("You must select at least one collection.",
|
yield return new ValidationResult("You must select at least one collection.",
|
||||||
new string[] { nameof(CollectionIds) });
|
new string[] { nameof(CollectionIds) });
|
||||||
@ -179,13 +179,13 @@ namespace Bit.Core.Models.Api
|
|||||||
|
|
||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(Cipher.OrganizationId))
|
if (string.IsNullOrWhiteSpace(Cipher.OrganizationId))
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("Cipher OrganizationId is required.",
|
yield return new ValidationResult("Cipher OrganizationId is required.",
|
||||||
new string[] { nameof(Cipher.OrganizationId) });
|
new string[] { nameof(Cipher.OrganizationId) });
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!CollectionIds?.Any() ?? true)
|
if (!CollectionIds?.Any() ?? true)
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("You must select at least one collection.",
|
yield return new ValidationResult("You must select at least one collection.",
|
||||||
new string[] { nameof(CollectionIds) });
|
new string[] { nameof(CollectionIds) });
|
||||||
@ -221,7 +221,7 @@ namespace Bit.Core.Models.Api
|
|||||||
|
|
||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
if(!Ciphers?.Any() ?? true)
|
if (!Ciphers?.Any() ?? true)
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("You must select at least one cipher.",
|
yield return new ValidationResult("You must select at least one cipher.",
|
||||||
new string[] { nameof(Ciphers) });
|
new string[] { nameof(Ciphers) });
|
||||||
@ -230,27 +230,27 @@ namespace Bit.Core.Models.Api
|
|||||||
{
|
{
|
||||||
var allHaveIds = true;
|
var allHaveIds = true;
|
||||||
var organizationIds = new HashSet<string>();
|
var organizationIds = new HashSet<string>();
|
||||||
foreach(var c in Ciphers)
|
foreach (var c in Ciphers)
|
||||||
{
|
{
|
||||||
organizationIds.Add(c.OrganizationId);
|
organizationIds.Add(c.OrganizationId);
|
||||||
if(allHaveIds)
|
if (allHaveIds)
|
||||||
{
|
{
|
||||||
allHaveIds = !(!c.Id.HasValue || string.IsNullOrWhiteSpace(c.OrganizationId));
|
allHaveIds = !(!c.Id.HasValue || string.IsNullOrWhiteSpace(c.OrganizationId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!allHaveIds)
|
if (!allHaveIds)
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("All Ciphers must have an Id and OrganizationId.",
|
yield return new ValidationResult("All Ciphers must have an Id and OrganizationId.",
|
||||||
new string[] { nameof(Ciphers) });
|
new string[] { nameof(Ciphers) });
|
||||||
}
|
}
|
||||||
else if(organizationIds.Count != 1)
|
else if (organizationIds.Count != 1)
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("All ciphers must be for the same organization.");
|
yield return new ValidationResult("All ciphers must be for the same organization.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!CollectionIds?.Any() ?? true)
|
if (!CollectionIds?.Any() ?? true)
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("You must select at least one collection.",
|
yield return new ValidationResult("You must select at least one collection.",
|
||||||
new string[] { nameof(CollectionIds) });
|
new string[] { nameof(CollectionIds) });
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user