# Technical documentation — lambda_java

Evidence date: 2026-10-10. Source: `main` at `7055717` (`git@bitbucket.org:overdrive/lambda_java.git`).

Secret values, sample JWTs, and compiled-in client secrets are not copied here. The files that contain them are named so they can be rotated.

## Architecture

One AWS SAM stack. API Gateway REST API `IM-CAPITAL-BACKEND-SERVICE` invokes Java Lambda handlers. A REQUEST authorizer Lambda is the default authorizer. Handlers call MySQL, SSM, and external HTTP APIs. One function is on a one-hour schedule and drains SQS into S3.

```text
template.yaml
  Parameters.Environment = dev | prod
  Globals.Function: java17, 1536 MB, timeout 30s, VPC, SECRET_NAME, REGION
  AWSApiGateway (stage = Environment, CORS, MyAuthorizer)
  MyAuthorizer
  one function per route (CodeUri: IM-CAPITAL-BACKEND-SERVICE)
  SQSLogToFile schedule rate(1 hour)
```

All functions use the same execution role ARN:

`arn:aws:iam::597707462019:role/IMCapital_serverless_deployment`

Each function also declares an inline policy for `ec2:DescribeNetworkInterfaces`, `CreateNetworkInterface`, `DeleteNetworkInterface`, `DescribeInstances`, and `AttachNetworkInterface` on `Resource: *`. Secrets Manager, SSM, S3, and SQS actions are not in that inline policy. Whether the shared role grants them is **Unknown** (the role is not in this repo).

Account id `597707462019` is in the role ARN only.

## Repository layout

```text
lambda_java/
  .gitignore
  bitbucket-pipelines.yml
  sonar-project.properties
  IM-CAPITAL-BACKEND-SERVICE/
    README.md                  SAM hello-world text
    template.yaml              source template
    samconfig.toml
    events/event.json          sample event; contains tokens
    .aws-sam/build/            committed build output, including a rendered template
    .aws-sam/build.toml
    IM-CAPITAL-BACKEND-SERVICE/
      pom.xml
      IM-CAPITAL-BACKEND-SERVICE.iml
      src/main/java/co/ke/imcapital/
        handlers/              Lambda entry points
        function/              SQL and HTTP helpers
        entity/
        io/                    AuthPolicy (unused by Authorizer)
        responce/              GatewayResponse, LegalEntitiesResponse
      src/test/java/co/ke/imcapital/
      src/test/java/resources/event.json
```

`CodeUri` is the inner `IM-CAPITAL-BACKEND-SERVICE` directory (the Maven module). Package name is `co.ke.imcapital`. The response package is spelled `responce`.

## Languages and frameworks

| Item | Value | Evidence |
|---|---|---|
| Language | Java | `pom.xml` |
| SAM runtime | `java17` | `Globals.Function.Runtime` |
| Maven source/target properties | 1.8 | `pom.xml` `maven.compiler.source` / `target` |
| Compiler plugin | source 9, target 9 | `maven-compiler-plugin` configuration |
| Packaging | `maven-shade-plugin` 3.2.4 on `package` | `pom.xml` |
| Tests | JUnit 4.13.2, JUnit Jupiter 5.11.0-M2, Mockito 5.12.0, PowerMock 2.0.9 | `pom.xml` |
| HTTP | `HttpURLConnection` and OkHttp 4.9.1 | function classes |
| JSON | Jackson 2.13.4.1 and `com.amazonaws.lambda.thirdparty.org.json` | handlers |
| SQL | MySQL Connector/J 8.0.33, `DriverManager` | `ConnectionRetriever` |
| AWS SDK | v2.21.16 for Secrets Manager, SSM, S3, SQS. BOM import is 2.20.45 and the property `aws.java.sdk.version` is 2.21.16 | `pom.xml` |
| Lambda Java libs | core 1.2.2, events 3.11.0 | `pom.xml` |

**Discrepancy:** template runtime is 17, compiler plugin is 9, properties are 1.8. Which bytecode is produced depends on the JDK that runs Maven. **Unknown / Requires Confirmation** which JDK was used for the committed `.aws-sam` build.

## API

Stage name is the `Environment` parameter (`dev` or `prod`). CORS allows GET, POST, OPTIONS from origin `*`.

The route table is in `features.md`. Globals: memory 1536, timeout 30, architecture `x86_64`. `InitiatePayment` sets timeout 45.

VPC on every function:

- Security group `sg-0089502251d79aa23`
- Subnets `subnet-08882af1374cdd5d2`, `subnet-0b9b9e77b66be7a4d`

These ids are not parameterized per environment.

Environment variables on every function:

| Name | Value in template |
|---|---|
| `SECRET_NAME` | `imcapital-dev-mysql-aurora` |
| `REGION` | `eu-west-1` |

`SECRET_NAME` does not change when `parameter_overrides` is `Environment=prod`.

Authorizer: `FunctionPayloadType: REQUEST`, identity header `Authorization`, invoke role `MyApiGatewayRole`. That role trusts `amplify.amazonaws.com`, `apigateway.amazonaws.com`, and `lambda.amazonaws.com`, and allows `apigateway:*` on `*` plus `lambda:InvokeFunction` on the authorizer ARN.

`GetToken` names handler `co.ke.imcapital.handlers.GetToken`. That class is not in `src/main/java`. Deploying the template as written fails when SAM or CloudFormation cannot find the handler. `TokenRetrieval` is the closest class and is not wired.

## Persistence

JDBC URL shape (password omitted):

`jdbc:mysql://<host>:<port>/IMCapital`

Secret JSON keys read by `ConnectionRetriever`: `host`, `username`, `password`, `port`.

Tables and columns named in SQL. This is not a schema dump. Types, keys, and indexes are **Unknown**.

| Table | Columns referenced | Writers / readers |
|---|---|---|
| `user` | `id`, `user_name`, `email`, `name`, `given_name`, `family_name`, `im_customer_identifier`, `accepted_terms_timestamp`; `SELECT *` also expects optional `phone_number`, `postal_address` when present | User create/update/select, terms |
| `sanlam_link` | `user_id`, `sanlam_customer_identifier`, `member_no`, `parent_no` | Insert on user create and on first purchase |
| `transaction` | `id`, `account`, `member_number`, `value`, `type`, `status`, `process`, `im_customer_identifier`, `created_by`, `updated_by` | Insert, select, update `process` |
| `sessions` | `ip_address`, `session_id`, `mac`, `created_by`, `logout_time` | Insert, update logout |
| `white_listed` | `CIF`, `Member No` | Select |
| `sanlam_member` | `last_kes_number`, `last_usd_number` | Select all, update |
| `settings` | `setting_name`, `value` | Select |
| `logs` | `log_title`, `classification`, `message`, `reference`, `created_at` | Insert via `DatabaseLog`; select via `S3Logs` |
| `logs_exported_time` | `time_exported` | Max select, insert |
| `log` | none named (`SELECT *`) | `Logs.java` only |

No migration tool, ORM, or seed is in the repo.

## External HTTP

SSM parameter names are listed in `prd.md`. `Parameter` uses region `eu-west-1` and returns a value only for type `SecureString`.

Hosts compiled into source, not loaded from SSM:

| Host | Used by |
|---|---|
| `https://test-upg-dxp.imbank.com/auth/realms/inm-retail/protocol/openid-connect/token` | `AccessToken` |
| `https://test-dxp.imbank.com/api/access-control/client-api/v2/legalentities/me` | `IMUserDetails` |
| `https://172.24.11.135:2284/KECBSRESTService/1.0/CustomerDetailsInq` | `IMUserDetails` |
| `https://172.24.11.135:2284/KEOAuthTokenService/1.0/GetToken` | `IMUserDetails` |
| `http://api-uat.sanlameastafrica.com/im/api/account/balance/` | `AccountBalance` (fixed account path) |
| `http://api-uat.sanlameastafrica.com/im/api/customer` | `SanlamUserCreation` (not the purchase-flow path) |
| `https://client.sanlameastafrica.com:8080/login` | `StatementData.postGetToken` |
| `http://105.27.230.181:3002/statementData` | `StatementData` |
| `http://105.27.230.181:3002/subGroupMembers` | `SubGroupMember` |

`AccessToken.java` and `IMUserDetails.java` contain client id and client secret literals. `events/event.json` and `src/test/java/resources/event.json` contain bearer tokens and a username and email. Values are omitted. **Human review:** treat them as exposed and rotate.

`IMAccounts` prepends `https://` when an SSM URL has no scheme.

## Queues, storage, schedule

| Resource | Name | Behaviour |
|---|---|---|
| SQS | `log_queue` in `eu-west-1` | `SQSLogCreation` creates the queue if needed, then sends a message with `delaySeconds` 5. Not declared in `template.yaml`. |
| S3 | `imcapital-dev-app-logs` | Key prefix `logs/`, file name `yyyyMMddHHmm.log`, content type `text/plain`. Bucket is not in the template. |
| Schedule | `rate(1 hour)` | `SQSLogToFile` only. The same schedule on `WriteLogs` is commented out. |

`S3Log.logToS3` lists the same bucket and reads the last object key. `WriteS3Logs` is the handler and has no active event.

## Deployment

`samconfig.toml` version 0.1.

| Profile | Stack | S3 bucket | Region | Override |
|---|---|---|---|---|
| `default` and `dev` | `IM-CAPITAL-BACKEND-DEV` | `imcapital-dev-lambda-deployment` | `eu-west-1` | `Environment=dev` |
| `prod` | `IM-CAPITAL-BACKEND-PROD` | `imcapital-prod-lambda-deployment` | `eu-west-1` | `Environment=prod` |

Shared deploy flags: `CAPABILITY_IAM`, `confirm_changeset = true`, `resolve_s3 = true`, `disable_rollback = true`, `s3_prefix` `IM-CAPITAL-BACKEND-DEV` on the default deploy block. Build: `cached = true`, `parallel = true`. Validate: `lint = true`. Local API and Lambda: `warm_containers = EAGER`.

The stock README says `sam build` then `sam deploy --guided`, Java 8, and Docker. `samconfig.toml` already has the guided answers, so a later deploy can use the profiles. The README's Java 8 instruction disagrees with the template's Java 17 runtime.

`.aws-sam/build/template.yaml` is committed (1462 lines in the init commit). It is build output. The source of truth for edits is `IM-CAPITAL-BACKEND-SERVICE/template.yaml`. Whether the committed build matches the source template byte for byte was not re-built here.

## Pipeline and static analysis

`bitbucket-pipelines.yml`:

- Image `maven:3.3.9`
- Clone depth `full`
- On branch `main` only: pipe `sonarsource/sonarqube-scan:2.0.1`
- Variables `SONAR_HOST_URL` and `SONAR_TOKEN` from repository or workspace variables
- Cache `~/.sonar`
- No `mvn test`, no `sam build`, no deploy

`sonar-project.properties` sets only:

`sonar.projectKey=overdrive_lambda_java_429c71b3-ef0b-4a41-9d30-9147798c8de6`

No coverage, exclusions, or source path. Sonar defaults apply.

`sonarqube_scan.bat` is not in the git tree.

## Testing

Tests live in `IM-CAPITAL-BACKEND-SERVICE/IM-CAPITAL-BACKEND-SERVICE/src/test/java`. Surefire 2.22.2. Example: `UserCreationTest` mocks `ConnectionRetriever` and the user helpers and drives the handler.

They were not run during this documentation pass. **Unknown** whether `mvn test` passes on Java 17. PowerMock 2.0.9 is on the classpath; several tests use Mockito without PowerMock.

Local invoke sample: `events/event.json`. The README command `sam local invoke HelloWorldFunction` does not match any resource name.

## Security notes (observation, not a remediation plan)

- Client secrets and live-looking tokens are in git (`AccessToken.java`, `IMUserDetails.java`, both `event.json` files).
- `PaymentInitiation` and parts of the I&M client code disable TLS verification.
- Many SQL statements concatenate request strings into the query (`user_name`, `cif`, `email`, `setting_name`, `im_customer_identifier`).
- `TokenCheck` returns the supplied access token in the response body.
- CORS origin is `*`.
- `Parameter` can call `System.exit(1)` inside a Lambda.
- The authorizer allows any token that can read a master I&M service agreement. It does not bind the agreement id to the caller.
- Log messages in `CheckSum` write the public key material at info level.
- Dev secret name, dev log bucket, and UAT Sanlam hosts are what prod would use if the template were deployed with `Environment=prod`.

## Development process

No contributor guide beyond the SAM sample README. Git history is four commits on 2024-06-24, authors Jaco Brits (repo bootstrap and Bitbucket-generated Sonar files) and Dirk (the `init` import). One branch: `main`.

## Items that need human confirmation

- Deployed stack status for `IM-CAPITAL-BACKEND-DEV` and `IM-CAPITAL-BACKEND-PROD`.
- IAM role permissions for Secrets Manager, SSM, SQS, and S3.
- MySQL schema and whether the `NewMemberNumber` update matches a row.
- Rotation of credentials committed in source and sample events.
- Whether explicit OPTIONS methods are authorised.
- Which JDK and SAM CLI version produced `.aws-sam/build`.
