Oracle DBA - User, Role & Access Management
User & account lifecycle, schemas, roles, privileges, profiles, password policy, access
reviews, and the plan-only Tier-3 runbooks for proxy authentication, CMU / LDAP / AD,
Enterprise User Security, user drop, and privileged grants — for Oracle 19c and 23ai
(EE; single-instance and Data Guard; on-prem). Everything self-executing is T1
(observe, read-only) or T2 (reversible, dry-run-default). Directory-integration
setup, user drop, and DBA-class grants are T3 and live only in
references/runbooks.md — they print exact commands for a human and never self-execute.
Secrets rule (load-bearing): no plaintext password is ever set, passed, or logged.
Accounts are created wallet / external / global identified; resets use EXPIRE or
wallet-rotate (rotate the secret in the Oracle Wallet out of band). Plaintext-credential
patterns are rejected with ERR_SECRET (8).
Scope & risk map
| Task |
Level |
Tier |
Mechanism (script / sql / runbook path) |
Idempotent? |
| User account creation |
L1 |
T2 |
scripts/create_user.sh / .ps1 -> sql/create_user.sql |
yes — existing user -> noop; created ACCOUNT LOCK |
| User password reset |
L1 |
T2 |
scripts/reset_password.sh / .ps1 -> sql/reset_password.sql (--mode expire|wallet-rotate) |
yes — already in target state -> noop; no plaintext |
| Account lock / unlock |
L1 |
T2 |
scripts/lock_unlock_account.sh / .ps1 -> sql/lock_unlock_account.sql |
yes — already locked/unlocked -> noop; refuses system accts |
| Expired account handling (report) |
L1 |
T1 |
scripts/expired_accounts.sh report / .ps1 report -> sql/expired_accounts_report.sql |
yes — read-only |
| Expired account handling (remediate) |
L1 |
T2 |
scripts/expired_accounts.sh remediate / .ps1 remediate -> sql/expired_account_remediate.sql (--strategy reexpire|extend) |
yes — already remediated -> noop; refuses system accts |
| User account drop / deletion |
L1 |
T3 |
references/runbooks.md#drop-user (deprovision = LOCK+EXPIRE via the T2 scripts first) |
no — DROP USER CASCADE is irreversible |
| Standard access request fulfillment |
L1 |
T2 |
scripts/grant_access.sh / .ps1 -> sql/grant_access.sql (--operation grant|revoke) |
yes — granted/absent -> noop; high-risk refused -> runbook |
| Schema creation |
L2 |
T2 |
scripts/create_schema.sh / .ps1 -> sql/create_schema.sql |
yes — existing schema -> creation noop; baseline re-asserted |
| Role creation & assignment |
L2 |
T2 |
scripts/create_role.sh / .ps1 -> sql/create_role.sql |
yes — existing role -> noop; re-grant of held privs no-ops |
| System / object privilege grants |
L2 |
T2 |
scripts/grant_privilege.sh grant/revoke / .ps1 -> sql/grant_privilege.sql + sql/revoke_privilege.sql |
yes — held grant / absent revoke -> noop |
| System / object privilege review |
L2 |
T1 |
scripts/grant_privilege.sh review / .ps1 review -> sql/privilege_review.sql |
yes — read-only |
| Profile creation & management (apply) |
L2 |
T2 |
scripts/profile_manage.sh apply / .ps1 apply -> sql/profile_apply.sql |
yes — limits matching -> skipped; all-match -> noop |
| Profile inventory (list) |
L2 |
T1 |
scripts/profile_manage.sh list / .ps1 list -> sql/profile_list.sql |
yes — read-only |
| Password policy / verify function enforcement |
L2 |
T2 |
scripts/password_policy.sh / .ps1 -> sql/password_verify_function.sql (attach via profile_manage) |
yes — CREATE OR REPLACE refreshes body |
| Periodic access review / recertification |
L1 |
T1 |
scripts/access_review.sh / .ps1 -> sql/access_review.sql |
yes — read-only (emits KEYVAL metrics) |
| Proxy authentication — review (pre-flight/verify) |
L3 |
T1 |
scripts/directory_integration_review.sh proxy / .ps1 proxy -> sql/proxy_auth_review.sql |
yes — read-only |
| Proxy authentication — setup |
L3 |
T3 |
references/runbooks.md#proxy-auth |
reverse via REVOKE CONNECT THROUGH |
| CMU / LDAP / AD — review (pre-flight/verify) |
L3 |
T1 |
scripts/directory_integration_review.sh cmu / .ps1 cmu -> sql/centrally_managed_users_review.sql |
yes — read-only |
| CMU / LDAP / AD integration — setup |
L3 |
T3 |
references/runbooks.md#cmu |
reverse: drop global users/roles, LDAP_DIRECTORY_ACCESS=NONE, remove wallet/ldap.ora |
| EUS — review (pre-flight/verify) |
L3 |
T1 |
scripts/directory_integration_review.sh eus / .ps1 eus -> sql/enterprise_user_security_review.sql |
yes — read-only |
| Enterprise User Security (EUS) — setup |
L3 |
T3 |
references/runbooks.md#eus |
reverse: unregister directory, drop global schema/roles |
| Privileged (DBA-class) grant (escape hatch) |
L2/L3 |
T3 |
references/runbooks.md#privileged-grant |
reverse via REVOKE / password-file removal |
Preconditions
- SQLcl MCP connection
dba_ai_conn is reachable and wallet-backed. All in-DB
prechecks, postchecks, idempotency probes, and sql/*.sql run through it via
connect_db / Connect-Db (mechanism sqlcl-mcp). Never SYS/SYSTEM — except inside
a T3 runbook that explicitly says AS SYSDBA (e.g. setting LDAP_DIRECTORY_ACCESS,
directory registration), supplied interactively by the operator.
dba_ai_conn least privilege: it must legitimately hold the authority it delegates —
CREATE USER/ALTER USER/CREATE ROLE/CREATE PROFILE, and any privilege it grants
must be held WITH ADMIN OPTION (or it must be the object's grantor). DBA-class grants
are refused by the T2 scripts and routed to references/runbooks.md#privileged-grant.
- Secrets resolve from the Oracle Wallet / external password store. No plaintext
anywhere. Accounts are wallet/external/global identified; password "reset" = EXPIRE or
wallet rotation (out of band). Any
identified by / password= / user/pass@db pattern
in args is rejected with ERR_SECRET (8).
- Helpers are sourced from
_common (scripts/lib.sh / lib.ps1): connect_db,
log_event, emit_metric, require_dry_run, guard_blocked_dry_run,
require_approval_token, precheck, postcheck, print_banner. Not reinvented.
- Logging: structured
key=value lines to the canonical paths
/var/log/oracle-dba/user-role-access-management/user-role-access-management.log (Linux) /
C:/ProgramData/oracle-dba/logs/user-role-access-management/user-role-access-management.log
(Windows).
- T2 dry-run default: every T2 script previews and changes nothing unless given
--execute. Multitenant: set the correct container (ALTER SESSION SET CONTAINER)
via dba_ai_conn; users/roles/profiles/proxies are container-local (LOCAL, not common).
Data Guard: run all DDL/GRANT on the PRIMARY (it replicates via redo); host-local
sqlnet.ora/ldap.ora/wallets must be replicated to each standby host by hand.
Procedures
Scripts ship as behaviour-equivalent .sh (Linux) / .ps1 (Windows) pairs. T2 scripts
default to --dry-run; pass --execute to act. See each script's --help. Every action
follows: trigger -> precheck (read-only guard) -> action -> postcheck (verify) -> rollback.
User account creation (L1, T2)
- Trigger: provision a new account; account-request intake.
- Precheck: default/temp tablespaces exist (
dba_tablespaces).
- Action:
scripts/create_user.sh --user U --default-ts USERS --temp-ts TEMP [--auth wallet|external|global] [--ext-name DN] --execute / .ps1.
- Postcheck: user present in
dba_users (created ACCOUNT LOCK).
- Rollback: removal is T3
references/runbooks.md#drop-user; creation is the reversible direction.
User password reset (L1, T2)
- Trigger: user locked out by expiry / needs a fresh credential.
- Precheck: user exists.
- Action:
scripts/reset_password.sh --user U [--mode expire|wallet-rotate] --execute / .ps1. No plaintext — EXPIRE forces self-service set; wallet-rotate unlocks for a wallet rotation done out of band.
- Postcheck: EXPIRE ->
EXPIRED; wallet-rotate -> not LOCKED.
- Rollback: user sets a new password (EXPIRE), or rotate the wallet back.
Account lock / unlock (L1, T2)
- Trigger: suspend (lock) or restore (unlock) access.
- Precheck: user exists; SQL refuses Oracle-maintained/system accounts.
- Action:
scripts/lock_unlock_account.sh --user U --action lock|unlock --execute / .ps1.
- Postcheck:
account_status reflects the requested lock state.
- Rollback: exact inverse (
unlock <-> lock).
Expired account handling (L1, T1 report / T2 remediate)
- Trigger: daily expiry sweep (report); remediate a specific expired account.
- Precheck (remediate): user exists; refuses system accounts.
- Action:
scripts/expired_accounts.sh report [--days 14] (read-only) or remediate --user U [--strategy reexpire|extend --profile P] --execute / .ps1.
- Postcheck: reexpire ->
EXPIRED; extend -> on new profile, not locked.
- Rollback: re-apply prior profile (extend) or let the user self-service (reexpire).
User account drop / deletion (L1, T3)
- Trigger: decommission an account. Default deprovisioning is LOCK + EXPIRE (T2, reversible) for a grace period; only then drop.
- Action: print
references/runbooks.md#drop-user, complete pre-flight (export first!), supply approval token; a human runs DROP USER ... CASCADE.
- Rollback: none for CASCADE — recover via Data Pump import / Backup & Recovery runbooks. Never auto-executed.
Standard access request fulfillment (L1, T2)
- Trigger: grant/revoke a PRE-APPROVED catalogued role or low-risk system privilege.
- Precheck: grantee exists (user or role).
- Action:
scripts/grant_access.sh --grantee G --type role|syspriv --name N [--operation grant|revoke] [--with-admin] --execute / .ps1. High-risk items refused -> #privileged-grant.
- Postcheck: grant present after GRANT / absent after REVOKE.
- Rollback: exact inverse (
--operation revoke).
Schema creation (L2, T2)
- Trigger: new application owner schema.
- Precheck: default/temp tablespaces exist.
- Action:
scripts/create_schema.sh --schema S --default-ts APPDATA --temp-ts TEMP [--quota UNLIMITED] --execute / .ps1 (wallet/external/global auth; object-create baseline; ACCOUNT LOCK).
- Postcheck: schema owner present in
dba_users.
- Rollback: T3
#drop-user.
Role creation & assignment (L2, T2)
- Trigger: define/assemble a standard access bundle and assign it.
- Precheck: list args are identifier-safe; nested roles/grantees must exist (SQL checks).
- Action:
scripts/create_role.sh --role R [--sys-privs '...'] [--role-privs '...'] [--assign-to '...'] --execute / .ps1. DBA-class members refused -> #privileged-grant.
- Postcheck: role present in
dba_roles.
- Rollback:
DROP ROLE / REVOKE (re-run with prior membership to converge).
System / object privilege grants (L2, T1 review / T2 grant/revoke)
- Trigger: delegate or remove a specific SYS/OBJ/ROLE privilege; audit current grants.
- Precheck: grantee exists;
dba_ai_conn holds the privilege WITH ADMIN OPTION/grantor.
- Action:
scripts/grant_privilege.sh review [--grantee G] (read-only) or grant|revoke --kind sys|obj|role --name N [--object OWNER.OBJ] [--admin] [--grant-option] --execute / .ps1.
- Postcheck: state in
dba_sys_privs/dba_role_privs/dba_tab_privs.
- Rollback:
revoke is the exact inverse of grant.
Profile creation & management (L2, T1 list / T2 apply)
- Trigger: enforce password/resource limits; audit profiles.
- Precheck: profile name valid (apply); none for list (read-only).
- Action:
scripts/profile_manage.sh list [--profile P] or apply --profile P [--failed-login-attempts N --password-life-time D --password-verify-func FN ...] --execute / .ps1.
- Postcheck: profile present; limits reflect desired values.
- Rollback: re-apply prior limit values.
Password policy / verify function enforcement (L2, T2)
- Trigger: install/refresh a password complexity verify function.
- Precheck: connecting schema is a privileged security schema (out of band, not SYS).
- Action:
scripts/password_policy.sh [--func ODB_VERIFY_PASSWORD] [--min-length 12] --execute / .ps1, then attach via profile_manage.sh --password-verify-func.
- Postcheck: function compiled
VALID.
- Rollback: detach (profile
PASSWORD_VERIFY_FUNCTION -> NULL/DEFAULT); DROP FUNCTION is the T3 escalation.
Periodic access review / recertification (L1, T1)
- Trigger: quarterly recertification; dormant-account / privileged-access audit.
- Precheck: none (read-only). On Data Guard run against the PRIMARY (LAST_LOGIN is primary-side).
- Action:
scripts/access_review.sh [--dormant-days 90] / .ps1 -> evidence pack + KEYVAL metrics.
- Postcheck: n/a — read-only; findings feed recertification.
- Rollback: none.
Proxy auth / CMU / EUS — review (L3 task family, T1 read-only)
- Trigger: pre-flight discovery before a T3 directory-integration change, or post-change verification.
- Action:
scripts/directory_integration_review.sh proxy|cmu|eus|all / .ps1 -> the matching sql/*_review.sql.
- Postcheck/Rollback: n/a — read-only. The setup of each is T3 (below).
Tier-3 runbooks
Directory-integration setup, user drop, and DBA-class privileged grants are
Tier 3 and live in references/runbooks.md: #proxy-auth, #cmu, #eus,
#drop-user, #privileged-grant.
These runbooks PRINT exact SQL/OS commands for a human operator and NEVER self-execute.
They are gated by require_approval_token / Require-ApprovalToken: an automated agent's
only sanctioned action is to print the relevant section and stop with ERR_APPROVAL (6)
until a human supplies a real change ticket/token (--token <TICKET> or
ODB_APPROVAL_TOKEN; placeholders rejected). The T2 scripts deliberately refuse DBA-class
grants (DBA, ALL PRIVILEGES, GRANT ANY *, SYSDBA/SYSOPER/..., BECOME USER,
ALTER SYSTEM/DATABASE, CREATE/ALTER/DROP USER) and point to #privileged-grant.
SYSDBA is a T3-only, interactively-supplied exception. When in doubt, runbook.
See references/version-notes.md for 19c-vs-23ai behavioural deltas, and
cron/crontab.d / cron/task-scheduler.xml for the schedulable T1/T2 jobs (read-only
reviews + expiry sweep; no T3 is ever scheduled).
1---2name: oracle-dba-user-role-access-management3description: USE THIS SKILL WHENEVER the user mentions database users / accounts, schemas, roles, privileges / grants / revokes, system or object privileges, profiles, password policy or a password verify / complexity function, account lock/unlock, expired / locked-out accounts, password reset/expire, default-password accounts, access requests / access provisioning or de-provisioning, periodic access review / recertification / dormant accounts, least privilege, proxy authentication (CONNECT THROUGH), Centrally Managed Users (CMU) / LDAP / Active Directory integration, or Enterprise User Security (EUS / OID / OUD) — EVEN IF they do not name the specific task — or the errors ORA-01017 (invalid login), ORA-28000 (account locked), ORA-28001 (password expired), ORA-01918 (user not exists), ORA-01031 (insufficient privileges), ORA-01919 (role not granted). Covers user/account lifecycle, schema & role creation/assignment, system/object grants (idempotent + reversible), profiles & password verify functions, standard access-reques4---5# Oracle DBA - User, Role & Access Management67User & account lifecycle, schemas, roles, privileges, profiles, password policy, access8reviews, and the plan-only Tier-3 runbooks for proxy authentication, CMU / LDAP / AD,9Enterprise User Security, user drop, and privileged grants — for Oracle 19c and 23ai10(EE; single-instance and Data Guard; on-prem). Everything self-executing is **T111(observe, read-only)** or **T2 (reversible, dry-run-default)**. Directory-integration12**setup**, **user drop**, and **DBA-class grants** are **T3** and live only in13`references/runbooks.md` — they print exact commands for a human and never self-execute.1415**Secrets rule (load-bearing):** no plaintext password is ever set, passed, or logged.16Accounts are created **wallet / external / global** identified; resets use **EXPIRE** or17**wallet-rotate** (rotate the secret in the Oracle Wallet out of band). Plaintext-credential18patterns are rejected with `ERR_SECRET` (8).1920## Scope & risk map2122| Task | Level | Tier | Mechanism (script / sql / runbook path) | Idempotent? |23|------|-------|------|------------------------------------------|-------------|24| User account creation | L1 | T2 | `scripts/create_user.sh` / `.ps1` -> `sql/create_user.sql` | yes — existing user -> noop; created ACCOUNT LOCK |25| User password reset | L1 | T2 | `scripts/reset_password.sh` / `.ps1` -> `sql/reset_password.sql` (`--mode expire`\|`wallet-rotate`) | yes — already in target state -> noop; no plaintext |26| Account lock / unlock | L1 | T2 | `scripts/lock_unlock_account.sh` / `.ps1` -> `sql/lock_unlock_account.sql` | yes — already locked/unlocked -> noop; refuses system accts |27| Expired account handling (report) | L1 | T1 | `scripts/expired_accounts.sh report` / `.ps1 report` -> `sql/expired_accounts_report.sql` | yes — read-only |28| Expired account handling (remediate) | L1 | T2 | `scripts/expired_accounts.sh remediate` / `.ps1 remediate` -> `sql/expired_account_remediate.sql` (`--strategy reexpire`\|`extend`) | yes — already remediated -> noop; refuses system accts |29| User account drop / deletion | L1 | **T3** | `references/runbooks.md#drop-user` (deprovision = LOCK+EXPIRE via the T2 scripts first) | no — `DROP USER CASCADE` is irreversible |30| Standard access request fulfillment | L1 | T2 | `scripts/grant_access.sh` / `.ps1` -> `sql/grant_access.sql` (`--operation grant`\|`revoke`) | yes — granted/absent -> noop; high-risk refused -> runbook |31| Schema creation | L2 | T2 | `scripts/create_schema.sh` / `.ps1` -> `sql/create_schema.sql` | yes — existing schema -> creation noop; baseline re-asserted |32| Role creation & assignment | L2 | T2 | `scripts/create_role.sh` / `.ps1` -> `sql/create_role.sql` | yes — existing role -> noop; re-grant of held privs no-ops |33| System / object privilege grants | L2 | T2 | `scripts/grant_privilege.sh grant`/`revoke` / `.ps1` -> `sql/grant_privilege.sql` + `sql/revoke_privilege.sql` | yes — held grant / absent revoke -> noop |34| System / object privilege review | L2 | T1 | `scripts/grant_privilege.sh review` / `.ps1 review` -> `sql/privilege_review.sql` | yes — read-only |35| Profile creation & management (apply) | L2 | T2 | `scripts/profile_manage.sh apply` / `.ps1 apply` -> `sql/profile_apply.sql` | yes — limits matching -> skipped; all-match -> noop |36| Profile inventory (list) | L2 | T1 | `scripts/profile_manage.sh list` / `.ps1 list` -> `sql/profile_list.sql` | yes — read-only |37| Password policy / verify function enforcement | L2 | T2 | `scripts/password_policy.sh` / `.ps1` -> `sql/password_verify_function.sql` (attach via `profile_manage`) | yes — CREATE OR REPLACE refreshes body |38| Periodic access review / recertification | L1 | T1 | `scripts/access_review.sh` / `.ps1` -> `sql/access_review.sql` | yes — read-only (emits KEYVAL metrics) |39| Proxy authentication — review (pre-flight/verify) | L3 | T1 | `scripts/directory_integration_review.sh proxy` / `.ps1 proxy` -> `sql/proxy_auth_review.sql` | yes — read-only |40| Proxy authentication — setup | L3 | **T3** | `references/runbooks.md#proxy-auth` | reverse via `REVOKE CONNECT THROUGH` |41| CMU / LDAP / AD — review (pre-flight/verify) | L3 | T1 | `scripts/directory_integration_review.sh cmu` / `.ps1 cmu` -> `sql/centrally_managed_users_review.sql` | yes — read-only |42| CMU / LDAP / AD integration — setup | L3 | **T3** | `references/runbooks.md#cmu` | reverse: drop global users/roles, `LDAP_DIRECTORY_ACCESS=NONE`, remove wallet/ldap.ora |43| EUS — review (pre-flight/verify) | L3 | T1 | `scripts/directory_integration_review.sh eus` / `.ps1 eus` -> `sql/enterprise_user_security_review.sql` | yes — read-only |44| Enterprise User Security (EUS) — setup | L3 | **T3** | `references/runbooks.md#eus` | reverse: unregister directory, drop global schema/roles |45| Privileged (DBA-class) grant (escape hatch) | L2/L3 | **T3** | `references/runbooks.md#privileged-grant` | reverse via `REVOKE` / password-file removal |4647## Preconditions4849- **SQLcl MCP connection `dba_ai_conn`** is reachable and wallet-backed. All in-DB50 prechecks, postchecks, idempotency probes, and `sql/*.sql` run through it via51 `connect_db` / `Connect-Db` (mechanism `sqlcl-mcp`). **Never SYS/SYSTEM** — except inside52 a T3 runbook that explicitly says `AS SYSDBA` (e.g. setting `LDAP_DIRECTORY_ACCESS`,53 directory registration), supplied interactively by the operator.54- **`dba_ai_conn` least privilege**: it must legitimately hold the authority it delegates —55 `CREATE USER`/`ALTER USER`/`CREATE ROLE`/`CREATE PROFILE`, and any privilege it grants56 must be held `WITH ADMIN OPTION` (or it must be the object's grantor). DBA-class grants57 are refused by the T2 scripts and routed to `references/runbooks.md#privileged-grant`.58- **Secrets resolve from the Oracle Wallet / external password store. No plaintext59 anywhere.** Accounts are wallet/external/global identified; password "reset" = EXPIRE or60 wallet rotation (out of band). Any `identified by` / `password=` / `user/pass@db` pattern61 in args is rejected with `ERR_SECRET` (8).62- **Helpers are sourced from `_common`** (`scripts/lib.sh` / `lib.ps1`): `connect_db`,63 `log_event`, `emit_metric`, `require_dry_run`, `guard_blocked_dry_run`,64 `require_approval_token`, `precheck`, `postcheck`, `print_banner`. Not reinvented.65- **Logging**: structured `key=value` lines to the canonical paths66 `/var/log/oracle-dba/user-role-access-management/user-role-access-management.log` (Linux) /67 `C:/ProgramData/oracle-dba/logs/user-role-access-management/user-role-access-management.log`68 (Windows).69- **T2 dry-run default**: every T2 script previews and changes nothing unless given70 `--execute`. **Multitenant**: set the correct container (`ALTER SESSION SET CONTAINER`)71 via `dba_ai_conn`; users/roles/profiles/proxies are container-local (LOCAL, not common).72 **Data Guard**: run all DDL/GRANT on the **PRIMARY** (it replicates via redo); host-local73 `sqlnet.ora`/`ldap.ora`/wallets must be replicated to each standby host by hand.7475## Procedures7677Scripts ship as behaviour-equivalent `.sh` (Linux) / `.ps1` (Windows) pairs. T2 scripts78default to `--dry-run`; pass `--execute` to act. See each script's `--help`. Every action79follows: **trigger -> precheck (read-only guard) -> action -> postcheck (verify) -> rollback**.8081### User account creation (L1, T2)82- **Trigger**: provision a new account; account-request intake.83- **Precheck**: default/temp tablespaces exist (`dba_tablespaces`).84- **Action**: `scripts/create_user.sh --user U --default-ts USERS --temp-ts TEMP [--auth wallet|external|global] [--ext-name DN] --execute` / `.ps1`.85- **Postcheck**: user present in `dba_users` (created ACCOUNT LOCK).86- **Rollback**: removal is T3 `references/runbooks.md#drop-user`; creation is the reversible direction.8788### User password reset (L1, T2)89- **Trigger**: user locked out by expiry / needs a fresh credential.90- **Precheck**: user exists.91- **Action**: `scripts/reset_password.sh --user U [--mode expire|wallet-rotate] --execute` / `.ps1`. No plaintext — EXPIRE forces self-service set; wallet-rotate unlocks for a wallet rotation done out of band.92- **Postcheck**: EXPIRE -> `EXPIRED`; wallet-rotate -> not `LOCKED`.93- **Rollback**: user sets a new password (EXPIRE), or rotate the wallet back.9495### Account lock / unlock (L1, T2)96- **Trigger**: suspend (lock) or restore (unlock) access.97- **Precheck**: user exists; SQL refuses Oracle-maintained/system accounts.98- **Action**: `scripts/lock_unlock_account.sh --user U --action lock|unlock --execute` / `.ps1`.99- **Postcheck**: `account_status` reflects the requested lock state.100- **Rollback**: exact inverse (`unlock` <-> `lock`).101102### Expired account handling (L1, T1 report / T2 remediate)103- **Trigger**: daily expiry sweep (report); remediate a specific expired account.104- **Precheck (remediate)**: user exists; refuses system accounts.105- **Action**: `scripts/expired_accounts.sh report [--days 14]` (read-only) or `remediate --user U [--strategy reexpire|extend --profile P] --execute` / `.ps1`.106- **Postcheck**: reexpire -> `EXPIRED`; extend -> on new profile, not locked.107- **Rollback**: re-apply prior profile (extend) or let the user self-service (reexpire).108109### User account drop / deletion (L1, **T3**)110- **Trigger**: decommission an account. **Default deprovisioning is LOCK + EXPIRE** (T2, reversible) for a grace period; only then drop.111- **Action**: print `references/runbooks.md#drop-user`, complete pre-flight (export first!), supply approval token; a human runs `DROP USER ... CASCADE`.112- **Rollback**: none for CASCADE — recover via Data Pump import / Backup & Recovery runbooks. Never auto-executed.113114### Standard access request fulfillment (L1, T2)115- **Trigger**: grant/revoke a PRE-APPROVED catalogued role or low-risk system privilege.116- **Precheck**: grantee exists (user or role).117- **Action**: `scripts/grant_access.sh --grantee G --type role|syspriv --name N [--operation grant|revoke] [--with-admin] --execute` / `.ps1`. High-risk items refused -> `#privileged-grant`.118- **Postcheck**: grant present after GRANT / absent after REVOKE.119- **Rollback**: exact inverse (`--operation revoke`).120121### Schema creation (L2, T2)122- **Trigger**: new application owner schema.123- **Precheck**: default/temp tablespaces exist.124- **Action**: `scripts/create_schema.sh --schema S --default-ts APPDATA --temp-ts TEMP [--quota UNLIMITED] --execute` / `.ps1` (wallet/external/global auth; object-create baseline; ACCOUNT LOCK).125- **Postcheck**: schema owner present in `dba_users`.126- **Rollback**: T3 `#drop-user`.127128### Role creation & assignment (L2, T2)129- **Trigger**: define/assemble a standard access bundle and assign it.130- **Precheck**: list args are identifier-safe; nested roles/grantees must exist (SQL checks).131- **Action**: `scripts/create_role.sh --role R [--sys-privs '...'] [--role-privs '...'] [--assign-to '...'] --execute` / `.ps1`. DBA-class members refused -> `#privileged-grant`.132- **Postcheck**: role present in `dba_roles`.133- **Rollback**: `DROP ROLE` / `REVOKE` (re-run with prior membership to converge).134135### System / object privilege grants (L2, T1 review / T2 grant/revoke)136- **Trigger**: delegate or remove a specific SYS/OBJ/ROLE privilege; audit current grants.137- **Precheck**: grantee exists; `dba_ai_conn` holds the privilege `WITH ADMIN OPTION`/grantor.138- **Action**: `scripts/grant_privilege.sh review [--grantee G]` (read-only) or `grant|revoke --kind sys|obj|role --name N [--object OWNER.OBJ] [--admin] [--grant-option] --execute` / `.ps1`.139- **Postcheck**: state in `dba_sys_privs`/`dba_role_privs`/`dba_tab_privs`.140- **Rollback**: `revoke` is the exact inverse of `grant`.141142### Profile creation & management (L2, T1 list / T2 apply)143- **Trigger**: enforce password/resource limits; audit profiles.144- **Precheck**: profile name valid (apply); none for list (read-only).145- **Action**: `scripts/profile_manage.sh list [--profile P]` or `apply --profile P [--failed-login-attempts N --password-life-time D --password-verify-func FN ...] --execute` / `.ps1`.146- **Postcheck**: profile present; limits reflect desired values.147- **Rollback**: re-apply prior limit values.148149### Password policy / verify function enforcement (L2, T2)150- **Trigger**: install/refresh a password complexity verify function.151- **Precheck**: connecting schema is a privileged security schema (out of band, not SYS).152- **Action**: `scripts/password_policy.sh [--func ODB_VERIFY_PASSWORD] [--min-length 12] --execute` / `.ps1`, then attach via `profile_manage.sh --password-verify-func`.153- **Postcheck**: function compiled `VALID`.154- **Rollback**: detach (profile `PASSWORD_VERIFY_FUNCTION` -> `NULL`/`DEFAULT`); `DROP FUNCTION` is the T3 escalation.155156### Periodic access review / recertification (L1, T1)157- **Trigger**: quarterly recertification; dormant-account / privileged-access audit.158- **Precheck**: none (read-only). On Data Guard run against the PRIMARY (LAST_LOGIN is primary-side).159- **Action**: `scripts/access_review.sh [--dormant-days 90]` / `.ps1` -> evidence pack + KEYVAL metrics.160- **Postcheck**: n/a — read-only; findings feed recertification.161- **Rollback**: none.162163### Proxy auth / CMU / EUS — review (L3 task family, T1 read-only)164- **Trigger**: pre-flight discovery before a T3 directory-integration change, or post-change verification.165- **Action**: `scripts/directory_integration_review.sh proxy|cmu|eus|all` / `.ps1` -> the matching `sql/*_review.sql`.166- **Postcheck/Rollback**: n/a — read-only. The **setup** of each is T3 (below).167168## Tier-3 runbooks169170Directory-integration **setup**, **user drop**, and **DBA-class privileged grants** are171Tier 3 and live in **`references/runbooks.md`**: `#proxy-auth`, `#cmu`, `#eus`,172`#drop-user`, `#privileged-grant`.173174These runbooks **PRINT exact SQL/OS commands for a human operator and NEVER self-execute.**175They are gated by `require_approval_token` / `Require-ApprovalToken`: an automated agent's176only sanctioned action is to print the relevant section and stop with `ERR_APPROVAL` (6)177until a human supplies a real change ticket/token (`--token <TICKET>` or178`ODB_APPROVAL_TOKEN`; placeholders rejected). The T2 scripts deliberately refuse DBA-class179grants (`DBA`, `ALL PRIVILEGES`, `GRANT ANY *`, `SYSDBA`/`SYSOPER`/..., `BECOME USER`,180`ALTER SYSTEM`/`DATABASE`, `CREATE`/`ALTER`/`DROP USER`) and point to `#privileged-grant`.181SYSDBA is a T3-only, interactively-supplied exception. When in doubt, runbook.182183See `references/version-notes.md` for 19c-vs-23ai behavioural deltas, and184`cron/crontab.d` / `cron/task-scheduler.xml` for the schedulable T1/T2 jobs (read-only185reviews + expiry sweep; no T3 is ever scheduled).