diff --git a/src/Core/Abstractions/IApiService.cs b/src/Core/Abstractions/IApiService.cs index a44665afb..6184065cf 100644 --- a/src/Core/Abstractions/IApiService.cs +++ b/src/Core/Abstractions/IApiService.cs @@ -12,6 +12,7 @@ namespace Bit.Core.Abstractions { string ApiBaseUrl { get; set; } string IdentityBaseUrl { get; set; } + string EventsBaseUrl { get; set; } bool UrlsSet { get; } Task DeleteCipherAsync(string id); @@ -46,5 +47,6 @@ namespace Bit.Core.Abstractions Task> GetHibpBreachAsync(string username); Task PostTwoFactorEmailAsync(TwoFactorEmailRequest request); Task PutDeviceTokenAsync(string identifier, DeviceTokenRequest request); + Task PostEventsCollectAsync(EventRequest request); } } diff --git a/src/Core/Abstractions/IEnvironmentService.cs b/src/Core/Abstractions/IEnvironmentService.cs index 5df4e0dd2..8ce168128 100644 --- a/src/Core/Abstractions/IEnvironmentService.cs +++ b/src/Core/Abstractions/IEnvironmentService.cs @@ -11,6 +11,7 @@ namespace Bit.Core.Abstractions string IdentityUrl { get; set; } string NotificationsUrl { get; set; } string WebVaultUrl { get; set; } + string EventsUrl { get; set; } string GetWebVaultUrl(); Task SetUrlsAsync(EnvironmentUrlData urls); diff --git a/src/Core/Enums/EventType.cs b/src/Core/Enums/EventType.cs new file mode 100644 index 000000000..7f8786180 --- /dev/null +++ b/src/Core/Enums/EventType.cs @@ -0,0 +1,45 @@ +namespace Bit.Core.Enums +{ + public enum EventType : int + { + User_LoggedIn = 1000, + User_ChangedPassword = 1001, + User_Updated2fa = 1002, + User_Disabled2fa = 1003, + User_Recovered2fa = 1004, + User_FailedLogIn = 1005, + User_FailedLogIn2fa = 1006, + User_ClientExportedVault = 1007, + + Cipher_Created = 1100, + Cipher_Updated = 1101, + Cipher_Deleted = 1102, + Cipher_AttachmentCreated = 1103, + Cipher_AttachmentDeleted = 1104, + Cipher_Shared = 1105, + Cipher_UpdatedCollections = 1106, + Cipher_ClientViewed = 1107, + Cipher_ClientToggledPasswordVisible = 1108, + Cipher_ClientToggledHiddenFieldVisible = 1109, + Cipher_ClientCopiedPassword = 1110, + Cipher_ClientCopedHiddenField = 1111, + Cipher_ClientAutofilled = 1112, + + Collection_Created = 1300, + Collection_Updated = 1301, + Collection_Deleted = 1302, + + Group_Created = 1400, + Group_Updated = 1401, + Group_Deleted = 1402, + + OrganizationUser_Invited = 1500, + OrganizationUser_Confirmed = 1501, + OrganizationUser_Updated = 1502, + OrganizationUser_Removed = 1503, + OrganizationUser_UpdatedGroups = 1504, + + Organization_Updated = 1600, + Organization_PurgedVault = 1601, + } +} diff --git a/src/Core/Models/Data/EnvironmentUrlData.cs b/src/Core/Models/Data/EnvironmentUrlData.cs index ae510a264..744541e19 100644 --- a/src/Core/Models/Data/EnvironmentUrlData.cs +++ b/src/Core/Models/Data/EnvironmentUrlData.cs @@ -8,5 +8,6 @@ public string Icons { get; set; } public string Notifications { get; set; } public string WebVault { get; set; } + public string Events { get; set; } } } diff --git a/src/Core/Models/Domain/EnvironmentUrls.cs b/src/Core/Models/Domain/EnvironmentUrls.cs index 6f6b43577..52cfff8d3 100644 --- a/src/Core/Models/Domain/EnvironmentUrls.cs +++ b/src/Core/Models/Domain/EnvironmentUrls.cs @@ -5,5 +5,6 @@ public string Base { get; set; } public string Api { get; set; } public string Identity { get; set; } + public string Events { get; set; } } } diff --git a/src/Core/Models/Request/EventRequest.cs b/src/Core/Models/Request/EventRequest.cs new file mode 100644 index 000000000..817491c68 --- /dev/null +++ b/src/Core/Models/Request/EventRequest.cs @@ -0,0 +1,11 @@ +using Bit.Core.Enums; +using Bit.Core.Models.Domain; + +namespace Bit.Core.Models.Request +{ + public class EventRequest + { + public EventType Type { get; set; } + public string CipherId { get; set; } + } +} diff --git a/src/Core/Services/ApiService.cs b/src/Core/Services/ApiService.cs index 7583ab664..15d70265f 100644 --- a/src/Core/Services/ApiService.cs +++ b/src/Core/Services/ApiService.cs @@ -52,6 +52,7 @@ namespace Bit.Core.Services public bool UrlsSet { get; private set; } public string ApiBaseUrl { get; set; } public string IdentityBaseUrl { get; set; } + public string EventsBaseUrl { get; set; } public void SetUrls(EnvironmentUrls urls) { @@ -60,20 +61,27 @@ namespace Bit.Core.Services { ApiBaseUrl = urls.Base + "/api"; IdentityBaseUrl = urls.Base + "/identity"; + EventsBaseUrl = urls.Base + "/events"; return; } - if(!string.IsNullOrWhiteSpace(urls.Api) && !string.IsNullOrWhiteSpace(urls.Identity)) - { - ApiBaseUrl = urls.Api; - IdentityBaseUrl = urls.Identity; - return; - } - // Local Dev - //ApiBaseUrl = "http://localhost:4000"; - //IdentityBaseUrl = "http://localhost:33656"; + + ApiBaseUrl = urls.Api; + IdentityBaseUrl = urls.Identity; + EventsBaseUrl = urls.Events; + // Production - ApiBaseUrl = "https://api.bitwarden.com"; - IdentityBaseUrl = "https://identity.bitwarden.com"; + if(string.IsNullOrWhiteSpace(ApiBaseUrl)) + { + ApiBaseUrl = "https://api.bitwarden.com"; + } + if(string.IsNullOrWhiteSpace(IdentityBaseUrl)) + { + IdentityBaseUrl = "https://identity.bitwarden.com"; + } + if(string.IsNullOrWhiteSpace(EventsBaseUrl)) + { + EventsBaseUrl = "https://events.bitwarden.com"; + } } #region Auth APIs @@ -298,6 +306,38 @@ namespace Bit.Core.Services #endregion + #region Event APIs + + public async Task PostEventsCollectAsync(EventRequest request) + { + using(var requestMessage = new HttpRequestMessage()) + { + requestMessage.Method = HttpMethod.Post; + requestMessage.RequestUri = new Uri(string.Concat(EventsBaseUrl, "/collect")); + requestMessage.Headers.Add("Device-Type", _deviceType); + var authHeader = await GetActiveBearerTokenAsync(); + requestMessage.Headers.Add("Authorization", string.Concat("Bearer ", authHeader)); + requestMessage.Content = new StringContent(JsonConvert.SerializeObject(request, _jsonSettings), + Encoding.UTF8, "application/json"); + HttpResponseMessage response; + try + { + response = await _httpClient.SendAsync(requestMessage); + } + catch + { + throw new ApiException(HandleWebError()); + } + if(!response.IsSuccessStatusCode) + { + var error = await HandleErrorAsync(response, false); + throw new ApiException(error); + } + } + } + + #endregion + #region HIBP APIs public Task> GetHibpBreachAsync(string username) diff --git a/src/Core/Services/EnvironmentService.cs b/src/Core/Services/EnvironmentService.cs index 6304990d8..6e6c5b3a8 100644 --- a/src/Core/Services/EnvironmentService.cs +++ b/src/Core/Services/EnvironmentService.cs @@ -25,6 +25,7 @@ namespace Bit.Core.Services public string IdentityUrl { get; set; } public string IconsUrl { get; set; } public string NotificationsUrl { get; set; } + public string EventsUrl { get; set; } public string GetWebVaultUrl() { @@ -58,6 +59,7 @@ namespace Bit.Core.Services IdentityUrl = envUrls.Identity = urls.Identity; IconsUrl = urls.Icons; NotificationsUrl = urls.Notifications; + EventsUrl = envUrls.Events = urls.Events; _apiService.SetUrls(envUrls); } @@ -69,6 +71,7 @@ namespace Bit.Core.Services urls.Identity = FormatUrl(urls.Identity); urls.Icons = FormatUrl(urls.Icons); urls.Notifications = FormatUrl(urls.Notifications); + urls.Events = FormatUrl(urls.Events); await _storageService.SaveAsync(Constants.EnvironmentUrlsKey, urls); BaseUrl = urls.Base; WebVaultUrl = urls.WebVault; @@ -76,6 +79,7 @@ namespace Bit.Core.Services IdentityUrl = urls.Identity; IconsUrl = urls.Icons; NotificationsUrl = urls.Notifications; + EventsUrl = urls.Events; var envUrls = new EnvironmentUrls(); if(!string.IsNullOrWhiteSpace(BaseUrl)) @@ -86,6 +90,7 @@ namespace Bit.Core.Services { envUrls.Api = ApiUrl; envUrls.Identity = IdentityUrl; + envUrls.Events = EventsUrl; } _apiService.SetUrls(envUrls);