Skip to content

fix: access control - #57

Merged
saijaku0 merged 3 commits into
mainfrom
fix/access-control
Feb 18, 2026
Merged

fix: access control#57
saijaku0 merged 3 commits into
mainfrom
fix/access-control

Conversation

@saijaku0

@saijaku0 saijaku0 commented Feb 18, 2026

Copy link
Copy Markdown
Owner

Add new behavior, solve problem: If patient A can take access to data of Patient B. Now they cannot

Summary by CodeRabbit

  • New Features

    • Role-based authorization added across doctor management and schedule operations; commands now enforce role requirements.
  • Bug Fixes

    • Fixed address parameter handling during user/patient registration.
    • Doctor profile photo upload now accepts and preserves content-type.
  • Documentation

    • Added XML documentation for appointment overlap query helper.

@coderabbitai

coderabbitai Bot commented Feb 18, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds role-based authorization: new AuthorizeAttribute and AuthorizationBehavior<TRequest,TResponse>, applies attributes to doctor commands, updates MediatR pipeline registration to use open-generic behaviors, and introduces handler-level user checks; also fixes parameter naming/formatting in user creation code and minor docs.

Changes

Cohort / File(s) Summary
MediatR configuration
src/Booking/Booking.API/Program.cs
Replaced closed ValidationBehavior registration with open-generic AddBehavior(typeof(IPipelineBehavior<,>)) and AddOpenBehavior registrations for AuthorizationBehavior and ValidationBehavior.
Authorization infra
src/Booking/Booking.Application/Common/Security/AuthorizeAttribute.cs, src/Booking/Booking.Application/Common/Behaviors/AuthorizationBehavior.cs
Added AuthorizeAttribute (Roles, Policy) and AuthorizationBehavior<TRequest,TResponse> that inspects attributes, requires current user, and validates roles via IIdentityService, throwing UnauthorizedAccessException/ForbiddenAccessException.
Commands annotated
src/Booking/Booking.Application/Doctors/Command/.../CreateDoctorCommand.cs, .../DeleteDoctorCommand.cs, .../UpdateDoctorCommand.cs, .../UpdateDoctorPhotoCommand.cs, .../UpdateScheduleConfigCommand.cs
Applied [Authorize(Roles = ...)] to doctor commands; UpdateDoctorPhotoCommand signature extended with ContentType.
Handler auth checks
src/Booking/Booking.Application/Doctors/Command/UpdateScheduleConfig/UpdateScheduleConfigCommandHandler.cs
Injected ICurrentUserService, validate current user and that user's doctor ID matches request; throw UnauthorizedAccessException/ForbiddenAccessException before proceeding.
Domain & DTO fixes
src/Booking/Booking.Domain/Entities/ApplicationUser.cs, src/Booking/Booking.Application/Identity/Commands/RegisterUser/...
Renamed adressaddress in CreatePatient and aligned handler call; small formatting/indentation adjustment in RegisterUserCommand.
Exceptions namespace
src/Booking/Booking.Application/Common/Exceptions/ForbiddenAccessException.cs
Moved ForbiddenAccessException into Booking.Application.Common.Exceptions (namespace relocation only).
Docs
src/Booking/Booking.Application/Common/Extension/AppointmentQueryExtensions.cs
Added XML documentation for WhereOverlaps extension method.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Pipeline as MediatR Pipeline
    participant Auth as AuthorizationBehavior
    participant User as ICurrentUserService
    participant IdSvc as IIdentityService
    participant Handler as Request Handler
    participant DB as Database

    Client->>Pipeline: Send Command (e.g., UpdateScheduleConfigCommand)
    Pipeline->>Auth: Invoke AuthorizationBehavior<TRequest,TResponse>
    Auth->>Auth: Inspect [Authorize] attributes on request type
    alt No [Authorize]
        Auth->>Handler: Call next handler
    else Has [Authorize]
        Auth->>User: Get current user ID
        alt No current user
            Auth-->>Client: Throw UnauthorizedAccessException
        else Current user exists
            Auth->>Auth: Filter attributes with Roles
            alt No Roles defined
                Auth->>Handler: Call next handler
            else Roles defined
                loop for each role
                    Auth->>IdSvc: IsInRoleAsync(userId, role)
                end
                alt User in any required role
                    Auth->>Handler: Call next handler
                else Not in required roles
                    Auth-->>Client: Throw ForbiddenAccessException
                end
            end
        end
    end
    Handler->>DB: Execute command logic
    DB-->>Handler: Result
    Handler-->>Client: Return response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • fix: access control #57: Similar MediatR pipeline changes and introduction/adjustments to AuthorizationBehavior and AuthorizeAttribute (strong overlap in auth wiring).
  • fix: application user #55: Touches ApplicationUser.CreatePatient parameter/name changes — related to the same domain fix.
  • feat: add remove doctor command #53: Modifies authorization annotations for doctor flows and delete operations, matching this PR's authorization changes.

Poem

🐰 I hop through code with careful paws,

I mark who may and who must pause,
Through MediatR lanes I guard each door,
Roles checked gently, then — explore!
A rabbit cheers: bookings safe once more 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'fix: access control' is partially related to the changeset but overly vague and generic. While the PR does implement access control features, the title lacks specificity about the main changes (authorization behavior, role-based access, security attributes). Consider a more specific title such as 'fix: implement authorization behavior and role-based access control' to better convey the primary changes and make the scope clearer to reviewers.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/access-control

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/Booking/Booking.Application/Common/Behaviors/AuthorizationBehavior.cs (1)

3-3: Remove unused import.

Booking.Application.Doctors.Command.DeleteDoctor is not used in this file.

Proposed fix
 using Booking.Application.Common.Interfaces;
 using Booking.Application.Common.Security;
-using Booking.Application.Doctors.Command.DeleteDoctor;
 using MediatR;
 using System.Reflection;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Booking/Booking.Application/Common/Behaviors/AuthorizationBehavior.cs` at
line 3, Remove the unused using directive "using
Booking.Application.Doctors.Command.DeleteDoctor;" from
AuthorizationBehavior.cs; open the AuthorizationBehavior class in that file,
delete that import line, then rebuild to confirm no references remain (ensure no
symbols from DeleteDoctor are used in AuthorizationBehavior such as in Handle or
constructor).
src/Booking/Booking.Application/Doctors/Command/UpdateScheduleConfig/UpdateScheduleConfigCommandHandler.cs (2)

32-33: Inconsistent async usage: FirstOrDefault should be FirstOrDefaultAsync.

Line 26 correctly uses FirstOrDefaultAsync, but line 33 uses synchronous FirstOrDefault. For consistency and to avoid blocking the thread, use the async version.

♻️ Proposed fix
-            var config = _context.DoctorScheduleConfigs
-                .FirstOrDefault(x => x.DoctorId == request.DoctorId);
+            var config = await _context.DoctorScheduleConfigs
+                .FirstOrDefaultAsync(x => x.DoctorId == request.DoctorId, cancellationToken);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Booking/Booking.Application/Doctors/Command/UpdateScheduleConfig/UpdateScheduleConfigCommandHandler.cs`
around lines 32 - 33, Replace the synchronous query call with the async variant:
change the call on _context.DoctorScheduleConfigs that currently uses
FirstOrDefault(x => x.DoctorId == request.DoctorId) to await
_context.DoctorScheduleConfigs.FirstOrDefaultAsync(x => x.DoctorId ==
request.DoctorId) and ensure the containing method
(UpdateScheduleConfigCommandHandler.Handle or similar) is async and returns a
Task, adding the necessary using for Microsoft.EntityFrameworkCore if missing.

24-30: Misleading variable name: doctorId holds a Doctor entity, not an ID.

The variable doctorId actually contains the full Doctor entity returned by FirstOrDefaultAsync. Consider renaming for clarity.

✏️ Suggested rename
-            var doctorId = await _context.Doctors
+            var doctor = await _context.Doctors
                 .AsNoTracking()
                 .FirstOrDefaultAsync(d => d.ApplicationUserId == currentUserId, cancellationToken)
                 ?? throw new ForbiddenAccessException("Current user is not a doctor.");

-            if (request.DoctorId != doctorId.Id)
+            if (request.DoctorId != doctor.Id)
                 throw new ForbiddenAccessException("You can only edit your own schedule.");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Booking/Booking.Application/Doctors/Command/UpdateScheduleConfig/UpdateScheduleConfigCommandHandler.cs`
around lines 24 - 30, The variable named `doctorId` is misleading because it
holds a Doctor entity retrieved via
`_context.Doctors.AsNoTracking().FirstOrDefaultAsync(...)`; rename it to
something like `doctor` (or `currentDoctor`) everywhere in
`UpdateScheduleConfigCommandHandler` so comparisons use `doctor.Id` (e.g., `if
(request.DoctorId != doctor.Id)`) and the thrown exceptions
(`ForbiddenAccessException`) remain unchanged; ensure all references to
`doctorId` in this method are updated to the new name to keep intent clear.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Booking/Booking.API/Program.cs`:
- Around line 45-46: Remove the invalid open-generic registration
AddBehavior(typeof(IPipelineBehavior<,>)) and rely on AddOpenBehaviors(new[] {
typeof(ValidationBehavior<,>), typeof(AuthorizationBehavior<,>) }) to register
the pipeline behaviors; if the original intent was to register a closed/Concrete
implementation, replace the call with the closed-generic overload
AddBehavior<IPipelineBehavior<TRequest,TResponse>, ConcreteImplementation>()
referring to AddBehavior and AddOpenBehaviors and the
ValidationBehavior/AuthorizationBehavior types to locate the code.

In `@src/Booking/Booking.Application/Common/Security/AuthorizeAttribute.cs`:
- Around line 17-18: The Policy property on the AuthorizeAttribute class is
non-nullable but lacks an initializer, causing a compiler warning; change its
declaration from a non-nullable string to a nullable string (i.e., make Policy
of type string?) so it can be null until used, by updating the Policy property
signature in the AuthorizeAttribute class.

In
`@src/Booking/Booking.Application/Doctors/Command/UpdateScheduleConfig/UpdateScheduleConfigCommandHandler.cs`:
- Line 2: Remove the unused using for
Booking.Application.Doctors.Command.DeleteDoctor in the
UpdateScheduleConfigCommandHandler file and add a using for
Booking.Application.Common.Exceptions so the ForbiddenAccessException references
resolve; specifically, delete the import of DeleteDoctor and add the import for
Booking.Application.Common.Exceptions at the top of the file so the
ForbiddenAccessException thrown in UpdateScheduleConfigCommandHandler compiles.

---

Nitpick comments:
In `@src/Booking/Booking.Application/Common/Behaviors/AuthorizationBehavior.cs`:
- Line 3: Remove the unused using directive "using
Booking.Application.Doctors.Command.DeleteDoctor;" from
AuthorizationBehavior.cs; open the AuthorizationBehavior class in that file,
delete that import line, then rebuild to confirm no references remain (ensure no
symbols from DeleteDoctor are used in AuthorizationBehavior such as in Handle or
constructor).

In
`@src/Booking/Booking.Application/Doctors/Command/UpdateScheduleConfig/UpdateScheduleConfigCommandHandler.cs`:
- Around line 32-33: Replace the synchronous query call with the async variant:
change the call on _context.DoctorScheduleConfigs that currently uses
FirstOrDefault(x => x.DoctorId == request.DoctorId) to await
_context.DoctorScheduleConfigs.FirstOrDefaultAsync(x => x.DoctorId ==
request.DoctorId) and ensure the containing method
(UpdateScheduleConfigCommandHandler.Handle or similar) is async and returns a
Task, adding the necessary using for Microsoft.EntityFrameworkCore if missing.
- Around line 24-30: The variable named `doctorId` is misleading because it
holds a Doctor entity retrieved via
`_context.Doctors.AsNoTracking().FirstOrDefaultAsync(...)`; rename it to
something like `doctor` (or `currentDoctor`) everywhere in
`UpdateScheduleConfigCommandHandler` so comparisons use `doctor.Id` (e.g., `if
(request.DoctorId != doctor.Id)`) and the thrown exceptions
(`ForbiddenAccessException`) remain unchanged; ensure all references to
`doctorId` in this method are updated to the new name to keep intent clear.

Comment thread src/Booking/Booking.API/Program.cs Outdated
Comment thread src/Booking/Booking.Application/Common/Security/AuthorizeAttribute.cs Outdated
@saijaku0
saijaku0 merged commit 5442c2d into main Feb 18, 2026
2 of 3 checks passed
@sonarqubecloud

Copy link
Copy Markdown

@saijaku0

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Feb 18, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@saijaku0
saijaku0 deleted the fix/access-control branch February 18, 2026 22:22
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.

1 participant