Skip to main content
Version: 4.40.x.x Java 8 ELS

ScriptState

nevisAuth 4.38.x HTTP client changes

The new HTTP client shipped with nevisAuth 4.38.0.12 will likely require changes in this auth state configuration, specifically in the area of certificate configuration and handling.

Visit the migration guide for additional information.

Introduction and overview

The ScriptState is used to compile and evaluate scripts written in Groovy or JavaScript or any other JSR-223 supported scripting language. With access to the request and response objects and other nevisAuth beans, special user requirements can be scripted and, thus, this state serves as an alternative to a custom AuthState implementation. The following list shows the differences between a custom AuthState and a script in the ScriptState:

  • Custom AuthState
    • Can be configured
    • Can be tested with the nevisAuth test harness
    • Can be debugged
  • ScriptState
    • Rapid prototyping / rapid demonstrations
    • No complex project setup
    • Easy deployment
    • No restart of nevisAuth necessary if script changes.

Scripts can either be embedded into the AuthState or through an external file. If the script is stored in another file, changes to it will be detected without restarting the nevisAuth instance. The scripts are compiled into Java byte code and are recompiled only if an external script file is used and the file contents change.

JavaScript support deprecation

JavaScript support for ScriptState is deprecated and will be removed with the November 2023 rolling release.

Scripts can access context data from the current request. The following beans are bound to the script's context:

  • request

    Type: AuthRequest

    The request object. Can be used to access request data such as the resource, userid, client certificate, language and more.

  • response

    Type: AuthResponse

    The response object. Can be used to set response data such as the content, the auth level, cookies, the userid and more.

  • oocd

    Type: OutOfContextDataService

    The OutOfContextDataService object as described in the nevisAuth SDK.

  • LOG

    Type: Tracer

    The logging service.

  • session

    Type: java.util.Map

    Contains the session data. If the session was not available, the system returns an empty java.util.Map. The changes to the Map will have no effect, that is, the creation of the session will not take place.

  • notes

    Type: java.util.Properties

    The collection of notes.

  • outargs

    Type: java.util.Properties

    The collection of outargs.

  • inargs

    Type: java.util.Properties

    The collection of inargs.

  • parameters

    Type: java.util.Map

    Defines an unmodifiable Map containing the configuration parameters. All configuration properties have the following name: parameter.[parameterName].

Description

The following table and chapters describe the characteristics of the AuthState.

TopicDescription
Classch.nevis.esauth.auth.states.scripting.ScriptState
LoggingScript
Auditingnone
Markernone
Methodsprocess(all events)

Properties

  • scriptLanguage (string, "groovy")

    The language used in the script. Only JavaScript and Groovy are supported. Other JSR233 languages can be used if the appropriate libraries are put on the class path. JavaScript is deprecated and will be removed with the November 2023 rolling release.

  • script (string, -)

    Either the script itself or the path to the script on the file system. If the script is located on the file system, the value of the script property must start with file://.

    caution

    While it is possible to specify a multi-line script as the value of this property, this is strongly discouraged. During parsing of the AuthState XML, any line breaks present in this property value will be collapsed to single spaces, which can distort the syntax and semantics of the script. We recommend referencing a script file on the file system instead.

  • scriptTraceGroup (string, "Script")

    The trace group used for logging from the script.

  • parameter.[parameterName] (string, -)

    Properties prefixed with the string parameter are accessible from within the script via the parameters bean using the parameterName. For example: parameters.get("parameterName").

  • parameter.httpclient.* (String)

    Configure the outgoing HTTP communication. For a list of valid HTTP properties, see HTTP Client.

Input

Depends on the script.

Transitions

Depends on the script.

Output

Depends on the script.

Errors

Depends on the script.

  • lasterror=99

    lasterrorinfo=Script failed to compile

Notes

Depends on the script.

Example

Example inline scripting with JavaScript
<AuthState name="Script" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="true">
<ResultCond name="default" next="Script"/>
<Response value="AUTH_CONTINUE">
<Gui name="AuthDialog" label="login.test.label">
<GuiElem name="info" type="info"
label="${notes:result}"/>
<GuiElem name="input" type="text"
label="Print factorial of: "/>
<GuiElem name="submit" type="button"
label="submit.button.label" value="Continue"/>
</Gui>
</Response>
<property name="scriptLanguage" value="javascript"/>
<property name="script" value="notes.setProperty('result', Math.sqrt(parseInt(inargs.getProperty('input'))));"/>
</AuthState>
"Example
<AuthState name="Script" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="true">
<ResultCond name="default" next="Script"/>
<Response value="AUTH_CONTINUE">
<Gui name="AuthDialog" label="login.test.label">
<GuiElem name="info" type="info"
label="${notes:result}"/>
<GuiElem name="input" type="text"
label="Print factorial of: "/>
<GuiElem name="submit" type="button"
label="submit.button.label" value="Continue"/>
</Gui>
</Response>
<property name="script" value="file:///var/opt/nevisauth/default/conf/factorial.gy"/>
</AuthState>
"Example
<AuthState name="Script" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="true">
<ResultCond name="default" next="Script"/>
<Response value="AUTH_CONTINUE">
<Gui name="AuthDialog" label="login.test.label">
<GuiElem name="info" type="info" label="${notes:result}"/>
<GuiElem name="input" type="text" label="Print factorial of: "/>
<GuiElem name="submit" type="button" label="submit.button.label" value="Continue"/>
</Gui>
</Response>
<property name="scriptLanguage" value="javascript"/>
<property name="script" value="file:///var/opt/nevisauth/default/conf/factorial.js"/>
</AuthState>

HTTP processing

See HTTP Client for more information.

The next examples will support you when replacing the HttpAuthState by the ScriptState. All Groovy scripts use the following ScriptState configuration to retrieve the parameters they require:

ScriptState AuthState configuration
<AuthState name="HttpScriptState" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false">
<ResultCond name="error" next="AuthErrorGui"/>
<ResultCond name="ok" next="AuthDone"/>
<property name="parameter.url" value="https://siven.ch/application"/>
<property name="parameter.httpclient.tls.keyObjectRef" value="DefaultKeyStore"/>
<property name="parameter.httpclient.tls.trustStoreRef" value="DefaultTrustStore"/>
<property name="parameter.json" value="{ &quot;attribute&quot;: &quot;value&quot; }"/>
<property name="script" value="file:///<path>/httpScript.groovy"/>
</AuthState>

The following examples do not require additional libraries. They use the HttpClient interface provided by nevisAuth and execute an HTTP request on the URL specified in the parameters. Configuration of the HttpClient can be done by adding the configurations options as parameters, see the section below for all options.

GET request

The script in the following example creates an HTTP connection and logs the result of the operation. Further configuration can be provided in parameters.


def url = parameters.get('url')

// The HTTP client we create here is not reused, so it is put into a try-with-resources block
// which means the the client is closed and thus it's resources are freed once the block ends.
try {
def httpClient = HttpClients.create()
def httpResponse = Http.get().url(url).build().send(httpClient)
LOG.info('Response Message: ' + httpResponse.reasonPhrase())
LOG.info('Response Status Code: ' + httpResponse.code())
LOG.info('Response: ' + httpResponse.bodyAsString())

if (httpResponse.code() == 200) {
response.setResult('ok')
} else {
LOG.error('Unexcpected HTTP response code: ' + httpResponse.code())
response.setResult('error')
response.setError(1, 'Unexpected HTTP reponse')
}
} catch (all) {
// Handle exception and set the transition
LOG.error('error: ' + all, all)
response.setResult('error')
response.setError(1, 'Exception during HTTP call')
}
Instance lifecycle of HTTP client

The HttpClient in the ScriptState can have 3 different lifecycles. It is recommended to use the per ScriptState instance approach.

Per nevisAuth instance

Using the default Auth "global" HttpClient can be done by not specifying anything in the send method.

    def httpResponse = Http.get().url(url).build().send()

Per ScriptState instance

Creating the HTTP Client using the HttpClients.create() method will create a ScriptState bound HttpClient which will be reused across multiple requests for this ScriptState. The HTTP Client is not shared across different ScriptState instances. This method without any parameters is specific and only available in the ScriptState, because in the ScriptState, you cannot define variables which are preserved across multiple requests. HttpClients.create() enables the usage of connection pooling. The parameters for the HTTP Client are automatically taken from the ScriptState parameters, therefore no parameters are neccesary in the method call.

def httpClient = HttpClients.create()
...

Per request

Using any other HttpClients.create methods which takes a parameter will not cache the HTTP Client, so it will be thrown away after used, so the advantage of connection pooling is lost.

try (def httpClient = HttpClients.create(parameters)) {
...

POST request

The script in the next example executes an HTTP POST request using a json payload from the configuration parameters.

HTTP POST request

def url = parameters.get('url')
def payload = parameters.get('json')

try {
def httpClient = HttpClients.create()
def httpResponse = Http.post()
.url(url)
.header("Accept", "application/json")
.entity(Http.entity()
.content(payload)
.contentType("application/json")
.charSet("utf-8")
.build())
.build()
.send(httpClient)

LOG.info('Response Message: ' + httpResponse.reasonPhrase())
LOG.info('Response Status Code: ' + httpResponse.code())
LOG.info('Response: ' + httpResponse.bodyAsString())

if (httpResponse.code() == 200) {
response.setResult('ok')
} else {
LOG.error('Unexcpected HTTP response code: ' + httpResponse.code())
response.setResult('error')
response.setError(1, 'Unexpected HTTP reponse')
}
} catch (all) {
// Handle exception and set the transition
LOG.error('error: ' + all, all)
response.setResult('error')
response.setError(1, 'Exception during HTTP call')
}

PATCH request with the Auth "global" HTTP Client

The script in the this example executes an HTTP PATCH request with the Auth "global" HTTP Client. This is possible with the method send(), where no HTTP client is provided in the code below.

HTTP PATCH request

def url = parameters.get('url')
def payload = parameters.get('json')

def httpResponse = Http.patch()
.url(url)
.header("Accept", "application/json")
.entity(Http.entity()
.content(payload)
.contentType("application/json")
.charSet("utf-8")
.build())
.build()
.send()

LOG.info('Response Message: ' + httpResponse.reasonPhrase())
LOG.info('Response Status Code: ' + httpResponse.code())
LOG.info('Response: ' + httpResponse.bodyAsString())

HTTP DELETE with Latin-1 character encoded response body with per request HTTP Client

The script in this example executes a delete request using a HTTP Client created on the fly. Note that in this case the HTTP Client is thrown away when the request is processed.

HTTP DELETE request
import java.nio.charset.StandardCharsets

def url = parameters.get('url')

try (def httpClient = HttpClients.create(parameters)) {
def httpResponse = Http.delete()
.url(url)
.header("Accept", "application/json")
.build()
.send(httpClient)

LOG.info('Response Message: ' + httpResponse.reasonPhrase())
LOG.info('Response Status Code: ' + httpResponse.code())
httpResponse.body().ifPresent(bodyArray ->
LOG.info('Response: ' + new String(bodyArray, StandardCharsets.ISO_8859_1))
)

if (httpResponse.code() == 200) {
response.setResult('ok')
} else {
LOG.error('Unexcpected HTTP response code: ' + httpResponse.code())
response.setResult('error')
response.setError(1, 'Unexpected HTTP reponse')
}
}

Building a URI

For building parameters only into a URI, it is recommended to simply use the combination of HttpRequestBuilder # url(String) and HttpRequestBuilder # urlParameter(String, String) methods. For more complex URI building, the URIBuilder class is recommended.

ch.nevis.esauth.util.httpclient.api.uri.URIBuilder Example
import ch.nevis.esauth.util.httpclient.api.uri.URIBuilder
import java.util.Collections

URI uri = URIBuilder.http().localhost()
.port(443)
.path("/robotics")
.parameter("siven", "terminator")
.parameters(Collections.singletonMap("dr", "baley"))
.build()

LOG.info("Built URI: " + uri.toString())

For detailed documentation, see the Api documentation.

nevisIDM REST API access

The nevisIDM REST API can be accessed via the IdmRestClient from ScriptStates.