using System; using System.Collections.Generic; using System.Data; using System.Linq; using Bit.Core.Enums; using Bit.Core.Models; using DbUp.Engine; using Newtonsoft.Json; namespace Bit.Migrator.DbScripts { class ScriptMigrateU2FToWebAuthn : IScript { public string ProvideScript(Func commandFactory) { var cmd = commandFactory(); cmd.CommandText = "SELECT Id, TwoFactorProviders FROM [dbo].[User] WHERE TwoFactorProviders IS NOT NULL"; var users = new List(); using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { var id = reader.GetGuid(0); var twoFactorProviders = reader.GetString(1); if (string.IsNullOrWhiteSpace(twoFactorProviders)) { continue; } var providers = JsonConvert.DeserializeObject>(twoFactorProviders); if (!providers.ContainsKey(TwoFactorProviderType.U2f)) { continue; } var u2fProvider = providers[TwoFactorProviderType.U2f]; if (!u2fProvider.Enabled || !HasProperMetaData(u2fProvider)) { continue; } var u2fKeys = LoadKeys(u2fProvider); var webAuthnKeys = u2fKeys.Select(key => (key.Item1, key.Item2.ToWebAuthnData())); var webAuthnProvider = new TwoFactorProvider { Enabled = true, MetaData = webAuthnKeys.ToDictionary(x => x.Item1, x => (object)x.Item2) }; providers[TwoFactorProviderType.WebAuthn] = webAuthnProvider; users.Add(new User { Id = id, TwoFactorProviders = JsonConvert.SerializeObject(providers), }); } } foreach (User user in users) { var command = commandFactory(); command.CommandText = "UPDATE [dbo].[User] SET TwoFactorProviders = @twoFactorProviders WHERE Id = @id"; var idParameter = command.CreateParameter(); idParameter.ParameterName = "@id"; idParameter.Value = user.Id; var twoFactorParameter = command.CreateParameter(); twoFactorParameter.ParameterName = "@twoFactorProviders"; twoFactorParameter.Value = user.TwoFactorProviders; command.Parameters.Add(idParameter); command.Parameters.Add(twoFactorParameter); command.ExecuteNonQuery(); } return ""; } private bool HasProperMetaData(TwoFactorProvider provider) { return (provider?.MetaData?.Count ?? 0) > 0; } private List> LoadKeys(TwoFactorProvider provider) { var keys = new List>(); // Support up to 5 keys for (var i = 1; i <= 5; i++) { var keyName = $"Key{i}"; if (provider.MetaData.ContainsKey(keyName)) { var key = new TwoFactorProvider.U2fMetaData((dynamic)provider.MetaData[keyName]); if (!key?.Compromised ?? false) { keys.Add(new Tuple(keyName, key)); } } } return keys; } private class User { public Guid Id { get; set; } public string TwoFactorProviders { get; set; } } } }