---
title: SAIF.Framework.Security.EnvironmentApi business rules
summary: Enumerated business rules with per-rule trigger, enforcement, and outcomes.
status: draft
owner: <owner>
last_reviewed: 2026-07-23
library: SAIF.Framework.Security.EnvironmentApi
reference_type: package
---

# SAIF.Framework.Security.EnvironmentApi business rules

This page captures business rules enforced by this library.

## BR-001: Configuration key must resolve or fail loudly

| Field | Value |
| --- | --- |
| **Statement** | A configuration key not found in the developer override file, the in-memory cache, or the fetched section must fail loudly rather than silently return a default value. |
| **Trigger** | `GetConfigSettingInfo` exhausts all lookup sources (override file, cache, and section fetch) for a requested key. |
| **Enforcement** | Throws `ApplicationException("The key '{0}' for track '{1}' and section '{2}' was not found")`. |
| **Outcome** | Callers cannot silently receive a missing or default config value. |
| **Source** | SAIF.Framework.Security.EnvironmentApi\Services.Common\EnvironmentServiceBase.cs |

---

## BR-002: Environment API base URL must be present

| Field | Value |
| --- | --- |
| **Statement** | The Environment API base URL must be present as an OS environment variable before any environment-dependent operation executes. |
| **Trigger** | Any access to `EnvironmentApiUrl` when `Environment.GetEnvironmentVariable("EnvironmentApiUrl")` is null. |
| **Enforcement** | Throws `MissingEnvironmentUrlVariableException`. |
| **Outcome** | Environment-dependent operations cannot proceed without a valid Environment API endpoint configured. |
| **Source** | SAIF.Framework.Security.EnvironmentApi\Services.Common\EnvironmentServiceBase.cs, SAIF.Framework.Security.EnvironmentApi\Exceptions\MissingEnvironmentUrlVariableException.cs |

---

## BR-003: Configuration payload must declare a refresh interval

| Field | Value |
| --- | --- |
| **Statement** | The environment configuration payload must declare a refresh-check interval. |
| **Trigger** | A newly downloaded `ConfigurationSettings.Settings` dictionary lacking the `checkforupdatedsettingsinseconds` key. |
| **Enforcement** | Throws `KeyNotFoundException`. |
| **Outcome** | The refresh-throttling mechanism cannot silently operate with an undefined interval. |
| **Source** | SAIF.Framework.Security.EnvironmentApi\Services.Common\EnvironmentServiceBase.cs |

---

## BR-004: Developer settings override requires developer-machine status

| Field | Value |
| --- | --- |
| **Statement** | A developer-settings override file is only honored on a machine already classified as a developer machine. |
| **Trigger** | Request for `DeveloperSettingsFile` when `IsDeveloperMachine` is false. |
| **Enforcement** | The `DeveloperSettingsFile` getter only resolves the file path when `IsDeveloperMachine` is true; otherwise it falls back to an empty string. |
| **Outcome** | Non-developer machines cannot use local configuration overrides to bypass the central Environment API. |
| **Source** | SAIF.Framework.Security.EnvironmentApi\Services.Common\EnvironmentServiceBase.cs |

---

## BR-005: Developer-machine classification requires tag-number host and AD membership

| Field | Value |
| --- | --- |
| **Statement** | A host is treated as a "developer machine" only if its name matches a tag-number pattern (`^[TV]\d+$`, case-insensitive) *and* it is a member of the `developer_machine_users` AD group. |
| **Trigger** | `IsDeveloperMachine` property access. |
| **Enforcement** | A regex check in `IsMachineTagNumber` short-circuits to `false` before the AD membership check via `MachineSecurity.IsAuthorized`. |
| **Outcome** | Only hosts named with the tag-number convention and enrolled in the AD group are treated as developer machines; all other hosts skip the AD lookup entirely. |
| **Source** | SAIF.Framework.Security.EnvironmentApi\Services.Common\EnvironmentServiceBase.cs |

---

## BR-006: Machine/service account authorization via AD group membership

| Field | Value |
| --- | --- |
| **Statement** | A machine/service account is authorized against an AD "resource" group only if it is found — directly or via nested/recursive group membership — in that AD group, with account names compared case-insensitively after trimming `$` suffixes. |
| **Trigger** | `MachineSecurityService.IsAuthorized` call. |
| **Enforcement** | String comparison loop in `MachineSecurityService.IsAuthorized` against members returned by `ADService.GetUsersByGroup`. |
| **Outcome** | Machine accounts are authorized only when actual AD group membership (including nested groups) is confirmed. |
| **Source** | SAIF.Framework.Security.EnvironmentApi\Core\Services\MachineSecurityService.cs |

---

## BR-007: Production vs. non-production AD resource-name convention

| Field | Value |
| --- | --- |
| **Statement** | AD resource group names for production vs. non-production must be distinguished by suffix. |
| **Trigger** | `IsAuthorized(isResource: true, isProductionResource: false)`. |
| **Enforcement** | `groupName += "Test"` then `groupName += "Resource"` in `MachineSecurityService.IsAuthorized`, matching the SAIF `<ResourceName>Resource` / `<ResourceName>TestResource` convention. |
| **Outcome** | Production and non-production authorization checks are targeted at distinct AD groups, preventing accidental cross-environment authorization. |
| **Source** | SAIF.Framework.Security.EnvironmentApi\Core\Services\MachineSecurityService.cs |

---

## BR-008: System hash must not be expired

| Field | Value |
| --- | --- |
| **Statement** | A system hash used for secure config retrieval must not be older than its configured expiry window (allowing a caller-provided buffer). |
| **Trigger** | `RemoveSystemHashSaltAndValidateLifetime` call. |
| **Enforcement** | Throws `SystemHashExpiredException` when the un-salted creation timestamp falls outside the valid `[now-buffer, expires)` window. |
| **Outcome** | Expired system hashes cannot be used to retrieve secure configuration settings. |
| **Source** | SAIF.Framework.Security.EnvironmentApi\Services.Common\SystemHashUtil.cs |

---

## BR-009: Global section is the sole non-custom section

| Field | Value |
| --- | --- |
| **Statement** | A "custom section" of configuration is any section other than the global section.* |
| **Trigger** | `ConfigKey.IsCustomSection`/`GetCustomSection`/`FormattedKey` evaluation. |
| **Enforcement** | Compares `Section` against `Constants.GlobalSection` ("global") case-insensitively. |
| **Outcome** | Section-scoped configuration keys are distinguished from global keys for lookup and formatting purposes. |
| **Source** | SAIF.Framework.Security.EnvironmentApi\Core\Contracts\Core\Configurations\ConfigurationSettings.cs |

## Notes
- `MachineSecurityService.IsAuthorized` (enforcing [BR-006](#br-006-machineservice-account-authorization-via-ad-group-membership) and [BR-007](#br-007-production-vs-non-production-ad-resource-name-convention)) fails closed (returns `false`) on any AD-lookup exception, logging only via `Debug.WriteLine`* — see [security-authorization.md](security-authorization.md) for the full authorization architecture.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Configuration](configuration.md)
- [Security and authorization](security-authorization.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.Framework.Security.EnvironmentApi\Services.Common\EnvironmentServiceBase.cs
- SAIF.Framework.Security.EnvironmentApi\Exceptions\MissingEnvironmentUrlVariableException.cs
- SAIF.Framework.Security.EnvironmentApi\Core\Services\MachineSecurityService.cs
- SAIF.Framework.Security.EnvironmentApi\Services.Common\SystemHashUtil.cs
- SAIF.Framework.Security.EnvironmentApi\Core\Contracts\Core\Configurations\ConfigurationSettings.cs
