5. Ticket API

The dashboard server provides a REST API for working with tickets.

Note

One API design decision was how open to make the API - that is, would the API automatically restrict data sent back for certain requests. For example, we have the concept of public and private tickets. When a GET request is made, do we want the API to return all tickets or a subset such as all public and all private belonging to the current user?

The API is currently restrictive - a GET request to /api/tickets would return all public activity tickets and all private tickets owned by the calling user.

The API is intended to require authentication. The plan is that the client would have obtained the token for the calling user and included it with the API call. The server will then look up the attributes for the user referenced by the token. These would be:

  • username - user name (in Ops-trust) of the calling user
  • trustgroup - trust group the user is logged into
  • admin - is the user an admin in the trust group

This has not been implemented yet as we need to determine how to integrate this with Trident and their login tokens, as well as have a Trident instance running.

In the interim, the server currently authenticates users with the Dashboard via their Ops-trust (Trident) usernames and passwords and establishes a persistent login session for the user. This can be used to protect the API endpoints when accessing via the Dashboard client, but would prevent other clients from accessing. So the API is not protected by the current authentication mechanism in order to allow other clients access until we get the token authentication implemented.

Until token authentication is implemented however, requests from clients other than the Dashboard will not be able to retrieve user private tickets.

5.1. HTTP Verbs

GET     /api/ticket      list
POST    /api/ticket      create
GET     /api/ticket/:id  show
PUT     /api/ticket/:id  update
DELETE  /api/ticket/:id  delete

5.2. Responses

The API returns JSON. JSON responses follow the unofficial JSEND spec. See http://labs.omniti.com/labs/jsend/wiki for more information.

Successful requests will return JSON with a status of success and a data property with the JSON result.

{
   "data": <json>
   "status": "success"
}

The data property will generally be in the following form for one ticket:

"data": {
  "ticket": {
    <json describing ticket>
  }
}

or for multiple tickets:

"data": {
  "tickets": [ <array of json where each one describes a ticket>]
}

Since mitigations are a special form of ticket, we use the terms mitigation and mitigations as keys to their response:

"data": {
  "mitigation": {
    <json describing mitigation ticket>
  }
}

Requests that do not send back data (such as delete) will return with data set to null:

{
  "status": "success",
  "data": null
}

Unsuccessful requests will return JSON with an error message and a status of error:

{
    "message": "You do not have permission to access this ticket",
    "status": "error"
}

Requests that failed due to invalid data or parameters submitted may generate a fail response:

{
  "status": "fail",
  "data": <wrapper for reason request failed>
}

For example:

{
  "status": "fail",
  "data": {
    "name": "A name for the new ticket is required"
  }
}

Note

Currently most errors are reported as error rather than fail. We are working on refactoring so that errors that should be reported as fail are done so.

An HTTP status code is included in the response headers. For example, the following request returns with 400:

$ curl -k -I http://192.168.56.103/api/ticket
HTTP/1.1 400 Bad Request
Server: nginx/1.8.0
Date: Wed, 13 Jan 2016 16:44:48 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 96
Connection: keep-alive
X-Powered-By: Express
ETag: W/"60-kIP4LSNmFtWUQFvEw0Zo/g"
set-cookie: connect.sid=s%3Ah6h88KJrXfxT4Ycabe1dk5aTFY26SRx8.o7d2T9y03YrbbR8ssnmF0tFEpfV9VNI6F7l9oJQEgAg; Path=/; HttpOnly

5.3. Retrieve a list of tickets

Returns list of tickets

5.3.1. No parameters

When no parameters are provided, the system defaults to the following parameters:

type: 'activity',

This will return all public activities plus any private activities belonging to the calling user. Invoked via GET http://dashboard_url/api/ticket/, returns HTTP status code and string reply.

Using curl:

curl -k https://dashboard_url/api/ticket/

Sample response:

{ "data": [
   "ticket:1",
   "ticket:2",
   "ticket:3" ]
}

5.3.2. With query parameters

private: boolean
type: string
ownedBy: string
open: boolean

type can be mitigation or activity. For mitigation tickets, no other parameters are needed, and any extra provided are ignored.

$ curl -k http://192.168.56.103/api/ticket?type=mitigation | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1177  100  1177    0     0  34609      0 --:--:-- --:--:-- --:--:-- 35666
{
    "data": [
        {
            "data": [
                [
                    1450004398760,
                    0
                ],
                [
                    1450068277926,
                    6
                ],
                [
                    1450085780836,
                    13
                ],
                [
                    1452404888722,
                    329
                ],
                [
                    1452478109289,
                    343
                ]
            ],
            "ips": {
                "data": [],
                "user": null
            },
            "key": "dims:ticket:mitigation:1",
            "metadata": {
                "createdTime": 1452682798841,
                "creator": "lparsons",
                "description": "IPs needing mitigation. As you mitigate IPs, submit them here.",
                "initialNum": 1408,
                "mitigatedNum": 343,
                "modifiedTime": 1452682798841,
                "name": "Action Needed: 12/12/2015 Compromised IPs",
                "num": 1,
                "open": true,
                "private": false,
                "type": "mitigation",
                "unknownNum": 864
            }
        }
    ],
    "status": "success"
}

5.4. Creating an activity

/**
 Returns a ticket: ticket key, ticket metadata, list of associated topic keys
 @return HTTP Status code and string reply.

 @example
 Example response:
        {"data": {
          "ticket":
            {"num":"1",
            "creator":"testuser",
            "type":"data",
            "createdTime":"1418060768120",
            "open":"true"},
          "key":"ticket:1",
          "topics":["ticket:1:data:cif:results:result1.txt",
                    "ticket:1:data:cif:results:result2.txt"]
          }
        }
 @example How to invoke
   GET https://dashboard_url/api/ticket/ticket:1

   Using curl:
     curl -k https://dashboard_url/api/ticket/ticket:1

 @param {string} id Ticket key in format ticket:<num>
/

/**
 Creates a new ticket
 @method create
 @return HTTP Status code and string reply.
        {"data": {
          "ticket":
            {"num":"2",
            "creator":"testuser",
            "type":"data",
            "createdTime":"1418060768120",
            "open":"true"},
          "key":"ticket:2"
          }
        }
 @example

   POST https://dashboard_url/api/ticket/
     body:
     {
       "type": "data",
       "creator": "testuser"
     }

    Using curl:
      curl --data "type=data&creator=testuser" -k https://dashboard_url/api/ticket

 @param {string} type Type of ticket being created
 @param {string} creator Username of user creating ticket (optional if user logged in,
                ignored if user logged in)
/

/**
 Adds a topic (metadata) to a ticket and saves the data (content)
 @method addTopic
 @return HTTP Status code and string reply.
 @example
 Sample json response:

   {"data":{
      "topic":{
        "parent":{
           "num":"12","creator":"testUser","type":"analysis","createdTime":"1418131797522","open":"true"
         },
         "type":"analysis",
         "name":"namesearch:result2",
         "dataType":"hash"
         },
        "content":{"firstname":"bob","lastname":"johnson"},
        "key":"ticket:12:analysis:namesearch:result2"
       }
    }

 @example
 Example URI
     POST https://dashboard_url/api/ticket/ticket:27/topic
     body:
     {
       "name": "cif:results:1418060768120",
       "dataType": "string",
       "content": <string content>
     }
 Note that content in this example could be JSON that is stringified. Content could also be content of a
 file, base64'd, as in
     POST https://dashboard_URL/api/ticket/ticket:28/topic
     body:
     {
       "name": "mal4s:result:resul1.png",
       "dataType": "string",
       "content": <base64 content of a .png file>
     }

 Using curl with hash content (content is uri encoded):
      curl --data "name=namesearch:results&dataType=hash&content=%7B%22firstname%22:%22bob%22,%22lastname%22:%22johnson%22%7D" -k https://dashboard_url/api/ticket/ticket:12/topic

 A successful response from the curl command might look like the following (line feeds added for clarity - reponse is just a string):
      {"data":{
       "topic":{
         "parent":{"num":"12","creator":"testUser","type":"analysis","createdTime":"1418131797522","open":"true"},
         "type":"analysis","name":"namesearch:result2","dataType":"hash"},
         "content":{"firstname":"bob","lastname":"johnson"},"key":"ticket:12:analysis:namesearch:result2"}}

 @param {string} id Ticket key in format ticket:<num>
 @param {string} name Name of the topic - this represents the last part of the topic key after
                      ticket:<num>:<ticket_type>:
 @param {string} dataType Redis data structure to store the contents in - can be string or hash
 @param {string} content  Content to be stored

 Note that content is optional if type is string. If no content is specified, then an empty string
 is stored at the topic key. You would use this if you want to use the contents of a file as the
 data to be stored. First create the topic with a type of string and no content. Then you use the
 returned topic key and do an update (PUT) of the topic with the uploaded file.

 You cannot overwrite an existing topic with the same key. An error is returned if the topic already
 exists
/

/**
 Retrieves a ticket topic's metadata and content. Invoked via GET

 <pre>Sample response:

    { "data":{
      "topic":{
         "parent":{
           "num":"12","creator":"testUser","type":"analysis","createdTime":"1418131797522","open":"true"
          },
         "type":"analysis",
         "name":"namesearch:result2",
         "dataType":"hash"
        },
        "content":{"firstname":"bob","lastname":"johnson"},
        "key":"ticket:12:analysis:namesearch:result2"
       }
     }</pre>

 @method showTopic

 @example

     GET https://dashboard_url/api/ticket/topic/ticket:27:analysis:namesearch:result2

    Using curl:

      curl -k https://dashboard_url/api/ticket/topic/ticket:27:analysis:namesearch:result2

 @param {string} id Ticket topic key in format ticket:<num>:<type>:<topic_name>
 @return HTTP Status code and string reply.
/

/**
 Updates a ticket topic. You can only update content.
 @method updateTopic
 @return HTTP Status code and string reply.
   {"data":{
      "topic":{
        "parent":{
           "num":"12","creator":"testUser","type":"analysis","createdTime":"1418131797522","open":"true"
         },
         "type":"analysis",
         "name":"namesearch:result2",
         "dataType":"hash"
         },
        "content":{"firstname":"john","lastname":"johnson"},
        "key":"ticket:12:analysis:namesearch:result2"
       }
    }
 @example

     PUT https://dashboard_url/api/ticket/ticket:27/topic
     body:
     {
       "content": <string content>
     }
   Note that content in this example could be JSON that is stringified. Content could also be content of a
   file, base64'd, as in
     PUT https://dashboard_URL/api/ticket/ticket:28/topic
     body:
     {
       "content": <base64 content of a .png file>
     }

    Using curl with hash content (content is uri encoded):
      curl --data "content=%7B%22firstname%22:%22john%22,%22lastname%22:%22johnson%22%7D" -k https://dashboard_url/api/ticket/ticket:12/topic

    A successful response from the curl command might look like the following (line feeds added for clarity - reponse is just a string):
      {"data":{
       "topic":{
         "parent":{"num":"12","creator":"testUser","type":"analysis","createdTime":"1418131797522","open":"true"},
         "type":"analysis","name":"namesearch:result2","dataType":"hash"},
         "content":{"firstname":"john","lastname":"johnson"},"key":"ticket:12:analysis:namesearch:result2"}}

 @param {string} id Topic key in format ticket:<num>:<type>:<topic_name>
 @param {string} content  Content to be stored

/