---
title: SAIF.Business.View.Policy security and authorization
summary: Authorization requirements, AD resource groups, security enforcement points, and data-level access control.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-07-31
library: SAIF.Business.View.Policy
reference_type: package
---

# SAIF.Business.View.Policy security and authorization

This page documents the authorization mechanisms implemented by this library. Authentication is handled entirely by infrastructure (NTLM and Active Directory) and is not application-level; all code receives a Windows identity on the request context.

## Authorization architecture

Authorization in `SAIF.Business.View.Policy` is mixed. A single Active Directory resource membership gates access to SAIF-secured policies, and everything else is data-level authorization: the library asks the database whether the signed-in principal owns, is assigned to, or is grouped with the policy in question. `clsPolicySecurity` is the single class that makes these decisions, and it answers two questions — "may this principal see this policy?" ([Authorize a policy for the current user](operations/authorize-policy-for-user.md)) and "which policies may this principal see?" ([Build the permitted policy list](operations/build-permitted-policy-list.md)).

The posture is fail-closed throughout. The per-policy decision starts at refuse and is only raised to allow by a matching user-type branch, and the permitted-policy list starts empty and stays empty when no branch matches ([BR-002](business-rules.md#br-002-invalid-or-unnumbered-policies-are-never-accessible), [BR-019](business-rules.md#br-019-policy-lists-fail-closed)).

Two data stores participate. Oracle answers the agent, policyholder, and auditor questions through the caller-supplied data portal; a separate SQL Server store, configured from the `BDS_*` settings described on the [Configuration](configuration.md) page, answers the group-membership question. A misconfiguration of the second store is therefore a security-relevant failure: group membership cannot be evaluated, and the fail-closed default applies.*

```mermaid
flowchart TD
    Caller[Consuming application] --> Sec[clsPolicySecurity]
    Sec --> Gate{Secure policy?}
    Gate -->|Yes| AD[clsSecurityPrincipal.IsAuthorized<br>SAIFpolicyInfo]
    AD --> ADS[(Active Directory<br>via the framework package)]
    Gate -->|No| UT[clsUserType classification]
    AD --> UT
    UT --> Own[Ownership and assignment checks]
    Own --> OraDB[(Oracle policy store<br>caller-supplied data portal)]
    Own --> SqlDB[(BDS SQL Server store<br>BDS_USER, bdsDatabase, BDS_DATABASENAME)]
    OraDB --> Decision([Allow or refuse])
    SqlDB --> Decision
```

## AD resource catalog

| Resource name | Purpose | Enforcement point | Source |
| --- | --- | --- | --- |
| `SAIFpolicyInfo` | Permits viewing a policy flagged as a SAIF-secured policy. Without this membership the policy is refused to every user type, including SAIF staff ([BR-001](business-rules.md#br-001-secure-saif-policies-require-explicit-authorization)). | `clsPolicySecurity.IsPolicyValidForUser` | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |

Resource names are base names. The framework appends `Resource` in production and `TestResource` outside production; the suffix is applied inside `clsSecurityPrincipal.IsAuthorized` and is not visible in this library.*

### Data-driven authorization resources

Association types carry an optional security group name loaded from the association type code table. This library exposes the value and the "is this type secured?" predicate, but performs no membership check against it — that check belongs to the consuming application layer.

| Source | Description | Enforcement point | Source file |
| --- | --- | --- | --- |
| `ASSOCIATION_TYPE_CODE.SECURITY_GROUP_NAME` | Security group governing an association type, loaded with the code table ([SQL-003](sql/SQL-003-association-type-code-table.md), [ASSOCIATION_TYPE_CODE](database-tables.md#association-type-code)). | `clsAssociationTypeCode.SecurityGroupName`, `AssociationTypeExtensions.IsSecuredType` | `Business/SAIF.Business.View.Policy/Associations/clsAssociationTypeCode.vb` |
| `IPolicyAssociationType.SecurityGroupName` | The same value surfaced through the association-type adapter. | `clsAssociationType.SecurityGroupName` | `Business/SAIF.Business.View.Policy/Associations/clsAssociationType.vb` |

The concrete group names are table data rather than source literals, so they cannot be enumerated from this repository.**

## Authorization enforcement points

### Attribute-based authorization

None. The library declares no controllers, no filters, and no authorization attributes.

### Programmatic authorization checks

| ID | Class | Method | Resource / Role | Check type | Source |
| --- | --- | --- | --- | --- | --- |
| EP-01 | `clsPolicySecurity` | `IsPolicyValidForUser(clsPolicyData)` | AD resource `SAIFpolicyInfo`, then the user-type branch | AD resource membership followed by user-type dispatch | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |
| EP-02 | `clsPolicySecurity` | `IsPolicyValidForUser(Decimal)` | Same as EP-01, after loading the policy | Overload that retrieves the policy ([Retrieve policy header](operations/retrieve-policy-header.md)) and delegates to EP-01 | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |
| EP-03 | `clsPolicySecurity` | `IsPolicyValidForParentPolicy` | Policyholder ownership or a shared parent association | Data-level entity ownership | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |
| EP-04 | `clsPolicySecurity` | `IsInGroup` | Group and chapter membership of the policy | Data-level scalar procedure `cp_policy_in_group` on the BDS SQL Server store | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |
| EP-05 | `clsPolicySecurity` | `CanSafetyConsultantAccessPolicy` | Loss-control consultant assignment on the policy | Data-level prefix match against the team assignment name | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |
| EP-06 | `clsPolicySecurity` | `CanAuditorAccessPolicy` | Fee-auditor vendor assignment inside a sixty-day audit window | Data-level Oracle scalar query over the audit tables | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |
| EP-07 | `clsPolicySecurity` | `Agent branch of `IsPolicyValidForUser`` | Policy agent number or agency sequence number | Data-level in-memory attribute comparison | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |
| EP-08 | `clsPolicySecurity` | `PolicyList` | The permitted policy set for the principal's user type | Data-level dispatch across five distinct scope queries | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |

Two further gates change what a principal receives without refusing the policy itself.

| ID | Class | Member | Effect | Source |
| --- | --- | --- | --- | --- |
| EP-09 | `clsPolicyData` | `DisplayAffiliatedPolices` | Hides affiliated policies from quick-file users and restricts policyholders to parent policies ([BR-011](business-rules.md#br-011-affiliated-policy-visibility-depends-on-user-access-level-and-affiliate-type)). | `Business/SAIF.Business.View.Policy/clsPolicyData.vb` |
| EP-10 | `clsAffiliatedPolicyCollection` | `AfterRetrieve` row filter | Removes affiliated policies that do not belong to the requesting agent or agency ([BR-020](business-rules.md#br-020-affiliated-policies-are-filtered-to-the-requesting-agent-or-agency-when-specified)). | `Business/SAIF.Business.View.Policy/clsAffiliatedPolicyCollection.vb` |

### Per-user-type decision matrix

| User type | Decision rule | Enforcement point | Rule |
| --- | --- | --- | --- |
| Any, on a secure policy | Refused unless the principal holds `SAIFpolicyInfo`; evaluated before every other check | EP-01 | [BR-001](business-rules.md#br-001-secure-saif-policies-require-explicit-authorization) |
| Any, on an invalid policy | Refused when the policy is invalid or its number is zero | EP-01 | [BR-002](business-rules.md#br-002-invalid-or-unnumbered-policies-are-never-accessible) |
| SAIF employee, MCO, medical provider, bookkeeper | Allowed unconditionally | EP-01 | [BR-003](business-rules.md#br-003-internal-and-provider-user-types-have-unrestricted-policy-visibility) |
| Agent | Allowed when the trimmed user number equals the policy's agent number or agency sequence number | EP-07 | [BR-004](business-rules.md#br-004-agents-see-only-their-own-book) |
| Policyholder | Allowed when the target is the user's own policy or shares a parent association with it | EP-03 | [BR-005](business-rules.md#br-005-policyholders-see-their-own-policy-and-policies-under-the-same-parent-association) |
| Group | Allowed when the group-membership procedure returns a count greater than zero | EP-04 | [BR-006](business-rules.md#br-006-group-users-see-only-policies-in-their-group-or-chapter) |
| Loss control | Allowed when the loss-control assignment name starts with the user number, truncated at a `%` wildcard; demo users match on the user-id name | EP-05 | [BR-007](business-rules.md#br-007-loss-control-consultants-see-only-policies-they-are-assigned-to) |
| Auditor | Allowed when a current, non-cancelled audit links the vendor number to the policy | EP-06 | [BR-008](business-rules.md#br-008-fee-auditors-see-only-policies-with-a-recent-non-cancelled-audit) |
| Unmatched | Refused — the fail-closed default | EP-01 | [BR-019](business-rules.md#br-019-policy-lists-fail-closed) |

## Data-level authorization

| ID | Entity type | Validation method | Mechanism | Source |
| --- | --- | --- | --- | --- |
| DL-01 | Policy | `IsPolicyValidForUser` secure-policy gate | Composite: the policy's secure-policy code combined with AD resource membership | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |
| DL-02 | Policy | Agent branch of `IsPolicyValidForUser` | Ownership: the trimmed user number is compared with the policy's agent number and agency sequence number | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |
| DL-03 | Policy | `IsPolicyValidForParentPolicy` | Ownership by association: the principal's own policy is loaded and the target allowed only when it is that policy or shares a parent association | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |
| DL-04 | Policy, in the BDS SQL Server store | `IsInGroup` | Scalar procedure `cp_policy_in_group` with group, chapter, and policy number; access when the count exceeds zero | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |
| DL-05 | Team assignment | `CanSafetyConsultantAccessPolicy` | Prefix match: the upper-cased, trimmed assignment name must start with the user number truncated at the first `%` wildcard | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |
| DL-06 | Premium audit | `CanAuditorAccessPolicy` | Oracle scalar query joining the vendor number to audits on the policy, with cancel and completed dates guarded to the last sixty days; access when the count exceeds zero | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |

### Permitted-policy scoping queries

`PolicyList` ([Build the permitted policy list](operations/build-permitted-policy-list.md)) enumerates the policies a principal may see. Each user type has its own scope query, and four of the six paths depend on the BDS SQL Server store.

| User type | Scope query | Database | Parameters | Rule |
| --- | --- | --- | --- | --- |
| Agent | `cp_get_agent_online_policy_list`, skipped entirely when the user number is not numeric | SQL Server (BDS) | `@AgentNum`, `@ForAgency` | [BR-018](business-rules.md#br-018-agent-policy-lists-are-produced-only-for-numeric-agent-identifiers) |
| Policyholder | Projection of the affiliated policy collection ([Retrieve affiliated policies](operations/retrieve-affiliated-policies.md)) | Oracle | Policy number and affiliate type | [BR-020](business-rules.md#br-020-affiliated-policies-are-filtered-to-the-requesting-agent-or-agency-when-specified) |
| Loss control | `cp_claimprofile_get_sc_book` | SQL Server (BDS) | `@losscontrol` | — |
| Group | `cp_claimprofile_get_group_policy_list` | SQL Server (BDS) | `@GroupKeyCode`, `@SpecificChapterCode`, `@rptDate` | [BR-006](business-rules.md#br-006-group-users-see-only-policies-in-their-group-or-chapter) |
| Auditor | Inline Oracle query over the audit and policy tables | Oracle | `:VendorNum` | [BR-008](business-rules.md#br-008-fee-auditors-see-only-policies-with-a-recent-non-cancelled-audit) |
| Unmatched | None — an empty list is returned | — | — | [BR-019](business-rules.md#br-019-policy-lists-fail-closed) |

### Access-level derivation and demo masking

`ConvertUserTypeToUserAccessLevel` maps each user type onto a ranked access level, doubling every external type into a demo variant ([BR-013](business-rules.md#br-013-user-type-maps-to-a-ranked-access-level)). The access level is not itself an authorization decision, but it drives what data a principal receives ([BR-012](business-rules.md#br-012-demo-access-levels-are-treated-as-a-distinct-user-population)).

| Consumer | Behaviour gated by access level | Source |
| --- | --- | --- |
| `clsPolicyData.IsDemoUser` | Reports true for the five demo access levels | `Business/SAIF.Business.View.Policy/clsPolicyData.vb` |
| `clsPolicyData.DisplayAffiliatedPolices` | Quick-file users see no affiliated policies; policyholders see them conditionally | `Business/SAIF.Business.View.Policy/clsPolicyData.vb` |
| `clsAgency` | Agency detail is masked for the five demo levels | `Business/SAIF.Business.View.Policy/clsAgency.vb` |
| `clsPolicyAddress` | Address detail is masked for demo agent, group, and policyholder levels | `Business/SAIF.Business.View.Policy/clsPolicyAddress.vb` |

## Security interfaces and contracts

| Interface / Class | Role | Consumers | Source |
| --- | --- | --- | --- |
| `clsPolicySecurity` | The library's only authorization class; owns the per-policy decision and the permitted-policy list | Consuming applications | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |
| `clsPolicySecurity.IPolicySecurityDataProvider` | Declares the policy attributes an authorization decision needs: policy number, secure-policy flag, validity, agent number, agency sequence number, and loss-control consultant identity and name | `clsPolicyData` | `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb` |
| `clsSecurityPrincipal` | Supplies the identity and answers the AD resource membership question | `clsPolicySecurity` | `SAIF.BOF.Framework` package** |
| `clsUserType` | Classifies the principal into the user types the decision matrix branches on | `clsPolicySecurity` | `SAIF.BOF.Business` package** |

## Segregation of duties

No segregation-of-duties rules are implemented in this library. There are no mutually-exclusive privilege checks, no employee-access queries, and no menu-structure reads.

## Machine and application authorization

No machine-account or service-account authorization exists. The only non-user identity is the database login taken from the `BDS_USER` setting, which is a connection identity rather than an authorization subject. See [Configuration](configuration.md).

## Inherited security

### From `clsSecurityPrincipal` (`SAIF.Framework.Core`)

| Field | Value |
| --- | --- |
| **Mechanism** | Windows identity supplied by infrastructure authentication, with Active Directory resource membership resolved by the framework.† |
| **Enforcement** | `IsAuthorized` resolves a base resource name to the environment-specific AD group and answers the membership question; this library supplies only the base name.† |
| **Origin** | `SAIF.Framework.Core` — `clsSecurityPrincipal` |
| **Source** | Retrieved from indexed documentation via Legends CLI |

## Notes

- AD resource names on this page are base names. The framework appends `Resource` in production and `TestResource` outside production at run time.
- No WCF security service client exists in this library. Whether the framework's `IsAuthorized` reaches such a service cannot be determined from this repository.**
- The user-type classification that the whole decision matrix branches on is implemented in a dependency package and is not traceable here.**

> **Note:** Statements marked with `†` are derived from dependency library
> documentation via the Legends CLI. Statements marked with `*` were inferred
> from code structure or contextual evidence. Statements marked with `**` could
> not be confirmed; treat as unknown until verified.

## Related documentation

- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Database tables](database-tables.md)
- [SQL catalog](sql/index.md)
- [Procedures](procedures/index.md)
- [API reference](api/index.md)
- [Configuration](configuration.md)

## Sources

- `Business/SAIF.Business.View.Policy/clsPolicySecurity.vb`
- `Business/SAIF.Business.View.Policy/clsPolicyData.vb`
- `Business/SAIF.Business.View.Policy/clsAgency.vb`
- `Business/SAIF.Business.View.Policy/clsPolicyAddress.vb`
- `Business/SAIF.Business.View.Policy/clsAffiliatedPolicyCollection.vb`
- `Business/SAIF.Business.View.Policy/Associations/clsAssociationTypeCode.vb`
- `Business/SAIF.Business.View.Policy/Associations/clsAssociationType.vb`
