diff --git a/src/Console/Program.cs b/src/Console/Program.cs index 8fb786ab..353f598b 100644 --- a/src/Console/Program.cs +++ b/src/Console/Program.cs @@ -111,6 +111,7 @@ namespace Bit.Console string email = null; string masterPassword = null; string token = null; + string orgId = null; if(_usingArgs) { @@ -120,10 +121,14 @@ namespace Bit.Console email = parameters["e"]; masterPassword = parameters["p"]; } - if(parameters.Count == 3 && parameters.ContainsKey("t")) + if(parameters.Count >= 3 && parameters.ContainsKey("t")) { token = parameters["t"]; } + if(parameters.Count >= 3 && parameters.ContainsKey("o")) + { + orgId = parameters["o"]; + } } else { @@ -160,7 +165,44 @@ namespace Bit.Console Con.WriteLine("Two-step login is enabled on this account. Please enter your verification code."); Con.Write("Verification code: "); token = Con.ReadLine().Trim(); - result = await Core.Services.AuthService.Instance.LogInTwoFactorAsync(token, email, result.MasterPasswordHash); + result = await Core.Services.AuthService.Instance.LogInTwoFactorWithHashAsync(token, email, + result.MasterPasswordHash); + } + + if(result.Success && result.Organizations.Count > 1) + { + Organization org = null; + if(string.IsNullOrWhiteSpace(orgId)) + { + org = result.Organizations.FirstOrDefault(o => o.Id == orgId); + } + else + { + Con.WriteLine(); + Con.WriteLine(); + for(int i = 0; i < result.Organizations.Count; i++) + { + Con.WriteLine("{0}. {1}", i + 1, result.Organizations[i].Name); + } + Con.Write("Select your organization: "); + var orgIndexInput = Con.ReadLine().Trim(); + int orgIndex; + if(int.TryParse(orgIndexInput, out orgIndex)) + { + org = result.Organizations[orgIndex]; + } + } + + if(org == null) + { + result.Success = false; + result.ErrorMessage = "Organization not found."; + Core.Services.AuthService.Instance.LogOut(); + } + else + { + Core.Services.SettingsService.Instance.Organization = org; + } } Con.WriteLine(); diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 9ed20f81..bc41741e 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -55,6 +55,7 @@ + diff --git a/src/Core/Models/LoginResult.cs b/src/Core/Models/LoginResult.cs index 61609855..d84fb0b6 100644 --- a/src/Core/Models/LoginResult.cs +++ b/src/Core/Models/LoginResult.cs @@ -12,5 +12,6 @@ namespace Bit.Core.Models public string ErrorMessage { get; set; } public bool TwoFactorRequired { get; set; } public string MasterPasswordHash { get; set; } + public List Organizations { get; set; } } } diff --git a/src/Core/Models/Organization.cs b/src/Core/Models/Organization.cs new file mode 100644 index 00000000..d5179e04 --- /dev/null +++ b/src/Core/Models/Organization.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Bit.Core.Models +{ + public class Organization + { + public Organization(ProfileOrganizationResponseModel org) + { + Name = org.Name; + Id = org.Id; + } + + public string Name { get; set; } + public string Id { get; set; } + } +} diff --git a/src/Core/Services/AuthService.cs b/src/Core/Services/AuthService.cs index 63dcb66a..91c49be0 100644 --- a/src/Core/Services/AuthService.cs +++ b/src/Core/Services/AuthService.cs @@ -1,4 +1,5 @@ -using Bit.Core.Models; +using Bit.Core.Enums; +using Bit.Core.Models; using Bit.Core.Utilities; using System; using System.Collections.Generic; @@ -29,6 +30,7 @@ namespace Bit.Core.Services } public bool Authenticated => !string.IsNullOrWhiteSpace(TokenService.Instance.AccessToken); + public bool OrganizationSet => SettingsService.Instance.Organization != null; public void LogOut() { @@ -68,8 +70,7 @@ namespace Bit.Core.Services return result; } - await ProcessLogInSuccessAsync(response.Result); - return result; + return await ProcessLogInSuccessAsync(response.Result); } public async Task LogInTwoFactorAsync(string token, string email, string masterPassword) @@ -97,24 +98,54 @@ namespace Bit.Core.Services var response = await ApiService.Instance.PostTokenAsync(request); - var result = new LoginResult(); if(!response.Succeeded) { + var result = new LoginResult(); result.Success = false; result.ErrorMessage = response.Errors.FirstOrDefault()?.Message; return result; } - result.Success = true; - await ProcessLogInSuccessAsync(response.Result); - return result; + return await ProcessLogInSuccessAsync(response.Result); } - private Task ProcessLogInSuccessAsync(TokenResponse response) + private async Task ProcessLogInSuccessAsync(TokenResponse response) { TokenService.Instance.AccessToken = response.AccessToken; TokenService.Instance.RefreshToken = response.RefreshToken; - return Task.FromResult(0); + + var result = new LoginResult(); + + var profile = await ApiService.Instance.GetProfileAsync(); + if(profile.Succeeded) + { + var adminOrgs = profile.Result.Organizations.Where(o => + o.Status == OrganizationUserStatusType.Confirmed && + o.Type != OrganizationUserType.User); + if(!adminOrgs.Any()) + { + LogOut(); + result.Success = false; + result.ErrorMessage = "You are not an admin of any organizations."; + return result; + } + + result.Organizations = adminOrgs.Select(o => new Organization(o)).ToList(); + if(result.Organizations.Count == 1) + { + SettingsService.Instance.Organization = new Organization(adminOrgs.First()); + } + + result.Success = true; + return result; + } + else + { + LogOut(); + result.Success = false; + result.ErrorMessage = "Could not load profile."; + return result; + } } } } diff --git a/src/Core/Services/SettingsService.cs b/src/Core/Services/SettingsService.cs index 892f7793..77a13655 100644 --- a/src/Core/Services/SettingsService.cs +++ b/src/Core/Services/SettingsService.cs @@ -127,6 +127,19 @@ namespace Bit.Core.Services } } + public Organization Organization + { + get + { + return Settings.Organization; + } + set + { + Settings.Organization = value; + SaveSettings(); + } + } + public ServerConfiguration Server { get @@ -147,6 +160,7 @@ namespace Bit.Core.Services public EncryptedData AccessToken { get; set; } public EncryptedData RefreshToken { get; set; } public ServerConfiguration Server { get; set; } + public Organization Organization { get; set; } } } }