mirror of
https://github.com/bitwarden/directory-connector
synced 2025-12-15 07:43:27 +00:00
login, logout, setting fixes
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Bit.Core.Models;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
@@ -38,6 +39,7 @@ namespace Bit.Console
|
|||||||
Con.WriteLine("Main Menu");
|
Con.WriteLine("Main Menu");
|
||||||
Con.WriteLine("=================================");
|
Con.WriteLine("=================================");
|
||||||
Con.WriteLine("1. Log in to bitwarden");
|
Con.WriteLine("1. Log in to bitwarden");
|
||||||
|
Con.WriteLine("2. Log out");
|
||||||
Con.WriteLine("2. Configure directory connection");
|
Con.WriteLine("2. Configure directory connection");
|
||||||
Con.WriteLine("3. Sync directory");
|
Con.WriteLine("3. Sync directory");
|
||||||
Con.WriteLine("4. Start/stop background service");
|
Con.WriteLine("4. Start/stop background service");
|
||||||
@@ -56,20 +58,21 @@ namespace Bit.Console
|
|||||||
await LogInAsync();
|
await LogInAsync();
|
||||||
break;
|
break;
|
||||||
case "2":
|
case "2":
|
||||||
|
case "logout":
|
||||||
|
case "signout":
|
||||||
|
await LogOutAsync();
|
||||||
|
break;
|
||||||
case "dir":
|
case "dir":
|
||||||
case "directory":
|
case "directory":
|
||||||
|
await DirectoryAsync();
|
||||||
break;
|
break;
|
||||||
case "3":
|
|
||||||
case "sync":
|
case "sync":
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "4":
|
|
||||||
case "svc":
|
case "svc":
|
||||||
case "service":
|
case "service":
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "5":
|
|
||||||
case "exit":
|
case "exit":
|
||||||
case "quit":
|
case "quit":
|
||||||
case "q":
|
case "q":
|
||||||
@@ -96,20 +99,28 @@ namespace Bit.Console
|
|||||||
|
|
||||||
private static async Task LogInAsync()
|
private static async Task LogInAsync()
|
||||||
{
|
{
|
||||||
|
if(Core.Services.AuthService.Instance.Authenticated)
|
||||||
|
{
|
||||||
|
Con.WriteLine("You are already logged in as {0}.", Core.Services.TokenService.Instance.AccessTokenEmail);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
string email = null;
|
string email = null;
|
||||||
string masterPassword = null;
|
string masterPassword = null;
|
||||||
|
string token = null;
|
||||||
|
|
||||||
if(_usingArgs)
|
if(_usingArgs)
|
||||||
{
|
{
|
||||||
if(_args.Length != 3)
|
var parameters = ParseParameters();
|
||||||
|
if(parameters.Count >= 2 && parameters.ContainsKey("e") && parameters.ContainsKey("p"))
|
||||||
{
|
{
|
||||||
Con.ForegroundColor = ConsoleColor.Red;
|
email = parameters["e"];
|
||||||
Con.WriteLine("Invalid arguments.");
|
masterPassword = parameters["p"];
|
||||||
Con.ResetColor();
|
}
|
||||||
|
if(parameters.Count == 3 && parameters.ContainsKey("t"))
|
||||||
|
{
|
||||||
|
token = parameters["t"];
|
||||||
}
|
}
|
||||||
|
|
||||||
email = _args[1];
|
|
||||||
masterPassword = _args[2];
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -119,15 +130,33 @@ namespace Bit.Console
|
|||||||
masterPassword = ReadSecureLine();
|
masterPassword = ReadSecureLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await Core.Services.AuthService.Instance.LogInAsync(email, masterPassword);
|
if(string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(masterPassword))
|
||||||
|
{
|
||||||
|
Con.WriteLine();
|
||||||
|
Con.WriteLine();
|
||||||
|
Con.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Con.WriteLine("Invalid input parameters.");
|
||||||
|
Con.ResetColor();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(result.TwoFactorRequired)
|
LoginResult result = null;
|
||||||
|
if(string.IsNullOrWhiteSpace(token))
|
||||||
|
{
|
||||||
|
result = await Core.Services.AuthService.Instance.LogInAsync(email, masterPassword);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = await Core.Services.AuthService.Instance.LogInTwoFactorAsync(email, masterPassword, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(string.IsNullOrWhiteSpace(token) && result.TwoFactorRequired)
|
||||||
{
|
{
|
||||||
Con.WriteLine();
|
Con.WriteLine();
|
||||||
Con.WriteLine();
|
Con.WriteLine();
|
||||||
Con.WriteLine("Two-step login is enabled on this account. Please enter your verification code.");
|
Con.WriteLine("Two-step login is enabled on this account. Please enter your verification code.");
|
||||||
Con.Write("Verification code: ");
|
Con.Write("Verification code: ");
|
||||||
var token = Con.ReadLine().Trim();
|
token = Con.ReadLine().Trim();
|
||||||
result = await Core.Services.AuthService.Instance.LogInTwoFactorAsync(token, email, result.MasterPasswordHash);
|
result = await Core.Services.AuthService.Instance.LogInTwoFactorAsync(token, email, result.MasterPasswordHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +178,25 @@ namespace Bit.Console
|
|||||||
masterPassword = null;
|
masterPassword = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Task LogOutAsync()
|
||||||
|
{
|
||||||
|
if(Core.Services.AuthService.Instance.Authenticated)
|
||||||
|
{
|
||||||
|
Core.Services.AuthService.Instance.LogOut();
|
||||||
|
Con.WriteLine("You have successfully logged out!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Con.WriteLine("You are not logged in.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task DirectoryAsync()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private static string ReadSecureLine()
|
private static string ReadSecureLine()
|
||||||
{
|
{
|
||||||
var input = string.Empty;
|
var input = string.Empty;
|
||||||
@@ -175,5 +223,21 @@ namespace Bit.Console
|
|||||||
}
|
}
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IDictionary<string, string> ParseParameters()
|
||||||
|
{
|
||||||
|
var dict = new Dictionary<string, string>();
|
||||||
|
for(int i = 1; i < _args.Length; i = i + 2)
|
||||||
|
{
|
||||||
|
if(!_args[i].StartsWith("-"))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dict.Add(_args[i].Substring(1), _args[i + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public bool Authenticated => !string.IsNullOrWhiteSpace(TokenService.Instance.AccessToken);
|
public bool Authenticated => !string.IsNullOrWhiteSpace(TokenService.Instance.AccessToken);
|
||||||
|
|
||||||
|
public void LogOut()
|
||||||
|
{
|
||||||
|
TokenService.Instance.AccessToken = null;
|
||||||
|
TokenService.Instance.RefreshToken = null;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<LoginResult> LogInAsync(string email, string masterPassword)
|
public async Task<LoginResult> LogInAsync(string email, string masterPassword)
|
||||||
{
|
{
|
||||||
var normalizedEmail = email.Trim().ToLower();
|
var normalizedEmail = email.Trim().ToLower();
|
||||||
@@ -65,7 +71,21 @@ namespace Bit.Core.Services
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<LoginResult> LogInTwoFactorAsync(string token, string email, string masterPasswordHash)
|
public async Task<LoginResult> LogInTwoFactorAsync(string token, string email, string masterPassword)
|
||||||
|
{
|
||||||
|
var normalizedEmail = email.Trim().ToLower();
|
||||||
|
var key = CryptoService.Instance.MakeKeyFromPassword(masterPassword, normalizedEmail);
|
||||||
|
|
||||||
|
var result = await LogInTwoFactorWithHashAsync(token, email,
|
||||||
|
CryptoService.Instance.HashPasswordBase64(key, masterPassword));
|
||||||
|
|
||||||
|
key = null;
|
||||||
|
masterPassword = null;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LoginResult> LogInTwoFactorWithHashAsync(string token, string email, string masterPasswordHash)
|
||||||
{
|
{
|
||||||
var request = new TokenRequest
|
var request = new TokenRequest
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace Bit.Core.Services
|
|||||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||||
"\\bitwarden\\DirectoryConnector");
|
"\\bitwarden\\DirectoryConnector");
|
||||||
|
|
||||||
private IDictionary<string, object> _settings;
|
private SettingsModel _settings;
|
||||||
|
|
||||||
private SettingsService() { }
|
private SettingsService() { }
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDictionary<string, object> Settings
|
public SettingsModel Settings
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -47,83 +47,44 @@ namespace Bit.Core.Services
|
|||||||
using(var sr = new StreamReader(s, Encoding.UTF8))
|
using(var sr = new StreamReader(s, Encoding.UTF8))
|
||||||
using(var jsonTextReader = new JsonTextReader(sr))
|
using(var jsonTextReader = new JsonTextReader(sr))
|
||||||
{
|
{
|
||||||
_settings = serializer.Deserialize<IDictionary<string, object>>(jsonTextReader);
|
_settings = serializer.Deserialize<SettingsModel>(jsonTextReader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _settings == null ? new Dictionary<string, object>() : _settings;
|
return _settings == null ? new SettingsModel() : _settings;
|
||||||
}
|
}
|
||||||
set
|
}
|
||||||
|
|
||||||
|
private void SaveSettings()
|
||||||
|
{
|
||||||
|
lock(_locker)
|
||||||
{
|
{
|
||||||
lock(_locker)
|
if(!Directory.Exists(_baseStoragePath))
|
||||||
{
|
{
|
||||||
if(!Directory.Exists(_baseStoragePath))
|
Directory.CreateDirectory(_baseStoragePath);
|
||||||
{
|
}
|
||||||
Directory.CreateDirectory(_baseStoragePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
_settings = value;
|
_settings = Settings;
|
||||||
var filePath = $"{_baseStoragePath}\\settings.json";
|
var filePath = $"{_baseStoragePath}\\settings.json";
|
||||||
using(var s = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read))
|
using(var s = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read))
|
||||||
using(var sw = new StreamWriter(s, Encoding.UTF8))
|
using(var sw = new StreamWriter(s, Encoding.UTF8))
|
||||||
{
|
{
|
||||||
var json = JsonConvert.SerializeObject(_settings);
|
var json = JsonConvert.SerializeObject(_settings, Formatting.Indented);
|
||||||
sw.Write(json);
|
sw.Write(json);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Set(string key, object value)
|
|
||||||
{
|
|
||||||
if(Contains(key))
|
|
||||||
{
|
|
||||||
Settings[key] = value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Settings.Add(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Settings = Settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Remove(string key)
|
|
||||||
{
|
|
||||||
Settings.Remove(key);
|
|
||||||
Settings = Settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Contains(string key)
|
|
||||||
{
|
|
||||||
return Settings.ContainsKey(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Get<T>(string key)
|
|
||||||
{
|
|
||||||
if(Settings.ContainsKey(key))
|
|
||||||
{
|
|
||||||
return (T)Settings[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
return default(T);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EncryptedData AccessToken
|
public EncryptedData AccessToken
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return Get<EncryptedData>("AccessToken");
|
return Settings.AccessToken;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if(value == null)
|
Settings.AccessToken = value;
|
||||||
{
|
SaveSettings();
|
||||||
Remove("AccessTolen");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set("AccessToken", value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,18 +92,19 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return Get<EncryptedData>("RefreshToken");
|
return Settings.RefreshToken;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if(value == null)
|
Settings.RefreshToken = value;
|
||||||
{
|
SaveSettings();
|
||||||
Remove("RefreshToken");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set("RefreshToken", value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SettingsModel
|
||||||
|
{
|
||||||
|
public EncryptedData AccessToken { get; set; }
|
||||||
|
public EncryptedData RefreshToken { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user