---
title: Get Active Directory user data or access
summary: Retrieve a default printer, a cached access decision, or contact details for an AD user.
status: draft
owner: SAIF.Core Documentation Team
last_reviewed: 2026-09-24
library: SAIF.Services.Web.Proxies
reference_type: package
operation_id: OP-WP-01
trigger: A caller request; no in-repository caller constructs ActiveDirectoryServiceProxy
---

# Get Active Directory user data or access

`ActiveDirectoryServiceProxy` wraps the ActiveDirectory SOAP service to answer three independent AD questions — a user's default printer, a cached role-access decision, and a mapped contact record — without exposing the generated `clsADServicesSoapClient` to callers.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-WP-01 |
| **Trigger** | A caller request; no in-repository code constructs this wrapper |
| **Inputs** | `userId`, `role`, `useExternalDomain` |
| **Outputs** | A printer name, a boolean access decision, or a `SAIF.Core.Contracts.Contact` |
| **Primary components** | `ActiveDirectoryServiceProxy.GetDefaultPrinter`, `ActiveDirectoryServiceProxy.HasAccess`, `ActiveDirectoryServiceProxy.GetContact` |
| **Entry point** | `Services/SAIF.Services.Web.Proxies/ActiveDirectoryServiceProxy.cs` |

## Steps

1. Construct `ActiveDirectoryServiceProxy(serviceUrl)`: guard `serviceUrl` with `Enforce.ArgumentNotNullOrEmpty` ([WP-BR-001](../business-rules.md#wp-br-001-required-arguments-must-be-non-null-or-non-empty)), then build a default `BasicHttpBinding` client with no explicit security.
   - On failure: an argument exception is thrown before any wire client is created.
2. **`GetDefaultPrinter(userId)`:** guard `userId` is not null or empty ([WP-BR-001](../business-rules.md#wp-br-001-required-arguments-must-be-non-null-or-non-empty)), then call the wire `GetDefaultPrinter` operation and return its result unchanged.
3. **`HasAccess(userId, role, useExternalDomain)`:** ([WP-BR-002](../business-rules.md#wp-br-002-access-check-selects-internal-or-external-cache))
   - If `useExternalDomain` is true: call wire `HasAccessCacheExternal(userId, role)`.
   - Otherwise: call wire `HasAccessCache(userId, role)`.
   - The boolean result is returned unchanged in both branches.
4. **`GetContact(userId)`:** call wire `GetUserInfo(userId)`, then map the returned `USERINFOSTRUCT` fields to a `SAIF.Core.Contracts.Contact`: ([WP-BR-003](../business-rules.md#wp-br-003-null-ad-attributes-become-empty-strings))
   - `Address`/`City`/`State`/`Zip` become `PrimaryAddress`.
   - `FaxNumber` becomes `PrimaryFax`.
   - `First`/`Last`/`Middle` become `Name`.
   - `Telephone` becomes `PrimaryPhone`.
   - `Email` and `Gender` are copied directly.
   - Every null field is coalesced to `""`.
   - On failure: there is no null guard on `userId`, and a null `userInfo` returned by the wire call throws `NullReferenceException`.
5. `Dispose()` calls `webService.Close()` on the generated client without an `Abort()` fallback on the fault path.

## Process flow

The following diagram visualizes the three independent request paths through this wrapper.

```mermaid
flowchart TD
	Start([Caller invokes wrapper method]) --> Which{Which method?}
	Which -->|GetDefaultPrinter| GP[Guard userId] --> GPCall[Call wire GetDefaultPrinter] --> GPRet([Return printer name])
	Which -->|HasAccess| HA{useExternalDomain?}
	HA -->|Yes| HAExt[Call HasAccessCacheExternal] --> HARet([Return boolean])
	HA -->|No| HAInt[Call HasAccessCache] --> HARet
	Which -->|GetContact| GC[Call wire GetUserInfo] --> GCMap["Map USERINFOSTRUCT to Contact<br>(nulls become empty strings)"] --> GCRet([Return Contact])
```

## Failure and edge-case behavior

- A null or empty `serviceUrl` or `userId` (for `GetDefaultPrinter`) throws an argument exception before any wire call is made.
- A null `userInfo` returned from `GetUserInfo` throws `NullReferenceException` in `GetContact`, because there is no null guard on the wire result.
- `Dispose()` calling `Close()` without `Abort()` can itself throw if the underlying channel has faulted*.

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

## Sources
- `Services/SAIF.Services.Web.Proxies/ActiveDirectoryServiceProxy.cs`
