Missing Administrator Account - RodrigoMattosoSilveira/EnterpriseRemoteSystems GitHub Wiki
Missing Administrator Account
Stop the Bite 28C backend
Only one backend should access the local SQLite database during this procedure.
Stop the running backend with Ctrl+C.
2. Create a temporary Development worktree
cd /Users/rodrigosilveira/projects/EnterpriseRemoteSystems
git fetch origin
git worktree add --detach \
../EnterpriseRemoteSystems-development-bootstrap \
origin/development
Confirm that Development contains Bite 28B authentication:
test -f \
../EnterpriseRemoteSystems-development-bootstrap/backend/migrations/000042_user_authentication_foundation.up.sql \
&& echo "Bite 28B authentication is present"
If that file is missing, do not continue with this procedure.
3. Point Development at the existing Bite 28C local database
Check the database configuration in your main working tree:
cd /Users/rodrigosilveira/projects/EnterpriseRemoteSystems
grep -E '^(DB_PATH|DATABASE_PATH)=' backend/.env
If neither variable is present, the normal default database is:
/Users/rodrigosilveira/projects/EnterpriseRemoteSystems/backend/data/app.db
Set the exact database file:
LOCAL_DB="/Users/rodrigosilveira/projects/EnterpriseRemoteSystems/backend/data/app.db"
Adjust that value if your .env specifies another file.
Confirm it exists:
ls -lh "$LOCAL_DB"
4. Start the Development backend against that database
cd /Users/rodrigosilveira/projects/EnterpriseRemoteSystems-development-bootstrap
DB_PATH="$LOCAL_DB" make local-backend
Leave this terminal running.
Bite 28C did not introduce a new database migration in the files you listed, so the Bite 28B authentication schema should remain compatible.
5. Inspect the bootstrap administrator actor
Set the API address:
BASE_URL="http://127.0.0.1:8080/api/v1"
Retrieve the Actor records and preserve the complete response:
HTTP_STATUS=$(
curl -sS \
-D /tmp/ers-development-authz-actors.headers \
-o /tmp/ers-development-authz-actors.json \
-w '%{http_code}' \
-H "X-Actor-ID: bootstrap-admin" \
-H "X-Tenant-ID: default" \
"$BASE_URL/authz/actors"
)
echo "HTTP $HTTP_STATUS"
jq . /tmp/ers-development-authz-actors.json
Expected:
HTTP 200
Now extract bootstrap-admin using a query that supports both possible response shapes:
jq -e '
def actors:
if (.data | type) == "array" then
.data
elif (.data.items | type) == "array" then
.data.items
else
[]
end;
actors
| map(select(.actorKey == "bootstrap-admin"))
| first
| if . == null then
error("bootstrap-admin was not found")
else
{
actorRecordId: .id,
actorKey: .actorKey,
displayName: .displayName,
active: .active,
applicationAdminGrants: [
(.roleGrants // .grants // [])[]
| select(
.roleCode == "APPLICATION_ADMIN"
and .tenantId == "*"
)
]
}
end
' /tmp/ers-development-authz-actors.json
Expected result should resemble:
{
"actorRecordId": "some-internal-uuid",
"actorKey": "bootstrap-admin",
"displayName": "Bootstrap Administrator",
"active": true,
"applicationAdminGrants": [
{
"roleCode": "APPLICATION_ADMIN",
"tenantId": "*",
"active": true
}
]
}
Confirm:
- actorKey is bootstrap-admin.
- active is true.
- applicationAdminGrants contains at least one record.
- Its roleCode is APPLICATION_ADMIN.
- Its tenantId is *.
- If the response includes grant active, it is true.
Extract the internal Actor record ID
After the preceding command succeeds:
BOOTSTRAP_ACTOR_RECORD_ID=$(
jq -er '
def actors:
if (.data | type) == "array" then
.data
elif (.data.items | type) == "array" then
.data.items
else
[]
end;
actors[]
| select(.actorKey == "bootstrap-admin")
| .id
' /tmp/ers-development-authz-actors.json
)
echo "Bootstrap Actor record ID: $BOOTSTRAP_ACTOR_RECORD_ID"
The result should be a UUID or other internal database identifier—not:
bootstrap-admin
bootstrap-admin is the Actor key; BOOTSTRAP_ACTOR_RECORD_ID is the internal Actor record ID required when creating the authentication account.
If the command says that bootstrap-admin was not found, list the available Actor keys:
jq -r '
if (.data | type) == "array" then
.data
elif (.data.items | type) == "array" then
.data.items
else
[]
end
| .[]
| [.actorKey, .displayName, (.active | tostring)]
| @tsv
' /tmp/ers-development-authz-actors.json
6. Check whether it already has an authentication account
curl -sS \
-H "X-Actor-ID: bootstrap-admin" \
-H "X-Tenant-ID: default" \
"$BASE_URL/auth/accounts" \
| jq \
--arg actorId "$BOOTSTRAP_ACTOR_RECORD_ID" \
'.data[]
| select(.actorId == $actorId)
| {
accountId: .id,
login,
active,
actorActive,
mustChangePassword
}'
Proceed according to the result.
Option A — No account exists
Choose credentials:
ADMIN_LOGIN="[email protected]"
ADMIN_PASSWORD='Local-ERS-Admin-Password-28C!'
Create the account:
jq -n \
--arg actorId "$BOOTSTRAP_ACTOR_RECORD_ID" \
--arg login "$ADMIN_LOGIN" \
--arg password "$ADMIN_PASSWORD" \
'{
actorId: $actorId,
login: $login,
temporaryPassword: $password,
mustChangePassword: false
}' \
| curl -sS \
-D /tmp/ers-create-local-admin.headers \
-o /tmp/ers-create-local-admin.json \
-w 'HTTP %{http_code}\n' \
-H "X-Actor-ID: bootstrap-admin" \
-H "X-Tenant-ID: default" \
-H "Content-Type: application/json" \
--data-binary @- \
"$BASE_URL/auth/accounts"
cat /tmp/ers-create-local-admin.headers
jq . /tmp/ers-create-local-admin.json
Expected:
HTTP 201
Option B — An account exists, but its password is unknown
Extract its account ID and login:
ACCOUNT_JSON=$(
curl -sS \
-H "X-Actor-ID: bootstrap-admin" \
-H "X-Tenant-ID: default" \
"$BASE_URL/auth/accounts" \
| jq -c \
--arg actorId "$BOOTSTRAP_ACTOR_RECORD_ID" \
'.data[]
| select(.actorId == $actorId)'
)
ACCOUNT_ID=$(jq -er '.id' <<<"$ACCOUNT_JSON")
ADMIN_LOGIN=$(jq -er '.login' <<<"$ACCOUNT_JSON")
ADMIN_PASSWORD='Local-ERS-Admin-Password-28C!'
echo "Account: $ACCOUNT_ID"
echo "Login: $ADMIN_LOGIN"
Issue a reset token:
RESET_TOKEN=$(
curl -sS \
-X POST \
-H "X-Actor-ID: bootstrap-admin" \
-H "X-Tenant-ID: default" \
"$BASE_URL/auth/accounts/$ACCOUNT_ID/password-reset-tokens" \
| jq -er '.data.token'
)
echo "Reset token obtained."
Reset the password:
jq -n \
--arg token "$RESET_TOKEN" \
--arg password "$ADMIN_PASSWORD" \
'{
token: $token,
newPassword: $password
}' \
| curl -sS \
-o /tmp/ers-reset-local-admin.json \
-w 'HTTP %{http_code}\n' \
-H "Content-Type: application/json" \
--data-binary @- \
"$BASE_URL/auth/password/reset"
Expected:
HTTP 204
The reset token is one-time use and should not be retained.
7. Stop Development and restart Bite 28C
Stop the Development backend with Ctrl+C.
Then restart the backend from the real Bite 28C working tree:
cd /Users/rodrigosilveira/projects/EnterpriseRemoteSystems
make local-backend
8. Verify the new Bite 28C login
In another terminal:
BASE_URL="http://127.0.0.1:8080/api/v1"
ADMIN_COOKIE="/tmp/ers-b28c-admin.cookies"
rm -f "$ADMIN_COOKIE"
Use the ADMIN_LOGIN and ADMIN_PASSWORD established above:
jq -n \
--arg login "$ADMIN_LOGIN" \
--arg password "$ADMIN_PASSWORD" \
'{
login: $login,
password: $password
}' \
| curl -sS \
-D /tmp/ers-b28c-admin-login.headers \
-o /tmp/ers-b28c-admin-login.json \
-w 'HTTP %{http_code}\n' \
-c "$ADMIN_COOKIE" \
-H "Content-Type: application/json" \
--data-binary @- \
"$BASE_URL/auth/login"
jq . /tmp/ers-b28c-admin-login.json
Expected:
HTTP 200
Then verify Application Admin access:
curl -sS \
-D /tmp/ers-b28c-admin-actors.headers \
-o /tmp/ers-b28c-admin-actors.json \
-w 'HTTP %{http_code}\n' \
-b "$ADMIN_COOKIE" \
-H "X-Tenant-ID: default" \
"$BASE_URL/authz/actors"
jq . /tmp/ers-b28c-admin-actors.json
Expected:
HTTP 200
9. Remove the temporary worktree
After the administrator login works under Bite 28C:
cd /Users/rodrigosilveira/projects/EnterpriseRemoteSystems
git worktree remove \
../EnterpriseRemoteSystems-development-bootstrap