ScriptState
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. 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.
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.
Topic | Description |
---|---|
Class | ch.nevis.esauth.auth.states.scripting.ScriptState |
Logging | Script |
Auditing | none |
Marker | none |
Methods | process(all events) |
Properties
scriptLanguage
(string, "groovy")The language used in the script. Only Groovy is supported.
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://
.cautionWhile 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.
addAutoImports
(boolean, true)Appends the following imports in the beginning of the script for convenience. Set it to
false
to turn this feature off.nevisAuth API:
HttpClients
- special binding in the ScriptState providing extra features on the top of the regularHttpClients
class. So technically this is the only one which will be not an import.HttpClient
Http
java.time
API:DateTimeFormatter
Instant
LocalDate
LocalDateTime
OffsetDateTime
ZonedDateTime
ZoneOffset
ZoneId
ChronoUnit
Duration
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
<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>
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:
<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="{ "attribute": "value" }"/>
<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')
}
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)) {
...
Parameter resolving by default in parameter.httpclient.*
is not done.
In case you need that, you can you use the following creation method:
try (def httpClient = HttpClients.create(parameters, request, response)) {
...
Note, that this approach is not available for the Per ScriptState instance approach, as there, the first request data would be set for the rest requests which would be dangerous.
POST request
The script in the next example executes an HTTP POST request using a json payload from the configuration parameters.
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 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.
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.
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.
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.