From 13a2206735e49af27e483ff41f677a11a8cc9882 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Tue, 9 Apr 2019 16:13:32 -0400 Subject: [PATCH] user service --- src/Core/Abstractions/IUserService.cs | 26 ++++ src/Core/Models/Data/OrganizationData.cs | 45 ++++++ src/Core/Models/Domain/Organization.cs | 78 +++++++++++ src/Core/Services/UserService.cs | 167 +++++++++++++++++++++++ 4 files changed, 316 insertions(+) create mode 100644 src/Core/Abstractions/IUserService.cs create mode 100644 src/Core/Models/Data/OrganizationData.cs create mode 100644 src/Core/Models/Domain/Organization.cs create mode 100644 src/Core/Services/UserService.cs diff --git a/src/Core/Abstractions/IUserService.cs b/src/Core/Abstractions/IUserService.cs new file mode 100644 index 000000000..b282acf54 --- /dev/null +++ b/src/Core/Abstractions/IUserService.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Bit.Core.Enums; +using Bit.Core.Models.Data; +using Bit.Core.Models.Domain; + +namespace Bit.Core.Abstractions +{ + public interface IUserService + { + Task CanAccessPremiumAsync(); + Task ClearAsync(); + Task ClearOrganizationsAsync(string userId); + Task> GetAllOrganizationAsync(); + Task GetEmailAsync(); + Task GetKdfAsync(); + Task GetKdfIterationsAsync(); + Task GetOrganizationAsync(string id); + Task GetSecurityStampAsync(); + Task GetUserIdAsync(); + Task IsAuthenticatedAsync(); + Task ReplaceOrganizationsAsync(Dictionary organizations); + Task SetInformationAsync(string userId, string email, KdfType kdf, int kdfIterations); + Task SetSecurityStampAsync(string stamp); + } +} \ No newline at end of file diff --git a/src/Core/Models/Data/OrganizationData.cs b/src/Core/Models/Data/OrganizationData.cs new file mode 100644 index 000000000..ac9d2d71f --- /dev/null +++ b/src/Core/Models/Data/OrganizationData.cs @@ -0,0 +1,45 @@ +using Bit.Core.Enums; +using Bit.Core.Models.Response; + +namespace Bit.Core.Models.Data +{ + public class OrganizationData + { + public OrganizationData(ProfileOrganizationResponse response) + { + Id = response.Id; + Name = response.Name; + Status = response.Status; + Type = response.Type; + Enabled = response.Enabled; + UseGroups = response.UseGroups; + UseDirectory = response.UseDirectory; + UseEvents = response.UseEvents; + UseTotp = response.UseTotp; + Use2fa = response.Use2fa; + UseApi = response.UseApi; + SelfHost = response.SelfHost; + UsersGetPremium = response.UsersGetPremium; + Seats = response.Seats; + MaxCollections = response.MaxCollections; + MaxStorageGb = response.MaxStorageGb; + } + + public string Id { get; set; } + public string Name { get; set; } + public OrganizationUserStatusType Status { get; set; } + public OrganizationUserType Type { get; set; } + public bool Enabled { get; set; } + public bool UseGroups { get; set; } + public bool UseDirectory { get; set; } + public bool UseEvents { get; set; } + public bool UseTotp { get; set; } + public bool Use2fa { get; set; } + public bool UseApi { get; set; } + public bool SelfHost { get; set; } + public bool UsersGetPremium { get; set; } + public int Seats { get; set; } + public int MaxCollections { get; set; } + public short? MaxStorageGb { get; set; } + } +} diff --git a/src/Core/Models/Domain/Organization.cs b/src/Core/Models/Domain/Organization.cs new file mode 100644 index 000000000..cb2857605 --- /dev/null +++ b/src/Core/Models/Domain/Organization.cs @@ -0,0 +1,78 @@ +using Bit.Core.Enums; +using Bit.Core.Models.Data; + +namespace Bit.Core.Models.Domain +{ + public class Organization + { + public Organization() { } + + public Organization(OrganizationData obj) + { + Id = obj.Id; + Name = obj.Name; + Status = obj.Status; + Type = obj.Type; + Enabled = obj.Enabled; + UseGroups = obj.UseGroups; + UseDirectory = obj.UseDirectory; + UseEvents = obj.UseEvents; + UseTotp = obj.UseTotp; + Use2fa = obj.Use2fa; + UseApi = obj.UseApi; + SelfHost = obj.SelfHost; + UsersGetPremium = obj.UsersGetPremium; + Seats = obj.Seats; + MaxCollections = obj.MaxCollections; + MaxStorageGb = obj.MaxStorageGb; + } + + public string Id { get; set; } + public string Name { get; set; } + public OrganizationUserStatusType Status { get; set; } + public OrganizationUserType Type { get; set; } + public bool Enabled { get; set; } + public bool UseGroups { get; set; } + public bool UseDirectory { get; set; } + public bool UseEvents { get; set; } + public bool UseTotp { get; set; } + public bool Use2fa { get; set; } + public bool UseApi { get; set; } + public bool SelfHost { get; set; } + public bool UsersGetPremium { get; set; } + public int Seats { get; set; } + public int MaxCollections { get; set; } + public short? MaxStorageGb { get; set; } + + public bool CanAccess + { + get + { + if(Type == OrganizationUserType.Owner) + { + return true; + } + return Enabled && Status == OrganizationUserStatusType.Confirmed; + } + } + + public bool IsManager + { + get + { + switch(Type) + { + case OrganizationUserType.Owner: + case OrganizationUserType.Admin: + case OrganizationUserType.Manager: + return true; + default: + return false; + } + } + } + + public bool IsAdmin => Type == OrganizationUserType.Owner || Type == OrganizationUserType.Admin; + public bool IsOwner => Type == OrganizationUserType.Owner; + } +} diff --git a/src/Core/Services/UserService.cs b/src/Core/Services/UserService.cs new file mode 100644 index 000000000..fc2597214 --- /dev/null +++ b/src/Core/Services/UserService.cs @@ -0,0 +1,167 @@ +using Bit.Core.Abstractions; +using Bit.Core.Enums; +using Bit.Core.Models.Data; +using Bit.Core.Models.Domain; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Bit.Core.Services +{ + public class UserService : IUserService + { + private string _userId; + private string _email; + private string _stamp; + private KdfType? _kdf; + private int? _kdfIterations; + + private const string Keys_UserId = "userId"; + private const string Keys_UserEmail = "userEmail"; + private const string Keys_Stamp = "securityStamp"; + private const string Keys_Kdf = "kdf"; + private const string Keys_KdfIterations = "kdfIterations"; + private const string Keys_OrganizationsFormat = "organizations_{0}"; + + private readonly IStorageService _storageService; + private readonly ITokenService _tokenService; + + public UserService(IStorageService storageService, ITokenService tokenService) + { + _storageService = storageService; + _tokenService = tokenService; + } + + public async Task SetInformationAsync(string userId, string email, KdfType kdf, int kdfIterations) + { + _email = email; + _userId = userId; + _kdf = kdf; + _kdfIterations = kdfIterations; + await Task.WhenAll( + _storageService.SaveAsync(Keys_UserEmail, email), + _storageService.SaveAsync(Keys_UserId, userId), + _storageService.SaveAsync(Keys_Kdf, (int)kdf), + _storageService.SaveAsync(Keys_KdfIterations, kdfIterations)); + } + + public async Task SetSecurityStampAsync(string stamp) + { + _stamp = stamp; + await _storageService.SaveAsync(Keys_Stamp, stamp); + } + + public async Task GetUserIdAsync() + { + if(_userId == null) + { + _userId = await _storageService.GetAsync(Keys_UserId); + } + return _userId; + } + + public async Task GetEmailAsync() + { + if(_email == null) + { + _email = await _storageService.GetAsync(Keys_UserEmail); + } + return _email; + } + + public async Task GetSecurityStampAsync() + { + if(_stamp == null) + { + _stamp = await _storageService.GetAsync(Keys_Stamp); + } + return _stamp; + } + + public async Task GetKdfAsync() + { + if(_kdf == null) + { + _kdf = (KdfType?)(await _storageService.GetAsync(Keys_Kdf)); + } + return _kdf; + } + + public async Task GetKdfIterationsAsync() + { + if(_kdfIterations == null) + { + _kdfIterations = await _storageService.GetAsync(Keys_KdfIterations); + } + return _kdfIterations; + } + + public async Task ClearAsync() + { + var userId = await GetUserIdAsync(); + await Task.WhenAll( + _storageService.RemoveAsync(Keys_UserId), + _storageService.RemoveAsync(Keys_UserEmail), + _storageService.RemoveAsync(Keys_Stamp), + _storageService.RemoveAsync(Keys_Kdf), + _storageService.RemoveAsync(Keys_KdfIterations), + ClearOrganizationsAsync(userId)); + _userId = _email = _stamp = null; + _kdf = null; + _kdfIterations = null; + } + + public async Task IsAuthenticatedAsync() + { + var token = await _tokenService.GetTokenAsync(); + if(token == null) + { + return false; + } + var userId = await GetUserIdAsync(); + return userId != null; + } + + public async Task CanAccessPremiumAsync() + { + var tokenPremium = _tokenService.GetPremium(); + if(tokenPremium) + { + return true; + } + var orgs = await GetAllOrganizationAsync(); + return orgs?.Any(o => o.UsersGetPremium && o.Enabled) ?? false; + } + + public async Task GetOrganizationAsync(string id) + { + var userId = await GetUserIdAsync(); + var organizations = await _storageService.GetAsync>( + string.Format(Keys_OrganizationsFormat, userId)); + if(organizations == null || !organizations.ContainsKey(id)) + { + return null; + } + return new Organization(organizations[id]); + } + + public async Task> GetAllOrganizationAsync() + { + var userId = await GetUserIdAsync(); + var organizations = await _storageService.GetAsync>( + string.Format(Keys_OrganizationsFormat, userId)); + return organizations?.Select(o => new Organization(o.Value)).ToList() ?? new List(); + } + + public async Task ReplaceOrganizationsAsync(Dictionary organizations) + { + var userId = await GetUserIdAsync(); + await _storageService.SaveAsync(string.Format(Keys_OrganizationsFormat, userId), organizations); + } + + public async Task ClearOrganizationsAsync(string userId) + { + await _storageService.RemoveAsync(string.Format(Keys_OrganizationsFormat, userId)); + } + } +}