Webhooks plugins tutorial

In this tutorial we are going to create a custom authentication method for HTTP endpoint for webhooks.

The tutorial plugin will be called xlr-mywebhooks-plugin.

The authentication method will be able to authenticate HTTP requests based on an URL parameter’s value. The user must configure the parameter name and a secret token.

Plugin structure

  1. First create a directory for the plugin:

mkdir xlr-mywebhooks-plugin

  1. Create the structure:
touch synthetic.xml
mkdir mywebhooks/
touch mywebhooks/UrlParameterTokenAuthentication.py

The synthetic.xml file will contain the custom types definitions while the mywebhooks directory will contain the python scripts.

  1. Enter the following text into the synthetic.xml file:
<synthetic xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns="http://www.xebialabs.com/deployit/synthetic"
           xsi:schemaLocation="http://www.xebialabs.com/deployit/synthetic synthetic.xsd">

</synthetic>

That is what an empty synthetic.xml file looks like, and you will add type definitions inside the <synthetic> node.

Custom authentication method: URL Parameter Token

  1. Open the synthetic.xml file add add the type definition of our custom authentication method:
<synthetic xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns="http://www.xebialabs.com/deployit/synthetic"
           xsi:schemaLocation="http://www.xebialabs.com/deployit/synthetic synthetic.xsd">

    <type type="mywebhooks.UrlParameterTokenAuthentication"
        extends="events.CustomJythonAuthentication"
        label="URL Parameter Token"
        description="Verify that the value of a given URL parameter matches the secret token.">
        <property name="parameter" kind="string" required="true"
            label="URL parameter"
            description="URL parameter name containing the token."/>
        <property name="token" kind="string" password="true" required="true"
            label="Token"
            description="Secret token value."/>
    </type>

</synthetic>
  1. Open the mywebhooks/UrlParameterTokenAuthentication.py file and write this script:
# required to decrypt password properties. Can only be used by packaged scripts.
from com.xebialabs.deployit.util import PasswordEncrypter

# the endpoint instance.
global endpoint
# the `mywebhooks.UrlParameterTokenAuthentication` instance. Holds the `parameter` and `token` properties
global config
# the HTTP headers. Python dictionary
global headers
# the URL parameters. Python dictionary. Each value is an array of strings.
global parameters
# the HTTP request body. Only available when endpoint.method == "POST"
global payload

# let's get the PasswordEncrypter instance
pe = PasswordEncrypter.getInstance()

# find the URL parameter named `config.parameter` and return its value
def getToken():
    return parameters[config.parameter] if config.parameter in parameters else ""

# True if one of the value of the `config.parameter` URL parameter is the decrypted `config.token`.
authenticated = pe.ensureDecrypted(config.token) == getToken()

Test the new custom authentication method

Setup a test endpoint

  1. Add the type definition from synthetic.xml to XL_RELEASE_HOME/ext/synthetic.xml
  2. Copy your mywebhooks directory to your XL_RELEASE_HOME/ext/ directory
  3. Restart Release
  4. Under Settings > Shared configuration > Webhooks and Event, create a new HTTP GET endpoint for Webhooks
  5. Name it, for example My Test Endpoint (GET)
  6. Set the path, for example test_endpoint_get
  7. Under authentication, a new option named ‘URL Parameter Token” should be available
  8. Select ‘URL Parameter Token’
  9. Configure the parameter field: type token
  10. Configure the token field: type tok3n
  11. Save the new HTTP endpoint for webhooks

Verify the test endpoint with the custom authentication method

  1. Check that it rejects requests with the bad token value:
curl -v 'http://your-xl-release-url/webhooks/test_endpoint_get?token=wr0ng'
> *   Trying ::1:5516...
> * TCP_NODELAY set
> * Connected to localhost (::1) port 5516 (#0)
> > GET /webhooks/test_endpoint_get?token=wr0ng HTTP/1.1
> > Host: localhost:5516
> > User-Agent: curl/7.66.0
> > Accept: */*
> >
> * Mark bundle as not supporting multiuse
> < HTTP/1.1 401 Unauthorized
> < Date: Mon, 24 Feb 2020 15:59:00 GMT
> < X-XSS-Protection: 1; mode=block
> < X-Content-Type-Options: nosniff
> < Content-Type: application/json
> < Content-Length: 238
> <
> * Connection #0 to host localhost left intact
> Unauthorized request for 'Configuration/Custom/Configurationc2f85e3a3d424d45a81f4b87fe32bc5e (My Test Endpoint (GET))' (authentication method: com.xebialabs.xlrelease.webhooks.authentication.CustomJythonAuthenticationMethod)%  
  1. Check that it rejects requests with the bad parameter name:
curl -v -X GET 'http://your-xl-release-url/webhooks/test_endpoint_get?wrong=tok3n'
> *   Trying ::1:5516...
> * TCP_NODELAY set
> * Connected to localhost (::1) port 5516 (#0)
> > GET /webhooks/test_endpoint_get?wrong=tok3n HTTP/1.1
> > Host: localhost:5516
> > User-Agent: curl/7.66.0
> > Accept: */*
> >
> * Mark bundle as not supporting multiuse
> < HTTP/1.1 401 Unauthorized
> < Date: Mon, 24 Feb 2020 15:57:49 GMT
> < X-XSS-Protection: 1; mode=block
> < X-Content-Type-Options: nosniff
> < Content-Type: application/json
> < Content-Length: 238
> <
> * Connection #0 to host localhost left intact
> Unauthorized request for 'Configuration/Custom/Configurationc2f85e3a3d424d45a81f4b87fe32bc5e (My Test Endpoint (GET))' (authentication method: com.xebialabs.xlrelease.webhooks.authentication.CustomJythonAuthenticationMethod)%
  1. Check that it accepts requests with the correct parameter name and value:
curl -v -X GET 'http://your-xl-release-url/webhooks/test_endpoint_get?token=tok3n'
> *   Trying ::1:5516...
> * TCP_NODELAY set
> * Connected to localhost (::1) port 5516 (#0)
> > GET /webhooks/test_endpoint_get?token=tok3n HTTP/1.1
> > Host: localhost:5516
> > User-Agent: curl/7.66.0
> > Accept: */*
> >
> * Mark bundle as not supporting multiuse
> < HTTP/1.1 200 OK
> < Date: Mon, 24 Feb 2020 15:56:54 GMT
> < X-XSS-Protection: 1; mode=block
> < X-Content-Type-Options: nosniff
> < Vary: Accept-Encoding, User-Agent
> < Content-Length: 0
> <
> * Connection #0 to host localhost left intact

Package your plugin

  1. Go to your plugin’s working directory (xlr-mywebhooks-plugin)
  2. Go up one level: cd ..
  3. Create a build directory: rm -rf xlr-mywebhooks-plugin-build && mkdir xlr-mywebhooks-plugin-build
  4. Enter the build directory: cd xlr-mywebhooks-plugin-build
  5. Add the MANIFEST.MF file: mkdir -p META-INF && echo 'Manifest-Version: 1.0' > META-INF/MANIFEST.MF
  6. Copy the synthetic.xml file: cp ../xlr-mywebhooks-plugin/synthetic.xml .
  7. Copy the scripts directory: cp -r ../xlr-mywebhooks-plugin/mywebhooks .
  8. Create the plugin: zip -r ../xlr-mywebhooks-plugin-0.1.jar .
  9. Exit the build directory: cd ..
  10. Your plugin is ready in your current directory, named xlr-mywebhooks-plugin-0.1.jar
  11. You can upload to your Release instance using the Plugin Manager screen (required being admin and a non-clustered installation).
  12. Alternatively, place the xlr-mywebhooks-plugin-0.1.jar file in the XL_RELEASE_HOME/plugin/__local__ directory
  13. Remember to remove your type definitions from XL_RELEASE_HOME/ext/synthetic.xml and remove the XL_RELEASE_HOME/ext/mywebhooks directory from your installation used during development and testing once you install the plugin.
  14. Restart Release