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

fix: Use service resource org, allow admin-scope to fetch/update dialogs #1529

Merged
merged 3 commits into from
Nov 26, 2024

Conversation

elsand
Copy link
Collaborator

@elsand elsand commented Nov 25, 2024

Description

This implements a proper handling of serviceprovider.admin-scope, where the "org"-value for the actual service resource is used instead of always being "digdir".

This also maintains the possibility for the admin-scope-wielder to access and update the dialog afterwards. The search-endpoint is however not changed (will only display actually owned dialogs, and requiring search-scope)

Related Issue(s)

Verification

  • Your code builds clean without any errors or warnings
  • Manual testing done (required)
  • Relevant automated test added (if you find this hard, leave it and we'll help out)

Documentation

  • Documentation is updated (either in docs-directory, Altinnpedia or a separate linked PR in altinn-studio-docs., if applicable)

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a new property OwnOrgShortName to enhance resource information.
    • Introduced conditional filtering in various query handlers to improve access control based on user roles.
    • Expanded testing coverage for service owners with admin capabilities.
  • Bug Fixes

    • Improved error handling for missing organization information in dialog creation.
  • Documentation

    • Updated test setup to reflect changes in dependencies for dialog creation tests.
  • Chores

    • Modified API call in tests to retrieve a larger number of dialog items.

@elsand elsand requested a review from a team as a code owner November 25, 2024 17:51
Copy link
Contributor

coderabbitai bot commented Nov 25, 2024

📝 Walkthrough
📝 Walkthrough
📝 Walkthrough

Walkthrough

This pull request introduces several modifications across various classes and interfaces within the Digdir.Domain.Dialogporten.Application and Digdir.Domain.Dialogporten.Infrastructure namespaces. Key changes include the addition of a new property OwnOrgShortName to the ServiceResourceInformation record and the introduction of a WhereIf extension method for conditional filtering based on user roles in several query handlers. Additionally, dependencies have been updated in the CreateDialogCommandHandler, and new constants have been added to enhance resource information handling.

Changes

File Path Change Summary
src/Digdir.Domain.Dialogporten.Application/Externals/IResourceRegistry.cs - Added property OwnOrgShortName to ServiceResourceInformation.
- Updated constructor of ServiceResourceInformation to accept four parameters.
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetActivityQuery.cs - Modified GetActivityQueryHandler to use WhereIf for conditional filtering based on user role.
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Search/SearchActivityQuery.cs - Updated SearchActivityQueryHandler to utilize WhereIf for filtering based on user role.
- Added import for WhereIf.
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/GetTransmissionQuery.cs - Modified GetTransmissionQueryHandler to use WhereIf for conditional filtering based on user role.
- Added import for WhereIf.
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/SearchTransmissionQuery.cs - Updated SearchTransmissionQueryHandler to use WhereIf for filtering based on user role.
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs - Replaced IUserOrganizationRegistry with IResourceRegistry.
- Updated logic for obtaining organization short name.
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs - Modified filtering logic in UpdateDialogCommandHandler to use WhereIf for user role-based filtering.
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogQuery.cs - Updated GetDialogQueryHandler to use WhereIf for conditional filtering based on user role.
- Enhanced error handling structure.
src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/LocalDevelopmentResourceRegistry.cs - Added constant LocalOrgShortName.
- Updated methods to include LocalOrgShortName in ServiceResourceInformation instantiation.
src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/ResourceRegistryClient.cs - Added required property OrgCode to CompetentAuthority.
- Updated FetchServiceResourceInformation to include OrgCode in ServiceResourceInformation constructor.
tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs - Replaced IUserOrganizationRegistry with IResourceRegistry in tests.
- Updated test setup to accommodate new resource registry logic.
tests/k6/common/sentinel.js - Updated API call in getSO function to include Limit=1000 in the query string.
tests/k6/tests/serviceowner/authorization.js - Added variable validSoAdmin for testing admin permissions.
- Updated logging to indicate when tests are executed with admin privileges.

Possibly related issues

Possibly related PRs

Suggested reviewers

  • MagnusSandgren
  • arealmaas

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@elsand elsand changed the title Use service resource org, allow admin-scope to fetch/update dialogs fix: Use service resource org, allow admin-scope to fetch/update dialogs Nov 25, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (7)
tests/k6/common/sentinel.js (1)

15-15: Add a comment explaining the limit choice.

Consider adding a comment explaining why 1000 was chosen as the limit value. This helps future maintainers understand if this is an arbitrary value or if there's a specific reason for it.

-            let r = getSO('dialogs/?Limit=1000&Search=' + sentinelValue + continuationToken, null, tokenOptions);
+            // Fetch 1000 items per request to minimize API calls during cleanup while staying within safe memory limits
+            let r = getSO('dialogs/?Limit=1000&Search=' + sentinelValue + continuationToken, null, tokenOptions);
src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/LocalDevelopmentResourceRegistry.cs (1)

Line range hint 7-7: Consider adding documentation about security implications

Since this implementation is used for local development and relates to admin-scope changes, consider adding XML documentation to:

  1. Clearly mark this as a development-only implementation
  2. Document the security implications and differences from production behavior
  3. Explain the relationship between organization IDs and admin-scope
tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs (1)

Line range hint 1-100: Add test coverage for admin-scope scenarios

The current test file only covers forbidden scenarios. Given that the PR's main objective is to "enhance the handling of the serviceprovider.admin-scope" and use the service resource's org value, we should add test coverage for:

  1. Successful dialog creation with admin-scope
  2. Verification that the correct organization value from the service resource is used

Would you like me to help generate the additional test cases? Here's a suggested structure:

[Fact]
public async Task CreateDialogCommand_Should_Succeed_With_Admin_Scope()
{
    // Arrange
    // ... setup similar to existing tests ...
    
    serviceAuthorizationSub
        .AuthorizeServiceResources(Arg.Any<DialogEntity>(), Arg.Any<CancellationToken>())
        .Returns(new Success());

    var expectedOrg = "test-org";
    var serviceResource = $"resource/{expectedOrg}/service";
    var createCommand = DialogGenerator.GenerateSimpleFakeDialog() with 
    { 
        ServiceResource = serviceResource 
    };

    resourceRegistrySub
        .GetResourceInformation(serviceResource, Arg.Any<CancellationToken>())
        .Returns(new ServiceResourceInformation(serviceResource, "foo", "912345678", expectedOrg));

    // ... rest of the test setup ...

    // Act
    var result = await commandHandler.Handle(createCommand, CancellationToken.None);

    // Assert
    Assert.True(result.IsT0); // Success
    // Add assertions to verify the org value was used correctly
}
tests/k6/tests/serviceowner/authorization.js (2)

29-31: Consider modernizing the scope check.

The logging enhancement is good, but the code can be improved:

  1. Use optional chaining to safely access nested properties
  2. Use includes() instead of indexOf() for better readability

Here's a suggested improvement:

-        if (tokenOptions && tokenOptions["scopes"] && tokenOptions["scopes"].indexOf("admin") > -1) {
+        if (tokenOptions?.scopes?.includes("admin")) {
             logSuffix += " (admin)";
         }
🧰 Tools
🪛 Biome (1.9.4)

[error] 29-29: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


Line range hint 109-113: Consider adding search tests with admin scope.

The file includes tests for search without proper scopes, but it might be valuable to add test cases that verify search behavior with admin scope to ensure complete coverage of the new functionality.

Would you like me to help generate additional test cases for search functionality with admin scope?

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogQuery.cs (1)

Line range hint 66-88: Consider performance optimization for eager loading.

While the explicit ordering is necessary for consistency, consider these optimizations:

  1. Split the query into smaller chunks if not all related data is always needed
  2. Consider adding a sparse fieldset parameter to load only required includes
  3. Add appropriate indexes for the OrderBy clauses

This could significantly improve performance for large datasets.

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs (1)

85-88: Consider adding logging for authorization decisions

For better security auditing and troubleshooting, consider adding debug-level logging when the service owner admin bypass is applied.

            .IgnoreQueryFilters()
+           .WithLogDebug(_logger, x => $"Service owner admin bypass: {_userResourceRegistry.IsCurrentUserServiceOwnerAdmin()}")
            .WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource))
            .FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between cc87c63 and 298b682.

📒 Files selected for processing (13)
  • src/Digdir.Domain.Dialogporten.Application/Externals/IResourceRegistry.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetActivityQuery.cs (2 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Search/SearchActivityQuery.cs (2 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/GetTransmissionQuery.cs (2 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/SearchTransmissionQuery.cs (2 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs (3 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogQuery.cs (2 hunks)
  • src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/LocalDevelopmentResourceRegistry.cs (3 hunks)
  • src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/ResourceRegistryClient.cs (1 hunks)
  • tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs (4 hunks)
  • tests/k6/common/sentinel.js (1 hunks)
  • tests/k6/tests/serviceowner/authorization.js (2 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
tests/k6/tests/serviceowner/authorization.js

[error] 29-29: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (23)
src/Digdir.Domain.Dialogporten.Application/Externals/IResourceRegistry.cs (2)

17-17: LGTM! Property addition aligns with PR objectives.

The new immutable property OwnOrgShortName is well-designed for storing the organization short name from the service resource.


20-26: Consider adding input validation and verify case sensitivity requirements.

While the implementation is functional, consider these improvements:

  1. Add null/empty validation for the new parameter
  2. Verify if case normalization is required for OwnOrgShortName as it might affect integration with external systems

Here's a suggested improvement:

 public ServiceResourceInformation(string resourceId, string resourceType, string ownerOrgNumber, string ownOrgShortName)
 {
+    ArgumentException.ThrowIfNullOrEmpty(resourceId);
+    ArgumentException.ThrowIfNullOrEmpty(resourceType);
+    ArgumentException.ThrowIfNullOrEmpty(ownerOrgNumber);
+    ArgumentException.ThrowIfNullOrEmpty(ownOrgShortName);
+
     ResourceId = resourceId.ToLowerInvariant();
     ResourceType = resourceType.ToLowerInvariant();
     OwnerOrgNumber = ownerOrgNumber.ToLowerInvariant();
     OwnOrgShortName = ownOrgShortName.ToLowerInvariant();
 }

Let's verify the usage of this constructor to ensure all callers are updated:

tests/k6/common/sentinel.js (1)

15-15: Verify the impact of increased limit.

The change to fetch 1000 items at once will reduce the number of API calls needed for cleanup, but please verify that this limit:

  1. Doesn't cause performance issues with the API
  2. Doesn't exceed memory constraints in the k6 test runner
  3. Is aligned with any rate limiting on the API
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Search/SearchActivityQuery.cs (2)

3-3: LGTM!

Clean addition of the required extension namespace.


41-41: Verify error handling and consistent behavior across endpoints.

The conditional filtering logic looks good and aligns with the PR objective of allowing admin-scope access while maintaining proper filtering for regular users.

Let's verify the implementation:

✅ Verification successful

Based on the shell script results, I can now generate the final response:

Error handling and access control are consistently implemented across endpoints

The codebase shows consistent patterns for resource access control:

  • All query handlers properly fetch user resource IDs via GetCurrentUserResourceIds
  • Null handling is managed through the UserResourceRegistry implementation
  • The WhereIf conditional filtering is consistently used with admin checks
  • Resource-based authorization is uniformly applied across Dialog, Activity, and related endpoints
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify consistent WhereIf usage and error handling across query handlers

# Check for consistent WhereIf usage pattern across query handlers
echo "Checking for consistent WhereIf usage pattern:"
ast-grep --pattern 'WhereIf(!$userRegistry.IsCurrentUserServiceOwnerAdmin(), $$$)'

# Check for potential null handling of resourceIds
echo "Checking resourceIds null handling:"
rg -A 5 "GetCurrentUserResourceIds"

# Check for similar query patterns in other handlers
echo "Checking for similar query patterns:"
ast-grep --pattern 'class $_QueryHandler : IRequestHandler<$_, $_> {
  $$$
  .WhereIf($_, $$$)
  $$$
}'

Length of output: 15447

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetActivityQuery.cs (2)

3-3: LGTM: Required import for extension methods

The added import statement is correctly placed and necessary for the new WhereIf extension method usage.


47-47: LGTM: Conditional filtering aligns with admin-scope requirements

The implementation correctly uses WhereIf to bypass resource filtering for admin users while maintaining restrictions for regular users. This aligns with the PR objective of enhancing serviceprovider.admin-scope handling.

Let's verify the test coverage for this change:

Consider adding a code comment explaining the admin bypass behavior for future maintainers:

+            // Bypass resource filtering for service owner admins
             .WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource))
✅ Verification successful

Let's also check if there are any existing integration tests:

And verify the UserResourceRegistry usage pattern:


Verified: Admin bypass pattern is consistently implemented across the codebase

The implementation in GetActivityQuery follows a well-established pattern used consistently throughout the codebase for handling service owner admin access:

  • The same WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource)) pattern is used in multiple query handlers including GetDialogQuery, SearchActivityQuery, and GetTransmissionQuery
  • Integration tests exist for both ServiceOwner and EndUser contexts in ActivityLogTests.cs
  • The IsCurrentUserServiceOwnerAdmin() check is properly implemented in UserResourceRegistry based on the service owner admin scope
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for related test files and their content
fd -e cs -g "*GetActivityQuery*Test*" | while read -r file; do
    echo "=== $file ==="
    rg -A 10 "IsCurrentUserServiceOwnerAdmin|WhereIf" "$file"
done

Length of output: 54


Script:

#!/bin/bash
# Let's try a broader search to find test files related to GetActivity
fd -e cs -g "*Activity*Test*" | while read -r file; do
    echo "=== $file ==="
    rg -A 10 "IsCurrentUserServiceOwnerAdmin|WhereIf|GetActivity" "$file"
done

# Also search for the implementation to understand the test context better
rg -l "class GetActivityQuery" 

Length of output: 1931


Script:

#!/bin/bash
rg -l "Integration.*GetActivity|GetActivity.*Integration"

Length of output: 59


Script:

#!/bin/bash
# Find similar patterns of IsCurrentUserServiceOwnerAdmin usage
rg "IsCurrentUserServiceOwnerAdmin\(\)" -A 5

Length of output: 8527

src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/LocalDevelopmentResourceRegistry.cs (3)

39-39: LGTM!

The changes correctly implement the new ServiceResourceInformation signature while maintaining the expected behavior for local development.


12-12: Verify if "ttd" is the correct test organization identifier

While this is a local development implementation, we should ensure that "ttd" is the correct identifier for test organization.

✅ Verification successful

"ttd" is correctly used as the test organization identifier

The verification shows consistent usage of "ttd" across the codebase:

  • Used in test assertions for Altinn resource URIs (app_ttd_some-app)
  • Used in test cases for organization attributes (urn:altinn:org)
  • Consistently represents a test organization in both test and local development contexts
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other occurrences of "ttd" to verify consistency
rg "ttd" --type cs -C 2

Length of output: 7178


Line range hint 53-58: Verify if OwnOrgShortName should be part of equality comparison

The equality comparer only checks ResourceId and OwnerOrgNumber. Verify if OwnOrgShortName should also be included in the comparison, especially if it can differ for the same OwnerOrgNumber.

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/SearchTransmissionQuery.cs (2)

3-3: LGTM!

The addition of the Extensions namespace is appropriate for the new WhereIf functionality.


51-51: Implementation looks correct, with some optimization opportunities.

The conditional filtering using WhereIf correctly implements the requirement to allow admin-scope users to access all dialogs while maintaining resource-based filtering for regular users.

Consider splitting this complex query into a separate method for better maintainability, given the extensive Include chain.

Let's verify the admin role check implementation:

✅ Verification successful

Admin role check implementation is secure and correctly used

The implementation is properly secured through OAuth2 scope validation:

  • IsCurrentUserServiceOwnerAdmin() checks for the digdir:dialogporten.serviceprovider.admin scope
  • The scope check is consistently used across all service owner endpoints
  • The admin check is properly integrated with the authorization system via IServiceResourceAuthorizer

The conditional filtering in the query is correctly implemented and aligns with the security model:

  • Regular users are filtered by their assigned resources
  • Admin users get full access through the scope-based bypass
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the IsCurrentUserServiceOwnerAdmin implementation
# Expected: Find the implementation to ensure it properly checks admin scope

# Search for the admin check implementation
ast-grep --pattern 'IsCurrentUserServiceOwnerAdmin()' 

# Look for related tests
rg -l "IsCurrentUserServiceOwnerAdmin.*Test"

Length of output: 102


Script:

#!/bin/bash
# Let's try a broader search for the interface and implementation
# Search for interface definition
rg -A 5 "interface.*UserResourceRegistry"

# Search for implementation class
rg -A 5 "class.*UserResourceRegistry"

# Search for any admin-related methods in the codebase
rg -A 3 "IsCurrentUser.*Admin"

# Search for any admin scope checks
rg -A 3 "ServiceOwnerAdmin"

Length of output: 14703

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/GetTransmissionQuery.cs (1)

3-3: LGTM: Import addition is appropriate

The added import for Common.Extensions is necessary for the WhereIf extension method and follows proper C# conventions.

tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs (1)

73-73: 🛠️ Refactor suggestion

Verify test relevance with new admin-scope behavior

Similar to the first test method, this test contains legacy dependencies and hardcoded organization values. Additionally, given that the PR objectives mention allowing admin-scope to fetch/update dialogs, we should verify if this non-owner test case is still relevant or needs modification.

Let's verify the admin-scope handling in the actual implementation:

Apply similar cleanup as suggested for the first test method:

  1. Remove unused userResourceRegistrySub
  2. Use meaningful organization values from the service resource
  3. Add test cases for admin-scope scenarios if not covered elsewhere

Also applies to: 86-88

tests/k6/tests/serviceowner/authorization.js (2)

22-23: LGTM! Test matrix covers all scenarios.

The permutations array correctly includes test cases for both regular and admin service owners, with appropriate positive and negative test cases.


8-8: LGTM! Verify scope string consistency across codebase.

The admin scope configuration looks correct and aligns with the PR objectives.

Let's verify the scope string format is consistent across the codebase:

✅ Verification successful

Scope string format is consistent across the codebase

The verification confirms that the scope string format digdir:dialogporten.serviceprovider and its extensions (.admin, .search) are consistently used across:

  • Authorization policy definitions in C# code
  • Test data and k6 test files
  • API documentation
  • Application constants
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any variations in the dialogporten.serviceprovider scope strings
# to ensure consistency across the codebase

rg -i "dialogporten\.serviceprovider[^\s\"']*" 

Length of output: 5767

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs (2)

33-33: LGTM: Dependencies properly configured

The new dependencies (IResourceRegistry, IServiceResourceAuthorizer) are correctly injected and validated with null checks, aligning with the PR's objective to use service resource information.

Also applies to: 43-43, 51-52


66-76: 🛠️ Refactor suggestion

Consider improving error handling and performance

Several suggestions for improvement:

  1. The code continues execution even after adding the domain error for missing service resource information. This could lead to null reference issues if dialog.Org is required downstream.

  2. The error message could be more specific by including the service resource identifier.

  3. Consider combining the service resource authorization and information retrieval to optimize performance.

Here's a suggested improvement:

-        var serviceResourceInformation = await _resourceRegistry.GetResourceInformation(dialog.ServiceResource, cancellationToken);
-        if (serviceResourceInformation is null)
-        {
-            _domainContext.AddError(new DomainFailure(nameof(DialogEntity.Org),
-                "Cannot find service owner organization shortname for referenced service resource."));
-        }
-        else
-        {
-            dialog.Org = serviceResourceInformation.OwnOrgShortName;
-        }
+        var serviceResourceInformation = await _resourceRegistry.GetResourceInformation(dialog.ServiceResource, cancellationToken);
+        if (serviceResourceInformation is null)
+        {
+            _domainContext.AddError(new DomainFailure(nameof(DialogEntity.Org),
+                $"Cannot find service owner organization shortname for service resource '{dialog.ServiceResource}'."));
+            return new DomainError(_domainContext.Failures);
+        }
+        
+        dialog.Org = serviceResourceInformation.OwnOrgShortName;

Let's verify if dialog.Org is required by checking its usage:

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogQuery.cs (2)

5-5: LGTM!

The added using directive is correctly placed and necessary for the WhereIf extension method.


89-89: LGTM! Verify consistent admin-scope behavior.

The conditional filtering correctly implements the requirement to bypass resource filtering for service owner admins while maintaining security for non-admin users.

Let's verify consistent admin-scope handling across related endpoints:

✅ Verification successful

Admin-scope behavior is consistently implemented across all related endpoints

The verification confirms consistent admin-scope handling across all service owner endpoints. The same WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource)) pattern is uniformly applied in:

  • Dialog queries (Get)
  • Dialog commands (Update)
  • Dialog activities queries (Get, Search)
  • Dialog transmissions queries (Get, Search)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent admin-scope handling patterns across query handlers
# Expected: Similar WhereIf conditions for admin-scope in other handlers

# Search for similar query handlers and their filtering logic
ast-grep --pattern 'class $_QueryHandler : IRequestHandler<$_, $_>
{
  $$$
  WhereIf($_, $_)
  $$$
}'

# Search for other usages of IsCurrentUserServiceOwnerAdmin
rg "IsCurrentUserServiceOwnerAdmin" -A 5

Length of output: 8625

src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/ResourceRegistryClient.cs (1)

195-196: LGTM! Verify API contract and constructor usage.

The changes correctly utilize both organization identifiers from the service resource, which aligns with the PR objective of enhancing admin-scope handling. The null check before mapping ensures safe null handling.

Let's verify the API contract and constructor usage:

✅ Verification successful

Let me gather more information about the ServiceResourceInformation constructor to verify its parameter requirements.


Let me search specifically for the ServiceResourceInformation class definition to verify its constructor parameters.


Let me search for the ServiceResourceInformation class in other potential locations and its usages.


Let me try one final search pattern to locate the ServiceResourceInformation class definition.


Based on the search results, I can now provide a final response. The code shows that ServiceResourceInformation is a record type defined in IResourceRegistry.cs with a constructor that takes exactly four parameters: resourceId, resourceType, ownerOrgNumber, and ownOrgShortName. The changes in ResourceRegistryClient.cs correctly map these parameters from the API response, and all usages of the constructor across the codebase follow this pattern.

LGTM! The constructor usage is correct and consistent.

The changes in ResourceRegistryClient.cs properly map the Organization and OrgCode from HasCompetentAuthority to the required constructor parameters. The null check before mapping ensures safe null handling, and all constructor calls across the codebase follow the same parameter pattern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify ServiceResourceInformation constructor usage and potential null OrgCode

# Check if OrgCode is marked as required in all CompetentAuthority classes
rg -A 2 "class CompetentAuthority"

# Find all ServiceResourceInformation constructor calls to ensure they're updated
ast-grep --pattern 'new ServiceResourceInformation($$$)'

Length of output: 2275


Script:

#!/bin/bash
# Check ServiceResourceInformation class definition and constructor
ast-grep --pattern 'class ServiceResourceInformation'

# Check if there are any null checks or validations for OrgCode
rg -A 5 "ServiceResourceInformation\(" src/Digdir.Domain.Dialogporten.Domain/

Length of output: 100471


Script:

#!/bin/bash
# Find ServiceResourceInformation class definition
rg -A 10 "class ServiceResourceInformation"

# Find files containing ServiceResourceInformation in the domain layer
fd ServiceResourceInformation src/Digdir.Domain.Dialogporten.Domain/

Length of output: 1511


Script:

#!/bin/bash
# Search for ServiceResourceInformation class in all source files
rg -g "*.cs" "public\s+(?:class|record)\s+ServiceResourceInformation" -A 5

# Search for ServiceResourceInformation constructor usages
ast-grep --pattern 'public ServiceResourceInformation($$$)'

Length of output: 138


Script:

#!/bin/bash
# Search for ServiceResourceInformation in all files, including interfaces and records
rg -g "*.cs" "ServiceResourceInformation" -B 2 -A 10

# Find all files containing ServiceResourceInformation in their name
fd -e cs ServiceResourceInformation

Length of output: 27188

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs (2)

86-86: Verify the service owner admin authorization logic

The addition of the WhereIf condition correctly implements the PR objective by allowing service owner admins to bypass the resource ID filter. However, let's verify the authorization implementation:

  1. Ensure that IsCurrentUserServiceOwnerAdmin() correctly identifies admin users
  2. Confirm that the resource filtering is appropriate for non-admin users
✅ Verification successful

Authorization logic is consistently implemented across service owner endpoints

The verification shows that IsCurrentUserServiceOwnerAdmin() is consistently used across multiple service owner endpoints (dialogs, activities, transmissions) with the same pattern:

  • All endpoints apply the same resource filtering for non-admin users
  • The implementation in IUserResourceRegistry correctly checks for the service owner admin scope
  • The authorization pattern is uniformly applied in query and command handlers
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the service owner admin authorization implementation

# Check for other usages of IsCurrentUserServiceOwnerAdmin
echo "Checking for other usages of IsCurrentUserServiceOwnerAdmin:"
rg "IsCurrentUserServiceOwnerAdmin" -A 2

# Check for related authorization checks
echo "Checking for related authorization patterns:"
ast-grep --pattern 'WhereIf(!$_.$_, x => resourceIds.Contains(x.$_))'

Length of output: 5118


85-88: Verify error handling for unauthorized access

The current implementation will return EntityNotFound when a non-admin user attempts to access an unauthorized dialog. Consider whether this should return a Forbidden result instead for better security practices.

✅ Verification successful

Based on the search results, I can now generate the final response:

Current error handling approach is consistent with codebase patterns

The current implementation returning EntityNotFound when a non-admin user attempts to access an unauthorized dialog is actually the correct approach, as evidenced by the codebase patterns:

  • All similar query operations (Get, Search) across different features consistently use the same pattern of .WhereIf(!IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource)) followed by EntityNotFound response
  • Forbidden responses are specifically used in command operations (Update, Delete, Purge) when explicitly checking resource type permissions, not for query filtering
  • This pattern effectively implements security through resource filtering rather than explicit authorization checks for queries, which is a valid security approach that doesn't leak information about unauthorized resources
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent authorization error handling patterns

# Look for similar authorization patterns
echo "Checking for similar authorization patterns:"
rg "WhereIf.*IsCurrentUserServiceOwnerAdmin.*resourceIds.Contains" -A 5

# Check for Forbidden results in similar contexts
echo "Checking for Forbidden results:"
rg "return.*Forbidden" -A 2

Length of output: 8699

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (5)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchSeenLogQuery.cs (2)

46-46: Consider improving readability of the admin check condition

The current condition !_userResourceRegistry.IsCurrentUserServiceOwnerAdmin() uses a double negative which makes it harder to understand at first glance.

Consider refactoring to make the logic more intuitive:

-            .WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource))
+            .WhereIf(_userResourceRegistry.IsCurrentUserServiceOwnerAdmin() == false, x => resourceIds.Contains(x.ServiceResource))

Or even better, extract the condition to a well-named variable:

+            var shouldFilterByResource = !_userResourceRegistry.IsCurrentUserServiceOwnerAdmin();
-            .WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource))
+            .WhereIf(shouldFilterByResource, x => resourceIds.Contains(x.ServiceResource))

46-46: Consider adding audit logging for admin access

Given that admins can now bypass resource filtering, it would be valuable to log these access patterns for security auditing purposes.

Consider adding audit logging when an admin accesses records across resource boundaries:

+            var isAdmin = _userResourceRegistry.IsCurrentUserServiceOwnerAdmin();
+            if (isAdmin)
+            {
+                _logger.LogInformation("Admin user accessing seen logs across resource boundaries for dialog {DialogId}", request.DialogId);
+            }
             .WhereIf(!isAdmin, x => resourceIds.Contains(x.ServiceResource))
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Purge/PurgeDialogCommand.cs (1)

45-48: Consider adding audit logging for admin actions.

Since admins can now purge any dialog, it would be valuable to add audit logging to track these administrative actions for security and compliance purposes.

Example implementation:

 var dialog = await _db.Dialogs
     .Include(x => x.Attachments)
     .Include(x => x.Activities)
     .WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource))
     .IgnoreQueryFilters()
-    .FirstOrDefaultAsync(x => x.Id == request.DialogId, cancellationToken);
+    .FirstOrDefaultAsync(x => x.Id == request.DialogId, cancellationToken);
+
+if (_userResourceRegistry.IsCurrentUserServiceOwnerAdmin())
+{
+    _logger.LogInformation(
+        "Admin user {UserId} purging dialog {DialogId} owned by {ServiceResource}",
+        _userResourceRegistry.GetCurrentUserId(),
+        request.DialogId,
+        dialog?.ServiceResource);
+}
tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs (2)

Line range hint 52-52: Improve test assertion readability

The IsT3 assertions are not immediately clear about what they're verifying. Consider creating a custom assertion method or constant to make the tests more maintainable and self-documenting.

Example improvement:

- Assert.True(result.IsT3); // Forbidden
+ const int ForbiddenResultIndex = 3;
+ Assert.True(result.IsT(ForbiddenResultIndex), "Expected a Forbidden result");

Or better yet, create a custom assertion extension:

public static class OneOfAssertions
{
    public static void AssertIsForbidden<T0, T1, T2>(this OneOf<T0, T1, T2, Forbidden> result)
    {
        Assert.True(result.IsT3, "Expected a Forbidden result");
    }
}

// Usage in test:
result.AssertIsForbidden();

Also applies to: 89-89


Line range hint 18-90: Add positive test cases to verify organization handling

The current tests only cover forbidden scenarios. To properly validate the PR objectives, consider adding:

  1. A positive test case that verifies successful dialog creation
  2. Verification that the correct organization value from the service resource is being used
  3. Test cases for different authorization scopes

Example structure for a positive test:

[Fact]
public async Task CreateDialogCommand_Should_Use_Service_Resource_Organization()
{
    // Arrange
    var createCommand = DialogGenerator.GenerateSimpleFakeDialog();
    var expectedOrg = createCommand.ServiceResource.Split('/')[1];
    
    resourceRegistrySub
        .GetResourceInformation(createCommand.ServiceResource, Arg.Any<CancellationToken>())
        .Returns(new ServiceResourceInformation(
            createCommand.ServiceResource,
            "foo",
            "912345678",
            expectedOrg));

    serviceAuthorizationSub
        .AuthorizeServiceResources(Arg.Any<DialogEntity>(), Arg.Any<CancellationToken>())
        .Returns(new Success());

    // Act
    var result = await commandHandler.Handle(createCommand, CancellationToken.None);

    // Assert
    Assert.True(result.IsT0); // Success
    var dialog = result.AsT0;
    Assert.Equal(expectedOrg, dialog.Organization);
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 298b682 and a2face6.

📒 Files selected for processing (5)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/GetSeenLogQuery.cs (2 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchSeenLogQuery.cs (2 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Delete/DeleteDialogCommand.cs (2 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Purge/PurgeDialogCommand.cs (2 hunks)
  • tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs (4 hunks)
🔇 Additional comments (9)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchSeenLogQuery.cs (2)

3-3: LGTM!

The addition of the Extensions namespace import is necessary for the WhereIf extension method and follows proper C# conventions.


46-46: Verify security implications of bypassing resource filtering for admins

While the implementation aligns with the PR objectives, the combination of IgnoreQueryFilters() with conditional filtering for admins requires careful consideration:

  1. Ensure this doesn't unintentionally expose sensitive data
  2. Verify that the admin scope check is properly validated upstream
✅ Verification successful

Let me analyze the results and generate additional verification scripts to complete the security assessment.


Let me perform one final verification to understand the user resource registry implementation.


Security implementation for admin access is properly implemented

The verification shows that the security implementation for admin access is robust and safe:

  1. Admin access is properly controlled through a dedicated scope digdir:dialogporten.serviceprovider.admin
  2. The IsCurrentUserServiceOwnerAdmin() check is consistently used across all service owner endpoints
  3. The pattern of using IgnoreQueryFilters() with WhereIf is implemented uniformly across all service owner queries, ensuring consistent access control
  4. The admin scope validation is performed at the authorization level before reaching the query handlers
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other instances of IgnoreQueryFilters to understand the global query filter setup
rg "IgnoreQueryFilters\(\)" -A 5

# Look for admin scope validation in the authentication/authorization pipeline
ast-grep --pattern 'serviceprovider.admin-scope'

Length of output: 17000


Script:

#!/bin/bash
# Search for IsCurrentUserServiceOwnerAdmin implementation and related authorization checks
ast-grep --pattern 'IsCurrentUserServiceOwnerAdmin'

# Look for global query filters that might be bypassed
rg "modelBuilder.*HasQueryFilter" -A 3

# Search for any authorization policies or requirements related to service owner admin
rg "AddPolicy|RequireAuthorizationAsync|AuthorizeAsync" -A 3

Length of output: 8651


Script:

#!/bin/bash
# Search for UserResourceRegistry implementation to understand admin validation
rg -g "*.cs" "class.*UserResourceRegistry" -A 10

# Look for service owner admin scope definition
rg -g "*.cs" "ServiceOwnerAdmin|serviceprovider.admin" -B 2 -A 2

Length of output: 14694

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/GetSeenLogQuery.cs (2)

3-3: LGTM: Import addition is appropriate

The addition of the Common.Extensions import is necessary for the WhereIf extension method usage.


48-48: LGTM: Proper implementation of admin-scope handling

The conditional filtering using WhereIf correctly implements the admin-scope requirements by:

  1. Only applying resource filtering for non-admin users
  2. Allowing admin users full access to dialog seen logs

Let's verify the consistent implementation of this pattern across other query handlers:

✅ Verification successful

Admin-scope handling pattern is consistently implemented across all dialog-related operations

The verification shows the same WhereIf(!_userResourceRegistry.IsCurrentUserServiceOwnerAdmin(), x => resourceIds.Contains(x.ServiceResource)) pattern is uniformly implemented across all relevant dialog operations:

  • Dialog queries (Get, Search)
  • Dialog commands (Delete, Update, Purge)
  • Dialog activities (Get, Search)
  • Dialog transmissions (Get, Search)
  • Dialog seen logs (Get, Search)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent WhereIf usage pattern across query handlers
# Expected: Similar patterns in other dialog-related queries

# Search for similar query patterns in other handlers
ast-grep --pattern 'WhereIf(!$userRegistry.IsCurrentUserServiceOwnerAdmin(), $expr)'

# Verify no direct resource filtering without admin checks
rg -l 'Contains\(.*ServiceResource\)' --type cs | xargs rg 'IsCurrentUserServiceOwnerAdmin'

Length of output: 2595

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Delete/DeleteDialogCommand.cs (2)

3-3: LGTM!

The addition of the extensions namespace is necessary for the WhereIf extension method usage.


46-46: Verify the WhereIf implementation and security implications.

The conditional filtering based on admin status aligns with the PR objective of implementing proper handling of serviceprovider.admin-scope. However, as this is a security boundary controlling access to dialogs, we should verify:

  1. The WhereIf extension method's implementation to ensure it properly translates to SQL
  2. The IsCurrentUserServiceOwnerAdmin check's implementation to confirm proper authorization
✅ Verification successful

Let me gather more information about the WhereIf implementation and the IsCurrentUserServiceOwnerAdmin method.


Let me search for more specific information about the UserResourceRegistry implementation.


Based on the gathered information, I can now provide a final response about the security implications of the WhereIf implementation.

The WhereIf and IsCurrentUserServiceOwnerAdmin implementation is secure and properly implemented.

  1. The WhereIf extension method (in QueryableExtensions.cs) is a straightforward conditional Where clause that properly translates to SQL:
public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool predicate, Expression<Func<TSource, bool>> queryPredicate)
    => predicate ? source.Where(queryPredicate) : source;
  1. The IsCurrentUserServiceOwnerAdmin check (in UserResourceRegistry.cs) is properly implemented using scope verification:
public bool IsCurrentUserServiceOwnerAdmin() => _user.GetPrincipal().HasScope(Constants.ServiceOwnerAdminScope);
  1. The security pattern is consistently applied across all service owner endpoints, ensuring uniform access control.

  2. The implementation follows the principle of defense in depth:

    • Scope-based authorization at the user level
    • Resource-based filtering at the query level
    • Additional resource type validation in specific operations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the WhereIf extension method implementation and admin check

# Check WhereIf extension implementation
echo "Checking WhereIf extension implementation:"
ast-grep --pattern 'public static IQueryable<T> WhereIf<T>($_)' 

# Check admin role verification implementation
echo "Checking admin role verification implementation:"
ast-grep --pattern 'IsCurrentUserServiceOwnerAdmin($_)'

# Check for similar security patterns
echo "Checking for similar security patterns in other handlers:"
rg -A 5 "WhereIf.*IsCurrentUserServiceOwnerAdmin"

Length of output: 10577


Script:

#!/bin/bash
# Search for WhereIf extension method implementation
echo "Searching for WhereIf extension method implementation:"
rg -A 5 "public static.*WhereIf.*IQueryable"

# Search for IsCurrentUserServiceOwnerAdmin implementation
echo "Searching for IsCurrentUserServiceOwnerAdmin implementation:"
rg -A 10 "IsCurrentUserServiceOwnerAdmin"

# Search for UserResourceRegistry implementation
echo "Searching for UserResourceRegistry implementation:"
ast-grep --pattern 'class UserResourceRegistry'

Length of output: 100395


Script:

#!/bin/bash
# Search for UserResourceRegistry class implementation
echo "Searching for UserResourceRegistry class implementation:"
rg -A 20 "class UserResourceRegistry"

# Search for IUserResourceRegistry interface
echo "Searching for IUserResourceRegistry interface:"
rg -A 10 "interface IUserResourceRegistry"

Length of output: 4042

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Purge/PurgeDialogCommand.cs (2)

3-3: LGTM!

The import of the extensions namespace is required for the new WhereIf functionality.


46-46: Verify admin-scope behavior across the application.

The implementation correctly allows admin users to bypass the resource ID filter while maintaining the filter for non-admin users. This aligns with the PR objectives.

Let's verify the consistent implementation of admin-scope handling:

✅ Verification successful

Let me generate additional scripts to verify the admin-scope behavior across the application.


Based on the results, I can now generate the final response:

Admin-scope behavior is consistently implemented across the application

The admin-scope check is uniformly implemented across all service owner endpoints using the IsCurrentUserServiceOwnerAdmin() method from IUserResourceRegistry. The pattern is consistently applied in:

  • All query operations (Get/Search) for dialogs, activities, transmissions, and seen logs
  • All command operations (Create/Update/Delete/Purge) for dialogs
  • The core authorization logic in IServiceResourceAuthorizer

The implementation correctly bypasses resource ID filtering for admin users while maintaining proper access control for non-admin users.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent implementation of admin-scope checks across dialog handlers

# Search for similar patterns in other dialog handlers
ast-grep --pattern 'WhereIf(!$userRegistry.IsCurrentUserServiceOwnerAdmin(), $expr)'

# Look for any direct calls to ensure we're not bypassing the check elsewhere
rg -A 2 "IsCurrentUserServiceOwnerAdmin|ServiceResource"

Length of output: 100161


Script:

#!/bin/bash
# Search for all instances where IsCurrentUserServiceOwnerAdmin is used in WhereIf conditions
ast-grep --pattern 'WhereIf(!$userRegistry.IsCurrentUserServiceOwnerAdmin(), $expr)'

# Search for all usages of IsCurrentUserServiceOwnerAdmin to ensure consistent implementation
ast-grep --pattern '$userRegistry.IsCurrentUserServiceOwnerAdmin()'

# Search for any potential bypass of admin checks
rg -l "ServiceOwnerAdminScope|IsCurrentUserServiceOwnerAdmin"

Length of output: 1643

tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs (1)

39-41: Previous comment about hardcoded organization value remains valid

The hardcoded "ttd" organization doesn't reflect the PR's objective of using the actual service resource's organization value.

Copy link

@elsand elsand merged commit 25277b5 into main Nov 26, 2024
24 checks passed
@elsand elsand deleted the fix/handle-admin-scope branch November 26, 2024 09:58
oskogstad pushed a commit that referenced this pull request Nov 26, 2024
🤖 I have created a release *beep* *boop*
---


##
[1.40.0](v1.39.0...v1.40.0)
(2024-11-26)


### Features

* **infra:** Upgrade to PostgreSQL v16
([#1521](#1521))
([c67dc27](c67dc27)),
closes [#1520](#1520)


### Bug Fixes

* **app:** Sub-parties sometimes missing from authorized parties
([#1534](#1534))
([f47112e](f47112e))
* Don't rethrow deserialization exceptions from FusionCache
([#1535](#1535))
([790feb8](790feb8))
* Use service resource org, allow admin-scope to fetch/update dialogs
([#1529](#1529))
([25277b5](25277b5))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
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.

4 participants