diff --git a/src/Api/Api.csproj b/src/Api/Api.csproj
index ab58a12375..d19830b90d 100644
--- a/src/Api/Api.csproj
+++ b/src/Api/Api.csproj
@@ -28,7 +28,7 @@
-
+
diff --git a/src/Api/Controllers/AccountsController.cs b/src/Api/Controllers/AccountsController.cs
index e54be78ab4..28d5636987 100644
--- a/src/Api/Controllers/AccountsController.cs
+++ b/src/Api/Controllers/AccountsController.cs
@@ -278,7 +278,7 @@ namespace Bit.Api.Controllers
if(userId.HasValue)
{
var date = await _userService.GetAccountRevisionDateByIdAsync(userId.Value);
- revisionDate = Core.Utilities.CoreHelpers.ToEpocMilliseconds(date);
+ revisionDate = CoreHelpers.ToEpocMilliseconds(date);
}
return revisionDate;
diff --git a/src/Api/settings.json b/src/Api/settings.json
index 15004a2ac1..dfa53f045b 100644
--- a/src/Api/settings.json
+++ b/src/Api/settings.json
@@ -2,7 +2,6 @@
"globalSettings": {
"siteName": "bitwarden",
"baseVaultUri": "http://localhost:4001/#",
- "jwtSigningKey": "THIS IS A SECRET. IT KEEPS YOUR TOKEN SAFE. :)",
"stripeApiKey": "SECRET",
"sqlServer": {
"connectionString": "SECRET"
@@ -11,13 +10,6 @@
"apiKey": "SECRET",
"replyToEmail": "hello@bitwarden.com"
},
- "push": {
- "apnsCertificateThumbprint": "SECRET",
- "apnsCertificatePassword": "SECRET",
- "gcmSenderId": "SECRET",
- "gcmApiKey": "SECRET",
- "gcmAppPackageName": "com.x8bit.bitwarden"
- },
"identityServer": {
"certificateThumbprint": "SECRET"
},
diff --git a/src/Billing/settings.json b/src/Billing/settings.json
index a6d8e2e760..06954fb44c 100644
--- a/src/Billing/settings.json
+++ b/src/Billing/settings.json
@@ -2,7 +2,6 @@
"globalSettings": {
"siteName": "bitwarden",
"baseVaultUri": "http://localhost:4001/#",
- "jwtSigningKey": "THIS IS A SECRET. IT KEEPS YOUR TOKEN SAFE. :)",
"stripeApiKey": "SECRET",
"sqlServer": {
"connectionString": "SECRET"
@@ -11,13 +10,6 @@
"apiKey": "SECRET",
"replyToEmail": "hello@bitwarden.com"
},
- "push": {
- "apnsCertificateThumbprint": "SECRET",
- "apnsCertificatePassword": "SECRET",
- "gcmSenderId": "SECRET",
- "gcmApiKey": "SECRET",
- "gcmAppPackageName": "com.x8bit.bitwarden"
- },
"identityServer": {
"certificateThumbprint": "SECRET"
},
diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj
index 9214db84c8..fbd297beda 100644
--- a/src/Core/Core.csproj
+++ b/src/Core/Core.csproj
@@ -52,16 +52,15 @@
-
+
-
-
+
-
+
diff --git a/src/Core/GlobalSettings.cs b/src/Core/GlobalSettings.cs
index aa22660be9..0a11540be7 100644
--- a/src/Core/GlobalSettings.cs
+++ b/src/Core/GlobalSettings.cs
@@ -4,11 +4,9 @@
{
public virtual string SiteName { get; set; }
public virtual string BaseVaultUri { get; set; }
- public virtual string JwtSigningKey { get; set; }
public virtual string StripeApiKey { get; set; }
public virtual SqlServerSettings SqlServer { get; set; } = new SqlServerSettings();
public virtual MailSettings Mail { get; set; } = new MailSettings();
- public virtual PushSettings Push { get; set; } = new PushSettings();
public virtual StorageSettings Storage { get; set; } = new StorageSettings();
public virtual AttachmentSettings Attachment { get; set; } = new AttachmentSettings();
public virtual IdentityServerSettings IdentityServer { get; set; } = new IdentityServerSettings();
@@ -51,15 +49,6 @@
}
}
- public class PushSettings
- {
- public string ApnsCertificateThumbprint { get; set; }
- public string ApnsCertificatePassword { get; set; }
- public string GcmSenderId { get; set; }
- public string GcmApiKey { get; set; }
- public string GcmAppPackageName { get; set; }
- }
-
public class IdentityServerSettings
{
public string CertificateThumbprint { get; set; }
diff --git a/src/Core/IDataObject.cs b/src/Core/IDataObject.cs
deleted file mode 100644
index 4796c75771..0000000000
--- a/src/Core/IDataObject.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System;
-
-namespace Bit.Core
-{
- public interface IDataObject where T : IEquatable
- {
- T Id { get; set; }
- void SetNewId();
- }
-}
diff --git a/src/Core/Identity/DuoTokenProvider.cs b/src/Core/Identity/DuoTokenProvider.cs
deleted file mode 100644
index d6b03c97ba..0000000000
--- a/src/Core/Identity/DuoTokenProvider.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Identity;
-using Bit.Core.Models.Table;
-using Bit.Core.Enums;
-using Bit.Core.Utilities.Duo;
-using System.Collections.Generic;
-using System.Net.Http;
-
-namespace Bit.Core.Identity
-{
- public class DuoTokenProvider : IUserTwoFactorTokenProvider
- {
- public Task CanGenerateTwoFactorTokenAsync(UserManager manager, User user)
- {
- if(!user.Premium)
- {
- return Task.FromResult(false);
- }
-
- var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
- var canGenerate = user.TwoFactorProviderIsEnabled(TwoFactorProviderType.Duo)
- && !string.IsNullOrWhiteSpace((string)provider?.MetaData["UserId"]);
-
- return Task.FromResult(canGenerate);
- }
-
- /// Ex: "auto", "push", "passcode:123456", "sms", "phone"
- public async Task GenerateAsync(string purpose, UserManager manager, User user)
- {
- if(!user.Premium)
- {
- return null;
- }
-
- var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
- var duoClient = new DuoApi((string)provider.MetaData["IKey"], (string)provider.MetaData["SKey"],
- (string)provider.MetaData["Host"]);
- var parts = purpose.Split(':');
-
- var parameters = new Dictionary
- {
- ["async"] = "1",
- ["user_id"] = (string)provider.MetaData["UserId"],
- ["factor"] = parts[0]
- };
-
- if(parameters["factor"] == "passcode" && parts.Length > 1)
- {
- parameters["passcode"] = parts[1];
- }
- else
- {
- parameters["device"] = "auto";
- }
-
- try
- {
- var response = await duoClient.JSONApiCallAsync>(HttpMethod.Post,
- "/auth/v2/auth", parameters);
-
- if(response.ContainsKey("txid"))
- {
- var txId = response["txid"] as string;
- return txId;
- }
- }
- catch(DuoException) { }
-
- return null;
- }
-
- public async Task ValidateAsync(string purpose, string token, UserManager manager, User user)
- {
- if(!user.Premium)
- {
- return false;
- }
-
- var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Duo);
- var duoClient = new DuoApi((string)provider.MetaData["IKey"], (string)provider.MetaData["SKey"],
- (string)provider.MetaData["Host"]);
-
- var parameters = new Dictionary
- {
- ["txid"] = token
- };
-
- try
- {
- var response = await duoClient.JSONApiCallAsync>(HttpMethod.Get,
- "/auth/v2/auth_status", parameters);
-
- var result = response["result"] as string;
- return string.Equals(result, "allow");
- }
- catch(DuoException)
- {
- // TODO: We might want to return true in some cases? What if Duo is down?
- }
-
- return false;
- }
- }
-}
diff --git a/src/Core/Models/Table/Cipher.cs b/src/Core/Models/Table/Cipher.cs
index ecbb7af3fa..07fe909876 100644
--- a/src/Core/Models/Table/Cipher.cs
+++ b/src/Core/Models/Table/Cipher.cs
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace Bit.Core.Models.Table
{
- public class Cipher : IDataObject
+ public class Cipher : ITableObject
{
private Dictionary _attachmentData;
diff --git a/src/Core/Models/Table/Collection.cs b/src/Core/Models/Table/Collection.cs
index 50aeaf2b28..d6a0bdecf6 100644
--- a/src/Core/Models/Table/Collection.cs
+++ b/src/Core/Models/Table/Collection.cs
@@ -3,7 +3,7 @@ using Bit.Core.Utilities;
namespace Bit.Core.Models.Table
{
- public class Collection : IDataObject
+ public class Collection : ITableObject
{
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
diff --git a/src/Core/Models/Table/Device.cs b/src/Core/Models/Table/Device.cs
index 21152a4904..4fb20652df 100644
--- a/src/Core/Models/Table/Device.cs
+++ b/src/Core/Models/Table/Device.cs
@@ -3,7 +3,7 @@ using Bit.Core.Utilities;
namespace Bit.Core.Models.Table
{
- public class Device : IDataObject
+ public class Device : ITableObject
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
diff --git a/src/Core/Models/Table/Folder.cs b/src/Core/Models/Table/Folder.cs
index cdfedff4b2..ec20cc3ec0 100644
--- a/src/Core/Models/Table/Folder.cs
+++ b/src/Core/Models/Table/Folder.cs
@@ -3,7 +3,7 @@ using Bit.Core.Utilities;
namespace Bit.Core.Models.Table
{
- public class Folder : IDataObject
+ public class Folder : ITableObject
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
diff --git a/src/Core/Models/Table/Group.cs b/src/Core/Models/Table/Group.cs
index e914fcd59a..502e2faf60 100644
--- a/src/Core/Models/Table/Group.cs
+++ b/src/Core/Models/Table/Group.cs
@@ -3,7 +3,7 @@ using Bit.Core.Utilities;
namespace Bit.Core.Models.Table
{
- public class Group : IDataObject
+ public class Group : ITableObject
{
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
diff --git a/src/Core/Models/Table/ITableObject.cs b/src/Core/Models/Table/ITableObject.cs
new file mode 100644
index 0000000000..8fb6a41946
--- /dev/null
+++ b/src/Core/Models/Table/ITableObject.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Bit.Core.Models.Table
+{
+ public interface ITableObject where T : IEquatable
+ {
+ T Id { get; set; }
+ void SetNewId();
+ }
+}
diff --git a/src/Core/Models/Table/Organization.cs b/src/Core/Models/Table/Organization.cs
index d25ddb51ca..36288cb53d 100644
--- a/src/Core/Models/Table/Organization.cs
+++ b/src/Core/Models/Table/Organization.cs
@@ -4,7 +4,7 @@ using Bit.Core.Enums;
namespace Bit.Core.Models.Table
{
- public class Organization : IDataObject, ISubscriber, IStorable, IStorableSubscriber, IRevisable
+ public class Organization : ITableObject, ISubscriber, IStorable, IStorableSubscriber, IRevisable
{
public Guid Id { get; set; }
public string Name { get; set; }
diff --git a/src/Core/Models/Table/OrganizationUser.cs b/src/Core/Models/Table/OrganizationUser.cs
index 34f3ca0710..7b2b5d55d7 100644
--- a/src/Core/Models/Table/OrganizationUser.cs
+++ b/src/Core/Models/Table/OrganizationUser.cs
@@ -4,7 +4,7 @@ using Bit.Core.Enums;
namespace Bit.Core.Models.Table
{
- public class OrganizationUser : IDataObject
+ public class OrganizationUser : ITableObject
{
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
diff --git a/src/Core/Models/Table/U2f.cs b/src/Core/Models/Table/U2f.cs
index a376e5186c..e8a97c7fe5 100644
--- a/src/Core/Models/Table/U2f.cs
+++ b/src/Core/Models/Table/U2f.cs
@@ -2,7 +2,7 @@
namespace Bit.Core.Models.Table
{
- public class U2f : IDataObject
+ public class U2f : ITableObject
{
public int Id { get; set; }
public Guid UserId { get; set; }
diff --git a/src/Core/Models/Table/User.cs b/src/Core/Models/Table/User.cs
index 4820f95192..0f2e7bd814 100644
--- a/src/Core/Models/Table/User.cs
+++ b/src/Core/Models/Table/User.cs
@@ -7,7 +7,7 @@ using System.Linq;
namespace Bit.Core.Models.Table
{
- public class User : IDataObject, ISubscriber, IStorable, IStorableSubscriber, IRevisable
+ public class User : ITableObject, ISubscriber, IStorable, IStorableSubscriber, IRevisable
{
private Dictionary _twoFactorProviders;
diff --git a/src/Core/Repositories/IRepository.cs b/src/Core/Repositories/IRepository.cs
index d577f35c31..bbb4e81af1 100644
--- a/src/Core/Repositories/IRepository.cs
+++ b/src/Core/Repositories/IRepository.cs
@@ -1,9 +1,10 @@
-using System;
+using Bit.Core.Models.Table;
+using System;
using System.Threading.Tasks;
namespace Bit.Core.Repositories
{
- public interface IRepository where TId : IEquatable where T : class, IDataObject
+ public interface IRepository where TId : IEquatable where T : class, ITableObject
{
Task GetByIdAsync(TId id);
Task CreateAsync(T obj);
diff --git a/src/Core/Repositories/SqlServer/Repository.cs b/src/Core/Repositories/SqlServer/Repository.cs
index 6352e4fad6..f8d4d7f919 100644
--- a/src/Core/Repositories/SqlServer/Repository.cs
+++ b/src/Core/Repositories/SqlServer/Repository.cs
@@ -4,10 +4,13 @@ using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Dapper;
+using Bit.Core.Models.Table;
namespace Bit.Core.Repositories.SqlServer
{
- public abstract class Repository : BaseRepository, IRepository where TId : IEquatable where T : class, IDataObject
+ public abstract class Repository : BaseRepository, IRepository
+ where TId : IEquatable
+ where T : class, ITableObject
{
public Repository(string connectionString, string schema = null, string table = null)
: base(connectionString)
diff --git a/src/Core/Services/Implementations/PushSharpPushNotificationService.cs b/src/Core/Services/Implementations/PushSharpPushNotificationService.cs
deleted file mode 100644
index 1357725238..0000000000
--- a/src/Core/Services/Implementations/PushSharpPushNotificationService.cs
+++ /dev/null
@@ -1,352 +0,0 @@
-using System;
-using System.Linq;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using Bit.Core.Repositories;
-using Newtonsoft.Json.Linq;
-using PushSharp.Google;
-using PushSharp.Apple;
-using Microsoft.AspNetCore.Hosting;
-using PushSharp.Core;
-using Bit.Core.Models.Table;
-using Bit.Core.Enums;
-using Newtonsoft.Json;
-using Microsoft.Extensions.Logging;
-using System.Diagnostics;
-using Bit.Core.Utilities;
-using Microsoft.AspNetCore.Http;
-using Bit.Core.Models;
-
-namespace Bit.Core.Services
-{
- [Obsolete]
- public class PushSharpPushNotificationService : IPushNotificationService
- {
- private readonly IDeviceRepository _deviceRepository;
- private readonly ILogger _logger;
- private readonly IHttpContextAccessor _httpContextAccessor;
- private GcmServiceBroker _gcmBroker;
- private ApnsServiceBroker _apnsBroker;
-
- public PushSharpPushNotificationService(
- IDeviceRepository deviceRepository,
- IHttpContextAccessor httpContextAccessor,
- ILogger logger,
- IHostingEnvironment hostingEnvironment,
- GlobalSettings globalSettings)
- {
- _deviceRepository = deviceRepository;
- _httpContextAccessor = httpContextAccessor;
- _logger = logger;
-
- InitGcmBroker(globalSettings);
- InitApnsBroker(globalSettings, hostingEnvironment);
- }
-
- public async Task PushSyncCipherCreateAsync(Cipher cipher)
- {
- await PushCipherAsync(cipher, PushType.SyncCipherCreate);
- }
-
- public async Task PushSyncCipherUpdateAsync(Cipher cipher)
- {
- await PushCipherAsync(cipher, PushType.SyncCipherUpdate);
- }
-
- public async Task PushSyncCipherDeleteAsync(Cipher cipher)
- {
- switch(cipher.Type)
- {
- case CipherType.Login:
- await PushCipherAsync(cipher, PushType.SyncLoginDelete);
- break;
- default:
- break;
- }
- }
-
- public async Task PushSyncFolderCreateAsync(Folder folder)
- {
- await PushFolderAsync(folder, PushType.SyncFolderCreate);
- }
-
- public async Task PushSyncFolderUpdateAsync(Folder folder)
- {
- await PushFolderAsync(folder, PushType.SyncFolderUpdate);
- }
-
- public async Task PushSyncFolderDeleteAsync(Folder folder)
- {
- await PushFolderAsync(folder, PushType.SyncFolderDelete);
- }
-
- private async Task PushCipherAsync(Cipher cipher, PushType type)
- {
- if(!cipher.UserId.HasValue)
- {
- // No push for org ciphers at the moment.
- return;
- }
-
- var message = new SyncCipherPushNotification
- {
- Id = cipher.Id,
- UserId = cipher.UserId,
- OrganizationId = cipher.OrganizationId,
- RevisionDate = cipher.RevisionDate
- };
-
- var excludedTokens = new List();
- var currentContext = _httpContextAccessor?.HttpContext?.
- RequestServices.GetService(typeof(CurrentContext)) as CurrentContext;
- if(!string.IsNullOrWhiteSpace(currentContext?.DeviceIdentifier))
- {
- excludedTokens.Add(currentContext.DeviceIdentifier);
- }
-
- await PushToAllUserDevicesAsync(cipher.UserId.Value, type, message, excludedTokens);
- }
-
- private async Task PushFolderAsync(Folder folder, PushType type)
- {
- var message = new SyncFolderPushNotification
- {
- Id = folder.Id,
- UserId = folder.UserId,
- RevisionDate = folder.RevisionDate
- };
-
- var excludedTokens = new List();
- var currentContext = _httpContextAccessor?.HttpContext?.
- RequestServices.GetService(typeof(CurrentContext)) as CurrentContext;
- if(!string.IsNullOrWhiteSpace(currentContext?.DeviceIdentifier))
- {
- excludedTokens.Add(currentContext.DeviceIdentifier);
- }
-
- await PushToAllUserDevicesAsync(folder.UserId, type, message, excludedTokens);
- }
-
- public async Task PushSyncCiphersAsync(Guid userId)
- {
- await PushSyncUserAsync(userId, PushType.SyncCiphers);
- }
-
- public async Task PushSyncVaultAsync(Guid userId)
- {
- await PushSyncUserAsync(userId, PushType.SyncVault);
- }
-
- public async Task PushSyncOrgKeysAsync(Guid userId)
- {
- await PushSyncUserAsync(userId, PushType.SyncOrgKeys);
- }
-
- public async Task PushSyncSettingsAsync(Guid userId)
- {
- await PushSyncUserAsync(userId, PushType.SyncSettings);
- }
-
- private async Task PushSyncUserAsync(Guid userId, PushType type)
- {
- var message = new SyncUserPushNotification
- {
- UserId = userId,
- Date = DateTime.UtcNow
- };
-
- await PushToAllUserDevicesAsync(userId, type, message, null);
- }
-
- private void InitGcmBroker(GlobalSettings globalSettings)
- {
- if(string.IsNullOrWhiteSpace(globalSettings.Push.GcmSenderId) || string.IsNullOrWhiteSpace(globalSettings.Push.GcmApiKey)
- || string.IsNullOrWhiteSpace(globalSettings.Push.GcmAppPackageName))
- {
- return;
- }
-
- var gcmConfig = new GcmConfiguration(globalSettings.Push.GcmSenderId, globalSettings.Push.GcmApiKey,
- globalSettings.Push.GcmAppPackageName);
-
- _gcmBroker = new GcmServiceBroker(gcmConfig);
- _gcmBroker.OnNotificationFailed += GcmBroker_OnNotificationFailed;
- _gcmBroker.OnNotificationSucceeded += (notification) =>
- {
- Debug.WriteLine("GCM Notification Sent!");
- };
- _gcmBroker.Start();
- }
-
- private void GcmBroker_OnNotificationFailed(GcmNotification notification, AggregateException exception)
- {
- exception.Handle(ex =>
- {
- // See what kind of exception it was to further diagnose
- if(ex is GcmNotificationException)
- {
- var notificationException = ex as GcmNotificationException;
-
- // Deal with the failed notification
- var gcmNotification = notificationException.Notification;
- var description = notificationException.Description;
-
- Debug.WriteLine($"GCM Notification Failed: ID={gcmNotification.MessageId}, Desc={description}");
- }
- else if(ex is GcmMulticastResultException)
- {
- var multicastException = ex as GcmMulticastResultException;
-
- foreach(var succeededNotification in multicastException.Succeeded)
- {
- Debug.WriteLine($"GCM Notification Failed: ID={succeededNotification.MessageId}");
- }
-
- foreach(var failedKvp in multicastException.Failed)
- {
- var n = failedKvp.Key;
- var e = failedKvp.Value;
-
- Debug.WriteLine($"GCM Notification Failed: ID={n.MessageId}, Desc={e.Message}");
- }
-
- }
- else if(ex is DeviceSubscriptionExpiredException)
- {
- var expiredException = ex as DeviceSubscriptionExpiredException;
-
- var oldId = expiredException.OldSubscriptionId;
- var newId = expiredException.NewSubscriptionId;
-
- Debug.WriteLine($"Device RegistrationId Expired: {oldId}");
-
- if(!string.IsNullOrWhiteSpace(newId))
- {
- // If this value isn't null, our subscription changed and we should update our database
- Debug.WriteLine($"Device RegistrationId Changed To: {newId}");
- }
- }
- else if(ex is RetryAfterException)
- {
- var retryException = (RetryAfterException)ex;
- // If you get rate limited, you should stop sending messages until after the RetryAfterUtc date
- Debug.WriteLine($"GCM Rate Limited, don't send more until after {retryException.RetryAfterUtc}");
- }
- else
- {
- Debug.WriteLine("GCM Notification Failed for some unknown reason");
- }
-
- // Mark it as handled
- return true;
- });
- }
-
- private void InitApnsBroker(GlobalSettings globalSettings, IHostingEnvironment hostingEnvironment)
- {
- if(string.IsNullOrWhiteSpace(globalSettings.Push.ApnsCertificatePassword)
- || string.IsNullOrWhiteSpace(globalSettings.Push.ApnsCertificateThumbprint))
- {
- return;
- }
-
- var apnsCertificate = CoreHelpers.GetCertificate(globalSettings.Push.ApnsCertificateThumbprint);
- if(apnsCertificate == null)
- {
- return;
- }
-
- var apnsConfig = new ApnsConfiguration(hostingEnvironment.IsProduction() ?
- ApnsConfiguration.ApnsServerEnvironment.Production : ApnsConfiguration.ApnsServerEnvironment.Sandbox,
- apnsCertificate.RawData, globalSettings.Push.ApnsCertificatePassword);
-
- _apnsBroker = new ApnsServiceBroker(apnsConfig);
- _apnsBroker.OnNotificationFailed += ApnsBroker_OnNotificationFailed;
- _apnsBroker.OnNotificationSucceeded += (notification) =>
- {
- Debug.WriteLine("Apple Notification Sent!");
- };
- _apnsBroker.Start();
-
- var feedbackService = new FeedbackService(apnsConfig);
- feedbackService.FeedbackReceived += FeedbackService_FeedbackReceived;
- feedbackService.Check();
- }
-
- private void ApnsBroker_OnNotificationFailed(ApnsNotification notification, AggregateException exception)
- {
- exception.Handle(ex =>
- {
- // See what kind of exception it was to further diagnose
- if(ex is ApnsNotificationException)
- {
- var notificationException = ex as ApnsNotificationException;
-
- // Deal with the failed notification
- var apnsNotification = notificationException.Notification;
- var statusCode = notificationException.ErrorStatusCode;
-
- Debug.WriteLine($"Apple Notification Failed: ID={apnsNotification.Identifier}, Code={statusCode}");
- }
- else
- {
- // Inner exception might hold more useful information like an ApnsConnectionException
- Debug.WriteLine($"Apple Notification Failed for some unknown reason : {ex.InnerException}");
- }
-
- // Mark it as handled
- return true;
- });
- }
-
- private void FeedbackService_FeedbackReceived(string deviceToken, DateTime timestamp)
- {
- // Remove the deviceToken from your database
- // timestamp is the time the token was reported as expired
- }
-
- private async Task PushToAllUserDevicesAsync(Guid userId, PushType type, object message, IEnumerable tokensToSkip)
- {
- var devices = (await _deviceRepository.GetManyByUserIdAsync(userId))
- .Where(d => !string.IsNullOrWhiteSpace(d.PushToken) && (!tokensToSkip?.Contains(d.PushToken) ?? true));
- if(devices.Count() == 0)
- {
- return;
- }
-
- if(_apnsBroker != null)
- {
- var appleNotification = new ApplePayloadPushNotification
- {
- Data = new PayloadPushNotification.DataObj(type, JsonConvert.SerializeObject(message))
- };
-
- var obj = JObject.FromObject(appleNotification);
-
- // Send to each iOS device
- foreach(var device in devices.Where(d => d.Type == DeviceType.iOS))
- {
- _apnsBroker.QueueNotification(new ApnsNotification
- {
- DeviceToken = device.PushToken,
- Payload = obj
- });
- }
- }
-
- // Android can send to many devices at once
- var androidDevices = devices.Where(d => d.Type == DeviceType.Android);
- if(_gcmBroker != null && androidDevices.Count() > 0)
- {
- var gcmData = new PayloadPushNotification.DataObj(type, JsonConvert.SerializeObject(message));
- var obj = JObject.FromObject(gcmData);
-
- _gcmBroker.QueueNotification(new GcmNotification
- {
- RegistrationIds = androidDevices.Select(d => d.PushToken).ToList(),
- Data = obj
- });
- }
- }
- }
-}
diff --git a/src/Core/Utilities/CoreHelpers.cs b/src/Core/Utilities/CoreHelpers.cs
index a178eec065..4572866ba2 100644
--- a/src/Core/Utilities/CoreHelpers.cs
+++ b/src/Core/Utilities/CoreHelpers.cs
@@ -5,7 +5,6 @@ using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
-using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
@@ -16,7 +15,7 @@ namespace Bit.Core.Utilities
public static class CoreHelpers
{
private static readonly long _baseDateTicks = new DateTime(1900, 1, 1).Ticks;
- private static readonly DateTime _epoc = new DateTime(1970, 1, 1);
+ private static readonly DateTime _epoc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
///
/// Generate sequential Guid for Sql Server.
diff --git a/src/Core/Utilities/DuoApi.cs b/src/Core/Utilities/DuoApi.cs
deleted file mode 100644
index 85f5772241..0000000000
--- a/src/Core/Utilities/DuoApi.cs
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
-Original source modified from https://github.com/duosecurity/duo_api_csharp
-
-=============================================================================
-=============================================================================
-
-ref: https://github.com/duosecurity/duo_api_csharp/blob/master/LICENSE
-
-Copyright (c) 2013, Duo Security, Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-using System;
-using System.Collections.Generic;
-using System.Net;
-using System.Security.Cryptography;
-using System.Text.RegularExpressions;
-using System.Text;
-using System.Globalization;
-using Newtonsoft.Json;
-using System.Net.Http;
-using System.Threading.Tasks;
-
-namespace Bit.Core.Utilities.Duo
-{
- public class DuoApi
- {
- public const string DefaultAgent = "Duo.NET, bitwarden";
-
- private readonly string _ikey;
- private readonly string _skey;
- private readonly string _host;
- private readonly string _userAgent;
-
- public DuoApi(string ikey, string skey, string host)
- : this(ikey, skey, host, null)
- { }
-
- protected DuoApi(string ikey, string skey, string host, string userAgent)
- {
- _ikey = ikey;
- _skey = skey;
- _host = host;
- _userAgent = string.IsNullOrWhiteSpace(userAgent) ? DefaultAgent : userAgent;
- }
-
- public async Task> ApiCallAsync(HttpMethod method, string path,
- Dictionary parameters, int? timeout = null, DateTime? date = null)
- {
- var canonParams = CanonicalizeParams(parameters);
- var query = string.Empty;
- if(method != HttpMethod.Post && method != HttpMethod.Put && parameters.Count > 0)
- {
- query = "?" + canonParams;
- }
-
- var url = $"https://{_host}{path}{query}";
-
- var dateString = DateToRFC822(date.GetValueOrDefault(DateTime.UtcNow));
- var auth = Sign(method.ToString(), path, canonParams, dateString);
-
- var client = new HttpClient();
- client.DefaultRequestHeaders.Add("Accept", "application/json");
- client.DefaultRequestHeaders.Add("Authorization", auth);
- client.DefaultRequestHeaders.Add("X-Duo-Date", dateString);
- client.DefaultRequestHeaders.Add("User-Agent", _userAgent);
-
- if(timeout.GetValueOrDefault(0) > 0)
- {
- client.Timeout = new TimeSpan(0, 0, 0, 0, timeout.Value);
- }
-
- var request = new HttpRequestMessage
- {
- RequestUri = new Uri(url),
- Method = method
- };
-
- if(method == HttpMethod.Post || method == HttpMethod.Put)
- {
- request.Content = new FormUrlEncodedContent(parameters);
- }
-
- HttpResponseMessage response = null;
- try
- {
- response = await client.SendAsync(request);
- }
- catch(WebException)
- {
- if(response?.Content == null)
- {
- throw;
- }
- }
-
- var result = await response.Content.ReadAsStringAsync();
- return new Tuple(result, response.StatusCode);
- }
-
- public async Task JSONApiCallAsync(HttpMethod method, string path, Dictionary parameters,
- int? timeout = null, DateTime? date = null) where T : class
- {
- var resTuple = await ApiCallAsync(method, path, parameters, timeout, date);
- var res = resTuple.Item1;
- HttpStatusCode statusCode = resTuple.Item2;
-
- try
- {
- var resDict = JsonConvert.DeserializeObject>(res);
- var stat = resDict["stat"] as string;
- if(stat == "OK")
- {
- return JsonConvert.DeserializeObject(resDict["response"].ToString());
- }
- else
- {
- var code = resDict["code"] as int?;
- var message = resDict["message"] as string;
-
- var messageDetail = string.Empty;
- if(resDict.ContainsKey("message_detail"))
- {
- messageDetail = resDict["message_detail"] as string;
- }
-
- throw new DuoApiException(code.GetValueOrDefault(0), statusCode, message, messageDetail);
- }
- }
- catch(Exception e)
- {
- throw new DuoBadResponseException(statusCode, e);
- }
- }
-
- private string CanonicalizeParams(Dictionary parameters)
- {
- var ret = new List();
- foreach(var pair in parameters)
- {
- var p = $"{WebUtility.UrlEncode(pair.Key)}={WebUtility.UrlEncode(pair.Value)}";
- // Signatures require upper-case hex digits.
- p = Regex.Replace(p, "(%[0-9A-Fa-f][0-9A-Fa-f])", c => c.Value.ToUpperInvariant());
- // Escape only the expected characters.
- p = Regex.Replace(p, "([!'()*])", c => "%" + Convert.ToByte(c.Value[0]).ToString("X"));
- p = p.Replace("%7E", "~");
- // UrlEncode converts space (" ") to "+". The
- // signature algorithm requires "%20" instead. Actual
- // + has already been replaced with %2B.
- p = p.Replace("+", "%20");
-
- ret.Add(p);
- }
-
- ret.Sort(StringComparer.Ordinal);
- return string.Join("&", ret.ToArray());
- }
-
- private string CanonicalizeRequest(string method, string path, string canon_params, string date)
- {
- string[] lines = { date, method.ToUpperInvariant(), _host.ToLower(), path, canon_params };
- return string.Join("\n", lines);
- }
-
- private string Sign(string method, string path, string canon_params, string date)
- {
- var canon = CanonicalizeRequest(method, path, canon_params, date);
- var sig = HmacSign(canon);
- var auth = $"{_ikey }:{sig}";
- var authBytes = Encoding.ASCII.GetBytes(auth);
- return $"Basic {Convert.ToBase64String(authBytes)}";
- }
-
- private string HmacSign(string data)
- {
- var keyBytes = Encoding.ASCII.GetBytes(_skey);
- var dataBytes = Encoding.ASCII.GetBytes(data);
-
- using(var hmac = new HMACSHA1(keyBytes))
- {
- var hash = hmac.ComputeHash(dataBytes);
- var hex = BitConverter.ToString(hash);
- return hex.Replace("-", "").ToLower();
- }
- }
-
- private string DateToRFC822(DateTime date)
- {
- // Can't use the "zzzz" format because it adds a ":"
- // between the offset's hours and minutes.
- var dateString = date.ToString("ddd, dd MMM yyyy HH:mm:ss", CultureInfo.InvariantCulture);
-
- // TODO: Get proper timezone offset. hardcoded to UTC for now.
- var offset = 0;
-
- string zone;
- // + or -, then 0-pad, then offset, then more 0-padding.
- if(offset < 0)
- {
- offset *= -1;
- zone = "-";
- }
- else
- {
- zone = "+";
- }
-
- zone += offset.ToString(CultureInfo.InvariantCulture).PadLeft(2, '0');
- dateString += (" " + zone.PadRight(5, '0'));
- return dateString;
- }
- }
-
- public class DuoException : Exception
- {
- public HttpStatusCode Status { get; private set; }
-
- public DuoException(HttpStatusCode status, string message, Exception inner)
- : base(message, inner)
- {
- Status = status;
- }
- }
-
- public class DuoApiException : DuoException
- {
- public int Code { get; private set; }
- public string ApiMessage { get; private set; }
- public string ApiMessageDetail { get; private set; }
-
- public DuoApiException(int code, HttpStatusCode status, string message, string messageDetail)
- : base(status, FormatMessage(code, message, messageDetail), null)
- {
- Code = code;
- ApiMessage = message;
- ApiMessageDetail = messageDetail;
- }
-
- private static string FormatMessage(int code, string message, string messageDetail)
- {
- return $"Duo API Error {code}: '{message}' ('{messageDetail}').";
- }
- }
-
- public class DuoBadResponseException : DuoException
- {
- public DuoBadResponseException(HttpStatusCode status, Exception inner)
- : base(status, FormatMessage(status, inner), inner)
- { }
-
- private static string FormatMessage(HttpStatusCode status, Exception inner)
- {
- var innerMessage = "(null)";
- if(inner != null)
- {
- innerMessage = string.Format("'{0}'", inner.Message);
- }
-
- return $"Got error '{innerMessage}' with HTTP status {(int)status}.";
- }
- }
-}
diff --git a/src/Identity/settings.json b/src/Identity/settings.json
index 1dae623b37..7e013645d9 100644
--- a/src/Identity/settings.json
+++ b/src/Identity/settings.json
@@ -2,7 +2,6 @@
"globalSettings": {
"siteName": "bitwarden",
"baseVaultUri": "http://localhost:4001/#",
- "jwtSigningKey": "THIS IS A SECRET. IT KEEPS YOUR TOKEN SAFE. :)",
"stripeApiKey": "SECRET",
"sqlServer": {
"connectionString": "SECRET"
@@ -11,13 +10,6 @@
"apiKey": "SECRET",
"replyToEmail": "hello@bitwarden.com"
},
- "push": {
- "apnsCertificateThumbprint": "SECRET",
- "apnsCertificatePassword": "SECRET",
- "gcmSenderId": "SECRET",
- "gcmApiKey": "SECRET",
- "gcmAppPackageName": "com.x8bit.bitwarden"
- },
"identityServer": {
"certificateThumbprint": "SECRET"
},