Skip to content

Cannot Create File-Based Scripts Without Corrupted Database Content #12913

@ksully-execute

Description

@ksully-execute

Config API Bug: Cannot Create File-Based Scripts Without Corrupted Database Content

Environment

  • Jans Version: 1.15.0
  • Installation: Fresh install (December 28, 2025)
  • Server: Ubuntu with LDAP backend
  • Config API: v1.15.0

Summary

The Config API POST /config/scripts endpoint automatically populates the script field with template code, even when:

  1. The script field is not included in the request JSON
  2. The script field is explicitly set to null in the request
  3. The moduleProperties specify location_type: "file"

This makes it impossible to create truly file-based scripts via the Config API. The template code is corrupted/incomplete Python that causes SyntaxError when Jans tries to load it.

Steps to Reproduce

Step 1: Create file-based script metadata (without script content)

# Get API token
TOKEN=$(curl -s -u "$CLIENT_ID:$CLIENT_SECRET" \
  https://yourserver.com/jans-auth/restv1/token \
  -d "grant_type=client_credentials&scope=https://jans.io/oauth/config/scripts.write" \
  | jq -r '.access_token')

# Create script with NO script field in JSON
curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  https://yourserver.com/jans-config-api/api/v1/config/scripts \
  -d '{
    "name": "test_file_based",
    "scriptType": "update_token",
    "programmingLanguage": "python",
    "moduleProperties": [
      {"value1": "location_type", "value2": "file"},
      {"value1": "location_path", "value2": "/opt/jans/jetty/jans-auth/custom/scripts/test.py"}
    ],
    "level": 100,
    "enabled": false
  }'

Step 2: Verify script field

# Get created script
curl -s -H "Authorization: Bearer $TOKEN" \
  https://yourserver.com/jans-config-api/api/v1/config/scripts/name/test_file_based \
  | jq '{name, scriptLength: (.script | length), locationType: (.moduleProperties[] | select(.value1=="location_type") | .value2)}'

Expected Result:

{
  "name": "test_file_based",
  "scriptLength": 0,  // or null
  "locationType": "file"
}

Actual Result:

{
  "name": "test_file_based",
  "scriptLength": 810,  // Template code inserted by API
  "locationType": "file"
}

Step 3: Check script content

curl -s -H "Authorization: Bearer $TOKEN" \
  https://yourserver.com/jans-config-api/api/v1/config/scripts/name/test_file_based \
  | jq -r '.script' | head -20

Actual Output:

from io.jans.service.cdi.util import CdiUtil
from io.jans.oxauth.security import Identity
from io.jans.model.custom.script.type.authz import ConsentGatheringType
from io.jans.util import StringHelper

import java
import random

class SampleScript(ConsentGatheringType):

    def __init__(self, currentTimeMillis):

    def init(self, configurationAttributes):
        return True
    ...

Issue: This is template/sample code that:

  • Is incomplete (missing code in __init__)
  • Is for wrong script type (ConsentGatheringType for an update_token script)
  • Causes SyntaxError when Jans tries to load it
  • Should NOT exist for file-based scripts

Step 4: Try to remove script field via PATCH

# Attempt 1: Set to empty string
curl -X PATCH \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json-patch+json" \
  https://yourserver.com/jans-config-api/api/v1/config/scripts/$INUM \
  --data '[{"op": "replace", "path": "/script", "value": ""}]'

# Check result
curl -s -H "Authorization: Bearer $TOKEN" \
  https://yourserver.com/jans-config-api/api/v1/config/scripts/$INUM \
  | jq '{scriptLength: (.script | length)}'

Result: scriptLength remains at 810 bytes (not cleared)

# Attempt 2: Set to null
curl -X PATCH \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json-patch+json" \
  https://yourserver.com/jans-config-api/api/v1/config/scripts/$INUM \
  --data '[{"op": "replace", "path": "/script", "value": null}]'

Result: Same - field not cleared

# Attempt 3: Remove field
curl -X PATCH \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json-patch+json" \
  https://yourserver.com/jans-config-api/api/v1/config/scripts/$INUM \
  --data '[{"op": "remove", "path": "/script"}]'

Result: Field persists in database

Expected Behavior

When creating a file-based script:

  1. Request JSON includes moduleProperties with:

    [
      {"value1": "location_type", "value2": "file"},
      {"value1": "location_path", "value2": "/path/to/script.py"}
    ]
  2. API should create script metadata with:

    • script field = null or empty
    • locationType = "file" (from moduleProperties)
    • locationPath = specified path
  3. Jans should load the Python code from the file path, not from database

Actual Behavior

  1. API automatically populates script field with template code
  2. Template code is incomplete/corrupted
  3. PATCH operations cannot remove the field
  4. Jans tries to load from database (corrupted template) instead of file
  5. Results in SyntaxError: mismatched input 'def' expecting INDENT at line 13

Impact

Critical Impact on File-Based Script Deployment:

  • Cannot deploy file-based custom scripts via Config API
  • Workaround: Must use Admin UI manually (not automatable)
  • Affects CI/CD pipelines and automated deployments
  • Makes it impossible to version-control script deployment

Our Use Case:

  • Need to deploy 4 custom authentication scripts
  • Scripts are 145-572 lines (too large for database field comfortably)
  • Want version control on files, not base64 in database
  • Config API bug blocks automated deployment

Workaround

Current workaround: Use Admin UI

  1. Deploy Python files to /opt/jans/jetty/jans-auth/custom/scripts/
  2. Manually create script metadata via Admin UI
  3. Select Location Type = "File"
  4. Leave Script text area empty
  5. Specify file path

This works but cannot be automated.

Proposed Fix

Option 1: Don't auto-populate script field when location_type=file

// In script creation logic
if (moduleProperties.contains("location_type", "file")) {
    // Don't set default template script
    script.setScript(null);
} else {
    // Only add template for database-stored scripts
    script.setScript(getDefaultTemplate(scriptType));
}

Option 2: Make PATCH remove operation actually work

Allow {"op": "remove", "path": "/script"} to set field to null/empty in database.

Option 3: Add POST parameter to skip template

{
  "name": "my_script",
  "skipTemplate": true,
  "moduleProperties": [...]
}

Additional Context

Tested on:

  • Fresh Jans 1.15.0 installation
  • Multiple script types (update_token, person_authentication)
  • Both POST new script and PATCH existing script operations

Logs showing corruption:

ERROR [io.jans.service.PythonService] - Failed to load python file
SyntaxError: mismatched input 'def' expecting INDENT (meus_update_token.py, line 13)

Script field content inspection:
The auto-generated template is 810 bytes of incomplete Python code with wrong imports for the script type.

Questions

  1. Is the template auto-population intentional for file-based scripts?
  2. If yes, why can't it be removed via PATCH?
  3. Is there a documented way to create file-based scripts via API without script field corruption?
  4. Should file-based scripts (locationType=file) take precedence over database script field?

Request

Please either:

  1. Fix Config API to not auto-populate script field when locationType=file, OR
  2. Document the correct API procedure for file-based script creation, OR
  3. Fix PATCH to allow removing the script field

Thank you!

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions