#!/usr/bin/env bash # OMNL Fineract — Staff + User for PT CAKRA office (President Director login, office-scoped). # Default office: resolve by CAKRA_OFFICE_EXTERNAL_ID or CAKRA_OFFICE_ID (default external OMNL-ID-JKT-CAKRA-001). # # Env (required unless CAKRA_GENERATE_PASSWORD=1): # OMNL_CAKRA_ADMIN_PASSWORD Password for Fineract user bpramukantoro # Optional: # CAKRA_GENERATE_PASSWORD=1 Generate a password and print it once to stderr (save securely). # CAKRA_USERNAME default bpramukantoro # CAKRA_OFFICE_ID integer office id (skips resolve by external id) # CAKRA_OFFICE_EXTERNAL_ID default OMNL-ID-JKT-CAKRA-001 # CAKRA_ROLE_NAME default "Office Admin" # # Requires: omnl-fineract/.env or .env with OMNL_FINERACT_* admin credentials. set -euo pipefail REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" CAKRA_USERNAME="${CAKRA_USERNAME:-bpramukantoro}" STAFF_FIRSTNAME="${CAKRA_STAFF_FIRSTNAME:-Bambang}" STAFF_LASTNAME="${CAKRA_STAFF_LASTNAME:-Pramukantoro}" CAKRA_OFFICE_EXTERNAL_ID="${CAKRA_OFFICE_EXTERNAL_ID:-OMNL-ID-JKT-CAKRA-001}" CAKRA_OFFICE_ID="${CAKRA_OFFICE_ID:-}" CAKRA_ROLE_NAME="${CAKRA_ROLE_NAME:-Office Admin}" CAKRA_GENERATE_PASSWORD="${CAKRA_GENERATE_PASSWORD:-0}" if [ -f "${REPO_ROOT}/omnl-fineract/.env" ]; then set +u; source "${REPO_ROOT}/omnl-fineract/.env" 2>/dev/null || true; set -u; fi if [ -f "${REPO_ROOT}/.env" ]; then set +u; source "${REPO_ROOT}/.env" 2>/dev/null || true; set -u; fi BASE_URL="${OMNL_FINERACT_BASE_URL:-}" TENANT="${OMNL_FINERACT_TENANT:-omnl}" ADMIN_USER="${OMNL_FINERACT_USER:-app.omnl}" ADMIN_PASS="${OMNL_FINERACT_PASSWORD:-}" CAKRA_PASS="${OMNL_CAKRA_ADMIN_PASSWORD:-}" [ -z "$BASE_URL" ] || [ -z "$ADMIN_PASS" ] && { echo "Set OMNL_FINERACT_BASE_URL and OMNL_FINERACT_PASSWORD" >&2; exit 1; } if [ -z "$CAKRA_PASS" ]; then if [ "$CAKRA_GENERATE_PASSWORD" = "1" ]; then CAKRA_PASS="$(openssl rand -base64 18 | tr -d '\n')" echo "Generated password for ${CAKRA_USERNAME} (save securely, not logged again):" >&2 echo "$CAKRA_PASS" >&2 else echo "Set OMNL_CAKRA_ADMIN_PASSWORD or run with CAKRA_GENERATE_PASSWORD=1" >&2 exit 1 fi fi CURL_OPTS=(-s -S -w "\n%{http_code}" -H "Fineract-Platform-TenantId: ${TENANT}" -H "Content-Type: application/json" -u "${ADMIN_USER}:${ADMIN_PASS}") if [ -z "$CAKRA_OFFICE_ID" ]; then offices_json=$(curl "${CURL_OPTS[@]}" "${BASE_URL}/offices" 2>/dev/null | sed '$d') CAKRA_OFFICE_ID=$(echo "$offices_json" | jq -r --arg e "$CAKRA_OFFICE_EXTERNAL_ID" '.[] | select(.externalId == $e) | .id' 2>/dev/null | head -1) fi if [ -z "$CAKRA_OFFICE_ID" ] || [ "$CAKRA_OFFICE_ID" = "null" ]; then echo "Could not resolve office id for externalId=$CAKRA_OFFICE_EXTERNAL_ID (create office first)." >&2 exit 1 fi users_json=$(curl "${CURL_OPTS[@]}" "${BASE_URL}/users" 2>/dev/null | sed '$d') existing_uid=$(echo "$users_json" | jq -r --arg u "$CAKRA_USERNAME" '.[] | select(.username == $u) | .id' 2>/dev/null | head -1) if [ -n "$existing_uid" ] && [ "$existing_uid" != "null" ]; then echo "User already exists: username=$CAKRA_USERNAME userId=$existing_uid" >&2 echo "USER_ID_CAKRA=$existing_uid" exit 0 fi EXISTING_STAFF=$(curl "${CURL_OPTS[@]}" "${BASE_URL}/staff?officeId=${CAKRA_OFFICE_ID}" 2>/dev/null | sed '$d') STAFF_ID=$(echo "$EXISTING_STAFF" | jq -r 'if type == "array" then (.[0].id // empty) else empty end' 2>/dev/null) if [ -n "$STAFF_ID" ]; then echo "Using existing staff id=$STAFF_ID for office $CAKRA_OFFICE_ID" >&2 else JOINING_DATE="${JOINING_DATE:-$(date +%Y-%m-%d)}" STAFF_JSON=$(jq -n --argjson officeId "$CAKRA_OFFICE_ID" --arg fn "$STAFF_FIRSTNAME" --arg ln "$STAFF_LASTNAME" --arg jd "$JOINING_DATE" '{ officeId: $officeId, firstname: $fn, lastname: $ln, joiningDate: $jd, dateFormat: "yyyy-MM-dd", locale: "en", isActive: true }') STAFF_OUT=$(curl "${CURL_OPTS[@]}" -X POST -d "$STAFF_JSON" "${BASE_URL}/staff" 2>/dev/null) STAFF_CODE=$(echo "$STAFF_OUT" | tail -n1) STAFF_RESP=$(echo "$STAFF_OUT" | sed '$d') [ "$STAFF_CODE" = "200" ] || [ "${STAFF_CODE:0:1}" = "2" ] || { echo "Staff failed $STAFF_CODE: $STAFF_RESP" >&2; exit 1; } STAFF_ID=$(echo "$STAFF_RESP" | jq -r '.resourceId // empty') [ -n "$STAFF_ID" ] || { echo "No staff resourceId" >&2; exit 1; } echo "Staff created id=$STAFF_ID" >&2 fi ROLES_JSON=$(curl "${CURL_OPTS[@]}" "${BASE_URL}/roles" 2>/dev/null | sed '$d') ROLE_ID=$(echo "$ROLES_JSON" | jq -r --arg rn "$CAKRA_ROLE_NAME" '(.[] | select(.name == $rn) | .id) // empty' 2>/dev/null | head -n1) if [ -z "$ROLE_ID" ] || [ "$ROLE_ID" = "null" ]; then ROLE_ID=$(echo "$ROLES_JSON" | jq -r '(.[] | select(.name == "Office Admin") | .id) // (.[] | select(.name != "Super user" and .name != "System") | .id) // .[0].id // 2' 2>/dev/null | head -n1) fi ROLE_ID=${ROLE_ID:-3} USER_JSON=$(jq -n --arg u "$CAKRA_USERNAME" --arg p "$CAKRA_PASS" --argjson sid "$STAFF_ID" --argjson oid "$CAKRA_OFFICE_ID" --arg fn "$STAFF_FIRSTNAME" --arg ln "$STAFF_LASTNAME" --argjson roleId "$ROLE_ID" '{ username: $u, password: $p, repeatPassword: $p, staffId: $sid, officeId: $oid, firstname: $fn, lastname: $ln, roles: [$roleId], passwordNeverExpires: true }') USER_OUT=$(curl "${CURL_OPTS[@]}" -X POST -d "$USER_JSON" "${BASE_URL}/users" 2>/dev/null) USER_CODE=$(echo "$USER_OUT" | tail -n1) USER_RESP=$(echo "$USER_OUT" | sed '$d') if [ "$USER_CODE" = "200" ] || [ "${USER_CODE:0:1}" = "2" ]; then NEW_UID=$(echo "$USER_RESP" | jq -r '.resourceId // empty') echo "User $CAKRA_USERNAME created for office $CAKRA_OFFICE_ID (userId=$NEW_UID)" >&2 echo "USER_ID_CAKRA=${NEW_UID:-unknown}" exit 0 fi echo "POST /users failed HTTP $USER_CODE: $USER_RESP" >&2 echo "Staff record is ready for manual linking: STAFF_ID_CAKRA=$STAFF_ID officeId=$CAKRA_OFFICE_ID" >&2 echo "If this tenant returns 500 on POST /users (known on some HYBX builds), create the user in the Fineract UI:" >&2 echo " Administration → Users → Create, office=$CAKRA_OFFICE_ID, link staff id $STAFF_ID, role Office Admin, username=$CAKRA_USERNAME" >&2 exit 1