---
title: SAIF.Business.External.Users business rules
summary: Enumerated business rules with per-rule trigger, enforcement, and outcomes.
status: draft
owner: <owner>
last_reviewed: 2026-07-30
library: SAIF.Business.External.Users
reference_type: package
---

# SAIF.Business.External.Users business rules

This page captures business rules enforced by or associated with this library.

## BR-EU-01: Authorized-user flag persistence

| Field | Value |
| --- | --- |
| **Statement** | An external user record carries an `IsAuthorizedUser` flag persisted to `EXT_USER.AUTHORIZED_USER_FLAG`; the flag gates access decisions in calling application layers.* |
| **Trigger** | Insert or update of an `EXT_USER` record. |
| **Enforcement** | `clsUser.InsertSQL` ([SQL-005](sql/SQL-005-saif-business-external-users.md)) and `clsUser.UpdateSQL` ([SQL-045](sql/SQL-045-saif-business-external-users.md)) bind the `:IsAuthorizedUser` parameter to `AUTHORIZED_USER_FLAG`. Consumption of the flag for access-gating is not visible within this library — it is presumed to occur in a calling application layer.* |
| **Outcome** | The flag value is persisted to and retrieved from `EXT_USER`. Access enforcement based on the flag is deferred to the consuming application.* |
| **Confidence** | Inferred |
| **Tables** | [EXT_USER](database-tables.md#ext_user) |
| **SQL** | [SQL-005](sql/SQL-005-saif-business-external-users.md), [SQL-015](sql/SQL-015-saif-business-external-users.md), [SQL-045](sql/SQL-045-saif-business-external-users.md) |
| **Source** | `Business\SAIF.Business.External.Users\clsUser.vb` |

---

## BR-EU-02: Deny-self-registration block-list

| Field | Value |
| --- | --- |
| **Statement** | A user matching a deny-self-registration block-list entry (by user-type code and customer-group ID) cannot self-register.** |
| **Trigger** | Self-registration request for a user-type/customer-group pair that exists in `EXT_USER_DENY_SELF_REG`.** |
| **Enforcement** | `clsUserDenySelfRegistration` and its collection represent the block-list; the exact matching/enforcement logic (e.g., which caller checks the list before allowing registration) was not traced in the analysis pass.** |
| **Outcome** | Presence of a matching row in `EXT_USER_DENY_SELF_REG` is expected to block self-registration for that user-type/customer-group pair.* |
| **Confidence** | Unknown |
| **Tables** | [EXT_USER_DENY_SELF_REG](database-tables.md#ext_user_deny_self_reg) |
| **SQL** | [SQL-007](sql/SQL-007-saif-business-external-users.md), [SQL-027](sql/SQL-027-saif-business-external-users.md), [SQL-048](sql/SQL-048-saif-business-external-users.md), [SQL-054](sql/SQL-054-saif-business-external-users.md) |
| **Source** | `Business\SAIF.Business.External.Users\clsUserDenySelfRegistration.vb` |

---

## BR-EU-03: Audit-assignment auditor distinctness

| Field | Value |
| --- | --- |
| **Statement** | The default auditor and the do-not-assign auditor on an audit assignment override record must be different individuals.* |
| **Trigger** | Insert or update of a `PA_ASSIGN_OVERRIDE` record via `clsAuditAssignmentOverride`. |
| **Enforcement** | `clsAuditAssignmentOverride.ValidAuditors` validation method (referenced in the DB analysis cross-references section).* |
| **Outcome** | If the default auditor ID and the do-not-assign auditor ID are identical, the record fails validation and an error is added.* |
| **Confidence** | Inferred |
| **Tables** | [PA_ASSIGN_OVERRIDE](database-tables.md#pa_assign_override) |
| **SQL** | [SQL-002](sql/SQL-002-saif-business-external-users.md), [SQL-010](sql/SQL-010-saif-business-external-users.md), [SQL-042](sql/SQL-042-saif-business-external-users.md) |
| **Source** | `Business\SAIF.Business.External.Users\clsAuditAssignmentOverride.vb` |

---

## BR-EU-04: Unique active email per non-test user

| Field | Value |
| --- | --- |
| **Statement** | An active, non-deleted, non-test external user must not share an email address with another active user.* |
| **Trigger** | User registration or update that would produce a duplicate active email address.* |
| **Enforcement** | [SQL-016](sql/SQL-016-saif-business-external-users.md) queries `EXT_USER` joining `EMAIL_ADDRESS` and `EXT_USER_TYPE` with filters `test_user_flag = 'N'` and `status_code <> 'DEL'`; the calling method uses the result to detect duplicates before persisting.* |
| **Outcome** | Duplicate detection prevents more than one active non-test user from sharing the same email address.* |
| **Confidence** | Inferred |
| **Tables** | [EXT_USER](database-tables.md#ext_user), [EMAIL_ADDRESS](database-tables.md#email_address), [EXT_USER_TYPE](database-tables.md#ext_user_type) |
| **SQL** | [SQL-016](sql/SQL-016-saif-business-external-users.md) |
| **Source** | `Business\SAIF.Business.External.Users\clsUser.vb` |

---

## BR-EU-05: Optimistic concurrency via UPDATE_NUM

| Field | Value |
| --- | --- |
| **Statement** | All update operations on BOF-managed tables include an `UPDATE_NUM` optimistic-concurrency check; a mismatch indicates a concurrent modification and the update is rejected.* |
| **Trigger** | Any update call on `clsUser`, `clsUserType`, `clsUserAccessGroup`, `clsUserDenySelfRegistration`, or `clsUserSecurityQuestion`.* |
| **Enforcement** | `UpdateSQL` for each affected class appends `AND update_num = :UpdateNum` to the `WHERE` clause and increments the persisted value (`UPDATE_NUM = :UpdateNum+1`). BOF row-count checks after the update detect a mismatch and add an error message.* |
| **Outcome** | Concurrent modifications to the same row are detected and surfaced as an error to the caller, preventing silent data loss.* |
| **Confidence** | Inferred |
| **Tables** | [EXT_USER](database-tables.md#ext_user), [EXT_USER_TYPE](database-tables.md#ext_user_type), [EXT_USER_ACCESS_GROUP](database-tables.md#ext_user_access_group), [EXT_USER_DENY_SELF_REG](database-tables.md#ext_user_deny_self_reg), [EXT_USER_SECURITY_QUESTION](database-tables.md#ext_user_security_question) |
| **SQL** | [SQL-045](sql/SQL-045-saif-business-external-users.md), [SQL-047](sql/SQL-047-saif-business-external-users.md), [SQL-048](sql/SQL-048-saif-business-external-users.md), [SQL-049](sql/SQL-049-saif-business-external-users.md), [SQL-050](sql/SQL-050-saif-business-external-users.md) |
| **Source** | `Business\SAIF.Business.External.Users\clsUser.vb`, `Business\SAIF.Business.External.Users\clsUserType.vb`, `Business\SAIF.Business.External.Users\clsUserAccessGroup.vb` |

---

## BR-EU-06: Password composition and personal-data exclusion

| Field | Value |
| --- | --- |
| **Statement** | A password must be 8-128 characters long, satisfy at least three enabled character classes, and must not contain the user ID, first name, or last name. |
| **Trigger** | Caller validates a password through `clsPassword.IsValid`. |
| **Enforcement** | `clsPassword.IsValid` checks minimum length, maximum length, class complexity, user-ID exclusion, and first-name/last-name exclusion, adding one validation message for each failed check. |
| **Outcome** | Invalid passwords return `IsValid = False` with the corresponding validation messages; only passwords that satisfy every check remain valid. |
| **Confidence** | Verified |
| **Source** | `Business\SAIF.Business.External.Users\clsPassword.vb` |

## Inherited business rules

This library's business objects inherit from `clsBusinessBase` and `clsBusinessCollectionBase` in `SAIF.BOF.Framework`. The Legends CLI has no indexed content for BOF framework base classes in this run; inherited framework-level rules (such as standard BOF row-count concurrency checks and error-message patterns) cannot be enumerated from documentation. Consult `SAIF.BOF.Framework` source directly for authoritative inherited rule definitions.*

---

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Database tables](database-tables.md)
- [SQL catalog](sql/index.md)
- [API reference](api/index.md)
- [Configuration](configuration.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
- `Business\SAIF.Business.External.Users\clsUser.vb`
- `Business\SAIF.Business.External.Users\clsUserDenySelfRegistration.vb`
- `Business\SAIF.Business.External.Users\clsAuditAssignmentOverride.vb`
- `Business\SAIF.Business.External.Users\clsUserType.vb`
- `Business\SAIF.Business.External.Users\clsUserAccessGroup.vb`
- `Business\SAIF.Business.External.Users\clsUserSecurityQuestion.vb`
