diff --git a/.github/workflows/build-beta.yml b/.github/workflows/build-beta.yml index add4a3d16..16fde6acb 100644 --- a/.github/workflows/build-beta.yml +++ b/.github/workflows/build-beta.yml @@ -63,7 +63,7 @@ jobs: nuget-version: 6.4.0 - name: Set up .NET - uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0 + uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 with: dotnet-version: '8.0.x' @@ -342,7 +342,7 @@ jobs: secrets: "devops-alerts-slack-webhook-url" - name: Notify Slack on failure - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 if: failure() env: SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bad49a53b..872305c0e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -305,7 +305,7 @@ jobs: nuget-version: 6.4.0 - name: Set up .NET - uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0 + uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 with: dotnet-version: '8.0.x' @@ -453,7 +453,7 @@ jobs: nuget-version: 6.4.0 - name: Set up .NET - uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0 + uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 with: dotnet-version: '8.0.x' @@ -733,7 +733,7 @@ jobs: secrets: "crowdin-api-token" - name: Upload Sources - uses: crowdin/github-action@c953b17499daa6be3e5afbf7a63616fb02d8b18d # v1.19.0 + uses: crowdin/github-action@67705afb6985401459cd143d5f5f00c9dc212f23 # v1.20.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} @@ -778,7 +778,7 @@ jobs: secrets: "devops-alerts-slack-webhook-url" - name: Notify Slack on failure - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 if: failure() env: SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }} diff --git a/.github/workflows/crowdin-pull.yml b/.github/workflows/crowdin-pull.yml index 8e1c8b61e..bf421e382 100644 --- a/.github/workflows/crowdin-pull.yml +++ b/.github/workflows/crowdin-pull.yml @@ -30,7 +30,7 @@ jobs: secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase" - name: Download translations - uses: crowdin/github-action@c953b17499daa6be3e5afbf7a63616fb02d8b18d # v1.19.0 + uses: crowdin/github-action@67705afb6985401459cd143d5f5f00c9dc212f23 # v1.20.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cc364879c..c7711bb60 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,7 +68,7 @@ jobs: - name: Download all artifacts if: ${{ inputs.release_type != 'Dry Run' }} - uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2 + uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4 with: workflow: build.yml workflow_conclusion: success @@ -76,7 +76,7 @@ jobs: - name: Dry Run - Download all artifacts if: ${{ inputs.release_type == 'Dry Run' }} - uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2 + uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4 with: workflow: build.yml workflow_conclusion: success @@ -130,7 +130,7 @@ jobs: - name: Download F-Droid .apk artifact if: ${{ inputs.release_type != 'Dry Run' }} - uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2 + uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4 with: workflow: build.yml workflow_conclusion: success @@ -139,7 +139,7 @@ jobs: - name: Dry Run - Download F-Droid .apk artifact if: ${{ inputs.release_type == 'Dry Run' }} - uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2 + uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4 with: workflow: build.yml workflow_conclusion: success diff --git a/src/App/App.csproj b/src/App/App.csproj index 3a03bdd51..470570363 100644 --- a/src/App/App.csproj +++ b/src/App/App.csproj @@ -260,4 +260,7 @@ + + + diff --git a/src/App/Platforms/Android/AndroidManifest.xml b/src/App/Platforms/Android/AndroidManifest.xml index f93717714..16e14afeb 100644 --- a/src/App/Platforms/Android/AndroidManifest.xml +++ b/src/App/Platforms/Android/AndroidManifest.xml @@ -1,5 +1,5 @@  - + diff --git a/src/App/Platforms/iOS/Info.plist b/src/App/Platforms/iOS/Info.plist index 23ecc90d6..6f07e2e3d 100644 --- a/src/App/Platforms/iOS/Info.plist +++ b/src/App/Platforms/iOS/Info.plist @@ -11,7 +11,7 @@ CFBundleIdentifier com.8bit.bitwarden CFBundleShortVersionString - 2024.4.0 + 2024.4.1 CFBundleVersion 1 CFBundleIconName diff --git a/src/App/Platforms/iOS/PrivacyInfo.xcprivacy b/src/App/Platforms/iOS/PrivacyInfo.xcprivacy new file mode 100644 index 000000000..16a52f332 --- /dev/null +++ b/src/App/Platforms/iOS/PrivacyInfo.xcprivacy @@ -0,0 +1,41 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryDiskSpace + NSPrivacyAccessedAPITypeReasons + + E174.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + 1C8F.1 + + + + + diff --git a/src/Core/Abstractions/IApiService.cs b/src/Core/Abstractions/IApiService.cs index ddf6a28fc..b60d90267 100644 --- a/src/Core/Abstractions/IApiService.cs +++ b/src/Core/Abstractions/IApiService.cs @@ -46,6 +46,7 @@ namespace Bit.Core.Abstractions Task PutShareCipherAsync(string id, CipherShareRequest request); Task PutDeleteCipherAsync(string id); Task PutRestoreCipherAsync(string id); + Task HasUnassignedCiphersAsync(); Task RefreshIdentityTokenAsync(); Task PreValidateSsoAsync(string identifier); Task SendAsync(HttpMethod method, string path, diff --git a/src/Core/Abstractions/ICipherService.cs b/src/Core/Abstractions/ICipherService.cs index e769c051a..59c395b44 100644 --- a/src/Core/Abstractions/ICipherService.cs +++ b/src/Core/Abstractions/ICipherService.cs @@ -36,5 +36,6 @@ namespace Bit.Core.Abstractions Task RestoreWithServerAsync(string id); Task CreateNewLoginForPasskeyAsync(Fido2ConfirmNewCredentialParams newPasskeyParams); Task CopyTotpCodeIfNeededAsync(CipherView cipher); + Task VerifyOrganizationHasUnassignedItemsAsync(); } } diff --git a/src/Core/Abstractions/IStateService.cs b/src/Core/Abstractions/IStateService.cs index 44ec8079f..0afb34da6 100644 --- a/src/Core/Abstractions/IStateService.cs +++ b/src/Core/Abstractions/IStateService.cs @@ -187,6 +187,8 @@ namespace Bit.Core.Abstractions Task GetPreAuthRegionAsync(); Task SetPreAuthRegionAsync(BwRegion value); Task ReloadStateAsync(); + Task GetShouldCheckOrganizationUnassignedItemsAsync(string userId = null); + Task SetShouldCheckOrganizationUnassignedItemsAsync(bool shouldCheck, string userId = null); [Obsolete("Use GetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")] Task GetPinProtectedAsync(string userId = null); [Obsolete("Use SetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")] diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index a56127385..f24e87016 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -46,6 +46,7 @@ namespace Bit.Core public const string PreLoginEmailKey = "preLoginEmailKey"; public const string ConfigsKey = "configsKey"; public const string DisplayEuEnvironmentFlag = "display-eu-environment"; + public const string UnassignedItemsBannerFlag = "unassigned-items-banner"; public const string RegionEnvironment = "regionEnvironment"; public const string DuoCallback = "bitwarden://duo-callback"; public const string NavigateToMessageCommand = "navigateTo"; @@ -138,6 +139,7 @@ namespace Bit.Core public static string ShouldConnectToWatchKey(string userId) => $"shouldConnectToWatch_{userId}"; public static string ScreenCaptureAllowedKey(string userId) => $"screenCaptureAllowed_{userId}"; public static string PendingAdminAuthRequest(string userId) => $"pendingAdminAuthRequest_{userId}"; + public static string ShouldCheckOrganizationUnassignedItemsKey(string userId) => $"shouldCheckOrganizationUnassignedItems_{userId}"; [Obsolete] public static string KeyKey(string userId) => $"key_{userId}"; [Obsolete] diff --git a/src/Core/Models/AppOptions.cs b/src/Core/Models/AppOptions.cs index 6682db887..6f99f40b9 100644 --- a/src/Core/Models/AppOptions.cs +++ b/src/Core/Models/AppOptions.cs @@ -27,6 +27,7 @@ namespace Bit.App.Models public bool HideAccountSwitcher { get; set; } public OtpData? OtpData { get; set; } public bool HasUnlockedInThisTransaction { get; set; } + public bool HasJustLoggedInOrUnlocked { get; set; } public void SetAllFrom(AppOptions o) { diff --git a/src/Core/Pages/Accounts/LockPage.xaml.cs b/src/Core/Pages/Accounts/LockPage.xaml.cs index bdf69b13b..b90b2c04e 100644 --- a/src/Core/Pages/Accounts/LockPage.xaml.cs +++ b/src/Core/Pages/Accounts/LockPage.xaml.cs @@ -233,6 +233,7 @@ namespace Bit.App.Pages } var previousPage = await AppHelpers.ClearPreviousPage(); + _appOptions.HasJustLoggedInOrUnlocked = true; App.MainPage = new TabsPage(_appOptions, previousPage); } } diff --git a/src/Core/Pages/Accounts/LoginApproveDevicePage.xaml.cs b/src/Core/Pages/Accounts/LoginApproveDevicePage.xaml.cs index 65d212b5a..8b46fc9e3 100644 --- a/src/Core/Pages/Accounts/LoginApproveDevicePage.xaml.cs +++ b/src/Core/Pages/Accounts/LoginApproveDevicePage.xaml.cs @@ -35,6 +35,8 @@ namespace Bit.App.Pages { return; } + + _appOptions.HasJustLoggedInOrUnlocked = true; var previousPage = await AppHelpers.ClearPreviousPage(); App.MainPage = new TabsPage(_appOptions, previousPage); } diff --git a/src/Core/Pages/Accounts/LoginPage.xaml.cs b/src/Core/Pages/Accounts/LoginPage.xaml.cs index 7139e57b1..434a06273 100644 --- a/src/Core/Pages/Accounts/LoginPage.xaml.cs +++ b/src/Core/Pages/Accounts/LoginPage.xaml.cs @@ -195,6 +195,8 @@ namespace Bit.App.Pages { return; } + + _appOptions.HasJustLoggedInOrUnlocked = true; var previousPage = await AppHelpers.ClearPreviousPage(); App.MainPage = new TabsPage(_appOptions, previousPage); } diff --git a/src/Core/Pages/Accounts/LoginPasswordlessRequestPage.xaml.cs b/src/Core/Pages/Accounts/LoginPasswordlessRequestPage.xaml.cs index a88f27a03..f8333f972 100644 --- a/src/Core/Pages/Accounts/LoginPasswordlessRequestPage.xaml.cs +++ b/src/Core/Pages/Accounts/LoginPasswordlessRequestPage.xaml.cs @@ -55,6 +55,8 @@ namespace Bit.App.Pages { return; } + + _appOptions.HasJustLoggedInOrUnlocked = true; var previousPage = await AppHelpers.ClearPreviousPage(); App.MainPage = new TabsPage(_appOptions, previousPage); } diff --git a/src/Core/Pages/Accounts/SetPasswordPage.xaml.cs b/src/Core/Pages/Accounts/SetPasswordPage.xaml.cs index 19013dba4..5a305cf6c 100644 --- a/src/Core/Pages/Accounts/SetPasswordPage.xaml.cs +++ b/src/Core/Pages/Accounts/SetPasswordPage.xaml.cs @@ -71,6 +71,8 @@ namespace Bit.App.Pages { return; } + + _appOptions.HasJustLoggedInOrUnlocked = true; var previousPage = await AppHelpers.ClearPreviousPage(); App.MainPage = new TabsPage(_appOptions, previousPage); } diff --git a/src/Core/Pages/Accounts/TwoFactorPage.xaml.cs b/src/Core/Pages/Accounts/TwoFactorPage.xaml.cs index 9322936ce..37828567b 100644 --- a/src/Core/Pages/Accounts/TwoFactorPage.xaml.cs +++ b/src/Core/Pages/Accounts/TwoFactorPage.xaml.cs @@ -206,6 +206,8 @@ namespace Bit.App.Pages { return; } + + _appOptions.HasJustLoggedInOrUnlocked = true; var previousPage = await AppHelpers.ClearPreviousPage(); App.MainPage = new TabsPage(_appOptions, previousPage); } diff --git a/src/Core/Pages/TabsPage.cs b/src/Core/Pages/TabsPage.cs index f37c4ee04..367d51e22 100644 --- a/src/Core/Pages/TabsPage.cs +++ b/src/Core/Pages/TabsPage.cs @@ -33,7 +33,7 @@ namespace Bit.App.Pages _keyConnectorService = ServiceContainer.Resolve("keyConnectorService"); _stateService = ServiceContainer.Resolve(); - _groupingsPage = new NavigationPage(new GroupingsPage(true, previousPage: previousPage)) + _groupingsPage = new NavigationPage(new GroupingsPage(true, previousPage: previousPage, appOptions: appOptions)) { Title = AppResources.MyVault, IconImageSource = "lock.png" diff --git a/src/Core/Pages/Vault/GroupingsPage/GroupingsPage.xaml.cs b/src/Core/Pages/Vault/GroupingsPage/GroupingsPage.xaml.cs index 46d65d87c..dea34cf22 100644 --- a/src/Core/Pages/Vault/GroupingsPage/GroupingsPage.xaml.cs +++ b/src/Core/Pages/Vault/GroupingsPage/GroupingsPage.xaml.cs @@ -1,5 +1,6 @@ using Bit.App.Abstractions; using Bit.App.Controls; +using Bit.App.Models; using Bit.Core.Abstractions; using Bit.Core.Enums; using Bit.Core.Models.Data; @@ -27,7 +28,7 @@ namespace Bit.App.Pages public GroupingsPage(bool mainPage, CipherType? type = null, string folderId = null, string collectionId = null, string pageTitle = null, string vaultFilterSelection = null, - PreviousPageInfo previousPage = null, bool deleted = false, bool showTotp = false) + PreviousPageInfo previousPage = null, bool deleted = false, bool showTotp = false, AppOptions appOptions = null) { _pageName = string.Concat(nameof(GroupingsPage), "_", DateTime.UtcNow.Ticks); InitializeComponent(); @@ -50,6 +51,7 @@ namespace Bit.App.Pages _vm.CollectionId = collectionId; _vm.Deleted = deleted; _vm.ShowTotp = showTotp; + _vm.AppOptions = appOptions; _previousPage = previousPage; if (pageTitle != null) { @@ -160,6 +162,8 @@ namespace Bit.App.Pages return; } + await _vm.CheckOrganizationUnassignedItemsAsync(); + // Push registration var lastPushRegistration = await _stateService.GetPushLastRegistrationDateAsync(); lastPushRegistration = lastPushRegistration.GetValueOrDefault(DateTime.MinValue); diff --git a/src/Core/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs b/src/Core/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs index a249b0b35..4430372b3 100644 --- a/src/Core/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs +++ b/src/Core/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs @@ -1,6 +1,7 @@ using System.Windows.Input; using Bit.App.Abstractions; using Bit.App.Controls; +using Bit.App.Models; using Bit.App.Utilities; using Bit.Core.Abstractions; using Bit.Core.Enums; @@ -45,6 +46,8 @@ namespace Bit.App.Pages private readonly IPasswordRepromptService _passwordRepromptService; private readonly IOrganizationService _organizationService; private readonly IPolicyService _policyService; + private readonly IConfigService _configService; + private readonly IEnvironmentService _environmentService; private readonly ILogger _logger; public GroupingsPageViewModel() @@ -61,6 +64,8 @@ namespace Bit.App.Pages _passwordRepromptService = ServiceContainer.Resolve("passwordRepromptService"); _organizationService = ServiceContainer.Resolve("organizationService"); _policyService = ServiceContainer.Resolve("policyService"); + _configService = ServiceContainer.Resolve(); + _environmentService = ServiceContainer.Resolve(); _logger = ServiceContainer.Resolve("logger"); Loading = true; @@ -104,6 +109,7 @@ namespace Bit.App.Pages public List Collections { get; set; } public List> NestedCollections { get; set; } + public AppOptions AppOptions { get; internal set; } protected override ICipherService cipherService => _cipherService; protected override IPolicyService policyService => _policyService; protected override IOrganizationService organizationService => _organizationService; @@ -699,5 +705,59 @@ namespace Bit.App.Pages var folders = decFolders.Where(f => _allCiphers.Any(c => c.FolderId == f.Id)).ToList(); return folders.Any() ? folders : null; } + + internal async Task CheckOrganizationUnassignedItemsAsync() + { + try + { + if (AppOptions?.HasJustLoggedInOrUnlocked != true) + { + return; + } + + AppOptions.HasJustLoggedInOrUnlocked = false; + + if (!await _configService.GetFeatureFlagBoolAsync(Core.Constants.UnassignedItemsBannerFlag) + || + !await _stateService.GetShouldCheckOrganizationUnassignedItemsAsync()) + { + return; + } + + var waitSyncTask = Task.Run(async () => + { + while (_syncService.SyncInProgress) + { + await Task.Delay(100); + } + }); + await waitSyncTask.WaitAsync(TimeSpan.FromMinutes(5)); + + if (!await _cipherService.VerifyOrganizationHasUnassignedItemsAsync()) + { + return; + } + + var message = _environmentService.SelectedRegion == Core.Enums.Region.SelfHosted + ? AppResources.OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong + : AppResources.OrganizationUnassignedItemsMessageUSEUDescriptionLong; + + var response = await _deviceActionService.DisplayAlertAsync(AppResources.Notice, + message, + null, + AppResources.RemindMeLater, + AppResources.Ok); + + if (response == AppResources.Ok) + { + await _stateService.SetShouldCheckOrganizationUnassignedItemsAsync(false); + } + } + catch (TimeoutException) { } + catch (Exception ex) + { + _logger.Exception(ex); + } + } } } diff --git a/src/Core/Resources/Localization/AppResources.Designer.cs b/src/Core/Resources/Localization/AppResources.Designer.cs index 3cecec4a9..5b28afc5d 100644 --- a/src/Core/Resources/Localization/AppResources.Designer.cs +++ b/src/Core/Resources/Localization/AppResources.Designer.cs @@ -4929,6 +4929,15 @@ namespace Bit.Core.Resources.Localization { } } + /// + /// Looks up a localized string similar to Notice. + /// + public static string Notice { + get { + return ResourceManager.GetString("Notice", resourceCulture); + } + } + /// /// Looks up a localized string similar to This account has two-step login set up, however, none of the configured two-step providers are supported on this device. Please use a supported device and/or add additional providers that are better supported across devices (such as an authenticator app).. /// @@ -5164,6 +5173,24 @@ namespace Bit.Core.Resources.Localization { } } + /// + /// Looks up a localized string similar to On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible.. + /// + public static string OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong { + get { + return ResourceManager.GetString("OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible.. + /// + public static string OrganizationUnassignedItemsMessageUSEUDescriptionLong { + get { + return ResourceManager.GetString("OrganizationUnassignedItemsMessageUSEUDescriptionLong", resourceCulture); + } + } + /// /// Looks up a localized string similar to Organization identifier. /// @@ -5795,6 +5822,15 @@ namespace Bit.Core.Resources.Localization { } } + /// + /// Looks up a localized string similar to Remind me later. + /// + public static string RemindMeLater { + get { + return ResourceManager.GetString("RemindMeLater", resourceCulture); + } + } + /// /// Looks up a localized string similar to Remove. /// diff --git a/src/Core/Resources/Localization/AppResources.fi.resx b/src/Core/Resources/Localization/AppResources.fi.resx index 636c39364..bfdb2bf88 100644 --- a/src/Core/Resources/Localization/AppResources.fi.resx +++ b/src/Core/Resources/Localization/AppResources.fi.resx @@ -560,7 +560,7 @@ Message shown when interacting with the server - Käytä salattua holviasi kirjautumalla sisään tai tai luo uusi tili. + Käytä salattua holviasi kirjautumalla sisään tai luo uusi tili. Hallinta diff --git a/src/Core/Resources/Localization/AppResources.it.resx b/src/Core/Resources/Localization/AppResources.it.resx index d899a46cf..2678f7032 100644 --- a/src/Core/Resources/Localization/AppResources.it.resx +++ b/src/Core/Resources/Localization/AppResources.it.resx @@ -2877,12 +2877,12 @@ Vuoi passare a questo account? Imposta un metodo di sblocco per modificare l'azione timeout cassaforte. - Duo two-step login is required for your account. + Per il tuo account è richiesta la verifica in due passaggi di Duo. - Follow the steps from Duo to finish logging in. + Segui i passaggi da Duo per completare l'accesso. - Launch Duo + Avvia Duo diff --git a/src/Core/Resources/Localization/AppResources.resx b/src/Core/Resources/Localization/AppResources.resx index 4d82ba0f7..312118517 100644 --- a/src/Core/Resources/Localization/AppResources.resx +++ b/src/Core/Resources/Localization/AppResources.resx @@ -2975,4 +2975,16 @@ Do you want to switch to this account? Your passkey will be saved to your Bitwarden vault for {0} + + Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible. + + + On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible. + + + Remind me later + + + Notice + diff --git a/src/Core/Resources/Localization/AppResources.ru.resx b/src/Core/Resources/Localization/AppResources.ru.resx index 7f38096de..564bc3406 100644 --- a/src/Core/Resources/Localization/AppResources.ru.resx +++ b/src/Core/Resources/Localization/AppResources.ru.resx @@ -2641,22 +2641,22 @@ Запомнить это устройство - Ключ доступа + Passkey - Ключи доступа + Passkeys Приложение - Редактирование ключа доступа невозможно, поскольку это приведет к его аннулированию + Редактирование passkey невозможно, поскольку это приведет к его аннулированию - Ключ доступа не будет скопирован + Passkey не будет скопирован - Ключ доступа не будет скопирован в клонированный элемент. Продолжить клонирование этого элемента? + Passkey не будет скопирован в клонированный элемент. Продолжить клонирование этого элемента? Скопировать приложение diff --git a/src/Core/Resources/Localization/AppResources.vi.resx b/src/Core/Resources/Localization/AppResources.vi.resx index 584115de3..f82778e28 100644 --- a/src/Core/Resources/Localization/AppResources.vi.resx +++ b/src/Core/Resources/Localization/AppResources.vi.resx @@ -1143,7 +1143,7 @@ Quá trình quét sẽ diễn ra tự động. Địa chỉ biểu tượng máy chủ - Tự động điền với Bitwarden + Tự động điền bằng Bitwarden Kho đã khóa diff --git a/src/Core/Services/ApiService.cs b/src/Core/Services/ApiService.cs index e1240ff23..44a9f414c 100644 --- a/src/Core/Services/ApiService.cs +++ b/src/Core/Services/ApiService.cs @@ -334,6 +334,11 @@ namespace Bit.Core.Services return SendAsync(HttpMethod.Put, string.Concat("/ciphers/", id, "/restore"), null, true, true); } + public Task HasUnassignedCiphersAsync() + { + return SendAsync(HttpMethod.Get, "/ciphers/has-unassigned-ciphers", null, true, true); + } + #endregion #region Attachments APIs diff --git a/src/Core/Services/CipherService.cs b/src/Core/Services/CipherService.cs index 65a021fbb..e22a5f5e9 100644 --- a/src/Core/Services/CipherService.cs +++ b/src/Core/Services/CipherService.cs @@ -835,6 +835,24 @@ namespace Bit.Core.Services await ClearCacheAsync(); } + public async Task VerifyOrganizationHasUnassignedItemsAsync() + { + var organizations = await _stateService.GetOrganizationsAsync(); + if (organizations?.Any() != true) + { + return false; + } + + try + { + return await _apiService.HasUnassignedCiphersAsync(); + } + catch (ApiException ex) when (ex.Error?.StatusCode == System.Net.HttpStatusCode.BadRequest) + { + return false; + } + } + // Helpers private async Task> MakeAttachmentKeyAsync(string organizationId, Cipher cipher = null, CipherView cipherView = null) diff --git a/src/Core/Services/StateService.cs b/src/Core/Services/StateService.cs index a6685d391..fbfeb18d3 100644 --- a/src/Core/Services/StateService.cs +++ b/src/Core/Services/StateService.cs @@ -1384,6 +1384,16 @@ namespace Bit.Core.Services await _storageMediatorService.SaveAsync(Constants.RegionEnvironment, value); } + public async Task GetShouldCheckOrganizationUnassignedItemsAsync(string userId = null) + { + return await _storageMediatorService.GetAsync(await ComposeKeyAsync(Constants.ShouldCheckOrganizationUnassignedItemsKey, userId)) ?? true; + } + + public async Task SetShouldCheckOrganizationUnassignedItemsAsync(bool shouldCheck, string userId = null) + { + await _storageMediatorService.SaveAsync(await ComposeKeyAsync(Constants.ShouldCheckOrganizationUnassignedItemsKey, userId), shouldCheck); + } + // Helpers [Obsolete("Use IStorageMediatorService instead")] diff --git a/src/Core/Utilities/ServiceContainer.cs b/src/Core/Utilities/ServiceContainer.cs index 7c525416e..3f8147a8e 100644 --- a/src/Core/Utilities/ServiceContainer.cs +++ b/src/Core/Utilities/ServiceContainer.cs @@ -45,6 +45,7 @@ namespace Bit.Core.Utilities var fileUploadService = new FileUploadService(apiService); var configService = new ConfigService(apiService, stateService, logger); var totpService = new TotpService(cryptoFunctionService); + var environmentService = new EnvironmentService(apiService, stateService, conditionedRunner); var cipherService = new CipherService(cryptoService, stateService, settingsService, apiService, fileUploadService, storageService, i18nService, () => searchService, configService, totpService, clipboardService, clearCipherCacheKey, allClearCipherCacheKeys); @@ -85,7 +86,6 @@ namespace Bit.Core.Utilities keyConnectorService, passwordGenerationService, policyService, deviceTrustCryptoService, passwordResetEnrollmentService); var exportService = new ExportService(folderService, cipherService, cryptoService); var auditService = new AuditService(cryptoFunctionService, apiService); - var environmentService = new EnvironmentService(apiService, stateService, conditionedRunner); var eventService = new EventService(apiService, stateService, organizationService, cipherService); var usernameGenerationService = new UsernameGenerationService(cryptoService, apiService, stateService); diff --git a/src/iOS.Autofill/Info.plist b/src/iOS.Autofill/Info.plist index 58f4be491..5f4581e77 100644 --- a/src/iOS.Autofill/Info.plist +++ b/src/iOS.Autofill/Info.plist @@ -11,7 +11,7 @@ CFBundleIdentifier com.8bit.bitwarden.autofill CFBundleShortVersionString - 2024.4.0 + 2024.4.1 CFBundleVersion 1 CFBundleLocalizations diff --git a/src/iOS.Autofill/iOS.Autofill.csproj b/src/iOS.Autofill/iOS.Autofill.csproj index a3ab038cf..2947ad3ee 100644 --- a/src/iOS.Autofill/iOS.Autofill.csproj +++ b/src/iOS.Autofill/iOS.Autofill.csproj @@ -195,4 +195,7 @@ + + + diff --git a/src/iOS.Extension/Info.plist b/src/iOS.Extension/Info.plist index 81214a1bf..42ec21d93 100644 --- a/src/iOS.Extension/Info.plist +++ b/src/iOS.Extension/Info.plist @@ -11,7 +11,7 @@ CFBundleIdentifier com.8bit.bitwarden.find-login-action-extension CFBundleShortVersionString - 2024.4.0 + 2024.4.1 CFBundleLocalizations en diff --git a/src/iOS.Extension/iOS.Extension.csproj b/src/iOS.Extension/iOS.Extension.csproj index d855ffde9..58a596b0f 100644 --- a/src/iOS.Extension/iOS.Extension.csproj +++ b/src/iOS.Extension/iOS.Extension.csproj @@ -147,4 +147,7 @@ Resources\ext-icon%403x.png + + + \ No newline at end of file diff --git a/src/iOS.ShareExtension/Info.plist b/src/iOS.ShareExtension/Info.plist index 49dd1466d..0c120a8d5 100644 --- a/src/iOS.ShareExtension/Info.plist +++ b/src/iOS.ShareExtension/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 2024.4.0 + 2024.4.1 CFBundleVersion 1 MinimumOSVersion diff --git a/src/iOS.ShareExtension/iOS.ShareExtension.csproj b/src/iOS.ShareExtension/iOS.ShareExtension.csproj index 5394ae9ae..429a91e17 100644 --- a/src/iOS.ShareExtension/iOS.ShareExtension.csproj +++ b/src/iOS.ShareExtension/iOS.ShareExtension.csproj @@ -110,4 +110,7 @@ + + + \ No newline at end of file diff --git a/src/watchOS/bitwarden/PrivacyInfo.xcprivacy b/src/watchOS/bitwarden/PrivacyInfo.xcprivacy new file mode 100644 index 000000000..5f229841b --- /dev/null +++ b/src/watchOS/bitwarden/PrivacyInfo.xcprivacy @@ -0,0 +1,41 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryDiskSpace + NSPrivacyAccessedAPITypeReasons + + E174.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + 1C8F.1 + + + + + diff --git a/src/watchOS/bitwarden/bitwarden.xcodeproj/project.pbxproj b/src/watchOS/bitwarden/bitwarden.xcodeproj/project.pbxproj index 32102d6bd..4c0bd271e 100644 --- a/src/watchOS/bitwarden/bitwarden.xcodeproj/project.pbxproj +++ b/src/watchOS/bitwarden/bitwarden.xcodeproj/project.pbxproj @@ -84,6 +84,8 @@ 1BDBFEB3290B5D07009C78C7 /* CoreDataHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BDBFEB2290B5D07009C78C7 /* CoreDataHelper.swift */; }; 1BF5F6DB29103066002DDC0C /* CipherService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF5F6DA29103066002DDC0C /* CipherService.swift */; }; 1BF5F6DE29103B86002DDC0C /* CipherServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF5F6DD29103B86002DDC0C /* CipherServiceMock.swift */; }; + B7A3ABF42BCEECD700A07178 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = B7A3ABF32BCEECD700A07178 /* PrivacyInfo.xcprivacy */; }; + B7A3ABF52BCEECD700A07178 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = B7A3ABF32BCEECD700A07178 /* PrivacyInfo.xcprivacy */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -267,6 +269,7 @@ B78CEC832A25455400539F0D /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = ""; }; B78CEC842A25455D00539F0D /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; B78CEC852A25456400539F0D /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; + B7A3ABF32BCEECD700A07178 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -322,6 +325,7 @@ 1B15612628B7F3D400610B9B = { isa = PBXGroup; children = ( + B7A3ABF32BCEECD700A07178 /* PrivacyInfo.xcprivacy */, 1B5BE45D295B472C00E0C323 /* GoogleService-Info.plist */, 1B15613128B7F3D400610B9B /* bitwarden */, 1B15614128B7F3D700610B9B /* bitwarden WatchKit App */, @@ -727,6 +731,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + B7A3ABF42BCEECD700A07178 /* PrivacyInfo.xcprivacy in Resources */, 1B15614328B7F3D800610B9B /* Assets.xcassets in Resources */, 1B5BE45F295B472C00E0C323 /* GoogleService-Info.plist in Resources */, ); @@ -739,6 +744,7 @@ 1B15615B28B7F3D900610B9B /* Preview Assets.xcassets in Resources */, 1B5BE460295B472C00E0C323 /* GoogleService-Info.plist in Resources */, 1B5AFF0729197822004478F9 /* Localizable.strings in Resources */, + B7A3ABF52BCEECD700A07178 /* PrivacyInfo.xcprivacy in Resources */, 1B15615828B7F3D900610B9B /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0;