Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 199 additions & 0 deletions docs/security-integration/01-ad-extract-specification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
# Active Directory Data Extract — Technical Specification

**Document:** AD-EXTRACT-SPEC-v1.0
**Audience:** Client IT Team / Active Directory Administrators
**Purpose:** Define the minimum AD data extract required by the integration platform when live AD connectivity is not available to the contractor

---

## 1. Overview

The integration platform requires a daily snapshot of Active Directory user accounts to:

- Maintain a local identity registry used as the canonical source of person identity
- Detect joiners, movers, and leavers (JML) without live directory access
- Enrich occupancy and evacuation records with department, manager, and status information
- Flag anomalies (e.g. disabled accounts with active building access)

This extract does **not** require the contractor to have any direct AD connectivity. It is produced by client IT infrastructure and delivered to the contractor's integration endpoint via an agreed secure transfer mechanism.

---

## 2. Delivery Specification

| Parameter | Value |
|-----------|-------|
| Format | JSON (preferred) or CSV |
| Frequency | Daily — delivered by **06:00 local time** |
| Transfer method | SFTP to contractor-hosted endpoint **or** HTTPS POST to integration API |
| Encryption | PGP encryption required if SFTP; TLS 1.2+ minimum if HTTPS |
| Filename convention | `ad_extract_YYYYMMDD.json` |
| Retention on source | Client retains 30 days of extract files |
| Alerting | If extract is not received by 07:00, integration platform raises an alert to both parties |

---

## 3. Required Fields

### 3.1 Employee / Permanent Staff

```json
{
"extract_date": "2026-06-03",
"extract_type": "full",
"users": [
{
"employee_id": "EMP-1042",
"sam_account_name": "jsmith",
"display_name": "John Smith",
"email": "john.smith@company.com",
"department": "Engineering",
"job_title": "Senior Engineer",
"manager_email": "jane.doe@company.com",
"office_location": "Building A",
"floor": "Floor 2",
"cost_centre": "CC-4400",
"account_enabled": true,
"account_expires": null,
"last_modified": "2026-05-01T09:00:00Z",
"person_type": "EMPLOYEE"
}
]
}
```

### 3.2 Contractors with AD Accounts

```json
{
"employee_id": "CONT-0055",
"sam_account_name": "b.builder.ext",
"display_name": "Bob Builder",
"email": "b.builder@bobbuilderco.com",
"department": "Facilities",
"job_title": "Contractor",
"manager_email": "facilities.manager@company.com",
"office_location": "Building A",
"floor": null,
"cost_centre": "CC-9900",
"account_enabled": true,
"account_expires": "2026-09-30T23:59:59Z",
"last_modified": "2026-04-15T08:00:00Z",
"person_type": "CONTRACTOR"
}
```

---

## 4. Field Definitions

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `employee_id` | string | **Yes** | Unique employee or contractor reference. Must match value stored in ACT badge system. |
| `sam_account_name` | string | **Yes** | AD logon name. Used as secondary deduplication key. |
| `display_name` | string | **Yes** | Full name as displayed in directory. |
| `email` | string | **Yes** | Primary SMTP address. **Used as the common join key across all integrated systems.** |
| `department` | string | **Yes** | Organisational department. Used for evacuation list grouping. |
| `job_title` | string | No | Role/position title. |
| `manager_email` | string | No | Direct manager's email address. Used to group evacuation lists by team. |
| `office_location` | string | **Yes** | Building or campus identifier. Filters records relevant to this deployment. |
| `floor` | string | No | Assigned floor, if known. Supplements ACT zone data. |
| `cost_centre` | string | No | Finance cost centre code. |
| `account_enabled` | boolean | **Yes** | `true` = active account. `false` = disabled/suspended. Critical for leaver detection. |
| `account_expires` | ISO 8601 or null | No | If set, account will auto-disable on this date. Used for proactive contractor badge expiry alerts. |
| `last_modified` | ISO 8601 | **Yes** | Timestamp of last change to the AD record. Used for delta detection. |
| `person_type` | enum | **Yes** | One of: `EMPLOYEE`, `CONTRACTOR`, `SERVICE_ACCOUNT`. Service accounts excluded from occupancy logic. |

---

## 5. Delta Detection Logic

The integration platform compares each day's extract to the previous day using `employee_id` as the primary key and `last_modified` as a change indicator.

```
NEW record → person_type = EMPLOYEE/CONTRACTOR → Joiner workflow
MISSING record → was present yesterday → Leaver workflow (flag ACT badge for review)
account_enabled: true → false → Leaver/suspension workflow
account_expires within 14 days → Expiry warning to facilities team
department/floor changed → Mover workflow (update occupancy zone mapping)
```

---

## 6. Scope Filtering

To avoid processing records for other sites, the extract should be pre-filtered by the client to include only users where `office_location` matches the relevant building(s). Alternatively, a full extract can be delivered and filtered on ingestion — confirm approach with client IT.

---

## 7. PowerShell Export Script (Reference)

The following script can be run by client AD administrators to generate the extract. It requires the `ActiveDirectory` PowerShell module.

```powershell
Import-Module ActiveDirectory

$extractDate = Get-Date -Format "yyyyMMdd"
$outputPath = "C:\ADExtracts\ad_extract_$extractDate.json"

$users = Get-ADUser -Filter {Enabled -eq $true -or Enabled -eq $false} `
-Properties EmployeeID, DisplayName, EmailAddress, Department, Title, `
Manager, Office, AccountExpirationDate, Enabled, `
WhenChanged, extensionAttribute1 |
Where-Object { $_.Office -like "*Building A*" } |
Select-Object @{N='employee_id';E={$_.EmployeeID}},
@{N='sam_account_name';E={$_.SamAccountName}},
@{N='display_name';E={$_.DisplayName}},
@{N='email';E={$_.EmailAddress}},
@{N='department';E={$_.Department}},
@{N='job_title';E={$_.Title}},
@{N='manager_email';E={
if ($_.Manager) {
(Get-ADUser $_.Manager -Properties EmailAddress).EmailAddress
} else { $null }
}},
@{N='office_location';E={$_.Office}},
@{N='floor';E={$_.extensionAttribute1}},
@{N='account_enabled';E={$_.Enabled}},
@{N='account_expires';E={
if ($_.AccountExpirationDate) {
$_.AccountExpirationDate.ToString("o")
} else { $null }
}},
@{N='last_modified';E={$_.WhenChanged.ToString("o")}},
@{N='person_type';E={
if ($_.SamAccountName -like "*.ext") { "CONTRACTOR" }
elseif ($_.SamAccountName -like "svc.*") { "SERVICE_ACCOUNT" }
else { "EMPLOYEE" }
}}

$output = @{
extract_date = (Get-Date -Format "yyyy-MM-dd")
extract_type = "full"
users = $users
}

$output | ConvertTo-Json -Depth 5 | Out-File -FilePath $outputPath -Encoding UTF8
Write-Host "Extract written to $outputPath — $($users.Count) records"
```

---

## 8. Data Protection Notes

- The extract contains personal data and is subject to applicable data protection legislation (e.g. UK GDPR / POPIA)
- The contractor must store the extract in an encrypted datastore and retain only the current and previous day's extract
- Extract files must not be shared with third parties
- A Data Processing Agreement (DPA) must be in place between client and contractor before first extract is delivered

---

## 9. Acceptance Criteria

| Test | Expected Result |
|------|----------------|
| Extract delivered by 06:00 | Alert suppressed |
| Extract not delivered by 07:00 | Alert raised to both parties |
| Employee in yesterday's extract missing today | Leaver workflow triggered within 15 minutes of ingestion |
| `account_enabled` flips to `false` | Badge review alert sent to facilities team |
| New employee appears | Joiner record created in identity registry |
Loading