A FastAPI application for testing IGA (Identity Governance and Administration) connectors with user and role CRUD operations, HTMX-powered UI, and bearer token authentication.
- User management (CRUD operations) with status tracking (Active, Disabled, Terminated)
- Role management with user assignments
- Bearer token authentication for API
- HTMX-powered reactive UI with Alpine.js for interactivity
- API activity tracking per token
- SQLite database with SQLAlchemy ORM
- Password hashing with bcrypt
- Auto-generated passwords for new users
- OpenAPI documentation for easy integration
- Python 3.9 or higher
- Git (optional, for cloning)
macOS/Linux:
./start-app.shWindows:
start-app.batCross-platform (Python):
python start-app.pyThe start script will:
- Create a virtual environment if needed
- Install dependencies automatically
- Start the application with auto-reload enabled
- Create virtual environment:
python -m venv .venv- Activate virtual environment:
# macOS/Linux:
source .venv/bin/activate
# Windows:
.venv\Scripts\activate- Install dependencies:
pip install -r requirements.txt- Run the application:
uvicorn app.main:app --reload- UI: http://localhost:8000
- API Documentation: http://localhost:8000/docs
- OpenAPI Schema: http://localhost:8000/openapi.json
The SQLite database (app.db) is automatically created on first run.
GET /api/v1/users- List all usersPOST /api/v1/users- Create new userGET /api/v1/users/{id}- Get user details with role IDsPATCH /api/v1/users/{id}- Update user (including status)DELETE /api/v1/users/{id}- Delete user
GET /api/v1/roles- List all rolesGET /api/v1/roles/{id}- Get role details with user IDsGET /api/v1/roles/{id}/users- Get full user objects assigned to a role
POST /api/v1/users/{user_id}/roles/{role_id}- Assign role to userDELETE /api/v1/users/{user_id}/roles/{role_id}- Remove role from user
GET /health- Application health check
All API calls require a bearer token in the Authorization header:
Authorization: Bearer <your-token-here>
Generate tokens through the UI's Token Management section.
# With auto-generated password
curl -X POST http://localhost:8000/api/v1/users \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]"
}'
# With specific password (min 6 chars)
curl -X POST http://localhost:8000/api/v1/users \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"password": "mySecurePass123"
}'Response (with auto-generated password):
{
"id": "2J9mNpQ3r5s7tUvWxYz1234567890",
"username": "jsmith",
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"display_name": "Jane Smith",
"status": "active",
"generated_password": "Xy#9kL$mN2pQ"
}Note: The generated_password field is only returned when a password is auto-generated.
curl -X PATCH http://localhost:8000/api/v1/users/{id} \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{
"status": "disabled"
}'# Assign a role to a user
curl -X POST http://localhost:8000/api/v1/users/{user_id}/roles/{role_id} \
-H "Authorization: Bearer YOUR_TOKEN_HERE"
# Remove a role from a user
curl -X DELETE http://localhost:8000/api/v1/users/{user_id}/roles/{role_id} \
-H "Authorization: Bearer YOUR_TOKEN_HERE"curl -X GET http://localhost:8000/api/v1/users/{id} \
-H "Authorization: Bearer YOUR_TOKEN_HERE"Response:
{
"id": "abc123",
"username": "jsmith",
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"display_name": "Jane Smith",
"status": "active",
"role_ids": ["role123", "role456"]
}curl -X GET http://localhost:8000/api/v1/roles/{role_id}/users \
-H "Authorization: Bearer YOUR_TOKEN_HERE"Response:
[
{
"id": "user123",
"username": "jdoe",
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]",
"display_name": "John Doe",
"status": "active"
},
{
"id": "user456",
"username": "jsmith",
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"display_name": "Jane Smith",
"status": "active"
}
]This application is designed to work with baton-http connectors. The API returns resource IDs instead of full objects in relationship fields to facilitate efficient resource graph building.
Key integration points:
UserWithRolesreturnsrole_idsarrayRoleWithUsersreturnsuser_idsarray- All IDs are stable SHA-1 hashes
- User status field supports: active, disabled, terminated
python -m pytestpython -m ruff check .
python -m mypy .python -m ruff format ..venv/bin/python generate_openapi.pyTOKEN="YOUR_TOKEN_FROM_UI_HERE"
for user in "john:doe:[email protected]" "jane:smith:[email protected]"; do
IFS=':' read -r first last email <<< "$user"
curl -X POST http://localhost:8000/api/v1/users \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"first_name\": \"$first\", \"last_name\": \"$last\", \"email\": \"$email\"}"
doneimport requests
token = "YOUR_TOKEN_FROM_UI_HERE"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
# Create user
user_data = {
"first_name": "Alice",
"last_name": "Williams",
"email": "[email protected]"
}
response = requests.post(
"http://localhost:8000/api/v1/users",
headers=headers,
json=user_data
)
user = response.json()
# Get existing role
response = requests.get(
"http://localhost:8000/api/v1/roles",
headers=headers
)
roles = response.json()
developer_role = next((r for r in roles if r["role_name"] == "Developer"), None)
if developer_role:
# Assign role to user
response = requests.post(
f"http://localhost:8000/api/v1/users/{user['id']}/roles/{developer_role['id']}",
headers=headers
)
print(f"Assigned {developer_role['role_name']} role to {user['display_name']}")uvicorn app.main:app --reload --port 8001# Stop the server first (CTRL+C)
rm app.db
# Restart - creates fresh database
uvicorn app.main:app --reloadEnsure virtual environment is activated:
source .venv/bin/activate # macOS/Linux
.venv\Scripts\activate # Windows- Verify Authorization header is included
- Check token hasn't been deleted
- Generate new token if needed
- Tokens: Store API tokens securely. Anyone with a token can access the API.
- Passwords:
- Auto-generated passwords are 12 characters with letters, numbers, and symbols
- Passwords are hashed with bcrypt before storage
- Generated passwords are only shown once at creation
- Local Testing: This application is designed for local testing. Do not expose to the internet without proper security measures.
- No Token Expiration: Tokens don't expire automatically. Delete them manually when no longer needed.
- API Activity: All API calls are logged and viewable in the UI.
This project is licensed under the MIT License.