---
title: Security role assignment
summary: Sets and exposes the single effective application security role a manager instance holds at any time.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-07-30
library: SAIF.Application
reference_type: package
operation_id: OP-APP-02
trigger: A subclass evaluates the current caller or application state and assigns a role through MarkUpdate, MarkViewOnly, or MarkNoAccess
---

# Security role assignment

`AppManagerBase` reduces application-level authorization to a single enum value, `AppSecurityRole`, and derives every caller-visible accessibility flag from that one value. A subclass's `OnCheckSecurity()` hook (declared `MustOverride`, implemented by consuming application managers) is the expected place to evaluate whatever criteria the application uses and then call one of the three role-setting methods.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-APP-02 |
| **Trigger** | Subclass `OnCheckSecurity()` implementation (or any other subclass code) calls `MarkUpdate()`, `MarkViewOnly()`, or `MarkNoAccess()` |
| **Inputs** | Whatever caller/application state the subclass's security evaluation logic examines (not part of this library) |
| **Outputs** | Updated `m_AppSecurityRole` value; derived `SecurityRole`, `IsAccessible`, `IsUpdateable`, `IsViewOnly`, `IsRestricted` property values |
| **Primary components** | `AppManagerBase.MarkUpdate`, `AppManagerBase.MarkViewOnly`, `AppManagerBase.MarkNoAccess`, `AppManagerBase.SecurityRole`, `AppManagerBase.IsAccessible`, `AppManagerBase.IsUpdateable`, `AppManagerBase.IsViewOnly`, `AppManagerBase.IsRestricted` |
| **Entry point** | `Application\SAIF.Application\AppManagerBase.vb` |

## Steps

1. **Manager starts with no access.** The private field `m_AppSecurityRole` is initialized to `AppSecurityRole.NoAccess` at declaration, so any manager instance is restricted by default until its security evaluation explicitly grants a different role ([BR-001](../business-rules.md#br-001-single-effective-application-security-role)).
2. **Subclass evaluates security state.** The subclass's `OnCheckSecurity()` override runs whatever caller/context checks the consuming application requires (for example, comparing an `AppSecurityRole` derived from an AD group lookup or resource check performed via [AD resource access check (cached)](ad-resource-access-check-cached.md)).
   - This step may embed a call into `UserAccess.HasAccess(...)` to decide which role to assign; see that operation's own steps for how the underlying authorization decision is made and cached.
3. **Assign exactly one role.** The subclass calls exactly one of:
   - `MarkUpdate()` — sets `m_AppSecurityRole = AppSecurityRole.Update`.
   - `MarkViewOnly()` — sets `m_AppSecurityRole = AppSecurityRole.ViewOnly`.
   - `MarkNoAccess()` — sets `m_AppSecurityRole = AppSecurityRole.NoAccess`.
   - Each method unconditionally overwrites `m_AppSecurityRole` with no validation of the prior value, so calling more than one in sequence simply leaves the role at whichever was called last ([BR-001](../business-rules.md#br-001-single-effective-application-security-role)).
4. **Derive read-only accessibility flags.** Callers read any of the following properties, each of which recomputes its result from the single `m_AppSecurityRole` value on every access (no caching):
   - `SecurityRole` returns `m_AppSecurityRole` directly.
   - `IsAccessible` returns `True` when `SecurityRole <> AppSecurityRole.NoAccess`.
   - `IsRestricted` returns `True` when `SecurityRole = AppSecurityRole.NoAccess`.
   - `IsUpdateable` returns `True` when `SecurityRole = AppSecurityRole.Update`.
   - `IsViewOnly` returns `True` when `SecurityRole = AppSecurityRole.ViewOnly`.
   - Because these are simple derivations of one field, `IsAccessible` and `IsRestricted` are always complements of each other, and exactly one of `IsUpdateable` / `IsViewOnly` / `IsRestricted` is `True` at any time ([BR-001](../business-rules.md#br-001-single-effective-application-security-role)).

## Process flow

The following diagram visualizes how a role assignment call updates the derived accessibility flags.

```mermaid
flowchart TD
	Start([Subclass OnCheckSecurity runs]) --> Eval[Evaluate caller / application state]
	Eval --> Which{Which role applies?}
	Which -->|Full access| MarkUpdate[MarkUpdate sets Update]
	Which -->|Read only| MarkViewOnly[MarkViewOnly sets ViewOnly]
	Which -->|No access| MarkNoAccess[MarkNoAccess sets NoAccess]
	MarkUpdate --> Derive[Callers read SecurityRole,<br>IsAccessible, IsUpdateable,<br>IsViewOnly, IsRestricted]
	MarkViewOnly --> Derive
	MarkNoAccess --> Derive
	Derive --> End([Caller adjusts UI / logic<br>based on flags])
```

## Failure and edge-case behavior

- `AppManagerBase` never calls `OnCheckSecurity()` itself in the code shown in this library — it is a `MustOverride` hook that the consuming application is responsible for invoking at the appropriate point in its own lifecycle; if a subclass never calls it (or never calls one of the `Mark*` methods), the manager remains at the default `NoAccess` role ([BR-001](../business-rules.md#br-001-single-effective-application-security-role)).
- Calling more than one `Mark*` method leaves the role at the last call; there is no guard against contradictory sequential assignment within a single evaluation.*
- No event or notification is raised when the role changes; callers must re-read the accessibility properties after each `OnCheckSecurity()` cycle to see updated values.*

> **Note:** Statements marked with `*` were inferred from code structure, naming conventions, or
> contextual evidence where direct confirmation was not available in source artifacts.

## Related documentation

- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [Security and authorization](../security-authorization.md)

## Sources

- `Application\SAIF.Application\AppManagerBase.vb`
- `Application\SAIF.Application\Enums.vb`
