---
title: SAIF.Business.Menu security and authorization
summary: Authorization requirements, database security-group model, enforcement points, and data-level access control.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-08-17
library: SAIF.Business.Menu
reference_type: package
---

# SAIF.Business.Menu security and authorization

This page documents authorization mechanisms implemented by this library. Authentication is handled entirely by infrastructure (NTLM/AD) and is out of scope here. `SAIF.Business.Menu` **is the authorization model** for the SAIF web applications in this repository: it contains no Active Directory code and no WCF `SecurityService` client. Instead, authorization is expressed as Oracle data — menu items are linked to security groups ([MENU_SECURITY](database-tables.md#menu_security)), users are linked to those same groups by name ([MENU_SECURITY_USER](database-tables.md#menu_security_user)), and `clsMenuAuthorizedItemCollection.GetMenuAuthorizedItems(dpi, menuUser)` resolves the effective, per-user set of reachable pages with a three-branch `UNION`. Every downstream authorization decision in this repository — the enforceable page check in `SAIF.Presentation.Web.MVC` and all menu/toolbar trimming in the presentation libraries — reads from this one projection.

## Authorization architecture

Authorization is data-driven and resolved entirely in Oracle, not through AD group membership checks, a WCF security service, or attribute-based authorization. The library performs authorization resolution (producing the set of items a user may reach), not denial — enforcement (redirect/deny) is the responsibility of the consuming presentation layer, which tests membership in the resolved set via [`FindPage`](operations/op-menu-02-check-page-authorization.md).

```mermaid
flowchart LR
    U[":MenuUser"] --> B1["Branch 1 — granted<br>MENU_SECURITY_USER join MENU_SECURITY<br>on SECURITY_GROUP_TXT (case-insensitive)"]
    U --> B3["Branch 3 — favorites<br>MENU_FAVORITE where MENU_USER = :MenuUser<br>and ITEM_INACTIVE_DATE is null"]
    B2["Branch 2 — unsecured<br>MENU_ITEM with no MENU_SECURITY row"] --> UN
    B1 --> UN["UNION to clsMenuAuthorizedItemCollection"]
    B3 --> UN
    UN --> C["MENU_PAGE join: page link, application code,<br>visibility flag, toolbar capability flags"]
    C --> D["Consumers: MVC page check,<br>menu and toolbar rendering"]
```

## AD resource catalog

No AD resource names, LDAP paths, `System.DirectoryServices`/`AccountManagement` usage, or authorization attributes exist in this library. The resource model is database security-group names, not AD resource names.

| Resource name | Purpose | Enforcement point | Source |
| --- | --- | --- | --- |
| _(none)_ | No Active Directory resource name is referenced anywhere in this library | — | — |

### Database security-group model (the actual resource catalog)

Group names are free-text values held in the database; no group name is hard-coded in source, so the concrete catalog of group values can only be enumerated from the `MENU_SECURITY` / `MENU_SECURITY_USER` tables in each environment.**

| Element | Table / column | Type surface | Meaning |
| --- | --- | --- | --- |
| Security group grant on a menu item | `MENU_SECURITY.SECURITY_GROUP_TXT` | `clsMenuSecurity.SecurityGroup` | Names a group that is permitted to reach the menu item identified by `MENU_ITEM_SEQ_NUM` |
| Security group membership | `MENU_SECURITY_USER.SECURITY_GROUP_TXT` | `clsMenuSecurityUser.SecurityGroupTxt` | Names the group the user belongs to |
| Menu user (subject) | `MENU_SECURITY_USER.MENU_USER` | `clsMenuSecurityUser.MenuUser` | The account name (upper-cased by callers) whose authorization is being resolved |
| Employee identifier | `MENU_SECURITY_USER.MENU_USER_EMPLID` | `clsMenuSecurityUser.MenuUserID` / `clsMenuAuthorizedItem.MenuUserEmplid` | Employee ID carried alongside the membership; surfaced on the authorized item but not used in any authorization predicate |
| Protected object | `MENU_ITEM.MENU_ITEM_SEQ_NUM` | `clsMenuItem`, `clsMenuAuthorizedItem.MenuItemSeqNum` | The unit of authorization — a page placed in a category |
| Protected resource identity | `MENU_PAGE.APPLICATION_CODE` + `MENU_PAGE.PAGE_LINK_TXT` | `clsMenuAuthorizedItem.ApplicationCode` / `.PageLink` | The `(application, page)` pair matched against the request URL by consumers |

## Authorization enforcement points

### Programmatic authorization checks

| Class | Method | Resource / Role | Check type | Source |
| --- | --- | --- | --- | --- |
| `clsMenuAuthorizedItemCollection` | `GetMenuAuthorizedItems(inDataportal, inMenuUser)` | Menu user vs. security groups | Resolution ([SQL-015](sql/SQL-015-retrieve-authorized-menu-items-for-user.md)) | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb |
| `clsMenuAuthorizedItemCollection` | `Criteria.RetrieveSQL` branch 1 (group-granted) | `MENU_SECURITY` / `MENU_SECURITY_USER` | Data-level predicate | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb |
| `clsMenuAuthorizedItemCollection` | `Criteria.RetrieveSQL` branch 2 (unsecured) | `MENU_ITEM` with no `MENU_SECURITY` row | Data-level predicate (fail-open) | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb |
| `clsMenuAuthorizedItemCollection` | `Criteria.RetrieveSQL` branch 3 (favorites) | `MENU_FAVORITE` | Data-level predicate | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb |
| `clsMenuAuthorizedItemCollection` | `GetAllMenuAuthorizedItems` / `Criteria.RetrieveAllSQL` | All users (administrative read) | Programmatic (unfiltered, `'UNSECURED'` substituted as user) | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb |
| `clsMenuAuthorizedItemCollection` | `Item` / `ItemExact` / `ItemByCategory` | In-memory filter of the authorized set | Lookup filter | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb |
| `clsMenuAuthorizedItemCollection` | `FindPage` / `FindPageExact` | `ApplicationCode` + page link | Page-level lookup (the predicate behind the enforceable page check in `SAIF.Presentation.Web.MVC`) | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb |
| `clsMenuAuthorizedItemCollection` | `SecuredAppItems` | Non-favorite, non-unsecured items | Projection | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb |
| `clsMenuAuthorizedItem` | `IsUnsecured` / `IsVisible` / `IsFavorite` | `UnsecuredFlag`, `VISIBLE_FLAG`, `FavoriteFlag` | Per-item classification | SAIF.Business.Menu/clsMenuAuthorizedItem.vb |
| `clsMenuAuthorizedItem` | `IsDisplayInsert` / `IsDisplaySave` / `IsDisplayDelete` / `IsDisplayPrint` / `IsDisplayDownload` / `IsDisplaySavePreferences` / `IsDisplayHelp` | `MENU_PAGE` `*_FLAG` columns | Capability model (toolbar gating) | SAIF.Business.Menu/clsMenuAuthorizedItem.vb |

This library performs *resolution*, not denial — the authorization predicate lives in the retrieval SQL `WHERE` clauses. There are no `Throw`-based access denials anywhere in this code.

### Authorization administration surface

These operations mutate the authorization model itself and therefore constitute privileged functionality. Nothing in this library authorizes the administrative operations themselves; the protection of these operations depends entirely on which application hosts them.*

| Operation | Statement | Bound values | Source |
| --- | --- | --- | --- |
| Grant a group access to a menu item | `INSERT INTO MENU_SECURITY` ([SQL-010](sql/SQL-010-insert-menu-security.md)) | `:MenuSecuritySeqNum`, `:MenuItemSeqNum`, `:SecurityGroup`, `SYSDATE`, `:UserId` | SAIF.Business.Menu/clsMenuSecurity.vb |
| Re-point or rename a grant | `UPDATE MENU_SECURITY` ([SQL-036](sql/SQL-036-update-menu-security.md)) | `:MenuItemSeqNum`, `:SecurityGroup`, `SYSDATE`, `:UserId` keyed by `:MenuSecuritySeqNum` | SAIF.Business.Menu/clsMenuSecurity.vb |
| Revoke a grant | `DELETE MENU_SECURITY` ([SQL-043](sql/SQL-043-delete-menu-security.md)) | keyed by `:MenuSecuritySeqNum` | SAIF.Business.Menu/clsMenuSecurity.vb |
| Add a user to a group | `INSERT INTO MENU_SECURITY_USER` ([SQL-011](sql/SQL-011-insert-menu-security-user.md)) | `MENU_SECURITY_USER_SEQ.NEXTVAL`, `:SecurityGroupTxt`, `:MenuUser`, `:MenuUserID` | SAIF.Business.Menu/clsMenuSecurityUser.vb |
| Change a membership | `UPDATE MENU_SECURITY_USER` ([SQL-037](sql/SQL-037-update-menu-security-user.md)) | `:SecurityGroupTxt`, `:MenuUser`, `:MenuUserID` keyed by `:MenuSecurityUserSeq` | SAIF.Business.Menu/clsMenuSecurityUser.vb |
| Remove a membership | `DELETE MENU_SECURITY_USER` ([SQL-044](sql/SQL-044-delete-menu-security-user.md)) | keyed by `:MenuSecurityUserSeq` | SAIF.Business.Menu/clsMenuSecurityUser.vb |
| List a user's memberships | `SELECT ... FROM MENU_SECURITY_USER WHERE MENU_USER = :MenuUser` ([SQL-030](sql/SQL-030-retrieve-menu-security-users-for-user.md)) | `:MenuUser` | SAIF.Business.Menu/clsMenuSecurityUserCollection.vb |
| List the groups granted on an item | `SELECT ... FROM MENU_SECURITY WHERE MENU_ITEM_SEQ_NUM = :MenuItemSeqNum` ([SQL-028](sql/SQL-028-retrieve-menu-security-for-item.md)) | `:MenuItemSeqNum` | SAIF.Business.Menu/clsMenuSecurityCollection.vb |

`MENU_SECURITY` writes stamp `UPDATE_DATE = SYSDATE` and `UPDATE_USER = :UserId`, giving an audit trail on grants. `MENU_SECURITY_USER` writes carry **no** update-date or update-user columns, so membership changes are not attributable from the row itself.

## Data-level authorization

| Entity type | Validation method | Mechanism | Source |
| --- | --- | --- | --- |
| `clsMenuAuthorizedItem` | `Criteria.RetrieveSQL` branches 1 and 3 | Subject-scoped retrieval — the authorization set is filtered in the database on the bind variable `:MenuUser`, never assembled client-side from a broader read | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb |
| `clsMenuItem` / `clsMenuAuthorizedItem` | `NOT EXISTS` branch, flagged `UnsecuredFlag = 'Y'` | Fail-open default for unconfigured items — a menu item with no `MENU_SECURITY` row is returned to every user rather than being denied | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb |
| `clsMenuFavorite` | Branch 3 join to `MENU_ITEM` with no reference to `MENU_SECURITY` | Favorites grant reachability independently of group membership — an active favorite row for the user adds the item to the authorized set even when no group grant exists* | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb |
| `clsMenuFavorite` | `FAV.ITEM_INACTIVE_DATE IS NULL` | Soft-delete respected on favorites | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb |
| `MENU_SECURITY` / `MENU_SECURITY_USER` | `upper(USR.SECURITY_GROUP_TXT) = upper(SEC.SECURITY_GROUP_TXT)` | Case-insensitive group matching — group names differing only in case are treated as the same group | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb |
| All | Named bind variables throughout | Parameterized binding — every user-supplied value enters SQL as a named bind variable (`:MenuUser`, `:MenuItemSeqNum`, `:SecurityGroup`, …); no string concatenation of caller input into SQL was found | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb, SAIF.Business.Menu/clsMenuSecurity.vb, SAIF.Business.Menu/clsMenuSecurityUser.vb |
| `clsApplicationCode` | Join to `BUSONLINE_APPS_CODE` | Referential scoping to active applications — item resolution joins the application table; consumers resolve the application via `GetActiveApplicationCodes` | SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb, SAIF.Business.Menu/clsApplicationCodeCollection.vb |

## Segregation of duties

No segregation-of-duties rules are checked anywhere in this library — no mutually exclusive privilege combination is validated in code.

## Security interfaces and contracts

No security-specific interfaces or base classes (e.g. `ISecurityManager`) are defined or consumed by this library. Authorization is expressed entirely through data (security-group tables) and the `clsMenuAuthorizedItemCollection` projection described above.

## Notes
- Mark inferred statements with `*` and unknown statements with `**`. Do not use bold inline confidence labels.
- Concrete security-group names cannot be enumerated from source — they are data-only values in `MENU_SECURITY` / `MENU_SECURITY_USER`.**
- No `BUSINESS RULES` region in any of the menu business objects is populated, so there is no code-level validation preventing, for example, a membership row that names a group no menu item grants, or a favorite pointing at an item the user has no grant for.
- The relationship between `MENU_SECURITY_USER.SECURITY_GROUP_TXT` values and Windows/AD groups is not expressed in code; whether these strings mirror AD group names or are an independent naming scheme is not determinable from this repository.**

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Database tables](database-tables.md)
- [SQL catalog](sql/index.md)
- [API reference](api/index.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts. Statements marked with `**` could not be confirmed; treat as unknown until verified.

## Sources
- SAIF.Business.Menu/clsMenuAuthorizedItemCollection.vb
- SAIF.Business.Menu/clsMenuAuthorizedItem.vb
- SAIF.Business.Menu/clsMenuSecurity.vb
- SAIF.Business.Menu/clsMenuSecurityCollection.vb
- SAIF.Business.Menu/clsMenuSecurityUser.vb
- SAIF.Business.Menu/clsMenuSecurityUserCollection.vb
- SAIF.Business.Menu/clsMenuFavorite.vb
- SAIF.Business.Menu/clsMenuItem.vb
- SAIF.Business.Menu/clsMenuPage.vb
- SAIF.Business.Menu/clsApplicationCodeCollection.vb
- SAIF.Business.Menu/SAIF.Business.Menu.vbproj
