Skip to content

Feature/current user role position - #102

Merged
yilmaztayfun merged 3 commits into
masterfrom
feature/current-user-role-position
Sep 1, 2026
Merged

Feature/current user role position#102
yilmaztayfun merged 3 commits into
masterfrom
feature/current-user-role-position

Conversation

@yilmaztayfun

@yilmaztayfun yilmaztayfun commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Summary by Sourcery

Extend current-user identity handling with role and position support plus reusable claim-header propagation.

New Features:

  • Expose the caller’s primary role and organizational position through the current-user abstraction.
  • Support capturing, restoring, and forwarding current-user claim headers across requests, background jobs, and downstream services.
  • Add documentation covering current-user claims, role handling, position, and header propagation.

Enhancements:

  • Consolidate current-user construction around BasicUserInfo and improve role parsing for comma- and space-separated headers.
  • Make position configurable through Aether claim types while preserving scoped ambient-user behavior.

Build:

  • Add a script for packing framework libraries into a local NuGet feed for unreleased consumer builds.

Documentation:

  • Document the Current User feature and its integration across supported framework components.

Tests:

  • Add coverage for current-user role and position resolution, middleware scoping, header capture/restoration/forwarding, and role parsing.

yilmaztayfun and others added 3 commits August 20, 2026 20:56
Add two members to ICurrentUser. Position carries the caller's
organizational posting from the `position` claim header. Role is the
first entry of Roles, for legacy systems that send a single `role`
claim where consumers otherwise write Roles?.FirstOrDefault() at
every call site.

Move the header/claim dictionary conversion into the SDK as
CurrentUserHeaderExtensions (Core): ChangeFromHeaders restores a
captured user in scopes with no ambient HTTP request — background
jobs, message consumers, resumed workflows — and ToForwardHeaders
turns the current user back into claim headers for outbound calls.
Both work over IReadOnlyDictionary, so Core carries no ASP.NET
dependency. HttpRequestCurrentUserExtensions (AspNetCore) is the
HTTP-side counterpart that captures those headers off a request.

Role parsing now accepts space-separated values and trims entries,
via the single ParseRolesFromHeader used by both the resolver and
ChangeFromHeaders. HeaderCurrentUserResolver previously did a bare
Split(','), which missed space-separated legacy values and turned an
empty `role` header into a one-element array holding "".

Change gains a BasicUserInfo overload, now the single code path; the
positional overload delegates to it, so call sites like
AetherCurrentUserMiddleware no longer need revisiting when the user
model grows a field. Position stays null when absent, unlike the
older fields that default to empty string, so consumers can fall
through with `?? fallback`.

BREAKING CHANGE: ICurrentUser gains Role, Position and a
Change(BasicUserInfo) overload. Types outside the framework that
implement ICurrentUser must add these members.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@yilmaztayfun yilmaztayfun self-assigned this Sep 1, 2026
@yilmaztayfun
yilmaztayfun requested review from a team September 1, 2026 06:50
@sourcery-ai

sourcery-ai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR extends ambient current-user propagation with nullable position and primary-role support, standardizes structured user changes, and adds configurable header capture, restoration, and forwarding for HTTP, background, and downstream-service scenarios. It documents and tests the complete flow, and adds a local NuGet packaging script to support consumer validation against unreleased framework builds.

Sequence diagram for current-user resolution and ambient access

sequenceDiagram
    participant Request as HTTP_Request
    participant Resolver as HeaderCurrentUserResolver
    participant Middleware as AetherCurrentUserMiddleware
    participant CurrentUser as ICurrentUser
    participant Service as ApplicationService

    Request->>Resolver: ResolveAsync()
    Resolver->>Request: GetClaimHeader()
    Resolver->>Resolver: ParseRolesFromHeader()
    Resolver-->>Middleware: BasicUserInfo
    Middleware->>CurrentUser: Change(BasicUserInfo)
    Middleware->>Service: next(context)
    Service->>CurrentUser: IsInRole() / Position
    Middleware->>CurrentUser: Dispose()
Loading

File-Level Changes

Change Details Files
Adds position and primary-role access to the ambient current-user model while preserving the full roles collection.
  • Extends claim types, user DTOs, and interfaces with nullable position and a first-role convenience property.
  • Adds a BasicUserInfo-based Change overload and updates middleware to use it.
  • Parses comma- or space-separated role headers and maps position through request resolution.
framework/src/BBT.Aether.Core/BBT/Aether/Users/AetherClaimTypes.cs
framework/src/BBT.Aether.Core/BBT/Aether/Users/BasicUserInfo.cs
framework/src/BBT.Aether.Core/BBT/Aether/Users/CurrentUser.cs
framework/src/BBT.Aether.Core/BBT/Aether/Users/ICurrentUser.cs
framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Security/AetherCurrentUserMiddleware.cs
framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Security/HeaderCurrentUserResolver.cs
Introduces reusable claim-header capture, restoration, and forwarding helpers for HTTP, background, and service-to-service flows.
  • Captures configured claim headers from HttpRequest while omitting absent values.
  • Restores scoped users from plain dictionaries and reliably returns the previous ambient user on disposal.
  • Serializes current-user claims for forwarding, including comma-joined roles and position.
  • Supports configurable claim header names through the existing mutable AetherClaimTypes values.
framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Security/HttpRequestCurrentUserExtensions.cs
framework/src/BBT.Aether.Core/BBT/Aether/Users/CurrentUserHeaderExtensions.cs
Documents the current-user feature and its trust-boundary, resolution, background-job, and forwarding usage.
  • Adds feature documentation covering properties, role semantics, middleware registration, and custom resolver replacement.
  • Documents claim capture/restoration and downstream header forwarding patterns.
  • Updates the framework feature and support matrix.
framework/docs/current-user/README.md
framework/docs/README.md
Adds comprehensive unit coverage for role/position mapping, scoped ambient state, header round trips, middleware behavior, and parsing edge cases.
  • Tests null and nested scope restoration semantics.
  • Tests HTTP header resolution and capture behavior.
  • Tests forwarding/restoration round trips, omitted empty values, and role parsing.
framework/test/BBT.Aether.AspNetCore.Tests/BBT/Aether/AspNetCore/Security/AetherCurrentUserMiddlewareTests.cs
framework/test/BBT.Aether.AspNetCore.Tests/BBT/Aether/AspNetCore/Security/HeaderCurrentUserResolverTests.cs
framework/test/BBT.Aether.AspNetCore.Tests/BBT/Aether/AspNetCore/Security/HttpRequestCurrentUserExtensionsTests.cs
framework/test/BBT.Aether.Infrastructure.Tests/BBT/Aether/Users/CurrentUserHeaderExtensionsTests.cs
framework/test/BBT.Aether.Infrastructure.Tests/BBT/Aether/Users/CurrentUserTests.cs
framework/test/BBT.Aether.Infrastructure.Tests/TestSupport/TestCurrentUserAccessor.cs
framework/test/BBT.Aether.AspNetCore.Tests/BBT.Aether.AspNetCore.Tests.csproj
Adds a repository-local packaging workflow for consuming unreleased framework builds without publishing them.
  • Packs all BBT.Aether projects into .local-feed with a configurable -local version.
  • Purges matching feed artifacts and global NuGet cache entries before repacking.
  • Stops on pack failures and reruns the failed project to expose diagnostics.
build/pack-local.sh

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: a7a5697a-168c-4eec-8d67-626bad0e8436


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@yilmaztayfun
yilmaztayfun merged commit 6bcd6f9 into master Sep 1, 2026
3 of 6 checks passed

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="framework/src/BBT.Aether.Core/BBT/Aether/Users/CurrentUser.cs" line_range="72" />
<code_context>
     {
+        Check.NotNull(user, nameof(user));
+
         var parentScope = currentUserAccessor.Current;
-        currentUserAccessor.Current = new BasicUserInfo(id, userName, name, surname, roles, actorUserId, actorUserName, consentId);
+        currentUserAccessor.Current = user;
         return new DisposeAction(() => { currentUserAccessor.Current = parentScope; });
     }
</code_context>
<issue_to_address>
**issue (bug_risk):** Change(BasicUserInfo) stores the caller-supplied mutable BasicUserInfo instance directly, so changing its properties or Roles array after entering the scope changes the ambient caller identity while the scope is active. The positional Change overload previously created a separate BasicUserInfo object, so this overload introduces observable aliasing.

**Triggers:** When a caller reuses or mutates the BasicUserInfo instance after passing it to Change.

**Suggested fix:** Copy the BasicUserInfo fields, and preferably clone the Roles array, before assigning the copy to currentUserAccessor.Current.

```suggestion
        currentUserAccessor.Current = new BasicUserInfo(
            user.Id,
            user.UserName,
            user.Name,
            user.Surname,
            user.Roles?.ToArray(),
            user.ActorUserId,
            user.ActorUserName,
            user.ConsentId,
            user.Position);
```
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 1 finding to address first, and this changes the identity data resolved from request headers, including role parsing, and adds forwarding of that identity to downstream services. If a role or caller field is interpreted incorrectly, downstream authorization or audit actions could occur under the wrong identity; reverting stops future propagation but cannot undo decisions already made.

Blocking findings: framework/src/BBT.Aether.Core/BBT/Aether/Users/CurrentUser.cs:72


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.


var parentScope = currentUserAccessor.Current;
currentUserAccessor.Current = new BasicUserInfo(id, userName, name, surname, roles, actorUserId, actorUserName, consentId);
currentUserAccessor.Current = user;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Change(BasicUserInfo) stores the caller-supplied mutable BasicUserInfo instance directly, so changing its properties or Roles array after entering the scope changes the ambient caller identity while the scope is active. The positional Change overload previously created a separate BasicUserInfo object, so this overload introduces observable aliasing.

Triggers: When a caller reuses or mutates the BasicUserInfo instance after passing it to Change.

Suggested fix: Copy the BasicUserInfo fields, and preferably clone the Roles array, before assigning the copy to currentUserAccessor.Current.

Suggested change
currentUserAccessor.Current = user;
currentUserAccessor.Current = new BasicUserInfo(
user.Id,
user.UserName,
user.Name,
user.Surname,
user.Roles?.ToArray(),
user.ActorUserId,
user.ActorUserName,
user.ConsentId,
user.Position);

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · 12 duplication

Metric Results
Complexity 0
Duplication 12

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@sonarqubecloud

sonarqubecloud Bot commented Sep 1, 2026

Copy link
Copy Markdown

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