[Policy] Personal Ownership (#1166)

* Initial commit of personal ownership policy

* Updated logic for returning from allowing cipher creation from notification

* fixed small edge case when user in one org // adjusted error message to match all platforms

* Removed test code
This commit is contained in:
Vincent Salucci 2020-12-14 08:46:54 -06:00 committed by GitHub
parent 0dd87bbf78
commit 6e40b7f25b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 74 additions and 1 deletions

View File

@ -23,6 +23,8 @@ namespace Bit.Droid.Autofill
private ICipherService _cipherService; private ICipherService _cipherService;
private IVaultTimeoutService _vaultTimeoutService; private IVaultTimeoutService _vaultTimeoutService;
private IStorageService _storageService; private IStorageService _storageService;
private IPolicyService _policyService;
private IUserService _userService;
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
FillCallback callback) FillCallback callback)
@ -89,6 +91,26 @@ namespace Bit.Droid.Autofill
return; return;
} }
_policyService ??= ServiceContainer.Resolve<IPolicyService>("policyService");
var personalOwnershipPolicies = await _policyService.GetAll(PolicyType.PersonalOwnership);
if (personalOwnershipPolicies != null)
{
_userService ??= ServiceContainer.Resolve<IUserService>("userService");
foreach (var policy in personalOwnershipPolicies)
{
if (policy.Enabled)
{
var org = await _userService.GetOrganizationAsync(policy.OrganizationId);
if (org != null && org.Enabled && org.UsePolicies && !org.IsAdmin
&& org.Status == OrganizationUserStatusType.Confirmed)
{
return;
}
}
}
}
var parser = new Parser(structure, ApplicationContext); var parser = new Parser(structure, ApplicationContext);
parser.Parse(); parser.Parse();

View File

@ -25,6 +25,7 @@ namespace Bit.App.Pages
private readonly IAuditService _auditService; private readonly IAuditService _auditService;
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
private readonly IEventService _eventService; private readonly IEventService _eventService;
private readonly IPolicyService _policyService;
private CipherView _cipher; private CipherView _cipher;
private bool _showNotesSeparator; private bool _showNotesSeparator;
private bool _showPassword; private bool _showPassword;
@ -78,6 +79,7 @@ namespace Bit.App.Pages
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService"); _collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
_eventService = ServiceContainer.Resolve<IEventService>("eventService"); _eventService = ServiceContainer.Resolve<IEventService>("eventService");
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
GeneratePasswordCommand = new Command(GeneratePassword); GeneratePasswordCommand = new Command(GeneratePassword);
TogglePasswordCommand = new Command(TogglePassword); TogglePasswordCommand = new Command(TogglePassword);
ToggleCardCodeCommand = new Command(ToggleCardCode); ToggleCardCodeCommand = new Command(ToggleCardCode);
@ -87,6 +89,7 @@ namespace Bit.App.Pages
Uris = new ExtendedObservableCollection<LoginUriView>(); Uris = new ExtendedObservableCollection<LoginUriView>();
Fields = new ExtendedObservableCollection<AddEditPageFieldViewModel>(); Fields = new ExtendedObservableCollection<AddEditPageFieldViewModel>();
Collections = new ExtendedObservableCollection<CollectionViewModel>(); Collections = new ExtendedObservableCollection<CollectionViewModel>();
AllowPersonal = true;
TypeOptions = new List<KeyValuePair<string, CipherType>> TypeOptions = new List<KeyValuePair<string, CipherType>>
{ {
@ -276,6 +279,7 @@ namespace Bit.App.Pages
public string ShowCardCodeIcon => ShowCardCode ? "" : ""; public string ShowCardCodeIcon => ShowCardCode ? "" : "";
public int PasswordFieldColSpan => Cipher.ViewPassword ? 1 : 4; public int PasswordFieldColSpan => Cipher.ViewPassword ? 1 : 4;
public int TotpColumnSpan => Cipher.ViewPassword ? 1 : 2; public int TotpColumnSpan => Cipher.ViewPassword ? 1 : 2;
public bool AllowPersonal { get; set; }
public void Init() public void Init()
{ {
@ -284,6 +288,7 @@ namespace Bit.App.Pages
public async Task<bool> LoadAsync(AppOptions appOptions = null) public async Task<bool> LoadAsync(AppOptions appOptions = null)
{ {
var policies = (await _policyService.GetAll(PolicyType.PersonalOwnership))?.ToList();
var myEmail = await _userService.GetEmailAsync(); var myEmail = await _userService.GetEmailAsync();
OwnershipOptions.Add(new KeyValuePair<string, string>(myEmail, null)); OwnershipOptions.Add(new KeyValuePair<string, string>(myEmail, null));
var orgs = await _userService.GetAllOrganizationAsync(); var orgs = await _userService.GetAllOrganizationAsync();
@ -292,6 +297,24 @@ namespace Bit.App.Pages
if (org.Enabled && org.Status == OrganizationUserStatusType.Confirmed) if (org.Enabled && org.Status == OrganizationUserStatusType.Confirmed)
{ {
OwnershipOptions.Add(new KeyValuePair<string, string>(org.Name, org.Id)); OwnershipOptions.Add(new KeyValuePair<string, string>(org.Name, org.Id));
if (policies != null && org.UsePolicies && !org.IsAdmin && AllowPersonal)
{
foreach (var policy in policies)
{
if (policy.OrganizationId == org.Id && policy.Enabled)
{
AllowPersonal = false;
// Remove personal ownership
OwnershipOptions.RemoveAt(0);
// Default to the organization who owns this policy for now (if necessary)
if (string.IsNullOrWhiteSpace(OrganizationId))
{
OrganizationId = org.Id;
}
break;
}
}
}
} }
} }
@ -364,6 +387,12 @@ namespace Bit.App.Pages
IdentityTitleOptions.FindIndex(k => k.Value == Cipher.Identity.Title); IdentityTitleOptions.FindIndex(k => k.Value == Cipher.Identity.Title);
OwnershipSelectedIndex = string.IsNullOrWhiteSpace(Cipher.OrganizationId) ? 0 : OwnershipSelectedIndex = string.IsNullOrWhiteSpace(Cipher.OrganizationId) ? 0 :
OwnershipOptions.FindIndex(k => k.Value == Cipher.OrganizationId); OwnershipOptions.FindIndex(k => k.Value == Cipher.OrganizationId);
// If the selected organization is on Index 0 and we've removed the personal option, force refresh
if (!AllowPersonal && OwnershipSelectedIndex == 0)
{
OrganizationChanged();
}
if ((!EditMode || CloneMode) && (CollectionIds?.Any() ?? false)) if ((!EditMode || CloneMode) && (CollectionIds?.Any() ?? false))
{ {
@ -410,6 +439,13 @@ namespace Bit.App.Pages
AppResources.Ok); AppResources.Ok);
return false; return false;
} }
if ((!EditMode || CloneMode) && !AllowPersonal && string.IsNullOrWhiteSpace(Cipher.OrganizationId))
{
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
AppResources.PersonalOwnershipSubmitError,AppResources.Ok);
return false;
}
Cipher.Fields = Fields != null && Fields.Any() ? Cipher.Fields = Fields != null && Fields.Any() ?
Fields.Where(f => f != null).Select(f => f.Field).ToList() : null; Fields.Where(f => f != null).Select(f => f.Field).ToList() : null;

View File

@ -3158,5 +3158,11 @@ namespace Bit.App.Resources {
return ResourceManager.GetString("DrawOverDescription3", resourceCulture); return ResourceManager.GetString("DrawOverDescription3", resourceCulture);
} }
} }
public static string PersonalOwnershipSubmitError {
get {
return ResourceManager.GetString("PersonalOwnershipSubmitError", resourceCulture);
}
}
} }
} }

View File

@ -1786,4 +1786,7 @@
<data name="DrawOverDescription3" xml:space="preserve"> <data name="DrawOverDescription3" xml:space="preserve">
<value>If enabled, accessibility will show a popup to augment the Autofill Service for older apps that don't support the Android Autofill Framework.</value> <value>If enabled, accessibility will show a popup to augment the Autofill Service for older apps that don't support the Android Autofill Framework.</value>
</data> </data>
<data name="PersonalOwnershipSubmitError" xml:space="preserve">
<value>Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections.</value>
</data>
</root> </root>

View File

@ -6,6 +6,7 @@
MasterPassword = 1, MasterPassword = 1,
PasswordGenerator = 2, PasswordGenerator = 2,
OnlyOrg = 3, OnlyOrg = 3,
RequireSso = 4 RequireSso = 4,
PersonalOwnership = 5,
} }
} }

View File

@ -20,6 +20,7 @@ namespace Bit.Core.Models.Data
UseTotp = response.UseTotp; UseTotp = response.UseTotp;
Use2fa = response.Use2fa; Use2fa = response.Use2fa;
UseApi = response.UseApi; UseApi = response.UseApi;
UsePolicies = response.UsePolicies;
SelfHost = response.SelfHost; SelfHost = response.SelfHost;
UsersGetPremium = response.UsersGetPremium; UsersGetPremium = response.UsersGetPremium;
Seats = response.Seats; Seats = response.Seats;
@ -38,6 +39,7 @@ namespace Bit.Core.Models.Data
public bool UseTotp { get; set; } public bool UseTotp { get; set; }
public bool Use2fa { get; set; } public bool Use2fa { get; set; }
public bool UseApi { get; set; } public bool UseApi { get; set; }
public bool UsePolicies { get; set; }
public bool SelfHost { get; set; } public bool SelfHost { get; set; }
public bool UsersGetPremium { get; set; } public bool UsersGetPremium { get; set; }
public int Seats { get; set; } public int Seats { get; set; }

View File

@ -20,6 +20,7 @@ namespace Bit.Core.Models.Domain
UseTotp = obj.UseTotp; UseTotp = obj.UseTotp;
Use2fa = obj.Use2fa; Use2fa = obj.Use2fa;
UseApi = obj.UseApi; UseApi = obj.UseApi;
UsePolicies = obj.UsePolicies;
SelfHost = obj.SelfHost; SelfHost = obj.SelfHost;
UsersGetPremium = obj.UsersGetPremium; UsersGetPremium = obj.UsersGetPremium;
Seats = obj.Seats; Seats = obj.Seats;
@ -38,6 +39,7 @@ namespace Bit.Core.Models.Domain
public bool UseTotp { get; set; } public bool UseTotp { get; set; }
public bool Use2fa { get; set; } public bool Use2fa { get; set; }
public bool UseApi { get; set; } public bool UseApi { get; set; }
public bool UsePolicies { get; set; }
public bool SelfHost { get; set; } public bool SelfHost { get; set; }
public bool UsersGetPremium { get; set; } public bool UsersGetPremium { get; set; }
public int Seats { get; set; } public int Seats { get; set; }

View File

@ -12,6 +12,7 @@ namespace Bit.Core.Models.Response
public bool UseTotp { get; set; } public bool UseTotp { get; set; }
public bool Use2fa { get; set; } public bool Use2fa { get; set; }
public bool UseApi { get; set; } public bool UseApi { get; set; }
public bool UsePolicies { get; set; }
public bool UsersGetPremium { get; set; } public bool UsersGetPremium { get; set; }
public bool SelfHost { get; set; } public bool SelfHost { get; set; }
public int Seats { get; set; } public int Seats { get; set; }