Skip to content

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

  1. Subclass supplies the principal. A concrete UserAccess subclass calls the protected SetPrincipal(inPrincipal As clsSecurityPrincipal) to store the framework security principal used for all subsequent authorization checks on that instance.
  2. Caller checks a single resource. HasAccess(inResourceName As String):
  3. Looks up inResourceName.ToLower in the private m_AccessControl sorted list.
  4. If the lower-cased key is already present: returns the cached boolean immediately without calling the principal (BR-003).
  5. If the key is not present: calls m_Principal.IsAuthorized(inResourceName) (using the original, non-lower-cased resource name), stores the result in m_AccessControl under the lower-cased key, and returns that result. > Origin: The actual authorization decision is made by clsSecurityPrincipal.IsAuthorized(...), provided by the SAIF.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.**
  6. Caller checks multiple resources. CheckAccess(ParamArray inResourceName As String()):
  7. Iterates every supplied resource name and calls HasAccess(inKey) for each, reassigning the loop's local Result variable to the return value on every iteration — so only the last resource name's result is ever held.
  8. After the loop completes, Result = Nothing resets that local variable to False (VB.NET's default for Boolean).
  9. The method has no return value and does not expose Result to the caller in any way — the entire computed value is discarded ([see failure note below]).
  10. 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 in m_AccessControl skips 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 (each HasAccess call 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" from CheckAccess alone get nothing usable back; they must call HasAccess per resource name themselves to read a result.
  • The explicit Result = Nothing after the loop in CheckAccess reinforces 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.*
  • HasAccess caches on the lower-cased resource name but calls IsAuthorized with the original-case resource name, so if clsSecurityPrincipal.IsAuthorized is 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 before HasAccess(...) runs, m_Principal is Nothing and the call to m_Principal.IsAuthorized(...) raises a NullReferenceException.*

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

  • Application\SAIF.Application\UserAccess.vb