---
title: SAIF.Framework.Web.Mvc security and authorization
summary: Authorization requirements, AD resource groups, security enforcement points, and data-level access control.
status: draft
owner: SAIF Documentation
last_reviewed: 2026-09-17
library: SAIF.Framework.Web.Mvc
reference_type: package
---

# SAIF.Framework.Web.Mvc security and authorization

This page documents authorization mechanisms implemented or consumed by this library. Authentication itself is supplied by infrastructure (NTLM/AD) and is outside the scope of this library; the analysis did not establish that all code paths receive a `WindowsIdentity` on the request context.* This library is a **framework/provider** assembly: it supplies the `SaifAuthorizeAttribute` enforcement mechanism that consuming applications decorate their own controllers and actions with, so almost all AD resource names enforced through it are supplied by consumers at the call site and are not present in this repository. Only one AD resource name is hard-coded here.

## Authorization architecture

Authorization is structured as a single declarative MVC filter (`SaifAuthorizeAttribute`) that delegates the actual role/AD decision to a service injected via Autofac property injection, plus one standalone programmatic role check (`HasGlimpseAccess`) that has no callers within this repository.

```mermaid
flowchart TD
    Caller[Controller action decorated with SaifAuthorize] --> Attr[SaifAuthorizeAttribute.AuthorizeCore]
    Attr --> UserSvc[IUserProfileService.GetSystemUser]
    UserSvc --> IsAuth[IApplicationUserProfile.IsAuthorized]
    IsAuth --> Decision{Any role satisfied?}
    Decision -->|Yes| Allow[Action proceeds]
    Decision -->|No| Deny[HandleUnauthorizedRequest]
    Deny --> Log[LogService.LogException SecurityException]
    Deny --> Redirect["RedirectResult:<br>processerror.aspx?MSG=Not Authorized"]
```

## AD resource catalog

| Resource name | Purpose | Enforcement point | Source |
| --- | --- | --- | --- |
| `GlimpseUser{env}Resource` — resolves to `GlimpseUserResource` (production) or `GlimpseUserTestResource` (non-production) | The confirmed check is negated: an authenticated user who is **not** a member of the named role currently receives access to the Glimpse diagnostics profiler UI, while a member of the role is denied. Whether this inversion is intentional could not be confirmed from this repository.** | `SAIF.Web.HttpContextExtensions.HasGlimpseAccess(HttpContextBase)` | `src/Web/Extensions/HttpContextExtensions.cs` |

The environment suffix is computed locally by string-matching `Server.MachineName.ToLower()` for `"prod"` rather than via `IEnvironmentService`; a production host name that does not contain `"prod"` would be evaluated against the test resource name.** No other AD group, resource, or role string literal exists anywhere in this library's source. This library performs no `System.DirectoryServices` / `PrincipalContext` / `DirectorySearcher` / LDAP access — it is not an AD-provider library.

## Authorization enforcement points

### Attribute-based authorization

| Controller / Action | Attribute | Resource name(s) | Source |
| --- | --- | --- | --- |
| None in this repository — the attribute is **defined** here, not applied to any controller in this library. Consuming applications apply it to their own controllers/actions. | `SaifAuthorizeAttribute` (sealed, inherits `System.Web.Mvc.AuthorizeAttribute`), applied as `[SaifAuthorize]` | Supplied by the consumer via `SaifAuthorizeAttribute(params string[] roles)` or `SaifAuthorizeAttribute(bool forceProductionRole, params string[] roles)` | `src/Web.Mvc/ActionFilters/SaifAuthorizeAttribute.cs` |

Enforcement members inside the attribute:

| Member | Behavior | Source |
| --- | --- | --- |
| `protected override bool AuthorizeCore(HttpContextBase httpContext)` | Guards `httpContext`, `UserProfileService`, `EnvironmentService` with `Enforce.ArgumentNotNull` ([BR-15](business-rules.md#br-15-authorization-filter-requires-its-injected-services)). Returns `true` immediately when `Roles` is empty — no roles configured means allow ([BR-16](business-rules.md#br-16-no-roles-declared-authorizes-everyone)). Resolves the caller with `UserProfileService.GetSystemUser(EnvironmentService.UserId())`; returns `false` when the user is null or `UserId` is empty ([BR-17](business-rules.md#br-17-no-resolvable-system-user-is-denied)). Otherwise splits `Roles` on `,` and returns `true` if any role satisfies `user.IsAuthorized(role, _ForceProductionRole)` — OR semantics, not AND ([BR-18](business-rules.md#br-18-access-granted-on-any-satisfied-role)). | `src/Web.Mvc/ActionFilters/SaifAuthorizeAttribute.cs` |
| `protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)` | Logs a `SecurityException` ("The user '{userId}' is not authorized for this view.") through `LogService.LogException` with application name, controller/action, and a context string containing the user id and full request URL ([BR-19](business-rules.md#br-19-authorization-denials-are-logged-as-a-security-exception)); then sets `filterContext.Result` to a `RedirectResult` to the hard-coded external error page `//saifonline/weberror/processerror.aspx?MSG=Not%20Authorized` ([BR-20](business-rules.md#br-20-denied-users-are-redirected-to-the-standard-error-page)). | `src/Web.Mvc/ActionFilters/SaifAuthorizeAttribute.cs` |

`Roles` is normalized in the constructor (`string.Join(",", roles.Select(r => r.Trim()))`). `forceProductionRole` (`_ForceProductionRole`) is forwarded to `IApplicationUserProfile.IsAuthorized` and instructs the downstream service to use the production resource name regardless of the current environment; the suffix logic itself lives in the dependency implementation, not in this repository.** The attribute's collaborators (`IUserProfileService`, `ILogService`, `IEnvironmentService`) are settable public properties populated by Autofac property injection via `RegisterFilterProvider()` / `RegisterWebApiFilterProvider(GlobalConfiguration.Configuration)` in `AutofacExtensions.cs`; if a consuming application omits that registration, `AuthorizeCore` throws from `Enforce.ArgumentNotNull` — the failure mode is fail-closed by exception.*

### Programmatic authorization checks

| Class | Method | Resource / Role | Check type | Source |
| --- | --- | --- | --- | --- |
| `SAIF.Web.HttpContextExtensions` | `HasGlimpseAccess(HttpContextBase)` | `GlimpseUser{env}Resource` | `IPrincipal.IsInRole` on `context.User`, **negated** | `src/Web/Extensions/HttpContextExtensions.cs` |

The verbatim check is `context.User != null && !context.User.IsInRole(string.Format("GlimpseUser{0}Resource", env))` — confirmed from source. The role test is negated, so a method named "has access" grants access to every authenticated caller *except* members of the group whose name identifies it as the Glimpse user group. Read against the SAIF `<ResourceName>Resource` naming convention (where the group normally enumerates users who *should* have the privilege), this reads as an inverted check rather than a deliberate deny-list.* `HasGlimpseAccess` has zero call sites within this repository — it is a public extension consumed only by downstream applications, typically from a Glimpse `IRuntimePolicy`/configuration hook.* Whether the inversion is intentional cannot be determined from this repository.**

## Data-level authorization

| Entity type | Validation method | Mechanism | Source |
| --- | --- | --- | --- |
| Business Online menu items | `MenuService.GetBusinessOnlineMenu(string mailId)` | Strips any `DOMAIN\` prefix from `mailId`, then queries `IRepository<BusinessOnlineMenuItem>` for rows where `MenuUser` equals the upper-cased mail id or the literal `"UNSECURED"`, and `IsVisible == "Y"` | `src/Web.Mvc/Classes/MenuService.cs` |

This is menu **visibility** scoping driven by the `menu` data model, not an authorization enforcement point — it does not prevent direct navigation to a hidden page. Page-level protection still depends on `SaifAuthorizeAttribute` being applied by the consuming application.*

## Segregation of duties

No segregation-of-duties types or mutually-exclusive-permission rules exist in this library.

## Security interfaces and contracts

| Interface / Class | Role | Consumers | Source |
| --- | --- | --- | --- |
| `IUserProfileService`, `IApplicationUserProfile` | Resolves the current system user and evaluates `IsAuthorized(role, forceProductionRole)` | `SaifAuthorizeAttribute`, `MenuControllerBase` (`ApplicationUser.IsExternal`) | `src/Web.Mvc/ActionFilters/SaifAuthorizeAttribute.cs`, `src/Web.Mvc/Controllers/MenuControllerBase.cs` |
| `ILogService` | Logs unhandled exceptions and authorization denials | `SaifAuthorizeAttribute.HandleUnauthorizedRequest`, `HandleExceptionAttribute.OnException` | `src/Web.Mvc/ActionFilters/SaifAuthorizeAttribute.cs`, `src/Web.Mvc/ActionFilters/HandleExceptionAttribute.cs` |
| `IEnvironmentService` | Supplies the current user id (`UserId()`) and configuration lookups | `SaifAuthorizeAttribute`, `WebContextService`, `AutofacExtensions` | `src/Web.Mvc/ActionFilters/SaifAuthorizeAttribute.cs`, `src/Web/WebContextService.cs` |

## Inherited security

### From IApplicationUserProfile in SAIF.Framework.Core

| Field | Value |
| --- | --- |
| **Mechanism** | Role/resource authorization decision via `IsAuthorized(string role, bool forceProductionRole)`† |
| **Enforcement** | The actual AD group membership evaluation and production/test resource-name suffixing happen inside this dependency method; `SaifAuthorizeAttribute` only supplies the role name(s) and the `forceProductionRole` flag† |
| **Origin** | `SAIF.Framework.Core` — `IApplicationUserProfile` |
| **Source** | Retrieved from indexed documentation via Legends CLI |

## Notes
- Mark inferred statements with `*` and unknown statements with `**`. Do not use bold inline confidence labels.
- AD resource names listed here use base names only. The framework appends `Resource` (production) or `TestResource` (non-production) at runtime.

> **Note:** Statements marked with † are derived from dependency library
> documentation via the Legends CLI. Statements marked with * were inferred
> from code structure or contextual evidence. Statements marked with ** could
> not be confirmed; treat as unknown until verified.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [API reference](api/index.md)

## Sources
- `src/Web.Mvc/ActionFilters/SaifAuthorizeAttribute.cs`
- `src/Web/Extensions/HttpContextExtensions.cs`
- `src/Web.Mvc/Classes/MenuService.cs`
- `src/Web.Mvc/Controllers/MenuControllerBase.cs`
- `src/Web.Mvc/Extensions/AutofacExtensions.cs`
- `src/Web/WebContextServiceClient.cs`
- `src/app.config`
- `src/SAIF.Framework.Web.Mvc.csproj`
