Documentation
Mendix ARX Module Design
Project-Agent-trust-merge / examples/mendix-integration-module.md
Overview
The ARX Mendix Module is a reusable package that abstracts ARX REST API integration for Mendix developers. It provides pre-built microflows, widgets, and utilities for policy enforcement, operation execution, and audit logging.
Architecture
`` Mendix Application ↓ [ArxPolicy_Checker Microflow] ← Check if operation allowed ↓ [Policy Result: PERMIT/ESCALATE/DENY] ↓ If DENY: Raise exception / show error If ESCALATE: Route to approval workflow If PERMIT: Continue to execution ↓ [ArxOperation_Execute Microflow] ← Execute operation with governance ↓ [Audit Log Entry] ← Stored in ARX backend ↓ [ArxAuditLog_Widget] ← Display history and compliance ``
Core Components
1. Policy Evaluation (ArxPolicy_Checker)
Purpose: Evaluate if an operation is allowed by policy without executing it.
Parameters:
Connector(String): Connector name (e.g., "okta", "crowdstrike")Operation(String): Operation name (e.g., "users:deactivate")Parameters(JSON): Operation parameters
Returns:
Verdict(String): "PERMIT", "ESCALATE", or "DENY"RiskScore(Integer): 0-100 risk assessmentPolicyId(String): Policy that generated verdictMessage(String): Human-readable explanationRequiresApproval(Boolean): Whether approval is needed
HTTP Call: ``` POST /v1/lowcode/policy-check Authorization: Bearer {API_KEY}
{ "connector": "okta", "operation": "users:deactivate", "params": {"user_id": "user@example.com"} } ```
Response Handler:
- Parse JSON response
- Extract verdict and risk_score
- Store in response entity
2. Operation Execution (ArxOperation_Execute)
Purpose: Execute a security operation with automatic policy enforcement, audit logging, and optional approval.
Parameters:
Connector(String)Operation(String)Parameters(JSON)RequireApproval(Boolean): Force approval requirementUserContext(JSON): Metadata for audit (app_name, user_id, etc.)
Returns:
Success(Boolean)Result(JSON): Operation resultError(String): Error message if failedAuditId(String): Audit log entry IDVerdict(String): Policy verdictRiskScore(Integer)RequiresApproval(Boolean)
HTTP Call: ``` POST /v1/lowcode/execute Authorization: Bearer {API_KEY}
{ "connector": "crowdstrike", "operation": "hosts:read", "params": {"hostname": "prod-server-01"}, "require_approval": false, "user_context": {"app_name": "Security Dashboard", "user_id": "user123"} } ```
Error Handling:
- Catch HTTP errors (401, 403, 404, 500)
- Log errors to Mendix logging
- Return error response to caller
- Store failed operation in audit log
3. Audit Log Display (ArxAuditLog_Widget)
Purpose: Display and filter operation history with policy verdicts and risk scores.
Features:
- DataGrid showing operation history
- Filter by:
- Connector
- Operation
- Verdict (PERMIT/ESCALATE/DENY)
- Risk level (LOW/MEDIUM/HIGH/CRITICAL)
- Date range
- User
- Export to CSV/PDF
- Risk color coding (green/yellow/red)
Data Attributes:
audit_id(String)connector(String)operation(String)verdict(String)risk_score(Integer)user_id(String)timestamp(DateTime)result(JSON)
Implementation Details
REST Client Configuration
Create a dedicated HTTP Client in Mendix for ARX calls:
``xml <httpClient> <name>ARXSecurityAPI</name> <baseUrl>https://api.arxsec.io</baseUrl> <timeout>30000</timeout> <defaultHeaders> <header name="Authorization" value="Bearer {ArxSecurityAPI_Key}"/> <header name="Content-Type" value="application/json"/> </defaultHeaders> <errorHandling> <handler statusCode="401"> <log level="ERROR" message="Unauthorized: Check API key"/> </handler> <handler statusCode="403"> <log level="ERROR" message="Forbidden: Insufficient permissions"/> </handler> <handler statusCode="404"> <log level="ERROR" message="Connector not found"/> </handler> <handler statusCode="500"> <log level="ERROR" message="Server error: Retry later"/> </handler> </errorHandling> </httpClient> ``
Microflow: CheckOperationPolicy
```xml <microflow name="CheckOperationPolicy"> <parameters> <parameter name="Connector" type="String"/> <parameter name="Operation" type="String"/> <parameter name="Parameters" type="String"/> <!-- JSON string --> </parameters>
<!-- Build request JSON --> <createVariableSetter variable="RequestBody"> { "connector": $Connector, "operation": $Operation, "params": $Parameters } </createVariableSetter>
<!-- Call ARX API --> <callRest endpoint="POST /v1/lowcode/policy-check"> <request>$RequestBody</request> </callRest>
<!-- Parse response --> <parseJson output="PolicyResponse" input="$ResponseBody"/>
<!-- Handle errors --> <ifElse condition="$ResponseStatusCode >= 400"> <true> <log level="ERROR" message="Policy check failed: {0}" args="[$ResponseBody]"/> <return>null</return> </true> <false> <return>$PolicyResponse</return> </false> </ifElse> </microflow> ```
Microflow: ExecuteSecurityOperation
```xml <microflow name="ExecuteSecurityOperation"> <parameters> <parameter name="Connector" type="String"/> <parameter name="Operation" type="String"/> <parameter name="Parameters" type="String"/> <!-- JSON --> <parameter name="RequireApproval" type="Boolean" default="false"/> </parameters>
<!-- Validate inputs --> <ifElse condition="empty($Connector) or empty($Operation)"> <true> <throwException message="Connector and Operation are required"/> </true> </ifElse>
<!-- Build request --> <createVariableSetter variable="ExecuteRequest"> { "connector": $Connector, "operation": $Operation, "params": parseJson($Parameters), "require_approval": $RequireApproval, "user_context": { "app_name": $CurrentUser.App, "user_id": $CurrentUser.UserID } } </createVariableSetter>
<!-- Call API --> <callRest endpoint="POST /v1/lowcode/execute"> <request>$ExecuteRequest</request> </callRest>
<!-- Parse response --> <parseJson output="ExecutionResult" input="$ResponseBody"/>
<!-- Check verdict --> <ifElse condition="$ExecutionResult.success"> <true> <log level="INFO" message="Operation {0} succeeded. Risk: {1}, Verdict: {2}" args="[$Operation, $ExecutionResult.risk_score, $ExecutionResult.verdict]"/> </true> <false> <log level="ERROR" message="Operation failed: {0}" args="[$ExecutionResult.error]"/> <throwException message="$ExecutionResult.error"/> </false> </ifElse>
<return>$ExecutionResult</return> </microflow> ```
Security Considerations
1. API Key Management
- Store API key in Mendix Vault (encrypted)
- Never log API keys
- Rotate keys every 90 days
- Use service account keys, not personal
2. Input Validation
- Validate connector names against allowed list
- Validate operation names (must be in OPERATION_MAP)
- Sanitize JSON parameters
- Enforce parameter type checking
3. Audit Trail
- Log all policy verdicts
- Log all executed operations
- Capture user context (who, when, what)
- Export audit logs to external SIEM
4. Rate Limiting
- Implement local rate limiting (X requests/minute)
- Respect ARX rate limit headers
- Queue requests if limit approaching
- Alert on rate limit exceeded
Testing
Unit Tests
Test each microflow in isolation:
``xml <test name="TestPolicyCheckerWithPermitVerdict"> <setup> <!-- Create test connector config --> </setup> <execute> <call microflow="CheckOperationPolicy"> <arg name="Connector">okta</arg> <arg name="Operation">users:read</arg> <arg name="Parameters">{"limit": 10}</arg> </call> </execute> <assert condition="$PolicyResponse.verdict = 'PERMIT'"/> <assert condition="$PolicyResponse.risk_score < 25"/> </test> ``
Integration Tests
Test end-to-end workflows:
``xml <test name="TestUserDeactivationWithApproval"> <setup> <!-- Configure high-risk policy for users:deactivate --> </setup> <execute> <!-- Call CheckOperationPolicy → should return ESCALATE --> <!-- Call ExecuteSecurityOperation → should queue for approval --> <!-- Verify audit log entry created --> </execute> <assert condition="$ExecutionResult.requires_approval = true"/> </test> ``
Version History
| Version | Changes | |---------|---------| | 1.0.0 | Initial release: Policy checker, operation executor, audit widget |
Roadmap
- [ ] v1.1: Batch operation execution
- [ ] v1.2: Approval workflow UI components
- [ ] v1.3: Mendix native widgets (not web-based)
- [ ] v1.4: Offline mode with sync-on-reconnect
- [ ] v2.0: GraphQL API support