SQL Proxy API (DBConnector)

Requirement

The City have a number of Data services (e.g. SQL Servers) that reside on the city network. There is no mechanism by which data can be accessed from these services from processes that are not resident on the Cityhall network. This includes boston.gov and other cloud based services.

Service Specification

Specification May 2021

API User Guide

The DBConnector micro-service is available at:

Flow

1. Authenticate (get or refresh an authToken)

The first step is to post to the /v1/auth endpoint, providing username and password credentials.

  • If authenticated, an Authentication Token, and Refresh Token will be returned.

  • By default, the Authentication Token (authToken) is valid for 180 seconds, and the Refresh Token for 900 seconds.

  • When your account was setup, a different lifetime may have been specified for authTokens issued using your credentials.

  • The authToken inherits role-based permissions that have been assigned to your credentials.

The authToken is then passed as a "bearer token" in the "Authroization" header - and can be used multiple times until it expires.

Example request header

HEADER "Authorization: Bearer xxxx-xxxxx-xxxxx"

When the Authentication Token expires, the Refresh Token can be passed to the /v1/auth/refresh endpoint and a new Authentication Token will be returned with a 180 second lifetime. A new Refresh Token will also be generated and returned with a 900 second lifetime. You could also just re-authenticate on the /v1/auth endpoint, but using the refresh token is faster and more efficient in the back-end because the user is not re-validated (using the DBConnector database) -saving database connection overhead.

2. Fetch or verify connToken (optional)

If you have not saved or been given a connToken (which is a uuid representing a connection string) then the connToken can be obtained from the /v1/connections/:name endpoint.

Note: You will need to pass the authToken in header.

3. Perform database/remoteAPI operation

Depending on the role attached to the authToken (see User Permissions), you can use the /select, /insert, /update, /delete and /query endpoints to interact with data on the remote host defined by a connToken.

Notes: 1. You will need to pass the authToken in the "Authorization" header of your request. 2. You will need to pass the connToken (and other parameters) in the body/payload of your request

Results from all endpoints will be returned in JSON format.

Terminology

Heartbeat

The ECS requires that an application have an endpoint /admin/ok which returns an HTTP Status Code of 200 when queried. The ECS container management functions use this to determine if the container is healthy or not. After some time, if that endpoint does not respond or return a 200, the task is stopped and restarted.

Heartbeat required by ECS

GET

This endpoint must return a 200 code when the task (instance of the dbconnector service) is running. A non 200 code will cause the task to be stopped.

{}

Authentication

Authenticate User

POST https://dbconnector.digital-staging.boston.gov/v1/auth

This endpoint is used to initially authenticate the user, and returns an Authentication Token which must be used in the header of all subsequent endpoint calls. - The Auth Token has a default lifetime of 180 seconds (3 min) (can be set per username), - The Refresh Token has an additional validity of 180 seconds (3 min).

Request Body

{
  "userid": 1,
  "authToken": "xxx-xxxx-xxxx",
  "refreshToken": "xxx-xxxx"
}

** If the user is found to be over-using the endpoint, (flooding) then 
   the following will be returned:
{"error": "No Data"}

Refresh Authentication Token

POST https://dbconnector.digital-staging.boston.gov/v1/auth/refresh

Using a valid Refresh Token (provided from /v1/auth endpoint), this endpoint will refresh a current (or expired) Authentication Token. Also generates a new Refresh Token.

Headers

Request Body

*** Normal response
{
    "userid": 6,
    "authToken": "XXXXXX",
    "refreshToken": "XXXXX"
}

Common Endpoint Error Messages

The following errors can be raised from calls to the various endpoints.

400: Bad Request

400 errors usually indicate some issue with the body or querystring submitted to the endpoint.

A JSON string is returned with an error node in it. The error description is a short explanation of the issue encountered.

{
    "error": <description>
}

401: Unauthorised

Generally, 401 errors are returned when there is an issue with the AuthToken provided in he header.

*** If no authToken was supplied
{"error": "Missing Authentication Token"}

*** If the authToken supplied has expired.
{"error": "Expired Token"}

*** If the authToken is unknown or badly formatted
{"error": "Bad Token"}

403: Forbidden

When a user attempts to perform a task with insufficient permissions, a 403 error is generated. The 403 Errors raised by DBConnector typically do not provide much information, but errors are logged. Typical 403 errors are:

  • Authenticating account or using token from an unregistered IPAddress

  • Account has insufficient permissions to perform requested task (see Permission description for each endpoint in this guide and also User Permissions section)

{}

200:OK - Flooding

If too many requests have been made by a user in a given time period, then the user will be blocked and a 200 response code will be returned with an error message.

*** Response if abuse detected
{
    "error": "No Data"
}

User Management

List all Users (paged)

GET https://dbconnector.digital-staging.boston.gov/v1/users

Returns a list of current users. Permission: ADMIN or OWNER. If the optional page & list parameters are provided, then a paged output will be returned (sorted by UserID)

Query Parameters

Headers

[
    {
        "ID": 1,
        "Username": "david.upton@boston.gov",
        "Password": "*****",
        "IPAddresses": "",
        "Enabled": true,
        "Role": 4096,
        "TTL": ""
    },
    {...},
    {...}
]

List a single User

GET https://dbconnector.digital-staging.boston.gov/v1/users/:useridentifier

Returns a single user. Permission: ADMIN, OWNER or the user specified in useridentifier. i.e. You can only read your own user record unless you are an ADMIN or OWNER.

Path Parameters

Headers

[
    {
        "ID": 1,
        "Username": "david.upton@boston.gov",
        "Password": "the-password",
        "IPAddresses": "",
        "Enabled": true,
        "Role": 4096,
        "Session": null,
        "TTL": ""
    }
]

List Connections available to a User

GET https://dbconnector.digital-staging.boston.gov/v1/users/:useridentifier/connections

Provides a list of connection strings that the user has been granted permission to use. Permission: ADMIN, OWNER or user defined by useridentifier. i.e. You can only read your own user record unless you are an ADMIN or OWNER.

Path Parameters

Headers

[
 {
   "Username":"david.upton@boston.gov",
   "userid":1,
   "connid":1,
   "Token":"xx-xx-xx-xx-xx",
   "ConnectionString":"{\"host\":\"MSSQL_CMDB\", \"port\":\"1433\", \"schema\":\"dbo\", \"database\":\"CMDB\", \"user\":\"myUser\", \"password\":\"myPassword\"}",
   "Description":"Updated Dummy",
   "Enabled":true,
   "Count":0,
   "LastUse":
   "2021-08-10T19:30:20.160Z"
  }
]

Add a new User

POST https://dbconnector.digital-staging.boston.gov/v1/users

Adds a new user. Permission: ADMIN or OWNER.

Headers

Request Body

{
    "id": 16
}

Update an existing User

PATCH https://dbconnector.digital-staging.boston.gov/v1/users/:useridentifier

Updates the specified user with information provided in the payload. Permission: ADMIN or OWNER

Path Parameters

Headers

{}

Delete an existing User

DELETE https://dbconnector.digital-staging.boston.gov/v1/users/:userid

Deletes the specified user. Permission: ADMIN or OWNER

Path Parameters

Headers

{}

Connection Strings

A connection string record contains all the information required for a suitable driver to connect to a remote system.

Each connection string record is defined by a unique UUID (the connToken) -and also a unique name.

The connToken is used to refer to the remote system in execution endpoints so that connectivity details do not need to be stored in and passed from the calling system.

The connToken (UUID) should never change once the connections string record is created, and therefore can be safely stored in the calling system.

List all Connections (paged)

GET https://dbconnector.digital-staging.boston.gov/v1/connections

Returns a list of all active remote system connection strings. Permission: ADMIN OR OWNER

Headers

List single Connection

GET https://dbconnector.digital-staging.boston.gov/v1/connections/:token

Returns the remote system connection string defined by the specified Connection Token. Permission: ADMIN, OWNER or by a user who has permission to use the connection.

Path Parameters

Headers

Find connToken

GET https://dbconnector.digital-staging.boston.gov/v1/connections/find/:name

Fetch a connection token using the tokens name. Permission: All authenticated users.

Path Parameters

Headers

List Users who may use a Connection

GET https://dbconnector.digital-staging.boston.gov/v1/connection/:token/users

Provides a list of users who have been granted permission to use a connection string. Permission: ADMIN or OWNER

Path Parameters

Headers

Add a new Connection

POST https://dbconnector.digital-staging.boston.gov/v1/connection

Saves a connection string. Permission: ADMIN or OWNER.

Headers

Request Body

{
    "connToken": "12342343412341"
}

Grant User Permission to Connection

POST https://dbconnector.digital-staging.boston.gov/v1/connection/:token/user/:userid

Grants a user permission to use a connection string. Permission: FULL/SUPER, ADMIN or OWNER.

Path Parameters

Headers

{}

Update an existing Connection

PATCH https://dbconnector.digital-staging.boston.gov/v1/connections/:token

Updates the remote system connection string defined by the specified Connection Token. Permission: ADMIN or OWNER..

Path Parameters

Headers

Request Body

{}

Delete an existing Connection

DELETE https://dbconnector.digital-staging.boston.gov/v1/connections/:token

Deletes the remote system connection string defined by the specified Connection Token. Permission: ADMIN or OWNER

Path Parameters

Headers

{}

Revoke User Permission for Connection

DELETE https://dbconnector.digital-staging.boston.gov/v1/connection/:token/user/:userid

Revoke an existing permission for a user to use a connection string. Permission: ADMIN or OWNER

Path Parameters

Headers

{}

Execution Endpoints

Running data commands on a Remote System.

General SQL Errors

Errors which occur whilst executing an data command on the host server are passed back as cleanly as possible. Except for the /query endpoint, syntax errors should not occur.

Errors which might occur include:

  • connection string errors (credentials, host DNS/IPAddress etc),

  • incorrectly named tables and/or incorrectly named fields,

  • insufficient permissions to perform task on host,

  • creating duplicate records,

  • table locks,

  • foreign key constraints,

  • ... etc ...

Actual error messages depend upon the drivers being used, and the wording of error reporting from the host. Generally a 400 error will be generated with a "cleaned" error message in this general JSON format:

{
    "error": "cleaned error message from host system"
}

Execute SQL Statement

POST https://dbconnector.digital-staging.boston.gov/v1/query/:driver

Runs a command (or commands) on the remote system. Depending on the command/s, returns a JSON Array (of Arrays) of Objects.

Path Parameters

Headers

Request Body

*** If the statement provided contains a single sql statement.
[
    {
        "ID": 1,
        "Name": "david",
        ...
    },
    {    
        "ID": 2,
        "Name": "Jim",
        ...
    },
    ...
]

*** If the statement provided contains multiple sql statements.
[
    [],
    [
        {
            "ID": 1,
            "Name": "david",
            ...
        },
        {    
            "ID": 2,
            "Name": "Jim",
            ...
        },
        ...
    ],
    [
        {
            "MyVar": "Good Job!"
        }
    ],
    ...
]

Note on statement and args (parameters)

To allow statements to be dynamic/re-used, the statement field may contain "named tokens" which will be substituted into the statement prior to execution. This creates a kind of stored procedure.

For Example:

statement: 'SELECT {a} FROM {c} ORDER BY {b};',
args: '{"a": "ID", "b": "CreateDate", "c": "dbo.users"}'

Expands out to:

SELECT ID FROM dbo.users ORDER BY CreateDate;

Calling Views

You can call views using the /query endpoint (and also the /select endpoint). Treat a view like a table.

Execute Stored Procedure

POST https://dbconnector.digital-staging.boston.gov/v1/exec/:driver

Execute a stored procedure on the remote system. Permission: ALTER, FULL, ADMIN or OWNER

Path Parameters

Headers

Request Body

*** If the stored procedure contains a single sql statement.
[
    {
        "ID": 1,
        "Name": "david",
        ...
    },
    {    
        "ID": 2,
        "Name": "Jim",
        ...
    },
    ...
]
*** If the stored procedure contains multiple statements
[
    [],
    [
        {
            "ID": 1,
            "Name": "david",
            ...
        },
        {    
            "ID": 2,
            "Name": "Jim",
            ...
        },
        ...
    ],
    [
        {
            "MyVar": "Good Job!"
        }
    ],
    ...
]

*** If the stored procedure executed contains a single sql statement 
    and output variables.
{
    result: [
        {
            "ID": 1,
            "Name": "david",
            ...
        },
        {    
            "ID": 2,
            "Name": "Jim",
            ...
        },
        ...
    ],
    output: {
        param1: "value",
        param2: "value",
        ...
    }
}
*** If the stored procedure executed contains multiple sql statements 
    and output variables.
{
    result: [
        [],
        [
            {
                "ID": 1,
                "Name": "david",
                ...
            },
            {    
                "ID": 2,
                "Name": "Jim",
                ...
            },
            ...
        ],
        [
            {
                "MyVar": "Good Job!"
            }
        ],
        ...
    ],
    output: {
        param1: "value",
        param2: "value",
        ...
    }
}

Run Select Query

POST https://dbconnector.digital-staging.boston.gov/v1/select/:driver

This runs a command on the remote system which is expected to return a paged data set in a JSON Array of Objects format.

Path Parameters

Headers

Request Body

[
    {
        "ID": 1,
        "Name": "david",
        ...
    },
    {    
        "ID": 2,
        "Name": "Jim",
        ...
    },
    ...
]      

Run Insert Query

POST https://dbconnector.digital-staging.boston.gov/v1/insert/:driver

This endpoint creates a new record (or records) in the specified table.

Path Parameters

Headers

Request Body

If a single new record is defined in the values array:
{
    "Identity": N
}
If multiple records defined in the values array:
{
    "result": "success"
}

Run Update Query

POST https://dbconnector.digital-staging.boston.gov/v1/update/:driver

This endpoint will update existing records in a table.

Path Parameters

Headers

Request Body

{
    "Updated": N
}

Run Delete Query

POST https://dbconnector.digital-staging.boston.gov/vi/delete/:driver

Path Parameters

Headers

Request Body

{
    "Deleted": N
}

User Permissions

Each user account defined in DBConnector has an assigned role.

Generally, the roles are hierarchical, so for example: a FULL-QUERY-USER can perform all the tasks a READ-USER can perform.

Where Arrays

In the filter fields for the /select, /update and /delete endpoints, the following directives can be used:

Why use an array of objects and not an object? Because some filters might define the same field twice, which would not make sense in an object. e.g. Suppose we wanted to run this: SELECT * FROM MyTable WHERE name ='a' OR name = 'b' or name ='c'; To accommodate the filter as an object we would have { "name": "a", "^name": "b", "^name": "c"} which is not a valid structure. So we use an array of objects thus: [ {"name": "a"}, {"^name": "b"}, {"^name": "c"} ]which is a valid structure.

Test Data

The endpoint can be tested using some test data pre-loaded into the application.

The credentials to use are:

username: devuser
password: Boston2021

These credentials can be used with the connToken

45826BE6-1E29-CC64-B49E-550B9610C2EA

Data contained in testTable available from connStr #4

  {
    "ID": 1,
    "Name": "David Upton",
    "Address": "1 Nevermind St, Nowhere",
    "Sex": "Male",
    "Age": 21
  },
  {
    "ID": 2,
    "Name": "Matt McGowan",
    "Address": "14 Glochester Park",
    "Sex": "Male",
    "Age": 21
  },
  {
    "ID": 3,
    "Name": "Phiilip Kelly",
    "Address": "2 Redline, Boston",
    "Sex": "Male",
    "Age": 21
  },
  {
    "ID": 4,
    "Name": "Stella Ubana",
    "Address": "3 Kids is too many, Boston",
    "Sex": "Female",
    "Age": 21
  },
  {
    "ID": 5,
    "Name": "Caroline Stjarnborg",
    "Address": "1 City Hall Plaza, Boston",
    "Sex": "Female",
    "Age": 21
  },
  {
    "ID": 6,
    "Name": "James Duffy",
    "Address": "1 Boston",
    "Sex": "Male",
    "Age": 21
  },
  {
    "ID": 7,
    "Name": "Sebastian Ebard",
    "Address": "2 Nice Street, Rosendale",
    "Sex": "Male",
    "Age": 21
  },
  {
    "ID": 8,
    "Name": "Jeanethe Falvey",
    "Address": "3 Nice Street, Rosendale",
    "Sex": "Female",
    "Age": 21
  }
]

Last updated