Skip to content

[PM-38176] feat: Expose policies client SDK for policy evaluation#2727

Open
fedemkr wants to merge 7 commits into
mainfrom
PM-38176/expose-policies-client-sdk
Open

[PM-38176] feat: Expose policies client SDK for policy evaluation#2727
fedemkr wants to merge 7 commits into
mainfrom
PM-38176/expose-policies-client-sdk

Conversation

@fedemkr

@fedemkr fedemkr commented May 29, 2026

Copy link
Copy Markdown
Member

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-38176

📔 Objective

Delegates policy filtering to the Bitwarden SDK via PoliciesClient.filterByType
when the policiesInAcceptedState feature flag is enabled. When the flag is off,
the existing native filter is used unchanged.

@fedemkr fedemkr added the ai-review Request a Claude code review label May 29, 2026
@github-actions github-actions Bot added app:password-manager Bitwarden Password Manager app context t:feature labels May 29, 2026
@github-actions

github-actions Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

This PR delegates policy filtering to the Bitwarden SDK via PoliciesClient.filterByType behind the policiesInAcceptedState feature flag, with the legacy native filter retained for the flag-off path. The new PolicyService+SdkMapping.swift provides bidirectional iOS↔SDK mapping for Policy/PolicyView, Organization/OrganizationUserPolicyContext, and the enum types; ClientService exposes a policies(for:) accessor wired through MockClientBuilder and a new bespoke MockPoliciesClient. The previously-flagged critical issue (filter dropped on the SDK path) has been resolved — policiesApplyingToUser now forwards filter into sdkFilterPolicies, and dedicated tests cover the getMasterPasswordPolicyOptions and isSendHideEmailDisabledByPolicy filter paths. SDK-path tests also cover provider-user mapping, empty/error degradation, and cache invalidation via replacePoliciesNew.

Code Review Details

No new findings. Prior critical/suggested/question feedback on threads has been addressed in subsequent commits or answered by the author.

Comment on lines +215 to +219
return try await sdkFilterPolicies(
organizations: organizations,
policyType: policyType,
userId: userId,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: SDK path drops the caller's filter, producing incorrect results for filtered callers.

Details and fix

policiesApplyingToUser receives a filter: ((Policy) -> Bool)? argument (line 191) and forwards it to the native path via policiesForUser, but the SDK branch calls sdkFilterPolicies(organizations:policyType:userId:) without passing filter. Inside sdkFilterPolicies the parameter defaults to nil, so the caller's filter is silently dropped.

This changes behavior when policiesInAcceptedState is enabled. Two real callers are affected:

  • isSendHideEmailDisabledByPolicy() (line 495) passes { policy[.disableHideEmail]?.boolValue == true }. In the SDK path the filter is lost, so the method returns true for any active .sendOptions policy on the user — even when disableHideEmail is false or absent. The existing test test_isSendHideEmailDisabledByPolicy_optionDisabled only covers the native path and would not catch this regression.
  • getMasterPasswordPolicyOptions() (line 414) passes { $0.data != nil }. In the SDK path, policies with data == nil are no longer pre-filtered, which (while less severe) is a behavior divergence from the legacy path.

Fix:

Suggested change
return try await sdkFilterPolicies(
organizations: organizations,
policyType: policyType,
userId: userId,
)
do {
return try await sdkFilterPolicies(
organizations: organizations,
policyType: policyType,
userId: userId,
filter: filter,
)
} catch {

Also recommend adding SDK-path test coverage that exercises a non-nil filter (e.g. mirror test_isSendHideEmailDisabledByPolicy_optionDisabled and test_getMasterPasswordPolicyOptions_* with configService.featureFlagsBool[.policiesInAcceptedState] = true).

@fedemkr fedemkr marked this pull request as ready for review May 29, 2026 15:41
@fedemkr fedemkr requested review from a team and matt-livefront as code owners May 29, 2026 15:41
@codecov

codecov Bot commented May 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.60360% with 81 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.26%. Comparing base (1b650ca) to head (b9fc9c5).

Files with missing lines Patch % Lines
...hared/UI/Billing/PremiumPlan/PremiumPlanView.swift 45.00% 11 Missing ⚠️
...Core/Vault/Services/PolicyService+SdkMapping.swift 91.00% 9 Missing ⚠️
...enKit/Core/Vault/Services/VaultClientService.swift 0.00% 7 Missing ⚠️
...d/UI/Tools/Send/SendItem/SendItemCoordinator.swift 12.50% 7 Missing ⚠️
...EditItem/AddEditCardItem/AddEditCardItemView.swift 0.00% 7 Missing ⚠️
...lt/Services/Fido2CredentialStoreServiceTests.swift 95.00% 5 Missing ⚠️
...tem/AddEditSendItem/AddEditSendItemProcessor.swift 37.50% 5 Missing ⚠️
...ult/Vault/AutofillList/VaultAutofillListView.swift 0.00% 4 Missing ⚠️
...ed/UI/Vault/Vault/VaultGroup/VaultGroupState.swift 33.33% 4 Missing ⚠️
.../Vault/VaultItem/AddEditItem/AddEditItemView.swift 42.85% 4 Missing ⚠️
... and 9 more
Additional details and impacted files
@@                                Coverage Diff                                 @@
##           PM-37962/provider-organizations-is-provider-user    #2727    +/-   ##
==================================================================================
  Coverage                                             87.26%   87.26%            
==================================================================================
  Files                                                  1918     1919     +1     
  Lines                                                172394   173206   +812     
==================================================================================
+ Hits                                                 150436   151151   +715     
- Misses                                                21958    22055    +97     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@fedemkr fedemkr changed the base branch from main to PM-37962/provider-organizations-is-provider-user May 29, 2026 15:41
@fedemkr fedemkr requested review from a team as code owners May 29, 2026 15:41
@fedemkr fedemkr changed the base branch from PM-37962/provider-organizations-is-provider-user to main May 29, 2026 15:42
@fedemkr fedemkr changed the base branch from main to PM-37962/provider-organizations-is-provider-user May 29, 2026 15:44
@fedemkr fedemkr added the hold This shouldn't be merged yet label May 29, 2026
@fedemkr fedemkr marked this pull request as draft May 29, 2026 15:46
@fedemkr fedemkr marked this pull request as ready for review May 29, 2026 18:51
Comment on lines 26 to 27
XCTAssertFalse(Organization.fixture(type: .custom).isAdmin)
XCTAssertFalse(Organization.fixture(type: .custom).isAdmin)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎨 SUGGESTED: Duplicate assertion — both lines check .custom after the .manager.custom rename.

Details and fix

Line 27 was previously XCTAssertFalse(Organization.fixture(type: .manager).isAdmin) and was rewritten to .custom when the enum case was removed. The result is two identical assertions on adjacent lines that add no coverage. Drop the duplicate:

Suggested change
XCTAssertFalse(Organization.fixture(type: .custom).isAdmin)
XCTAssertFalse(Organization.fixture(type: .custom).isAdmin)
XCTAssertFalse(Organization.fixture(type: .custom).isAdmin)

The same find/replace pattern was applied correctly in test_canManagePolicies and test_isExemptFromPolicies (each ends up with a single .custom assertion); only test_isAdmin ended up duplicated.

Comment thread BitwardenShared/Core/Vault/Models/Enum/OrganizationUserType.swift
@fedemkr fedemkr removed the hold This shouldn't be merged yet label Jun 1, 2026
@fedemkr fedemkr force-pushed the PM-37962/provider-organizations-is-provider-user branch from 5764120 to 561474d Compare June 4, 2026 20:08
Base automatically changed from PM-37962/provider-organizations-is-provider-user to main June 8, 2026 15:50
@fedemkr fedemkr force-pushed the PM-38176/expose-policies-client-sdk branch from 04bd2e7 to 3ed1311 Compare June 8, 2026 17:02
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Warning

@fedemkr Uploading code coverage report failed. Please check the "Upload to codecov.io" step of Process Test Reports job for more details.

import BitwardenSdk
import Foundation

class MockPoliciesClient: PoliciesClientProtocol {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ Could this use AutoMockable?

var mockPlatform: MockPlatformClientService
var mockPlatformIsPreAuth = false
var mockPolicies: MockPoliciesClient
var policiesError: Error?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ Can you alphabetize this?

/// - Parameter userId: The user ID mapped to the client instance.
/// - Returns: A `PoliciesClientProtocol` for policy data tasks.
///
func policies(for userId: String?) async throws -> PoliciesClientProtocol

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ Can you alphabetize this (there's a few instances down below too)?

}

/// `policies(for:)` returns a non-nil `PoliciesClientProtocol` for every user.
func test_policies() async throws {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ Can you alphabetize this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Request a Claude code review app:password-manager Bitwarden Password Manager app context t:feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants