Menu authorization data lookup¶
Menu authorization data lookup is the read-heavy core of the library: it records the many-to-many mapping between menu items and security groups (clsMenuSecurity) and between users and security groups (clsMenuSecurityUser), then combines both with MENU_ITEM, MENU_PAGE, BUSONLINE_APPS_CODE, MENU_CATEGORY_CODE, and MENU_FAVORITE in a three-part UNION query (clsMenuAuthorizedItemCollection) that returns every menu item a given user is authorized to see, flagged as secured, unsecured, or favorited. A caller with no user context gets a two-part UNION covering all secured and unsecured items without per-user favorites.
| Field | Value |
|---|---|
| Operation ID | OP-MNU-04 |
| Trigger | Caller requests clsMenuAuthorizedItemCollection with or without a MenuUser; caller retrieves/maintains clsMenuSecurity/clsMenuSecurityCollection or clsMenuSecurityUser/clsMenuSecurityUserCollection |
| Inputs | MenuUser (authorized-item lookup); MenuItemSeqNum, SecurityGroup (security-group mapping); SecurityGroupTxt, MenuUser, MenuUserID (user-to-group mapping) |
| Outputs | clsMenuAuthorizedItemCollection populated with the caller's authorized/favorited items (or all items); persisted clsMenuSecurity/clsMenuSecurityUser mapping rows; error messages on DML contention |
| Primary components | clsMenuAuthorizedItemCollection, clsMenuAuthorizedItem, clsMenuSecurity, clsMenuSecurityCollection, clsMenuSecurityUser, clsMenuSecurityUserCollection |
| Entry point | Business\SAIF.Business.Menu\clsMenuAuthorizedItemCollection.vb |
Steps¶
- Request the authorized-item projection. The caller invokes
clsMenuAuthorizedItemCollection.GetMenuAuthorizedItems(dataportal, menuUser)for a specific user, orGetAllMenuAuthorizedItems(dataportal)for the unfiltered projection. - Dispatch on user context.
BeforeRetrievechecksMyCriteria.MenuUser > String.Empty. - If a
MenuUseris present: bind:MenuUserand execute the three-part UNIONRetrieveSQL(SQL-016). - If no
MenuUseris present: execute the two-part UNIONRetrieveAllSQL(SQL-015), which takes no bind parameter. - Per-user UNION, part 1 — secured items. Joins
MENU_ITEM,MENU_SECURITY,MENU_SECURITY_USER,MENU_PAGE,BUSONLINE_APPS_CODE, andMENU_CATEGORY_CODE, matching the item'sMENU_SECURITYrecord to the user'sMENU_SECURITY_USERrecord on security-group text (BR-MNU-01). - The join predicate compares
upper(USR.SECURITY_GROUP_TXT) = upper(SEC.SECURITY_GROUP_TXT), so group matching is case-insensitive (BR-MNU-04). - Each returned row is flagged
UnsecuredFlag = 'N',FavoriteFlag = 'N'. - Per-user UNION, part 2 — unsecured items. Selects every item with
NOT EXISTS (SELECT 1 FROM menu_security SEC WHERE SEC.MENU_ITEM_SEQ_NUM = ITM.MENU_ITEM_SEQ_NUM)— items with noMENU_SECURITYrow at all are visible to every user regardless of group (BR-MNU-01). - Each returned row is flagged
UnsecuredFlag = 'Y'. - Per-user UNION, part 3 — active favorites. Joins
MENU_FAVORITEonMENU_ITEM_SEQ_NUM, filteringFAV.MENU_USER = :MenuUser AND FAV.ITEM_INACTIVE_DATE IS NULL— only the requesting user's own non-deactivated favorites are folded in, flaggedFavoriteFlag = 'Y'(BR-MNU-05; see Favorite menu management). - All-user UNION (no
MenuUser). Runs the same secured/unsecured pattern as steps 3–4 but without a:MenuUserbind or a favorites leg — the secured leg returnsUSR.MENU_USER AS MenuUserfor every user/item pairing rather than filtering to one user, and the unsecured leg tags rowsMenuUser = 'UNSECURED'. - Load the results.
AfterRetrievereads every row of the UNION result set and loads oneclsMenuAuthorizedItemper row, including the resolvedApplicationPath,Category,PageName,PageLink, and every toolbar flag/tooltip needed for menu rendering without a further join by the presentation layer. - In-memory lookup helpers. Once loaded, the collection exposes
Item(appCode, category, pageLink)andItemByCategory(category, pageLink)indexers that match on trimmed, upper-cased page-link text — optionally normalized viaLinkOnlyto strip query-string parameters — letting the presentation layer resolve a specific authorized item without a further database round trip.* - Maintain the security-group-to-item mapping.
clsMenuSecurity.NewMenuSecurityobtainsMENU_SECURITY_SEQ.NEXTVAL(SQL-004, DUAL);Save()executesInsertSQL(SQL-010, MENU_SECURITY) with the row-count contention check (BR-MNU-02).clsMenuSecurityCollectionretrieves by sequence key (SQL-026), by menu item (SQL-027), or unfiltered (SQL-028). - Maintain the user-to-group mapping.
clsMenuSecurityUser.Save()executesInsertSQL(SQL-011, MENU_SECURITY_USER) — the sequence for this table is generated inline in the insert statement itself (MENU_SECURITY_USER_SEQ.NEXTVAL) rather than via a separate scalar sequence call, unlike the other four tables in this library.*clsMenuSecurityUserCollection.BeforeRetrievebranches on whetherMyCriteria.MenuUseris populated:- Likely source defect: the branch is inverted relative to every other collection in this library — when
MenuUseris non-empty, it selectsretrieveForUserSQL(SQL-029) but does not callAddParm("MenuUser", ...), even thoughretrieveForUserSQLappendsWHERE MENU_USER = :MenuUser; whenMenuUseris empty, it selects the unfilteredRetrieveSQL(SQL-030) but does callAddParm("MenuUser", ...), even though that SQL text has no bind variable to receive it.** This would cause a missing-bind-variable error on the filtered path and a harmless unused parameter on the unfiltered path, rather than the intended per-user filtering behavior.
- Likely source defect: the branch is inverted relative to every other collection in this library — when
Process flow¶
The following diagram visualizes the primary authorized-menu-item retrieval flow.
flowchart TD
Start([Start: request authorized menu items]) --> CheckUser{MenuUser provided?}
CheckUser -->|Yes| SQL016[Execute per-user 3-part<br>UNION query with favorites]
CheckUser -->|No| SQL015[Execute all-user 2-part<br>UNION query without favorites]
SQL016 --> Load[Load one clsMenuAuthorizedItem per row]
SQL015 --> Load
Load --> Result([Return authorized item collection])
Result --> Presentation[Presentation layer enforces<br>final visibility/authorization decision]
Failure and edge-case behavior¶
- The security decision itself — whether to actually hide or show a menu item to the end user — is made by the consuming presentation layer using the
UnsecuredFlag/FavoriteFlagdata this library returns; this library only assembles and returns the candidate data set (BR-MNU-01). - Every insert on
clsMenuSecurity/clsMenuSecurityUserenforces the rows-affected-equals-1 contention check (BR-MNU-02). clsMenuSecurityUserCollection.BeforeRetrievehas an inverted parameter-binding branch (see step 10) that is a likely source defect; querying security users scoped to a specificMenuUsermay fail or silently return the wrong result set until corrected.**- Items with no
MENU_SECURITYrow are unsecured by default and visible to all users — there is no "deny by default" posture in this data model; an administrator who forgets to add aMENU_SECURITYrow for a new sensitive item leaves it visible to everyone (BR-MNU-01). - The all-user (
GetAllMenuAuthorizedItems) projection has no favorites leg at all, so favorite bookmarks never appear when noMenuUseris supplied. - Security-group matching is case-insensitive via
UPPER(), soSecurityGroupTxtvalues that differ only in case are treated as the same group acrossMENU_SECURITYandMENU_SECURITY_USER(BR-MNU-04).
Related documentation¶
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¶
Business\SAIF.Business.Menu\clsMenuAuthorizedItem.vbBusiness\SAIF.Business.Menu\clsMenuAuthorizedItemCollection.vbBusiness\SAIF.Business.Menu\clsMenuSecurity.vbBusiness\SAIF.Business.Menu\clsMenuSecurityCollection.vbBusiness\SAIF.Business.Menu\clsMenuSecurityUser.vbBusiness\SAIF.Business.Menu\clsMenuSecurityUserCollection.vb