# Business rules — unityAWS

Evidence date: 2026-09-30.

Source: `git@bitbucket.org:overdrive/unityaws.git`, `master` at `e9f10da` (2019-10-08).

No business-rules document exists in the source repository. Rules below are taken from the handler, `login.py`, and the pipeline file. Where the code behaves like a rule but nobody wrote it down as policy, the rule is marked **Inferred**. Credential values are omitted.

Silicon Ark's `unityNodejsApi` documents MD5 password checks and a `users` table on the same RDS host and database name. Those rules belong to that other repository. They are not implemented here, and this file does not copy them in as rules of unityAWS.

## BR-001 — Only the action string `login` calls the login function

**Inferred** from the single `if` in root `lambda_function.py`.

The comparison is `body["action"] == "login"` after `json.loads(event["body"])`. The match is case-sensitive. `Login`, `LOGIN`, or a missing key do not call `login()`. A missing key raises `KeyError` instead of returning the non-login body.

There is no allow-list beyond that one string.

Provenance: **Implemented** behaviour. Confidence: high.

## BR-002 — Login does not accept or reject a password

**Implemented** in `login()`.

The function receives the handler's username (parameter name `email`) and password and does not read them. The return is always:

```json
{"success": "false", "reason": "invalid"}
```

`success` is the string `"false"`, not a JSON boolean. `reason` is the string `"invalid"`. No other reason code exists.

This has been the whole body of `login()` since the function was added. No commit contains a password hash, a compare, or a query.

Provenance: **Implemented**. Confidence: high.

## BR-003 — A returned login failure is still HTTP 200

**Implemented** in the root handler's return.

The handler does not read `success` or `reason`. It always sets `statusCode` to 200 when it returns, including when the body says invalid.

Provenance: **Implemented**. Whether callers are supposed to ignore HTTP status and read `success` is **Unknown / Requires Confirmation**. Confidence: high for the status code.

## BR-004 — Any other parsed action is an empty JSON string at HTTP 200

**Implemented** since `e9f10da`.

`response` is initialised to `""`. If `action` is not `login`, that value is what `json.dumps` encodes. The body is a JSON string, not an object and not the login failure.

Provenance: **Implemented**. There is no documented list of valid actions. Confidence: high.

## BR-005 — The process must reach MySQL before it can answer

**Inferred** from import-time connect plus `sys.exit()`.

`login.py` connects when it is imported, which is when the root handler module loads. A failed connect logs `ERROR: Unexpected error: Could not connect to MySql instance.` and exits. The handler does not run, so BR-002's JSON is not returned.

The `except` clause has no exception type. The underlying error is not included in the log line.

`lambda/lambda_function.py` has the same connect-or-exit sequence, independent of `login.py`.

Provenance: **Implemented**. Confidence: high.

## BR-006 — The connection target is the QA database named in config

**Implemented** names in `config.py`:

- Database `unityqa`
- Host `unityqa.csigt2kcstpl.us-east-2.rds.amazonaws.com`
- User `dbadmin`
- Password present in the file and omitted here

`port = 3306` in `login.py` is not passed into `pymysql.connect`. The driver default is also 3306. Treating the server port as 3306 is **Inferred**.

No other database, host, or user appears in the application files.

Provenance: **Implemented** for the four config fields. Confidence: high.

## BR-007 — Credentials on a login call come from the event root

**Implemented** at `9ddf51c` and unchanged at `e9f10da`.

`action` comes from the parsed JSON body. `username` and `password` come from `event['username']` and `event['password']`, not from that JSON object.

A body that contains `username` and `password` and an event that does not have those keys still raises `KeyError` on the login path.

Provenance: **Implemented**. Whether the split is intentional is **Unknown / Requires Confirmation**. Confidence: high.

## BR-008 — The root response allows any browser origin

**Implemented** header `Access-Control-Allow-Origin: *` on the root handler's return, since `6ac019f`.

No other CORS header is set. There is no OPTIONS method. `lambda/lambda_function.py` does not set the header.

Provenance: **Implemented**. Confidence: high.

## BR-009 — Deployment updates one named function in one region

**Implemented** in `bitbucket-pipelines.yml`.

The pipe command is `update` (not create). Function name `qaFunction`. Region `us-east-2`. Zip file `code.zip`, built from `*` in that pipeline step.

The default pipeline has no branch filter and no manual trigger flag in the YAML. Only `master` exists.

Provenance: **Implemented** as pipeline text. Whether production policy allows that function name to be updated from this repository is **Unknown / Requires Confirmation**. Confidence: high for the file.

## Rules that are not in this repository

- No role, tenant, or permission check.
- No lockout, expiry, or attempt counter.
- No rule that a correct password succeeds. The code cannot succeed.
- No rule naming a required password length or character set.
- No rule that identifies the caller (IAM, API key, or token).
- No data-retention or audit rule. The only logs are the connect success line and the connect failure line.

## Discrepancy — shared database name, different rules elsewhere

`unityNodejsApi` in Silicon Ark records the same host, database name `unityqa`, and user `dbadmin`, and it contains SQL against a `users` table. unityAWS contains no SQL.

Whether the two repositories were required to enforce the same login rule is **Unknown / Requires Confirmation**. As of `e9f10da`, unityAWS does not enforce a credential check.
