AD resource access check (cached)¶
UserAccess is an abstract cache wrapper over the framework's clsSecurityPrincipal.IsAuthorized(...) check. A concrete subclass supplies the principal through the protected SetPrincipal(...) method; callers then ask HasAccess(resourceName) (or the multi-resource CheckAccess(...)) whether the current principal is authorized for a resource, and the first answer for any given resource name is cached for the life of the UserAccess instance.
| Field | Value |
|---|---|
| Operation ID | OP-APP-06 |
| Trigger | Caller invokes HasAccess(inResourceName) directly, or CheckAccess(ParamArray inResourceName()) for one or more resource names |
| Inputs | One or more AD resource-name strings; the clsSecurityPrincipal set on the subclass via SetPrincipal(...) |
| Outputs | Boolean authorization outcome for HasAccess; CheckAccess produces no caller-visible aggregate result (see failure notes) |
| Primary components | UserAccess.HasAccess, UserAccess.CheckAccess, UserAccess.SetPrincipal, clsSecurityPrincipal.IsAuthorized |
| Entry point | Application\SAIF.Application\UserAccess.vb |
Steps¶
- Subclass supplies the principal. A concrete
UserAccesssubclass calls the protectedSetPrincipal(inPrincipal As clsSecurityPrincipal)to store the framework security principal used for all subsequent authorization checks on that instance. - Caller checks a single resource.
HasAccess(inResourceName As String): - Looks up
inResourceName.ToLowerin the privatem_AccessControlsorted list. - If the lower-cased key is already present: returns the cached boolean immediately without calling the principal (BR-003).
- If the key is not present: calls
m_Principal.IsAuthorized(inResourceName)(using the original, non-lower-cased resource name), stores the result inm_AccessControlunder the lower-cased key, and returns that result. > Origin: The actual authorization decision is made byclsSecurityPrincipal.IsAuthorized(...), provided by theSAIF.BOF.Framework(SAIF.BOF.Security) NuGet package dependency. That package is consumed as a framework dependency rather than a locally documented operation in this repository; its own authorization logic is out of scope for this library's documentation.** - Caller checks multiple resources.
CheckAccess(ParamArray inResourceName As String()): - Iterates every supplied resource name and calls
HasAccess(inKey)for each, reassigning the loop's localResultvariable to the return value on every iteration — so only the last resource name's result is ever held. - After the loop completes,
Result = Nothingresets that local variable toFalse(VB.NET's default forBoolean). - The method has no return value and does not expose
Resultto the caller in any way — the entire computed value is discarded ([see failure note below]). - Subsequent checks for the same resource are served from cache. Any later
HasAccess(...)call for a resource name whose lower-cased form is already a key inm_AccessControlskips the principal check entirely, per BR-003.
Process flow¶
The following diagram visualizes the cache-first authorization check and the discarded-result behavior of the batch entry point.
flowchart TD
Start([Caller requests access check]) --> Which{HasAccess or CheckAccess?}
Which -->|HasAccess single resource| CacheCheck{Resource already<br>cached lower-cased?}
CacheCheck -->|Yes| ReturnCached([Return cached boolean])
CacheCheck -->|No| CallPrincipal[Call clsSecurityPrincipal.IsAuthorized]
CallPrincipal --> StoreCache[Store result under<br>lower-cased key]
StoreCache --> ReturnNew([Return new result])
Which -->|CheckAccess multiple resources| Loop[For each resource name:<br>call HasAccess, overwrite Result]
Loop --> Reset[Result = Nothing<br>after loop]
Reset --> Discard([No value returned to caller;<br>only cache side effects persist])
Failure and edge-case behavior¶
CheckAccess(...)populates the cache as a side effect (eachHasAccesscall inside the loop still caches its own resource), but the method itself exposes no aggregate authorization result — callers who intend to learn "is the user authorized for all of these resources" fromCheckAccessalone get nothing usable back; they must callHasAccessper resource name themselves to read a result.- The explicit
Result = Nothingafter the loop inCheckAccessreinforces that the local variable's final value is intentionally discarded, not returned — this looks like dead code that a caller could mistakenly assume has an effect.* HasAccesscaches on the lower-cased resource name but callsIsAuthorizedwith the original-case resource name, so ifclsSecurityPrincipal.IsAuthorizedis itself case-sensitive, the cache key normalization does not guarantee that two differently-cased calls for "the same" resource actually reach the principal with identical casing on the first (cache-populating) call.*- If
SetPrincipal(...)is never called beforeHasAccess(...)runs,m_PrincipalisNothingand the call tom_Principal.IsAuthorized(...)raises aNullReferenceException.*
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.
Related documentation¶
Sources¶
Application\SAIF.Application\UserAccess.vb