Skip to content

v1.0.39 - #103

Merged
yilmaztayfun merged 4 commits into
release-v1.0from
master
Sep 1, 2026
Merged

v1.0.39#103
yilmaztayfun merged 4 commits into
release-v1.0from
master

Conversation

@yilmaztayfun

@yilmaztayfun yilmaztayfun commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Summary by Sourcery

Expand current-user context handling and provide tooling for consuming unreleased framework packages locally.

New Features:

  • Add ambient current-user support for identity, roles, organizational position, delegation, consent, header capture, restoration, and downstream forwarding.
  • Add local NuGet packaging support for unreleased Aether framework builds.

Enhancements:

  • Support structured current-user changes through BasicUserInfo and normalize role parsing across comma- and space-separated headers.
  • Expose current-user documentation and feature support details.

Build:

  • Add a repository-local script for packing all Aether libraries into a local NuGet feed with version validation and cache cleanup.

Documentation:

  • Document current-user properties, resolution, background-job restoration, and service-to-service forwarding.

Tests:

  • Add coverage for current-user resolution, middleware scoping, role and position handling, header conversion, forwarding round trips, and nested scopes.

yilmaztayfun and others added 4 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>
…sition

Feature/current user role position
@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 release expands ambient caller identity with position and primary-role support, adds configurable header capture/restoration/forwarding for HTTP and background workflows, documents the integration model, provides a local NuGet packaging workflow, and backs the behavior with comprehensive tests.

Sequence diagram for current user resolution from HTTP headers

sequenceDiagram
    participant Request as HTTP_Request
    participant Resolver as HeaderCurrentUserResolver
    participant Middleware as AetherCurrentUserMiddleware
    participant User as ICurrentUser
    participant App as Application_Service

    Request->>Resolver: GetCurrentUserAsync()
    Resolver->>Request: GetClaimHeader()
    Resolver->>Resolver: ParseRolesFromHeader()
    Resolver-->>Middleware: BasicUserInfo
    Middleware->>User: Change(BasicUserInfo)
    Middleware->>App: next(context)
    App->>User: IsInRole() / Position
    App-->>Middleware: response
    Middleware->>User: Dispose()
Loading

Sequence diagram for current user capture and background restoration

sequenceDiagram
    participant Request as HTTP_Request
    participant Job as Background_Job
    participant User as ICurrentUser
    participant Service as Report_Service

    Request->>Request: GetCurrentUserHeaders()
    Request->>Job: EnqueueAsync(payload with ClaimHeaders)
    Job->>User: ChangeFromHeaders(payload.ClaimHeaders)
    User->>User: ParseRolesFromHeader()
    Job->>Service: GenerateAsync(reportId)
    Service->>User: UserName / Role / Position
    Job->>User: Dispose()
Loading

File-Level Changes

Change Details Files
Adds a complete ambient current-user identity model with position and primary-role support.
  • Extends user data and interface contracts with Position and Role.
  • Adds a BasicUserInfo-based Change overload while retaining the positional API.
  • Updates request resolution and middleware to populate the expanded model.
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, forwarding, and role-parsing utilities across HTTP and non-HTTP execution contexts.
  • Adds configurable position-header handling and normalized comma/space role parsing.
  • Supports capturing request headers for background work and restoring them with scoped ambient-user semantics.
  • Supports forwarding the current identity to downstream services with empty-value omission and round-trip compatibility.
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, integration points, trust assumptions, and local package iteration workflow.
  • Adds feature documentation covering resolution, background jobs, forwarding, and authorization guidance.
  • Adds a script that packs all framework libraries into a local NuGet feed and purges stale same-version cache entries.
  • Updates the framework feature matrix and documentation index.
framework/docs/current-user/README.md
framework/docs/README.md
build/pack-local.sh
Adds focused automated coverage for identity mapping, scoped restoration, role behavior, header helpers, and middleware execution.
  • Tests HTTP header resolution, capture, role parsing, and middleware lifecycle.
  • Tests header-based restoration and outbound forwarding round trips.
  • Adds isolated test accessors to avoid AsyncLocal state leaking between tests.
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

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

@yilmaztayfun
yilmaztayfun merged commit b1169cc into release-v1.0 Sep 1, 2026
3 of 5 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/CurrentUserHeaderExtensions.cs" line_range="41-50" />
<code_context>
+        }
+
+        return currentUser.Change(new BasicUserInfo(
+            headers.GetValueOrDefault(AetherClaimTypes.UserId),
+            headers.GetValueOrDefault(AetherClaimTypes.UserName),
+            headers.GetValueOrDefault(AetherClaimTypes.Name),
+            headers.GetValueOrDefault(AetherClaimTypes.SurName),
+            ParseRolesFromHeader(headers.GetValueOrDefault(AetherClaimTypes.Role)),
+            headers.GetValueOrDefault(AetherClaimTypes.ActorUserId),
+            headers.GetValueOrDefault(AetherClaimTypes.ActorSub),
+            headers.GetValueOrDefault(AetherClaimTypes.ConsentId),
+            headers.GetValueOrDefault(AetherClaimTypes.Position)));
+    }
+
</code_context>
<issue_to_address>
**issue (bug_risk):** ChangeFromHeaders performs case-sensitive lookups against IReadOnlyDictionary, so a valid claim-header dictionary whose keys use different casing silently restores a user with missing identity, roles, and position fields.

**Triggers:** When callers provide a regular case-sensitive Dictionary with HTTP-style header names in casing different from AetherClaimTypes.

**Suggested fix:** Look up headers with an ordinal case-insensitive comparer or normalize the input dictionary before reading it.

```suggestion
        string? GetHeader(string key) =>
            headers.FirstOrDefault(pair => StringComparer.OrdinalIgnoreCase.Equals(pair.Key, key)).Value;

        return currentUser.Change(new BasicUserInfo(
            GetHeader(AetherClaimTypes.UserId),
            GetHeader(AetherClaimTypes.UserName),
            GetHeader(AetherClaimTypes.Name),
            GetHeader(AetherClaimTypes.SurName),
            ParseRolesFromHeader(GetHeader(AetherClaimTypes.Role)),
            GetHeader(AetherClaimTypes.ActorUserId),
            GetHeader(AetherClaimTypes.ActorSub),
            GetHeader(AetherClaimTypes.ConsentId),
            GetHeader(AetherClaimTypes.Position)));
```
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 1 finding to address first, and this introduces and propagates an ambient caller identity from HTTP headers, which downstream authorization, auditing, and service calls may trust; accepting malformed or untrusted headers could grant access or attribute actions to the wrong user. Reverting stops future requests, but any access, action, or audit record produced under an incorrect identity would require separate investigation and repair.

Blocking findings: framework/src/BBT.Aether.Core/BBT/Aether/Users/CurrentUserHeaderExtensions.cs:50


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.

Comment on lines +41 to +50
return currentUser.Change(new BasicUserInfo(
headers.GetValueOrDefault(AetherClaimTypes.UserId),
headers.GetValueOrDefault(AetherClaimTypes.UserName),
headers.GetValueOrDefault(AetherClaimTypes.Name),
headers.GetValueOrDefault(AetherClaimTypes.SurName),
ParseRolesFromHeader(headers.GetValueOrDefault(AetherClaimTypes.Role)),
headers.GetValueOrDefault(AetherClaimTypes.ActorUserId),
headers.GetValueOrDefault(AetherClaimTypes.ActorSub),
headers.GetValueOrDefault(AetherClaimTypes.ConsentId),
headers.GetValueOrDefault(AetherClaimTypes.Position)));

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): ChangeFromHeaders performs case-sensitive lookups against IReadOnlyDictionary, so a valid claim-header dictionary whose keys use different casing silently restores a user with missing identity, roles, and position fields.

Triggers: When callers provide a regular case-sensitive Dictionary with HTTP-style header names in casing different from AetherClaimTypes.

Suggested fix: Look up headers with an ordinal case-insensitive comparer or normalize the input dictionary before reading it.

Suggested change
return currentUser.Change(new BasicUserInfo(
headers.GetValueOrDefault(AetherClaimTypes.UserId),
headers.GetValueOrDefault(AetherClaimTypes.UserName),
headers.GetValueOrDefault(AetherClaimTypes.Name),
headers.GetValueOrDefault(AetherClaimTypes.SurName),
ParseRolesFromHeader(headers.GetValueOrDefault(AetherClaimTypes.Role)),
headers.GetValueOrDefault(AetherClaimTypes.ActorUserId),
headers.GetValueOrDefault(AetherClaimTypes.ActorSub),
headers.GetValueOrDefault(AetherClaimTypes.ConsentId),
headers.GetValueOrDefault(AetherClaimTypes.Position)));
string? GetHeader(string key) =>
headers.FirstOrDefault(pair => StringComparer.OrdinalIgnoreCase.Equals(pair.Key, key)).Value;
return currentUser.Change(new BasicUserInfo(
GetHeader(AetherClaimTypes.UserId),
GetHeader(AetherClaimTypes.UserName),
GetHeader(AetherClaimTypes.Name),
GetHeader(AetherClaimTypes.SurName),
ParseRolesFromHeader(GetHeader(AetherClaimTypes.Role)),
GetHeader(AetherClaimTypes.ActorUserId),
GetHeader(AetherClaimTypes.ActorSub),
GetHeader(AetherClaimTypes.ConsentId),
GetHeader(AetherClaimTypes.Position)));

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 12 duplication

Metric Results
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