[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 IVaultTimeoutService _vaultTimeoutService;
private IStorageService _storageService;
private IPolicyService _policyService;
private IUserService _userService;
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
FillCallback callback)
@ -89,6 +91,26 @@ namespace Bit.Droid.Autofill
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);
parser.Parse();

View File

@ -25,6 +25,7 @@ namespace Bit.App.Pages
private readonly IAuditService _auditService;
private readonly IMessagingService _messagingService;
private readonly IEventService _eventService;
private readonly IPolicyService _policyService;
private CipherView _cipher;
private bool _showNotesSeparator;
private bool _showPassword;
@ -78,6 +79,7 @@ namespace Bit.App.Pages
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
GeneratePasswordCommand = new Command(GeneratePassword);
TogglePasswordCommand = new Command(TogglePassword);
ToggleCardCodeCommand = new Command(ToggleCardCode);
@ -87,6 +89,7 @@ namespace Bit.App.Pages
Uris = new ExtendedObservableCollection<LoginUriView>();
Fields = new ExtendedObservableCollection<AddEditPageFieldViewModel>();
Collections = new ExtendedObservableCollection<CollectionViewModel>();
AllowPersonal = true;
TypeOptions = new List<KeyValuePair<string, CipherType>>
{
@ -276,6 +279,7 @@ namespace Bit.App.Pages
public string ShowCardCodeIcon => ShowCardCode ? "" : "";
public int PasswordFieldColSpan => Cipher.ViewPassword ? 1 : 4;
public int TotpColumnSpan => Cipher.ViewPassword ? 1 : 2;
public bool AllowPersonal { get; set; }
public void Init()
{
@ -284,6 +288,7 @@ namespace Bit.App.Pages
public async Task<bool> LoadAsync(AppOptions appOptions = null)
{
var policies = (await _policyService.GetAll(PolicyType.PersonalOwnership))?.ToList();
var myEmail = await _userService.GetEmailAsync();
OwnershipOptions.Add(new KeyValuePair<string, string>(myEmail, null));
var orgs = await _userService.GetAllOrganizationAsync();
@ -292,6 +297,24 @@ namespace Bit.App.Pages
if (org.Enabled && org.Status == OrganizationUserStatusType.Confirmed)
{
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);
OwnershipSelectedIndex = string.IsNullOrWhiteSpace(Cipher.OrganizationId) ? 0 :
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))
{
@ -410,6 +439,13 @@ namespace Bit.App.Pages
AppResources.Ok);
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() ?
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);
}
}
public static string PersonalOwnershipSubmitError {
get {
return ResourceManager.GetString("PersonalOwnershipSubmitError", resourceCulture);
}
}
}
}

View File

@ -1786,4 +1786,7 @@
<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>
</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>

View File

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

View File

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

View File

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