# ProUserHandler

- File ID: file:1hyu1ez:prouserhandler-cs
- Path: NexusReader.Application/Security/Authorization/ProUserHandler.cs
- Role: handler
- Layer: application

## Purpose

Authorization handler that enforces 'Pro' user access by checking the current user's subscription and AI token usage against limits in the application's database.

## Key Details

Enforces two rules: unlimited-subscription bypass and per-user AI token quota. Uses IDbContextFactory to create a short-lived AppDbContext and loads SubscriptionPlan via EF Include; succeeds the Authorization requirement when either rule passes.

## Symbols

- ProUserHandler: Authorization handler that enforces 'Pro' user access by checking the current user's subscription and AI token usage against limits in the application's database.
- ProUserHandler(): Constructor: receives and stores a DbContext factory for use by the handler.
- HandleRequirementAsync(): Core authorization logic: loads the current user from the DB and grants the requirement when the user has unlimited tokens or remaining AI token quota.

## Dependencies

- No resolved dependencies.

## Calls

- No resolved calls.

## Unresolved References

- dependency-target: NexusReader.Data.Persistence
- dependency-target: NexusReader.Domain.Entities
- dependency-target: Microsoft.EntityFrameworkCore
- interaction-target: var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
- interaction-target: context.Succeed(requirement);
- interaction-target: context.Succeed(requirement);

## Coverage

- Symbols: 3
- Executable symbols: 2
- Executable symbols with control flow: 2
- Grounded edges: 3
- Unresolved references: 6
- Control-flow fragments: 2
- Unresolved jumps: 0
- Unsupported constructs: 0
- Control-flow coverage: 100%
- Required diagrams: 2
- Generated diagrams: 3

## Control Flow Fragments

- ProUserHandler: 3 nodes, 2 edges, 0 jumps, 0 unsupported constructs
- HandleRequirementAsync: 43 nodes, 49 edges, 0 jumps, 0 unsupported constructs

## Diagrams

### ProUserHandler Neighborhood

```mermaid
flowchart LR
    class_1hxasb_prouserhandler("ProUserHandler")
    file_1hyu1ez_prouserhandler_cs("ProUserHandler")
    method_14jdxkg_handlerequirementasync[["HandleRequirementAsync()"]]
    method_1va0nah_prouserhandler[["ProUserHandler()"]]
```

### ProUserHandler Execution Overview

```mermaid
flowchart TD
    class_1hxasb_prouserhandler("ProUserHandler")
    method_14jdxkg_handlerequirementasync[["HandleRequirementAsync()"]]
    method_1va0nah_prouserhandler[["ProUserHandler()"]]
    unresolved_unresolved_7uqyo8_interaction_target{"Unresolved: context.Succeed(requirement);"}
    unresolved_unresolved_yv01sb_interaction_target{"Unresolved: var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);"}
    class_1hxasb_prouserhandler -->|contains| method_14jdxkg_handlerequirementasync
    class_1hxasb_prouserhandler -->|contains| method_1va0nah_prouserhandler
    class_1hxasb_prouserhandler -->|unresolved interaction| unresolved_unresolved_7uqyo8_interaction_target
    class_1hxasb_prouserhandler -->|unresolved interaction| unresolved_unresolved_7uqyo8_interaction_target
    class_1hxasb_prouserhandler -->|unresolved interaction| unresolved_unresolved_yv01sb_interaction_target
```

### HandleRequirementAsync() Control Flow

_Warnings: diagram method_14jdxkg_handlerequirementasync__fragment_diagram exceeds preferred complexity thresholds (43 nodes, 49 edges)._

```mermaid
---
config:
  flowchart:
    defaultRenderer: "elk"
---
flowchart TD
    method_14jdxkg_handlerequirementasync__action_24_claim_read_24__linear("Read user id from ClaimsPrincipal")
    method_14jdxkg_handlerequirementasync__action_25_guard_clause_25__condition{"string.IsNullOrEmpty(userId)"}
    method_14jdxkg_handlerequirementasync__action_25_guard_clause_25__false("otherwise")
    method_14jdxkg_handlerequirementasync__action_25_guard_clause_25__merge("merge")
    method_14jdxkg_handlerequirementasync__action_25_guard_clause_25__true("return (no authorization attempt)")
    method_14jdxkg_handlerequirementasync__action_25_handlerequirementasync_branch_25_1__condition{"string.IsNullOrEmpty(userId)"}
    method_14jdxkg_handlerequirementasync__action_25_handlerequirementasync_branch_25_1__false("false")
    method_14jdxkg_handlerequirementasync__action_25_handlerequirementasync_branch_25_1__merge("merge")
    method_14jdxkg_handlerequirementasync__action_25_handlerequirementasync_branch_25_1__true("true")
    method_14jdxkg_handlerequirementasync__action_30_db_context_create_30__linear("Create DbContext from factory (scoped local using)")
    method_14jdxkg_handlerequirementasync__action_31_db_query_31__linear("Load user with subscription")
    method_14jdxkg_handlerequirementasync__action_31_handlerequirementasync_await_31_3__await[["Waits for async work"]]
    method_14jdxkg_handlerequirementasync__action_35_handlerequirementasync_branch_35_4__condition{"user == null"}
    method_14jdxkg_handlerequirementasync__action_35_handlerequirementasync_branch_35_4__false("false")
    method_14jdxkg_handlerequirementasync__action_35_handlerequirementasync_branch_35_4__merge("merge")
    method_14jdxkg_handlerequirementasync__action_35_handlerequirementasync_branch_35_4__true("true")
    method_14jdxkg_handlerequirementasync__action_35_null_check_35__linear("Return if user not found")
    method_14jdxkg_handlerequirementasync__action_40_branch_40__condition{"user.SubscriptionPlan?.IsUnlimitedTokens == true"}
    method_14jdxkg_handlerequirementasync__action_40_branch_40__false("return")
    method_14jdxkg_handlerequirementasync__action_40_branch_40__merge("merge")
    method_14jdxkg_handlerequirementasync__action_40_branch_40__true("context.Succeed(requirement)")
    method_14jdxkg_handlerequirementasync__action_41_handlerequirementasync_branch_41_6__condition{"user.SubscriptionPlan?.IsUnlimitedTokens == true"}
    method_14jdxkg_handlerequirementasync__action_41_handlerequirementasync_branch_41_6__false("false")
    method_14jdxkg_handlerequirementasync__action_41_handlerequirementasync_branch_41_6__merge("merge")
    method_14jdxkg_handlerequirementasync__action_41_handlerequirementasync_branch_41_6__true("true")
    method_14jdxkg_handlerequirementasync__action_47_branch_47__condition{"user.AITokensUsed < user.AITokenLimit"}
    method_14jdxkg_handlerequirementasync__action_47_branch_47__false("return")
    method_14jdxkg_handlerequirementasync__action_47_branch_47__merge("merge")
    method_14jdxkg_handlerequirementasync__action_47_branch_47__true("context.Succeed(requirement)")
    method_14jdxkg_handlerequirementasync__action_48_handlerequirementasync_branch_48_9__condition{"user.AITokensUsed < user.AITokenLimit"}
    method_14jdxkg_handlerequirementasync__action_48_handlerequirementasync_branch_48_9__false("false")
    method_14jdxkg_handlerequirementasync__action_48_handlerequirementasync_branch_48_9__merge("merge")
    method_14jdxkg_handlerequirementasync__action_48_handlerequirementasync_branch_48_9__true("true")
    method_14jdxkg_handlerequirementasync__action_52_implicit_fallback_52__linear("Deny by omission")
    method_14jdxkg_handlerequirementasync__entry(["HandleRequirementAsync entry"])
    method_14jdxkg_handlerequirementasync__action_24_handlerequirementasync_repository_read_24_0__linear[("Reads repository or persistence state")]
    method_14jdxkg_handlerequirementasync__action_43_handlerequirementasync_repository_read_43_7__linear[("Reads repository or persistence state")]
    method_14jdxkg_handlerequirementasync__action_50_handlerequirementasync_repository_read_50_10__linear[("Reads repository or persistence state")]
    method_14jdxkg_handlerequirementasync__action_27_handlerequirementasync_return_27_2__return(["Returns result"])
    method_14jdxkg_handlerequirementasync__action_37_handlerequirementasync_return_37_5__return(["Returns result"])
    method_14jdxkg_handlerequirementasync__action_44_handlerequirementasync_return_44_8__return(["Returns result"])
    method_14jdxkg_handlerequirementasync__action_51_handlerequirementasync_return_51_11__return(["Returns result"])
    method_14jdxkg_handlerequirementasync__implicit_return(["return"])
    method_14jdxkg_handlerequirementasync__action_24_claim_read_24__linear -->|next| method_14jdxkg_handlerequirementasync__action_25_guard_clause_25__condition
    method_14jdxkg_handlerequirementasync__action_24_handlerequirementasync_repository_read_24_0__linear -->|next| method_14jdxkg_handlerequirementasync__action_24_claim_read_24__linear
    method_14jdxkg_handlerequirementasync__action_25_guard_clause_25__condition -->|otherwise| method_14jdxkg_handlerequirementasync__action_25_guard_clause_25__false
    method_14jdxkg_handlerequirementasync__action_25_guard_clause_25__condition -->|return no authorization attempt| method_14jdxkg_handlerequirementasync__action_25_guard_clause_25__true
    method_14jdxkg_handlerequirementasync__action_25_guard_clause_25__false -->|next| method_14jdxkg_handlerequirementasync__action_25_guard_clause_25__merge
    method_14jdxkg_handlerequirementasync__action_25_guard_clause_25__merge -->|next| method_14jdxkg_handlerequirementasync__action_25_handlerequirementasync_branch_25_1__condition
    method_14jdxkg_handlerequirementasync__action_25_guard_clause_25__true -->|next| method_14jdxkg_handlerequirementasync__action_25_guard_clause_25__merge
    method_14jdxkg_handlerequirementasync__action_25_handlerequirementasync_branch_25_1__condition -->|false| method_14jdxkg_handlerequirementasync__action_25_handlerequirementasync_branch_25_1__false
    method_14jdxkg_handlerequirementasync__action_25_handlerequirementasync_branch_25_1__condition -->|true| method_14jdxkg_handlerequirementasync__action_25_handlerequirementasync_branch_25_1__true
    method_14jdxkg_handlerequirementasync__action_25_handlerequirementasync_branch_25_1__false -->|next| method_14jdxkg_handlerequirementasync__action_25_handlerequirementasync_branch_25_1__merge
    method_14jdxkg_handlerequirementasync__action_25_handlerequirementasync_branch_25_1__merge -->|return| method_14jdxkg_handlerequirementasync__action_27_handlerequirementasync_return_27_2__return
    method_14jdxkg_handlerequirementasync__action_25_handlerequirementasync_branch_25_1__true -->|next| method_14jdxkg_handlerequirementasync__action_25_handlerequirementasync_branch_25_1__merge
    method_14jdxkg_handlerequirementasync__action_27_handlerequirementasync_return_27_2__return -->|next| method_14jdxkg_handlerequirementasync__action_30_db_context_create_30__linear
    method_14jdxkg_handlerequirementasync__action_30_db_context_create_30__linear -->|next| method_14jdxkg_handlerequirementasync__action_31_handlerequirementasync_await_31_3__await
    method_14jdxkg_handlerequirementasync__action_31_db_query_31__linear -->|next| method_14jdxkg_handlerequirementasync__action_35_null_check_35__linear
    method_14jdxkg_handlerequirementasync__action_31_handlerequirementasync_await_31_3__await -->|next| method_14jdxkg_handlerequirementasync__action_31_db_query_31__linear
    method_14jdxkg_handlerequirementasync__action_35_handlerequirementasync_branch_35_4__condition -->|false| method_14jdxkg_handlerequirementasync__action_35_handlerequirementasync_branch_35_4__false
    method_14jdxkg_handlerequirementasync__action_35_handlerequirementasync_branch_35_4__condition -->|true| method_14jdxkg_handlerequirementasync__action_35_handlerequirementasync_branch_35_4__true
    method_14jdxkg_handlerequirementasync__action_35_handlerequirementasync_branch_35_4__false -->|next| method_14jdxkg_handlerequirementasync__action_35_handlerequirementasync_branch_35_4__merge
    method_14jdxkg_handlerequirementasync__action_35_handlerequirementasync_branch_35_4__merge -->|return| method_14jdxkg_handlerequirementasync__action_37_handlerequirementasync_return_37_5__return
    method_14jdxkg_handlerequirementasync__action_35_handlerequirementasync_branch_35_4__true -->|next| method_14jdxkg_handlerequirementasync__action_35_handlerequirementasync_branch_35_4__merge
    method_14jdxkg_handlerequirementasync__action_35_null_check_35__linear -->|next| method_14jdxkg_handlerequirementasync__action_35_handlerequirementasync_branch_35_4__condition
    method_14jdxkg_handlerequirementasync__action_37_handlerequirementasync_return_37_5__return -->|next| method_14jdxkg_handlerequirementasync__action_40_branch_40__condition
    method_14jdxkg_handlerequirementasync__action_40_branch_40__condition -->|return| method_14jdxkg_handlerequirementasync__action_40_branch_40__false
    method_14jdxkg_handlerequirementasync__action_40_branch_40__condition -->|context.Succeed requirement| method_14jdxkg_handlerequirementasync__action_40_branch_40__true
    method_14jdxkg_handlerequirementasync__action_40_branch_40__false -->|next| method_14jdxkg_handlerequirementasync__action_40_branch_40__merge
    method_14jdxkg_handlerequirementasync__action_40_branch_40__merge -->|next| method_14jdxkg_handlerequirementasync__action_41_handlerequirementasync_branch_41_6__condition
    method_14jdxkg_handlerequirementasync__action_40_branch_40__true -->|next| method_14jdxkg_handlerequirementasync__action_40_branch_40__merge
    method_14jdxkg_handlerequirementasync__action_41_handlerequirementasync_branch_41_6__condition -->|false| method_14jdxkg_handlerequirementasync__action_41_handlerequirementasync_branch_41_6__false
    method_14jdxkg_handlerequirementasync__action_41_handlerequirementasync_branch_41_6__condition -->|true| method_14jdxkg_handlerequirementasync__action_41_handlerequirementasync_branch_41_6__true
    method_14jdxkg_handlerequirementasync__action_41_handlerequirementasync_branch_41_6__false -->|next| method_14jdxkg_handlerequirementasync__action_41_handlerequirementasync_branch_41_6__merge
    method_14jdxkg_handlerequirementasync__action_41_handlerequirementasync_branch_41_6__merge -->|next| method_14jdxkg_handlerequirementasync__action_43_handlerequirementasync_repository_read_43_7__linear
    method_14jdxkg_handlerequirementasync__action_41_handlerequirementasync_branch_41_6__true -->|next| method_14jdxkg_handlerequirementasync__action_41_handlerequirementasync_branch_41_6__merge
    method_14jdxkg_handlerequirementasync__action_43_handlerequirementasync_repository_read_43_7__linear -->|return| method_14jdxkg_handlerequirementasync__action_44_handlerequirementasync_return_44_8__return
    method_14jdxkg_handlerequirementasync__action_44_handlerequirementasync_return_44_8__return -->|next| method_14jdxkg_handlerequirementasync__action_47_branch_47__condition
    method_14jdxkg_handlerequirementasync__action_47_branch_47__condition -->|return| method_14jdxkg_handlerequirementasync__action_47_branch_47__false
    method_14jdxkg_handlerequirementasync__action_47_branch_47__condition -->|context.Succeed requirement| method_14jdxkg_handlerequirementasync__action_47_branch_47__true
    method_14jdxkg_handlerequirementasync__action_47_branch_47__false -->|next| method_14jdxkg_handlerequirementasync__action_47_branch_47__merge
    method_14jdxkg_handlerequirementasync__action_47_branch_47__merge -->|next| method_14jdxkg_handlerequirementasync__action_48_handlerequirementasync_branch_48_9__condition
    method_14jdxkg_handlerequirementasync__action_47_branch_47__true -->|next| method_14jdxkg_handlerequirementasync__action_47_branch_47__merge
    method_14jdxkg_handlerequirementasync__action_48_handlerequirementasync_branch_48_9__condition -->|false| method_14jdxkg_handlerequirementasync__action_48_handlerequirementasync_branch_48_9__false
    method_14jdxkg_handlerequirementasync__action_48_handlerequirementasync_branch_48_9__condition -->|true| method_14jdxkg_handlerequirementasync__action_48_handlerequirementasync_branch_48_9__true
    method_14jdxkg_handlerequirementasync__action_48_handlerequirementasync_branch_48_9__false -->|next| method_14jdxkg_handlerequirementasync__action_48_handlerequirementasync_branch_48_9__merge
    method_14jdxkg_handlerequirementasync__action_48_handlerequirementasync_branch_48_9__merge -->|next| method_14jdxkg_handlerequirementasync__action_50_handlerequirementasync_repository_read_50_10__linear
    method_14jdxkg_handlerequirementasync__action_48_handlerequirementasync_branch_48_9__true -->|next| method_14jdxkg_handlerequirementasync__action_48_handlerequirementasync_branch_48_9__merge
    method_14jdxkg_handlerequirementasync__action_50_handlerequirementasync_repository_read_50_10__linear -->|return| method_14jdxkg_handlerequirementasync__action_51_handlerequirementasync_return_51_11__return
    method_14jdxkg_handlerequirementasync__action_51_handlerequirementasync_return_51_11__return -->|next| method_14jdxkg_handlerequirementasync__action_52_implicit_fallback_52__linear
    method_14jdxkg_handlerequirementasync__action_52_implicit_fallback_52__linear -->|return| method_14jdxkg_handlerequirementasync__implicit_return
    method_14jdxkg_handlerequirementasync__entry -->|next| method_14jdxkg_handlerequirementasync__action_24_handlerequirementasync_repository_read_24_0__linear
```

## Diagram Validation

- Status: warn
- Warning: control-flow: diagram method_14jdxkg_handlerequirementasync__fragment_diagram exceeds preferred complexity thresholds (43 nodes, 49 edges).
