This project is contains a React web application subproject that submits sequences, and a backend service that read them from configuration. See README files for each of these subprojects for details on build, run, etc.
csw-services start --location --auth --config --event --databasecat > /tmp/command-role-mapping.conf << 'EOF'
APS.primary.startSequence: [esw-user]
EOF
esw-gateway-server start -p 8090 -l -c /tmp/command-role-mapping.confNote: The
esw-userrole in the command role mapping is temporary to get things started.
cd ~/Desktop/Prototyping/aps-sequencer-prototype
sbt "runner/run sequencer -s APS -n primary -m APS_software_only_mode"cd ~/Desktop/Prototyping/aps-submitter-prototype/apssubmitterprototype-backend
source ~/.zshrc
sbt "run start --port 8084"cd ~/Desktop/Prototyping/aps-submitter-prototype/apssubmitterprototype-frontend
npm startGenerate and store the sequence file:
python3 -c "
import json
source = 'APS.sequenceSubmitter'
matrix = [[0.0] * 3 for _ in range(492)]
actuatorOffsets = {
'FloatMatrixKey': {
'keyName': 'actuatorOffsets',
'values': [matrix],
'units': 'millimeter'
}
}
sequence = [
{'_type': 'Setup', 'source': source, 'commandName': 'calc-colorstep', 'paramSet': []},
{'_type': 'Setup', 'source': source, 'commandName': 'cmd-m1cs-moves', 'paramSet': [actuatorOffsets]},
{'_type': 'Setup', 'source': source, 'commandName': 'calc-tt-offsets-to-acts', 'paramSet': []},
{'_type': 'Setup', 'source': source, 'commandName': 'calc-decompose-acts', 'paramSet': []}
]
print(json.dumps(sequence, indent=2))
" > ~/aps-sequence.json
cs launch csw-config-cli -- create /aps/sequences/testmode.json \
--in ~/aps-sequence.json \
--comment "APS software-only mode test sequence"Note: If the file already exists from a previous run, use
updateinstead ofcreate, then set it as the active version:cs launch csw-config-cli -- update /aps/sequences/testmode.json \ --in ~/aps-sequence.json \ --comment "update" cs launch csw-config-cli -- setActiveVersion /aps/sequences/testmode.json \ --id <version-id> --comment "set active"
- Open
http://localhost:3000in a browser - Log in with
esw-user1/esw-user1 - Enter config service path:
/aps/sequences/testmode.json - Click Load Template
- Click Submit Sequence
Expected response:
{
"_type": "Completed",
"runId": "...",
"result": {
"paramSet": []
}
}This documents the Keycloak configuration required to use the ESW Gateway with a frontend app in a CSW/ESW development environment.
Each time csw-services is restarted, these steps must be performed, as Keycloak is reset each time CSW is restarted.
- CSW services started with:
csw-services start --location --auth --config --event --database - Keycloak admin console:
http://localhost:8081(login:admin/admin) - Keycloak API base:
http://localhost:8081/admin/realms/TMT - ESW Gateway started with:
esw-gateway-server start -p 8090 -l -c /tmp/command-role-mapping.conf - The gateway's
auth-config(in itsapplication.conf) referencesclient-id = tmt-backend-appandrealm = TMT
The embedded Keycloak that csw-services starts does not include a tmt-backend-app
client by default, and tokens issued to the frontend do not include tmt-backend-app
in their audience. The gateway rejects all tokens silently with 403 Forbidden and
x-tmt-username: unknown in request headers.
This set of steps was derived by AI after hours of trial and error. These need to be tested once again to verify they work and are necessary.
Run this first. The token expires in 60 seconds so run subsequent steps quickly, or re-run this line before each step.
TOKEN=$(curl -s -X POST "http://localhost:8081/realms/master/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin&password=admin&grant_type=password&client_id=admin-cli" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")The gateway validates tokens against this client. It must exist in the TMT realm.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"http://localhost:8081/admin/realms/TMT/clients" \
-d '{"clientId": "tmt-backend-app", "enabled": true, "publicClient": true, "bearerOnly": false}'The frontend app uses tmt-frontend-app. You need its internal Keycloak UUID to
add a mapper to it.
curl -s -H "Authorization: Bearer $TOKEN" \
"http://localhost:8081/admin/realms/TMT/clients?clientId=tmt-frontend-app" \
| python3 -m json.tool | grep '"id"' | head -1Note the UUID value (e.g. 963dec63-a99f-4030-ad4e-57efe8c00207).
This adds tmt-backend-app to the aud claim of tokens issued by tmt-frontend-app,
which the gateway requires for token validation.
Replace <CLIENT-UUID> with the UUID from Step 3.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"http://localhost:8081/admin/realms/TMT/clients/<CLIENT-UUID>/protocol-mappers/models" \
-d '{
"name": "tmt-backend-app-audience",
"protocol": "openid-connect",
"protocolMapper": "oidc-audience-mapper",
"consentRequired": false,
"config": {
"included.client.audience": "tmt-backend-app",
"id.token.claim": "false",
"access.token.claim": "true"
}
}'The gateway checks for {subsystem}-user role for sequencer commands. For APS
sequencer submissions the user needs aps-user.
First, find the user ID:
curl -s -H "Authorization: Bearer $TOKEN" \
"http://localhost:8081/admin/realms/TMT/users" \
| python3 -m json.tool | grep -E '"id"|"username"'Then get the aps-user role ID:
ROLE_ID=$(curl -s -H "Authorization: Bearer $TOKEN" \
"http://localhost:8081/admin/realms/TMT/roles/aps-user" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")Then assign the role (replace <USER-UUID> with the user's ID):
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"http://localhost:8081/admin/realms/TMT/users/<USER-UUID>/role-mappings/realm" \
-d "[{\"id\": \"$ROLE_ID\", \"name\": \"aps-user\"}]"The gateway requires a command role mapping file. For APS sequencer access:
cat > /tmp/command-role-mapping.conf << 'CONF'
APS.primary.startSequence: [aps-user]
CONFesw-gateway-server start -p 8090 -l -c /tmp/command-role-mapping.confIn the frontend app, log in as the user you assigned aps-user to in Step 5.
The predefined user esw-user1 (password: esw-user1) is a good choice after
assigning aps-user to it.
- Steps 2–5 only need to be done once per Keycloak instance. They persist across
gateway and service restarts as long as
csw-servicesis not restarted. - If
csw-servicesis restarted, Keycloak resets and all steps must be repeated. - The
tmt-frontend-appclient UUID and user UUIDs are specific to each Keycloak instance — always look them up rather than hardcoding them. - The Keycloak admin UI at
http://localhost:8081only shows themasterrealm. The TMT realm is created programmatically and must be managed via the API. - The predefined users in the TMT realm (all with password = username) are:
config-admin1,config-user1,dummy-user,esw-user1,iris-user1,osw-user1,tcs-user1,wfos-user1
The project has following structure:
.
├── src
│ ├── assets
│ ├── components
│ ├── config
│ ├── contexts
│ ├── hooks
│ ├── models
│ ├── routes
│ ├── utils
├── test
├── typesassets: This directory contains all the files (images, audio etc) that are used by the UI component.components: This directory contain all the components created for this UI application.config: This contain the application specific configurations.contexts: This contain contexts like LocationServiceContext to pass and share data to nested react conponents.hooks: This contain helper hooks.useAuth.tsxThis file contain auth related helper hooks and exposes login, logout and auth constants.useQuery.tsxThis file contain hooks to query data asynchronous and expose other constants like loading, error to track query state.
routes: This contain route related files.Routes.tsxThis file uses react-router to describe frontend routes for this application.ProtectedRoute.tsxThis file contain auth protected frontend routes.
utils: This contain common utilities.Http.tshas generic helper functions written over fetch API to do GET, POST requests.api.tsThis file usesHttp.tsand provide application specific functions to do POST requests.resolveBackend.tsThis file contain helper function to resolve location of backend using location service.
test: This directory contains all the tests for the UI application.types: This directory contains all the types that needs to be imported externally for UI application.