Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AC-1698] Check if a user has 2FA enabled more efficiently #4524

Merged
merged 39 commits into from
Aug 8, 2024

Conversation

r-tome
Copy link
Contributor

@r-tome r-tome commented Jul 17, 2024

🎟️ Tracking

https://bitwarden.atlassian.net/browse/AC-1698

📔 Objective

The members page loads slowly for large organizations because the database calls to check each user's 2FA status are not optimized.

Current code flow:
OrganizationUsersController.Get has a looped async call to userService.TwoFactorIsEnabledAsync (per user): https://github.com/bitwarden/server/blob/master/src/Api/Controllers/OrganizationUsersController.cs#L93-L94
that calls CanAccessPremium if they have a premium 2FA method (e.g. yubikey)
that calls hasPremiumFromOrganization
that calls the db to get every orgUser for that user
So for example, if an org has 500 users with yubikey enabled, it’ll hit the database 500 times for the 1 request.
We need a more efficient way to check this for many orgUsers at once, preferably starting with their common organization.

This PR improves performance by making a single database call to check 2FA status for multiple users at once.

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 Reviewer guidelines

  • 👍 (:+1:) or similar for great changes
  • 📝 (:memo:) or ℹ️ (:information_source:) for notes or general info
  • ❓ (:question:) for questions
  • 🤔 (:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
  • 🎨 (:art:) for suggestions / improvements
  • ❌ (:x:) or ⚠️ (:warning:) for more significant problems or concerns needing attention
  • 🌱 (:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt
  • ⛏ (:pick:) for minor or nitpick changes

… premium access by organization ID

The code changes include:
- Addition of a new stored procedure [dbo].[OrganizationUserUserDetailsWithPremiumAccess_ReadByOrganizationId] to read organization user details with premium access by organization ID
- Modification of the IUserService interface to include an optional parameter for checking two-factor authentication with premium access
- Modification of the UserService class to handle the new optional parameter in the TwoFactorIsEnabledAsync method
- Addition of a new method GetManyDetailsWithPremiumAccessByOrganizationAsync in the IOrganizationUserRepository interface to retrieve organization user details with premium access by organization ID
- Addition of a new view [dbo].[OrganizationUserUserDetailsWithPremiumAccessView] to retrieve organization user details with premium access
Copy link

codecov bot commented Jul 17, 2024

Codecov Report

Attention: Patch coverage is 45.15366% with 232 lines in your changes missing coverage. Please review.

Project coverage is 41.83%. Comparing base (19dc7c3) to head (fa708e3).

Files Patch % Lines
...nConsole/Services/Implementations/PolicyService.cs 10.71% 49 Missing and 1 partial ⚠️
src/Core/Models/Data/UserWithCalculatedPremium.cs 4.25% 45 Missing ⚠️
...le/Services/Implementations/OrganizationService.cs 73.48% 28 Missing and 7 partials ⚠️
...Console/Controllers/OrganizationUsersController.cs 11.76% 29 Missing and 1 partial ⚠️
...ure.EntityFramework/Repositories/UserRepository.cs 0.00% 15 Missing ⚠️
src/Admin/Views/Users/Index.cshtml 0.00% 12 Missing ⚠️
...minConsole/Public/Controllers/MembersController.cs 45.45% 11 Missing and 1 partial ⚠️
...rFeatures/TwoFactorAuth/TwoFactorIsEnabledQuery.cs 85.00% 7 Missing and 5 partials ⚠️
src/Admin/Controllers/UsersController.cs 0.00% 11 Missing ⚠️
...frastructure.Dapper/Repositories/UserRepository.cs 0.00% 10 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4524      +/-   ##
==========================================
+ Coverage   41.74%   41.83%   +0.08%     
==========================================
  Files        1286     1288       +2     
  Lines       60671    61076     +405     
  Branches     5566     5613      +47     
==========================================
+ Hits        25329    25552     +223     
- Misses      34160    34331     +171     
- Partials     1182     1193      +11     

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

Copy link
Contributor

github-actions bot commented Jul 17, 2024

Logo
Checkmarx One – Scan Summary & Detailsf620cde3-084d-4e22-ab90-3bd3c2623759

New Issues

Severity Issue Source File / Package Checkmarx Insight
MEDIUM CSRF /src/Api/Controllers/CollectionsController.cs: 143 Attack Vector
MEDIUM CSRF /src/Billing/Controllers/PayPalController.cs: 66 Attack Vector
MEDIUM Privacy_Violation /src/Api/Auth/Controllers/TwoFactorController.cs: 488 Attack Vector
MEDIUM Privacy_Violation /src/Api/Auth/Controllers/WebAuthnController.cs: 178 Attack Vector
MEDIUM Privacy_Violation /src/Api/Controllers/DevicesController.cs: 133 Attack Vector
MEDIUM Privacy_Violation /src/Api/Auth/Controllers/AccountsController.cs: 412 Attack Vector
MEDIUM Privacy_Violation /src/Api/Vault/Controllers/CiphersController.cs: 953 Attack Vector
MEDIUM Privacy_Violation /src/Api/Auth/Controllers/AccountsController.cs: 847 Attack Vector
MEDIUM Privacy_Violation /src/Api/Auth/Controllers/AccountsController.cs: 829 Attack Vector
MEDIUM Privacy_Violation /src/Api/Auth/Controllers/AccountsController.cs: 549 Attack Vector
MEDIUM Privacy_Violation /src/Api/AdminConsole/Controllers/OrganizationsController.cs: 260 Attack Vector
MEDIUM Privacy_Violation /src/Api/Controllers/DevicesController.cs: 159 Attack Vector
MEDIUM Privacy_Violation /src/Api/AdminConsole/Controllers/OrganizationsController.cs: 376 Attack Vector
MEDIUM Privacy_Violation /src/Api/AdminConsole/Controllers/OrganizationsController.cs: 429 Attack Vector
MEDIUM Privacy_Violation /src/Core/Auth/Services/Implementations/AuthRequestService.cs: 147 Attack Vector
LOW Log_Forging /src/Api/Auth/Controllers/WebAuthnController.cs: 68 Attack Vector
LOW Log_Forging /src/Api/Auth/Controllers/WebAuthnController.cs: 153 Attack Vector
LOW Log_Forging /src/Api/Auth/Controllers/WebAuthnController.cs: 85 Attack Vector
LOW Log_Forging /src/Api/Auth/Controllers/AccountsController.cs: 839 Attack Vector
LOW Log_Forging /src/Api/Controllers/DevicesController.cs: 124 Attack Vector
LOW Log_Forging /src/Api/Auth/Controllers/AccountsController.cs: 541 Attack Vector
LOW Log_Forging /src/Api/Auth/Controllers/AccountsController.cs: 821 Attack Vector
LOW Log_Forging /src/Api/Vault/Controllers/CiphersController.cs: 945 Attack Vector
LOW Log_Forging /src/Api/Auth/Controllers/AccountsController.cs: 404 Attack Vector
LOW Log_Forging /src/Api/AdminConsole/Controllers/OrganizationsController.cs: 240 Attack Vector
LOW Log_Forging /src/Api/Controllers/DevicesController.cs: 150 Attack Vector
LOW Log_Forging /src/Api/AdminConsole/Controllers/OrganizationsController.cs: 341 Attack Vector
LOW Log_Forging /src/Api/AdminConsole/Controllers/OrganizationsController.cs: 404 Attack Vector
LOW Open_Redirect /src/Admin/Auth/Controllers/LoginController.cs: 50 Attack Vector

Fixed Issues

Severity Issue Source File / Package
MEDIUM CSRF /src/Billing/Controllers/StripeController.cs: 130
MEDIUM CSRF /src/Billing/Controllers/StripeController.cs: 117
MEDIUM CSRF /src/Api/AdminConsole/Public/Controllers/MembersController.cs: 89
MEDIUM CSRF /src/Api/AdminConsole/Controllers/OrganizationUsersController.cs: 217
MEDIUM Privacy_Violation /src/Core/Auth/Services/Implementations/AuthRequestService.cs: 158

@r-tome r-tome marked this pull request as ready for review July 30, 2024 15:33
@r-tome r-tome requested review from a team as code owners July 30, 2024 15:33
src/Core/Models/Data/UserDetails.cs Outdated Show resolved Hide resolved
src/Core/Repositories/IUserRepository.cs Show resolved Hide resolved
src/Core/Services/IUserService.cs Outdated Show resolved Hide resolved
src/Core/Repositories/IUserRepository.cs Outdated Show resolved Hide resolved
@@ -0,0 +1,31 @@
CREATE OR ALTER PROCEDURE [dbo].[User_ReadByIdsWithCalculatedPremium]
@Ids AS [dbo].[GuidIdArray] READONLY
AS
Copy link
Contributor

@rkac-bw rkac-bw Aug 6, 2024

Choose a reason for hiding this comment

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

Not sure if in scope, but we have been moving away from using data types to pass in arrays, now we would pass in json :

CREATE OR ALTER PROCEDURE [dbo].[User_ReadByIdsWithCalculatedPremium]
    @Ids NVARCHAR(MAX)
AS
BEGIN
    SET NOCOUNT ON;


    -- Declare a table variable to hold the parsed JSON data
    DECLARE @ParsedIds TABLE (Id UNIQUEIDENTIFIER);

    -- Parse the JSON input into the table variable
    INSERT INTO @ParsedIds (Id)
    SELECT value
    FROM OPENJSON(@Ids);

    -- Check if the input table is empty
    IF (SELECT COUNT(1) FROM @ParsedIds) < 1
    BEGIN
        RETURN(-1);
    END

        -- Main query to fetch user details and calculate premium access
        SELECT
            U.*,
            CASE
                WHEN U.[Premium] = 1
                    OR EXISTS (
                        SELECT 1
                        FROM [dbo].[OrganizationUser] OU
                        JOIN [dbo].[Organization] O ON OU.[OrganizationId] = O.[Id]
                        WHERE OU.[UserId] = U.[Id]
                          AND O.[UsersGetPremium] = 1
                          AND O.[Enabled] = 1
                    )
                    THEN 1
                ELSE 0
            END AS HasPremiumAccess
        FROM
            [dbo].[UserView] U
        WHERE
            U.[Id] IN (SELECT [Id] FROM @ParsedIds);
    
END;`''''

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've implemented that change. Thanks for providing the updated sproc!

rkac-bw
rkac-bw previously approved these changes Aug 7, 2024
Copy link
Contributor

@rkac-bw rkac-bw left a comment

Choose a reason for hiding this comment

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

lgtm

vincentsalucci
vincentsalucci previously approved these changes Aug 7, 2024
shane-melton
shane-melton previously approved these changes Aug 7, 2024
jlf0dev
jlf0dev previously approved these changes Aug 8, 2024
@r-tome r-tome requested review from shane-melton, rkac-bw, vincentsalucci and jlf0dev and removed request for eliykat August 8, 2024 14:09
Copy link
Member

@patrick-bitwarden patrick-bitwarden left a comment

Choose a reason for hiding this comment

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

Approving for AC after conflict resolution

@r-tome r-tome merged commit 8d69bb0 into main Aug 8, 2024
52 checks passed
@r-tome r-tome deleted the ac/ac-1698/members-page-optimizations branch August 8, 2024 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants