├── .gitignore ├── README.md ├── SECURITY.md ├── api ├── README.md ├── account_management.md ├── api_server_test_harness.md ├── client_management.md ├── code │ ├── JavaScript │ │ └── api-js.html │ ├── json │ │ ├── README.md │ │ └── network_settings_example.json │ ├── php │ │ ├── README.md │ │ └── simple_api_server_test_harness.php │ └── python27 │ │ └── cloudtrax │ │ └── __init__.py ├── error_codes.md ├── history.md ├── network_management.md ├── network_settings.md ├── networkgroup_management.md ├── node_management.md ├── powerstrip_management.md ├── site_survey.md ├── switch_management.md ├── time.md ├── user_management.md └── vouchers.md └── captive_portal ├── README.md ├── authentication ├── README.md ├── built_in │ └── README.md ├── http │ ├── README.md │ ├── code │ │ └── php │ │ │ ├── README.md │ │ │ ├── decode_password.php │ │ │ └── example_server.php │ ├── images │ │ ├── config-panel.png │ │ └── edit-splash-page.png │ └── password_decoding.md └── radius │ ├── README.md │ └── images │ └── radius_configuration.png ├── images └── 2014-05-20_architecture.png └── splash_pages ├── README.md ├── custom ├── README.md └── images │ └── edit-splash-page.png └── external ├── README.md ├── click_to_enter ├── README.md ├── code │ └── php │ │ ├── server.php │ │ └── uam.php └── images │ └── 2015-08-26_Click-To-Enter_nodb.png ├── code └── php │ ├── README.md │ ├── splash.php │ ├── uam_handle_form.php │ └── uam_simple_server.php └── images ├── 2014-06-25_Acct.png ├── 2014-06-26_Login.png ├── External_splash-page_configuration.png ├── cloudtrax_dashboard_auth_server_stub_success.png └── cloudtrax_dashboard_uam_config.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CloudTrax Technical Documentation 2 | 3 | *This is preliminary documentation. It applies only to networks running under CloudTrax 4.* 4 | 5 | This is technical documentation for CloudTrax-related services. These documents fall into two general areas: 6 | 7 | ### CloudTrax API ### 8 | Full technical detail of a [RESTful API](./api) that provides low-level access to your CloudTrax network and data. You can configure and fully manage your network and nodes and can build a full replacement of the CloudTrax Dashboard if desired. 9 | 10 | ### Captive Portal ### 11 | 12 | CloudTrax' [Captive Portal](./captive_portal) facilities allow you to host and access splash pages both internally within CloudTrax and externally using UAM. These pages can talk to a variety of authentication services, ranging from a built-in vouchering system, to RADIUS servers, to a backend Authentication Server of your own devising. 13 | 14 | ### Support/Issues ### 15 | 16 | Please contact support@cloudtrax.com or submit a request from https://help.cloudtrax.com in case there are issues or questions. -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | See [Vulnerability Disclosure Policy](https://www.kaseya.com/trust-center/vulnerability-disclosure-policy/). 6 | 7 | Please report security issues to disclosures@kaseya.com 8 | -------------------------------------------------------------------------------- /api/README.md: -------------------------------------------------------------------------------- 1 | # CloudTrax API 2 | 3 | *This is preliminary documentation. It applies only to networks running under CloudTrax 4.*
4 | 5 | *Note that there are no access charges associated with use of the CloudTrax API for users with fewer than 100 devices. For pricing on more than 100 devices, contact sales@openmesh.com. *
6 | 7 | ### Contents of this document 8 | 9 | * [Overview](#overview) 10 | * [Introduction](#intro) 11 | * [Required headers](#headers) 12 | * [Errors](#errors) 13 | * [Error elements](#error-elements) 14 | * [1009 "Success" elements](#1009-success) 15 | * [Authentication](#authentication) 16 | * [Keys](#keys) 17 | * [Retrieving API Keys](#getting_keys) 18 | * [Generating the Authorization header](#authorization-header) 19 | * [Generating the Signature header](#signature-header) 20 | * [An API Server test harness](#code) 21 | * [Endpoint Details](#details) 22 | 23 | 24 | ### Overview 25 | 26 | This document describes the CloudTrax API, an Application Programming Interface that allows you to create, access, configure, and manipulate your CloudTrax networks and their Access Points. The API provides the facilities to let you recreate, if desired, the functionality of the CloudTrax Dashboard and have it entirely under your own control. 27 | 28 | The API is comprised of the following endpoint collections: 29 | 30 | endpoints | description 31 | ---- | ----- 32 | [Account Management](account_management.md) | Create new user accounts, log in users with username and password, provide embedded web access to CloudTrax visuals, and manage service agreements 33 | [User Management](user_management.md) | Manage users with the new CloudTrax user system 34 | [Client Management](client_management.md) | Block and edit clients 35 | [History](history.md) | View traffic statistics in a given domain over a time span 36 | [Network Group Management](networkgroup_management.md) | Create, list and delete network groups 37 | [Network Management](network_management.md) | Create, list, and delete networks 38 | [Node Management](node_management.md) | Create, list, update, delete, and test for characteristics of Access Points (also called nodes) 39 | [Switch Management](switch_management.md) | Create, list, update, delete, and test for characteristics of Switches 40 | [Powerstrip Management](powerstrip_management.md) | Create, list, update, delete, and test for characteristics of Powerstrips 41 | [Site Survey](site_survey.md) | Scan Access Points in a network for neighboring Access Points 42 | [Time](time.md) | Synchronize time against the API server 43 | [Vouchers](vouchers.md) | Create, list, and update vouchers allowing per-user access to your networks 44 | 45 | In order to use the facilities of the API, you'll need to have the API keys relevant to your account and/or network(s). See [Retrieving API Keys](#getting_keys) for more information about this. 46 | 47 | 48 | ### Introduction 49 | 50 | The CloudTrax API is [RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer). It uses the HTTP methods (or *verbs*), GET, POST, PUT, and DELETE, to retrieve, create, update, and delete, respectively, CloudTrax-based *resources*. The resources themselves, as well as the operations performed on them, are specified in RESTful fashion by the *path* component of the URL that addresses the CloudTrax API server. 51 | 52 | Because the API is RESTful, it's the combination of HTTP method plus path that fully specifies, or describes, an API *endpoint* or call. For example, the endpoint that's described in this documentation as: 53 | 54 | `PUT /node/` 55 | 56 | will actually be invoked as: 57 | 58 | `https://api.cloudtrax.com/node/` 59 | 60 | with your HTTP client indicating separately that this call is a `PUT`. 61 | 62 | *Note:* the word *node* as used in the API and at places in this documentation is a nickname for *Access Point*. The two are equivalent. 63 | 64 | The effect of this endpoint is to update the node with id `node-id` with the information contained in the JSON structure that's passed in as the HTTP *message body*. The same path used in conjunction with the HTTP method `DELETE`: 65 | 66 | `DELETE /node/` 67 | 68 | has the effect of deleting that node. 69 | 70 | Here's another example in a bit more detail. The API call requesting a list of all CloudTrax networks belonging to a particular user is specified by the endpoint ` GET /network/list`. We could invoke this call using the curl HTTP client on the command line, requesting verbose output as follows: 71 | 72 | ```` 73 | curl -v https://api.cloudtrax.com/network/list 74 | ```` 75 | 76 | This would produce the following output, showing the GET followed by the three Request Headers produced by the curl client in this case: 77 | 78 | ```` 79 | > GET /network/list HTTP/1.1 80 | > User-Agent: curl/7.37.1 81 | > Host: api.cloudtrax.com 82 | > Accept: */* 83 | ... 84 | ```` 85 | 86 | If this call were successful, the body of the HTTP Response would return a JSON structure showing information on each network found. This particular call will fail however because all CloudTrax API calls need to be *authenticated* before they can be run (see [Authentication](#authentication) below), and we haven't done that here. What curl will show in this instance is a piece of JSON reporting the particular type of authentication error that occurred: 87 | 88 | ``` 89 | { 90 | "errors": [ 91 | { 92 | "code": 13001, 93 | "message": "No nonce or timestamp in header.", 94 | "context": "authorize", 95 | "values": { 96 | 97 | } 98 | } 99 | ] 100 | } 101 | ``` 102 | 103 | Since this particular call uses GET, you could in the spirit of experimentation also invoke it directly from the address bar of your favorite web browser to see what happens: 104 | 105 | ```` 106 | https://api.cloudtrax.com/network/list 107 | ```` 108 | 109 | This will return the same error-reporting JSON as above (though not as nicely prettified, since the HTML will blithely ignore all internal linefeeds and reduce whitespace to a minimum). 110 | 111 | If the above call *had* been properly authenticated and was successful, the returned JSON might have looked something like this: 112 | 113 | ```` 114 | { 115 | "networks": [ 116 | { 117 | "name": "hk_test_network", 118 | "id": 135587, 119 | "latitude": 49.44026050000000083, 120 | "longitude": -123.6724020000000053, 121 | "down_repeater": 1, 122 | "down_gateway": 0, 123 | "spare_nodes": 0, 124 | "node_count": 2 125 | } 126 | ] 127 | } 128 | ```` 129 | 130 | Several of the GET-based calls in this API use an HTTP *query-string* to pass additional information to the server. For example, the `GET /history/network/` call can add an optional "time span" parameter as in the following: 131 | 132 | ```` 133 | GET https://api.cloudtrax.com/history/network/12478?period=week 134 | ```` 135 | As already mentioned, POST and PUT requests that create new resources or update existing resources, respectively, pass required information in the body of the HTTP Request as a JSON structure. The `POST /network` request to create a new network, for example, will need to add a JSON body something like the following: 136 | 137 | ```` 138 | { 139 | "name":"newNetworkTest_2", 140 | "password":"passwordForNetwork", 141 | "email":"someEmail@example.com", 142 | "location":"Moose Jaw", 143 | "timezone":"Canada/Central", 144 | "country_code":"CA" 145 | } 146 | ```` 147 | 148 | 149 | 150 | ### Required headers 151 | The API expects all HTTP Requests to contain the following headers: 152 | 153 | header-name | header-value | notes 154 | ------ | ------ | ----- 155 | `Host:` | api.cloudtrax.com | Most HTTP libraries/clients generate this header automatically for the given URL. All CloudTrax API calls use this hostname. 156 | `Content-Length:` | length in bytes of the Request body | Generated automatically by most libraries/clients. Length will be 0 for body-less requests. 157 | `Content-Type:` | 'application/json' | PUT and POST requests will be passing JSON structures to the server. 158 | `OpenMesh-API-Version:` | 1 | Version of the API to use. 0 assumed if this header is missing. Incorrect versioning may cause difficult-to-diagnose errors. 159 | `Authorization:` | see [Authentication](#authentication) | Required on all requests. 160 | `Signature:` | see [Authentication](#authentication) | Required on all requests. 161 | 162 | 163 | 164 | ### Errors 165 | 166 | For a list of the actual errors that can occur during API operations, see the [Error codes](error_codes.md) document. 167 | 168 | Errors are returned in a consistent fashion throughout the API. A non-200 HTTP status code (e.g. 404 HTTP_NOT_FOUND or 403 HTTP_FORBIDDEN) provides a hint as to the general nature of the error, and the body of the returned JSON provides more detailed information. For instance, If you try to create a network with a name that already exists and use an invalid country code as well, you'll get an HTTP_FORBIDDEN error with the following body: 169 | 170 | ```` 171 | { 172 | "errors": [ 173 | { 174 | "code": 12001, 175 | "context": "name", 176 | "message": "String length (104) out of range (1 - 100).", 177 | "values": { 178 | "length": "104", 179 | "max": "100", 180 | "min": "1" 181 | } 182 | }, 183 | { 184 | "code": 12005, 185 | "context": "country_code", 186 | "message": "Unknown country code.", 187 | "values": {} 188 | } 189 | ] 190 | } 191 | ```` 192 | 193 | 194 | #### Error elements 195 | `"errors"` is an array of one or more specific error elements. Each element has the following fields: 196 | 197 | field | description 198 | ---- | ---- 199 | code | a unique error code 200 | context | further information about where the error occurred 201 | message | an English-language error string suitable for display 202 | values | a list of key-value pairs that were the specific values used to create the error message. Not all error messages have values. 203 | 204 | 205 | #### 1009 "Success" elements 206 | Code 1009 is a special *non-error* status-type code that is used to indicate "Success" on the completion of certain operations that do not themselves otherwise return JSON output. A successful [update node](node_management.md#update-node) call, for example, returns a JSON 1009 "Success" element to indicate a successful update: 207 | 208 | ````json 209 | { 210 | "code": 1009, 211 | "message": "Success.", 212 | "context": "update_node", 213 | "values": { 214 | 215 | } 216 | } 217 | ```` 218 | 219 | The exceptions are calls in the [Cloud AP's](#cloud-ap) endpoint collection. For technical reasons, none of these endpoints return JSON, and their HTTP Response status codes need to be checked directly for either 200 Success or 40x Failure. 220 | 221 | #### Error code listing 222 | See the [Error codes document](error_codes.md). 223 | 224 | 225 | ### Authentication 226 | 227 | As noted above, all CloudTrax API calls need to be authenticated. Authentication is the process of determining that the user making this call is known and acceptable to the system. This requires adding two additional headers to every HTTP Request: 'Authorization:' and 'Signature:'. We'll first take a brief look at keys. 228 | 229 | 230 | #### Keys 231 | 232 | Keys are an essential part of the authentication process, since they identify users and the degree of access they're allowed to the CloudTrax system. The term *key* is used here loosely: access to the API is actually controlled by a *key pair* consisting of a *key* and a *secret*, both of which are required for API access. Keys come in three types. 233 | 234 | type of key | provides access to 235 | ---- | ---- 236 | `account` | All networks belonging to an account, with the ability to create new networks 237 | `network` | A single network 238 | `application` | API endpoints that allow the creation of new accounts and log-in of users 239 | 240 | * `account` keys (also called *master-level* keys) are the most common type of key. They provide access to all networks assigned to an account and allow their account holders to create new networks on an *ad hoc* basis. 241 | 242 | * `network` keys allow their clients to use nearly all the same endpoints as an account-level key holder, but access is restricted to a single network. A typical use-case would be a hotel chain that maintains hotels in different cities. A different `network` key would be assigned to each hotel in the chain, restricting access at each hotel to its own network. Administrators at head office would be able to use an `account` key to oversee and manage all corporate networks system-wide. 243 | 244 | * `application` keys provide access to several endpoints that are not available to the other key types. These endpoints provide the functionality required to create new accounts and to retrieve account and network keys given username and password. An application, say a smartphone app, would use an `application` key to create new accounts and allows users to log in using their username and password. See the document [Account Management endpoints](account_management.md) for details. 245 | 246 | Whichever type of key you have, the authentication process described below is identical. 247 | 248 | 249 | #### Retrieving API Keys 250 | 251 | As described above, you must have the relevant key and secret for your account and/or network(s) in order to use the API as described in this document. To generate the needed key and secret, see the CloudTrax Help Center article "Generating an API Key". 252 | 253 | Note that currently there are no access charges associated with use of the CloudTrax API for users with fewer than 100 devices. For more than 100 devices, contact sales@openmesh.com for pricing. Open Mesh may enforce rate limiting for some uses of the API. In order to help constrain resource demands, we ask that you please attempt to design any systems utilizing the API to limit frequency of access to the API. 254 | 255 | 256 | 257 | #### Generating the Authorization header 258 | Generating the "Authorization:" header is straightforward. It is formed by the string concatenation of three key-value pairs: 259 | 260 | * an appropriate key, depending on which API is being called 261 | * a Unix timestamp 262 | * a so-called "Number-Used-Once" nonce 263 | 264 | An easy-to-fix error here, 13002, is occasioned by providing a timestamp that differs from the server's time by more than 15 minutes. Call the `GET /time` endpoint to retrieve the "correct" time (from the server's perspective), and adjust your own accordingly. Nonces, a cryptographic device used to ensure that older communications cannot be reused in replay attacks, also need to be unique across all API calls during a time window that varies between 15 to 30 minutes (13003). (This error is easily avoided by generating a random nonce on every call.) 265 | 266 | As noted above, all CloudTrax API calls are required to be signed by either an application-level or an account- or network-level key. Which is used depends on the particular call being made. 267 | 268 | Here's a piece of PHP showing the authentication operation: 269 | 270 | ````php 271 | $key = '1b88730ac5ba6000a1271e0b2a2edb5a163ce77bf9630850f22f8ca3de490a5f'; 272 | $nonce = 'ThisIsANonce'; 273 | $authorization = "key=" . $key . ",timestamp=" . time() . ",nonce=" . $nonce; 274 | $authorization_header = "Authorization: " . $authorization; 275 | ```` 276 | 277 | Note that there are no spaces in the concatenated authorization string (though there is one following the colon (":") in the Authorization header itself, as allowed by the HTTP protocol). 278 | 279 | 280 | #### Generating the Signature header 281 | Generating the "Signature:" header is a teeny bit more complicated perhaps, but not by much. You need to create an HMAC SHA-256 hashed form of the authorization string concatenated with the endpoint's path, keyed on the secret string shared between you and the CloudTrax server. Certainly the code is straightforward; here it is in PHP: 282 | 283 | ````php 284 | $signature = hash_hmac('sha256', $authorization . $path, $secret); 285 | $sig_header = "Signature: " . $signature; 286 | ```` 287 | 288 | One final wrinkle is that the above HMAC keyed hash example only works as is with GET and DELETE. If you're working with either POST or PUT endpoints that pass a JSON structure in the message body, you'll need to concatenate that body onto the first, "message" argument of the `hash_hmac` function: 289 | 290 | ````php 291 | $signature = hash_hmac('sha256', $authorization . $path . $jsonbody, $secret); 292 | ```` 293 | 294 | 295 | If you're getting back a 13000 "Signature wrong" error on your calls, check if you're attempting to create a signature on a PUT or POST without concatenating the HTTP message body as well. 296 | 297 | Whatever form the signature takes, the authorization and signature headers need to be present for every API request you make. 298 | 299 | 300 | ### An API Server test harness 301 | 302 | An accompanying document, [An API Server test harness](api_server_test_harness.md), provides PHP working-code examples of how to call individual API's from several of the endpoint collections: [Network Management](network_management.md) , [Node Management](node_management.md), and [Network Settings](network_settings.md). These calls cover the entire RESTful gamut of GET, POST, PUT, and DELETE and provide a representative sample of the types of endpoints in the API and how to call them. 303 | 304 | 305 | ### API Endpoint Details 306 | 307 | For reference purposes, here is the full list of endpoint collections comprising the CloudTrax API. Dive into the following sections for details about specific API endpoints: 308 | 309 | endpoints | description 310 | ---- | ----- 311 | [Account Management](account_management.md) | Create new user accounts, log in users with username and password, provide embedded web access to CloudTrax visuals, and manage service agreements 312 | [Network Management](network_management.md) | Create, list, and delete networks 313 | [Node Management](node_management.md) | Create, list, update, delete, and test for characteristics of nodes 314 | [History](history.md) | View traffic statistics in a given domain over a time span 315 | [Site Survey](site_survey.md) | Scan nodes in a network for neighboring Access Points 316 | [Time](time.md) | Synchronize time against the API server 317 | -------------------------------------------------------------------------------- /api/account_management.md: -------------------------------------------------------------------------------- 1 | # Account Management endpoints 2 | 3 | functionality | method | endpoint 4 | --- | --- | --- 5 | [create account](#create_account) | PUT | 6 | [edit account](#edit_account) | PUT | `/account/` 7 | [remove account](#remove_account) | DELETE | 8 | [edit account owner](#edit_account_owner) | PUT | `/account//user//owner` 9 | [log in and authenticate](#login) | POST | `/account/login` 10 | [create embed credentials](#embed) | GET | `/account/credential//embed` 11 | [delete embed credentials](#un-embed) | DELETE | `/account/credential//embed` 12 | [service agreement status](#service-agreement-status) | GET | `/account/service_agreement` 13 | [agree to service agreement](#service-agreement-agree) | PUT | `/account/service_agreement` 14 | [get audit logs](#get-audit-logs) | GET | `/account//auditlogs` 15 | 16 | 17 | 18 | ### create account 19 | 20 | *create account* is currently a restricted API and is not documented here at this time. 21 | 22 | 23 | ### edit account 24 | `PUT /account/` 25 | 26 | Edit account information. 27 | 28 | ##### example request 29 | 30 | PUT https://api-v2.cloudtrax.com/account/123 31 | 32 | ##### example input 33 | ```` JSON 34 | { 35 | "name" : "new account name" 36 | } 37 | ```` 38 | 39 | ##### output 40 | 41 | HTTP 200 42 | 43 | ##### example output 44 | 45 | ```` JSON 46 | { 47 | } 48 | ```` 49 | 50 | 51 | ### remove account 52 | 53 | *remove account* is currently a restricted API and is not documented here at this time. 54 | 55 | 56 | ### edit account owner 57 | `PUT /account//user//owner` 58 | 59 | Edit the account owner. 60 | 61 | ##### example request 62 | 63 | PUT https://api-v2.cloudtrax.com/account/123/user/2/owner 64 | 65 | ##### example input 66 | ```` JSON 67 | { 68 | } 69 | ```` 70 | 71 | ##### output 72 | 73 | HTTP 200 74 | 75 | ##### example output 76 | 77 | ```` JSON 78 | { 79 | } 80 | ```` 81 | 82 | 83 | ### log in 84 | `POST /account/login` 85 | 86 | Allow a user to log in using a username and password. These are passed in via , and an `account` or `network` key and secret are returned. This endpoint requires use of an `application` key (see the [CloudTrax API document](README.md#keys)). 87 | 88 | ##### example request 89 | 90 | POST https://api.cloudtrax.com/account/login 91 | 92 | ##### example input 93 | ```` JSON 94 | { 95 | "username" : "joe_blow", 96 | "password" : "some_password" 97 | } 98 | ```` 99 | 100 | ##### output 101 | 102 | Returns a key and secret or an appropriate error. The "expire" field if successful is a Unix timestamp. 103 | 104 | ##### example output 105 | 106 | ```` JSON 107 | { 108 | "key": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 109 | "secret": "0032665e503e866e331408bdb1edf02041abc53d21f6b9d70ce4495bee1de31d", 110 | "expire": 2145916800, 111 | "type": "master", 112 | "type_value": "BerlinCloudtrax", 113 | "read_only": 0, 114 | "last_network_name": "open mesh office lamai beach", 115 | "last_network_id": 92200 116 | } 117 | ```` 118 | 119 | 120 | ### create embed credentials 121 | `GET /account/credential//embed` 122 | 123 | Create 'embed' user credentials for a specific network. Returns a key and secret which provide limited access to sections of the CloudTrax Dashboard for embedding on a local website. The returned key and secret can be pasted into the HTML snippet that is accessible via the Dashboard's "External embeds" section under "Configure::Display". 124 | 125 | ##### example request 126 | 127 | GET https://api.cloudtrax.com/account/credential/12989/embed 128 | 129 | ##### example output 130 | 131 | ```` JSON 132 | { 133 | "key": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 134 | "secret": "0032665e503e866e331408bdb1edf02041abc53d21f6b9d70ce4495bee1de31d" 135 | } 136 | ```` 137 | 138 | 139 | 140 | ### delete embed credentials 141 | `DELETE /account/credential//embed` 142 | 143 | Delete a previously created embed credential. 144 | 145 | ##### example request 146 | 147 | DELETE https://api.cloudtrax.com/account/credential/12989/embed 148 | 149 | ##### output 150 | Code 14001 will be returned on a successful deletion; 14002 if the deletion failed. 151 | 152 | ##### example output 153 | 154 | ```` JSON 155 | { 156 | "code": 14001, 157 | "message": "Deletion of key/secret succeeded.", 158 | "context": "account", 159 | "details": "", 160 | "values": { 161 | } 162 | } 163 | ```` 164 | 165 | 166 | ### service agreement status 167 | 168 | `GET /account/service_agreement` 169 | 170 | Check if an `account` or `network` keyholder has agreed to Open-Mesh's latest service agreement. Log in as either an `account` or `network` user to do so. 171 | 172 | ##### example request 173 | 174 | GET https://api.cloudtrax.com/account/service_agreement 175 | 176 | ##### output 177 | The returned JSON package will indicate either `"valid" : true` or `"valid" : false`. 178 | 179 | ##### example output 180 | 181 | ```` JSON 182 | { 183 | "valid" : true 184 | } 185 | ```` 186 | 187 | 188 | ### agree to service agreement 189 | 190 | `PUT /account/service_agreement` 191 | 192 | Agree to a service agreement. Note that no JSON structure is required for this endpoint. 193 | 194 | ##### example request 195 | 196 | PUT https://api.cloudtrax.com/account/service_agreement 197 | 198 | ##### output 199 | 200 | Code 1009 is returned on success. 201 | 202 | ##### example output 203 | ```` JSON 204 | { 205 | "code": 1009, 206 | "message": "Success.", 207 | "context": "service_agreement_put", 208 | "details": "", 209 | "values": { 210 | 211 | } 212 | } 213 | ```` 214 | 215 | 216 | ### get audit logs 217 | `GET /account//auditlogs` 218 | 219 | Get a list of audit events. Audit events are a select set of fields that have changed for the specified account. 220 | 221 | ##### example request 222 | 223 | GET https://api-v2.cloudtrax.com/account/123/auditlogs 224 | 225 | ##### example input 226 | ```` JSON 227 | { 228 | } 229 | ```` 230 | 231 | ##### output 232 | 233 | HTTP 200 234 | 235 | ##### example output 236 | 237 | ```` JSON 238 | { 239 | "log_events":[ 240 | { 241 | "event_id":"abc", 242 | "date":"2017-06-09T00:00:00Z", 243 | "remote_address":"192.168.1.1", 244 | "account_id":123, 245 | "user_id":1, 246 | "user_email":"foo@bar.com", 247 | "support":false, /* audit event spawned by a support person, not a normal user */ 248 | "network_id":456, 249 | "network_name":"foo wifi", 250 | "user_action":"create", 251 | "target_type":"network", 252 | "target_id":"456", 253 | "fields":[ 254 | { 255 | "field":"ssid.1.wifi_name", 256 | "old_value":"old foo wifi", 257 | "new_value":"new foo wifi" 258 | } 259 | ] 260 | } 261 | ] 262 | } 263 | ```` 264 | 265 | -------------------------------------------------------------------------------- /api/api_server_test_harness.md: -------------------------------------------------------------------------------- 1 | # An API Server test harness 2 | 3 | The accompanying PHP script, [simple_api_server_test_harness.php] (code/php/simple_api_server_test_harness.php) can be invoked from the command line thusly: 4 | 5 | ```` 6 | php simple_api_server_test_harness.php 7 | ``` 8 | 9 | 10 | The script will run through a number of endpoint calls, writing the results of those calls to the console, along with any JSON packages returned. The endpoints currently invoked in the script are: 11 | 12 | * `GET /network` (list all networks) 13 | * `POST /network` (create a new network) 14 | * `POST /node` (create a new node) 15 | * `DELETE /node` (delete a node), and 16 | * `PUT /network//settings` (update a particular network attribute) 17 | 18 | The call to the `GET /network` endpoint (list all networks) is invoked as: 19 | ```` php 20 | call_api_server(Method::GET, "/network", NULL); 21 | ```` 22 | and the call to `POST /network` (create a new network) is invoked thusly: 23 | ```` php 24 | $data = array( 25 | 'mac' => 'AC:86:74:00:11:22', 26 | 'name' => 'Test-Node-#1' 27 | ); 28 | 29 | call_api_server(Method::POST, "/node/network/", $data); 30 | ```` 31 | 32 | where `` is a placeholder for the actual ID of the network you're deleting. The third parameter, `$data`, is a piece of JSON required by this endpoint as a POST method. 33 | 34 | Most of the calls are initially commented out, because the list of calls is "sequenced" in this example, and determining a parameter for one call in most cases depends on being able to visually inspect the output of a prior call, in order to replace a placeholder with an actual <network-id> or <node-id> value. Comments in the code explain this. 35 | 36 | When the script is run the first time, you'll initially get several authentication errors, because you need to replace the placeholder key and secret strings at the top of the file with the real thing. Once you're done that, `GET /network/list` should provide a JSON summary of your current network configuration, and `POST /network` will create a new minimal network. The rest of the calls await your uncommenting, one after another. 37 | 38 | Feel free to use and expand the test harness as you explore the API. 39 | -------------------------------------------------------------------------------- /api/client_management.md: -------------------------------------------------------------------------------- 1 | # Client Management endpoints 2 | 3 | This API component provides endpoints for blocking clients and editing client names. 4 | 5 | 6 | functionality | method | endpoint 7 | --- | --- | --- 8 | [get blocked clients](#get-blocked) | GET | `/client/network//block` 9 | [block/unblock clients](#set-blocked) | PUT | `/client/network//block` 10 | [edit clients](#edit-client) | PUT | `/client/network//edit` 11 | 12 | 13 | ### get blocked clients 14 | `GET /client/network//block` 15 | 16 | Get all blocked clients for a network. 17 | 18 | ##### example request 19 | 20 | ```` 21 | GET https://api.cloudtrax.com/client/network/123456/block 22 | ```` 23 | 24 | ##### output 25 | 26 | The API either returns HTTP status code 200 (success) or an HTTP error and JSON describing the error. On success, the API returns all blocked clients. 27 | 28 | ##### example output 29 | ```` json 30 | { 31 | "clients": { 32 | "00:00:00:00:00:01": { 33 | "name": "unittest client", 34 | "name_override": "test overwrite", 35 | "cid": "V28hO3syPho0szL5dqDeoRDBtdv9_cofjMygMbpmstQ=", 36 | "signal": { 37 | "antenna1": -64, 38 | "antenna2": -74 39 | }, 40 | "mcs": { 41 | "tx": 6 42 | }, 43 | "bitrate": { 44 | "rx": 52, 45 | "tx": 58.5 46 | }, 47 | "channel_width": 20, 48 | "band": "2.4GHz", 49 | "last_seen": "2015-10-04T23:18:58Z", 50 | "last_name": "andreas test", 51 | "last_node": "ac:86:74:00:00:04", 52 | "link": "wireless", 53 | "wifi_mode": "ac", 54 | "blocked": true, 55 | "os": "IOS", 56 | "os_version": "", 57 | "traffic": { 58 | "ssid2": { 59 | "bup": 60000000, 60 | "bdown": 54000000 61 | }, 62 | "ssid1": { 63 | "bup": 24000000, 64 | "bdown": 18000000 65 | } 66 | } 67 | }, 68 | "AB:CD:EF:14:3B:04": { 69 | "name": "", 70 | "name_override": "", 71 | "cid": "8DEnHhpg5sRvpnrumoCiuKQXJ-gA0Hmeyr2aHKXBMdE=", 72 | "signal": { 73 | 74 | }, 75 | "mcs": { 76 | "tx": 0 77 | }, 78 | "bitrate": { 79 | "rx": 0, 80 | "tx": 0 81 | }, 82 | "channel_width": 0, 83 | "band": "", 84 | "last_seen": "", 85 | "last_name": "", 86 | "last_node": "", 87 | "link": "", 88 | "wifi_mode": "", 89 | "blocked": true, 90 | "os": "IOS", 91 | "os_version": "", 92 | "traffic": { 93 | "ssid3": { 94 | "bup": 2400000, 95 | "bdown": 1800000 96 | }, 97 | "ssid2": { 98 | "bup": 6000000, 99 | "bdown": 5400000 100 | } 101 | } 102 | } 103 | } 104 | } 105 | ```` 106 | 107 | 108 | ### block/unblock client 109 | Block/unblock a client. 110 | 111 | ```` 112 | PUT /client/network//block 113 | ```` 114 | 115 | ##### example request 116 | 117 | ```` 118 | PUT https://api.cloudtrax.com/client/network/12345/block 119 | ```` 120 | ##### example input 121 | 122 | 123 | ````json 124 | { 125 | "block":1, 126 | "client_ids": [ 127 | "_4G6of2NARub8yqm-YJR3RveJYK1SI07uXhLygJSkOs=", 128 | "V28hO3syPho0szL5dqDeoRDBtdv9_cofjMygMbpmstQ=", 129 | ] 130 | } 131 | ```` 132 | 133 | ##### output 134 | 135 | The API returns HTTP status code 200 (success) or an HTTP error and JSON describing the error. 136 | 137 | 138 | 139 | ### override client name 140 | ```` 141 | PUT /client/network//edit 142 | ```` 143 | 144 | ##### example request 145 | 146 | ```` 147 | PUT https://api.cloudtrax.com/client/network/12345/edit 148 | ```` 149 | 150 | ##### example input 151 | 152 | 153 | ````json 154 | { 155 | "clients": [ 156 | { 157 | "id": "_4G6of2NARub8yqm-YJR3RveJYK1SI07uXhLygJSkOs=", 158 | "name": "new-name" 159 | }, 160 | { 161 | "id": "V28hO3syPho0szL5dqDeoRDBtdv9_cofjMygMbpmstQ=", 162 | "name": "another-new-name" 163 | } 164 | ] 165 | } 166 | ```` 167 | 168 | ##### output 169 | 170 | The API returns HTTP status code 200 (success) or an HTTP error and JSON describing the error. 171 | 172 | -------------------------------------------------------------------------------- /api/code/JavaScript/api-js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 43 | 44 | -------------------------------------------------------------------------------- /api/code/json/README.md: -------------------------------------------------------------------------------- 1 | The JSON in [network_settings_example.json](network_settings_example.json) is a result of calling the [`GET /network//settings`](../../network_settings.md#get-settings) endpoint that is detailed in [Network Settings endponts](../../network_settings.md). -------------------------------------------------------------------------------- /api/code/php/README.md: -------------------------------------------------------------------------------- 1 | The file [simple_api_server_test_harness.php](simple_api_server_test_harness.php) in the directory above is a PHP script that shows how to make a variety of calls to assorted endpoints in the API. See [An API Server test harness](../../api_server_test_harness.md) for details. -------------------------------------------------------------------------------- /api/code/php/simple_api_server_test_harness.php: -------------------------------------------------------------------------------- 1 | '; 8 | $secret = ''; 9 | 10 | # a fellah needs enums, whatcha gonna do?! 11 | class Method 12 | { 13 | const GET = 0; 14 | const POST = 1; 15 | const PUT = 2; 16 | const DELETE = 3; 17 | 18 | public static function nameForEnumValue($value) { 19 | switch($value) { 20 | case 0: return "GET"; 21 | case 1: return "POST"; 22 | case 2: return "PUT"; 23 | case 3: return "DELETE"; 24 | } 25 | } 26 | }; 27 | 28 | function print_debug_info($method, $endpoint, $headers) { 29 | print( "\n" ); 30 | print( "Method: " . Method::nameForEnumValue($method) . "\n"); 31 | print( "Endpoint: " . $endpoint . "\n" ); 32 | print_r($headers); 33 | } 34 | 35 | function build_headers($auth, $sign) { 36 | $headers = array(); 37 | $headers[] = "Authorization: " . $auth; 38 | $headers[] = "Signature: " . $sign; 39 | $headers[] = "Content-Type: application/json"; 40 | $headers[] = "OpenMesh-API-Version: 1"; 41 | return $headers; 42 | } 43 | 44 | function invoke_curl($method, $endpoint, $headers, $json) { 45 | 46 | $api_server = 'https://api.cloudtrax.com'; 47 | 48 | try { 49 | // get a curl handle then go to town on it 50 | 51 | $ch = curl_init($api_server . $endpoint); 52 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 53 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 54 | 55 | if ($method == Method::DELETE) 56 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); 57 | elseif ($method == Method::PUT) { 58 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); 59 | curl_setopt($ch, CURLOPT_POSTFIELDS, $json); 60 | } 61 | else if ($method == Method::POST) { 62 | curl_setopt($ch, CURLOPT_POST, 1); 63 | curl_setopt($ch, CURLOPT_POSTFIELDS, $json); 64 | } 65 | 66 | $result = curl_exec($ch); 67 | if ($result == FALSE) { 68 | if (curl_errno($ch) == 0) 69 | echo "@@@@ NOTE @@@@: nil HTTP return: This API call appears to be broken" . "\n"; 70 | else 71 | throw new Exception(curl_error($ch), curl_errno($ch)); 72 | } 73 | else 74 | echo "RESULT: \n" . $result . "\n"; 75 | } 76 | catch(Exception $e) { 77 | trigger_error( sprintf('Curl failed with error #%d: "%s"', 78 | $e->getCode(), $e->getMessage()), E_USER_ERROR); 79 | } 80 | } 81 | 82 | function call_api_server($method, $endpoint, $data) { 83 | 84 | global $key, $secret; 85 | 86 | $time = time(); 87 | $nonce = rand(); 88 | 89 | if ($method == Method::POST) 90 | assert( '$data != NULL /* @@@@ POST requires $data @@@@ */'); 91 | elseif ($method == Method::GET || $method == Method::DELETE) 92 | assert( '$data == NULL /* @@@ GET and DELETE take no $data @@@ */'); 93 | 94 | $path = $endpoint; 95 | // if present, concatenate encoded json to $endpoint for Signature: 96 | if ($data != NULL) { 97 | $json = json_encode($data); 98 | $path .= $json; 99 | } 100 | 101 | $authorization = "key=" . $key . ",timestamp=" . $time . ",nonce=" . $nonce; 102 | $signature = hash_hmac('sha256', $authorization . $path . $body, $secret); 103 | $headers = build_headers($authorization, $signature); 104 | 105 | print_debug_info($method, $endpoint, $headers); 106 | 107 | invoke_curl($method, $endpoint, $headers, $json); 108 | } 109 | 110 | 111 | // ===================================================== 112 | // EXAMPLE API ENDPOINT CALLS 113 | // ===================================================== 114 | 115 | // ----------------- 116 | // list all networks 117 | // ----------------- 118 | 119 | call_api_server(Method::GET, "/network/list", NULL); 120 | 121 | // ---------------------------- 122 | // create a new minimal network 123 | // ---------------------------- 124 | 125 | $data = array( 126 | 'name' => 'test-network-#10', 127 | 'password' => 'some_password', 128 | 'timezone' => 'America/Los_Angeles', 129 | 'country_code' => 'US' 130 | ); 131 | call_api_server(Method::POST, "/network", $data); 132 | 133 | // ------------------ 134 | // add a minimal node 135 | // ------------------ 136 | 137 | $data = array( 138 | 'mac' => 'AC:86:74:00:22:44', 139 | 'name' => 'Test-Node-#3' 140 | ); 141 | 142 | // initially commented out until we've had an opportunity to 143 | // visually inspect the output of the preceding call to pick 144 | // out a valid to use 145 | 146 | // call_api_server(Method::POST, "/node/network/", $data); 147 | 148 | // ---------------------------- 149 | // delete the node just created 150 | // ---------------------------- 151 | 152 | // similarly commented out until we've had an opportunity, after 153 | // running the prior API, to determine the of the new node 154 | // so we can delete it 155 | 156 | //call_api_server(Method::DELETE, "/node/", NULL); 157 | 158 | // ------------------------------------------------------------------ 159 | // get a list of nodes to verify that the one you deleted is gone. 160 | 161 | // (if you're really thorough, you'll run list nodes @before@ you 162 | // delete it as well to insure it was really there in the first place) 163 | // ------------------------------------------------------------------ 164 | 165 | //call_api_server(Method::GET, "/node/network//list", NULL); 166 | 167 | // ---------------------------------------------------------------------- 168 | // update the captive portal 'block' message for SSID 3, network 169 | // ---------------------------------------------------------------------- 170 | $data = array( 'ssids' => 171 | array( '3' => 172 | array( 'captive_portal' => 173 | array( 'block_message' => "Stop in the name of love!" ) 174 | ) 175 | ) 176 | ); 177 | 178 | //call_api_server(Method::PUT, "/network//settings", $data); 179 | 180 | ?> 181 | -------------------------------------------------------------------------------- /api/code/python27/cloudtrax/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | import base64 3 | import hashlib 4 | import hmac 5 | import json 6 | import random 7 | import requests 8 | import string 9 | import time 10 | 11 | class CloudTrax(object): 12 | 13 | def __init__(self, key, secret): 14 | self._key = key 15 | self._secret = secret 16 | self.api_server = 'https://api.cloudtrax.com' 17 | 18 | def _build_headers(self, endpoint, data=None): 19 | 20 | if (data == None): 21 | body = ''; 22 | else: 23 | body = data; 24 | 25 | # n log_2(52) bits of entropy, at 8 chars this is 45 bits 26 | nonce = ''.join([random.choice(string.ascii_letters) for _ in xrange(8)]) 27 | now = int(time.time()); 28 | 29 | authorization = "key=" + self._key + ",timestamp=" + str(now) + ",nonce=" + nonce; 30 | signature = hmac.new(key=self._secret, msg=authorization + endpoint + body, digestmod=hashlib.sha256).hexdigest(); 31 | 32 | headers = { 33 | "Authorization": authorization, 34 | "Signature": signature, 35 | "Content-Type": "application/json", 36 | "OpenMesh-API-Version": "1" 37 | } 38 | 39 | return headers 40 | 41 | def get(self, endpoint): 42 | headers = self._build_headers(endpoint); 43 | responce = requests.get(self.api_server + endpoint, headers=headers) 44 | return responce.json() 45 | 46 | def post(self, endpoint, data): 47 | body = json.dumps(data); 48 | headers = self._build_headers(endpoint, body); 49 | responce = requests.post(self.api_server + endpoint, headers=headers, data=body) 50 | return responce.json() 51 | 52 | def delete(self, endpoint): 53 | headers = self._build_headers(endpoint); 54 | responce = requests.delete(self.api_server + endpoint, headers=headers) 55 | return responce.json() 56 | 57 | if __name__ == "__main__": 58 | 59 | key = '' 60 | secret = '' 61 | 62 | api = CloudTrax(key,secret) 63 | 64 | # ----------------- 65 | # list all networks 66 | # ----------------- 67 | 68 | print "\n-- Network List --" 69 | result = api.get("/network/list") 70 | if 'errors' in result: 71 | print json.dumps(result['errors']) 72 | else: 73 | for x in result["networks"]: 74 | print str(x['id']) + " " + str(x['name']) 75 | 76 | # ---------------------------- 77 | # create a new minimal network 78 | # ---------------------------- 79 | 80 | #data = { 81 | # 'name': 'test-network-#10', 82 | # 'password': 'some_password', 83 | # 'timezone': 'America/Los_Angeles', 84 | # 'country_code': 'US' 85 | #} 86 | 87 | #del_id = None 88 | 89 | #print "\n-- Creating Network --" 90 | #result = api.post("/network", data) 91 | #if 'errors' in result: 92 | # print json.dumps(result['errors']) 93 | #else: 94 | # print "Created Netrork id# " + str(result['id']) 95 | # del_id = result['id'] 96 | 97 | # ----------------- 98 | # list all networks 99 | # ----------------- 100 | 101 | #print "\n-- Network List --" 102 | #result = api.get("/network/list") 103 | #if 'errors' in result: 104 | # print json.dumps(result['errors']) 105 | #else: 106 | # for x in result["networks"]: 107 | # print str(x['id']) + " " + str(x['name']) 108 | 109 | # ------------------------------- 110 | # delete the network just created 111 | # ------------------------------- 112 | 113 | #print "\n-- Deleting Network --" 114 | #if ('del_id' in locals()): 115 | # result = api.delete("/network/" + str(del_id)) 116 | # if 'errors' in result: 117 | # print json.dumps(result['errors']) 118 | # else: 119 | # if result['code'] == 1009: 120 | # print "Deleted network id# " + str(del_id) 121 | # else: 122 | # print json.dumps(result) 123 | 124 | # ----------------- 125 | # list all networks 126 | # ----------------- 127 | 128 | #print "\n-- Network List --" 129 | #result = api.get("/network/list") 130 | #if 'errors' in result: 131 | # print json.dumps(result['errors']) 132 | #else: 133 | # for x in result["networks"]: 134 | # print str(x['id']) + " " + str(x['name']) 135 | -------------------------------------------------------------------------------- /api/error_codes.md: -------------------------------------------------------------------------------- 1 | # API Error codes 2 | 3 | This table shows error codes and their associated descriptions. Strings enclosed by '$' are replaced during error processing. Note that code "1009" is a special "non-error" error code that is used to indicate "success" when an API call returns a 200 Response without any accompanying JSON output of its own. "14001" is one other example of a "non-error" error. 4 | 5 | (See [Errors](README.md#errors) for furtherdetails.) 6 | 7 | domain | error code | message 8 | ---- | ---- | --- 9 | **miscellaneous** | ------- | ----------------------------------------- 10 | misc | 1000 | "Exception: $reason$" 11 | misc | 1001 | "Key is not master." 12 | misc | 1002 | "Request unknown or not implemented." 13 | misc | 1003 | "Id is invalid."; 14 | misc | 1004 | "No network found." 15 | misc | 1005 | "Name cannot be empty." 16 | misc | 1006 | "Missing parameter." 17 | misc | 1007 | "Access point ($mac$) not found." 18 | misc | 1008 | "Mac address ($mac$) is in the wrong format. Must match regx $regx$." 19 | misc | 1009 | "Success" 20 | misc | 1016 | "Internal error." 21 | misc | 1017 | "Element not found." 22 | misc | 1018 | "Incorrect API privileges." 23 | misc | 1019 | "Internal error." 24 | **general info** | ------- | ------- 25 | general | 10000 | "You have one or more access points that are not upgradeable. These may include generic_ap51, OM1P, MR500 or older OM2P APs. Please try again once these are removed from your network." 26 | general | 10001 | "Your current captive portal is CoovaChilli, which is not available in the upgraded firmware. Please select a different captive portal configuration and try again." 27 | general | 10002 | "Some access points are running an older firmware version that requires them to be online before the upgrade can start. Please bring them online or remove them from the network." 28 | **history** | ------- | ------- 29 | history | 11000 | "Client list cannot be empty." 30 | history | 11001 | "Client id cannot be 0." 31 | history | 11002 | "Client name cannot be empty." 32 | **network managment** | ------- | ------- 33 | network | 12000 | "Network not found, create or migrate first." 34 | network | 12001 | "String length ($length$) out of range ($min$ - $max$)." 35 | network | 12002 | "Invalid characters. Used regx: $regx$." 36 | network | 12003 | "Unknown timezone." 37 | network | 12004 | "Int value ($value$) out of range ($min$ - $max$)." 38 | network | 12005 | "Unknown country code." 39 | network | 12006 | "Unknown ht_mode, expect (HT20/HT40+/HT40-/HT80+/HT80-/HT160+/HT160-)." 40 | network | 12007 | "Invalid channel." 41 | network | 12008 | "Unknown firmware release." 42 | network | 12009 | "More than 4 ssids are not allowed." 43 | network | 12010 | "Invalid day(s), use su|mo|tu|we|th|fr|sa." 44 | network | 12011 | "No id given." 45 | network | 12012 | "Id not found, use 0 to create a new entry." 46 | network | 12013 | "Too many elements or elements too large. Max is $max$." 47 | network | 12014 | "Unknown login method." 48 | network | 12015 | "Unknown splash page." 49 | network | 12016 | "Unknown auth type." 50 | network | 12017 | "Vlan tag \"$tag$\" is already in use." 51 | network | 12018 | "Too many entries. Maximum is $max." 52 | network | 12019 | "No entry. Minimum is 1." 53 | network | 12020 | "SSID ($id$) does not exist." 54 | network | 12021 | "Facebook gateway registration error (id: $id$)." 55 | network | 12022 | "Name already exists." 56 | network | 12023 | "Bcrypt error." 57 | network | 12024 | "Role should be 'image', 'page', or 'file'." 58 | network | 12025 | "File size ($size$) is too large. Max is $max$." 59 | network | 12026 | "Upload failure" 60 | network | 12027 | "Bad ssid id" 61 | network | 12028 | "Bad file id" 62 | network | 12029 | "Error during the delete process" 63 | network | 12030 | "File missing." 64 | network | 12047 | "Not permitted by network id." 65 | network | 19007 | "Network name invalid." 66 | **authentication** | ------- | ------- 67 | auth | 13000 | "Signature wrong." 68 | auth | 13001 | "No nonce or timestamp in header." 69 | auth | 13002 | "Timestamp too old or in the future." 70 | auth | 13003 | "Nonce already exists." 71 | auth | 13004 | "Version is not valid." 72 | auth | 13005 | "Unauthorized access from key $key$." 73 | auth | 13006 | "Read-only key does not have access to all components." 74 | auth | 13007 | "Expired key" 75 | auth | 13008 | "Master is not allowed access." 76 | auth | 13009 | "Network id doesn't match key." 77 | auth | 13010 | "No network id or master given." 78 | auth | 13011 | "Unknown key." 79 | auth | 13012 | "Nonce length ($length$) too long. Max is $max$." 80 | auth | 13015 | "Empty signature header." 81 | auth | 13016 | "Key is expired." 82 | **account management** | ------- | ------- 83 | accounts | 14000 | "User name cannot be empty." 84 | accounts | 14001 | "Deletion of key/secret succeeded." 85 | accounts | 14002 | "Deletion of key/secret failed." 86 | accounts | 14003 | "Method $method$ is unknown." 87 | **account (user management) management** | ------- | ------- 88 | account | 50000 | "Account not found." 89 | account | 50001 | "No permission to update this account." 90 | account | 50002 | "There is already an account with this name. Choose another name." 91 | account | 50003 | "Invalid name." 92 | **cloud AP's** | ------- | ------- 93 | cloud | 16000 | "Id invalid or pairing disabled." 94 | cloud | 16001 | "Mis-match of mac addresses: mac: 1. $auth_mac$ 2. $primary_mac$ 3. $node_mac$." 95 | cloud | 16002 | "No old settings." 96 | cloud | 16003 | "Key mac ($auth_mac$) doesn't match AP mac ($node_mac$)." 97 | cloud | 16004 | "Voucher string length ($len$) is too long. Max is $max$." 98 | cloud | 16005 | "Failure getting access point data." 99 | cloud | 16006 | "Failure getting health care data." 100 | cloud | 16007 | "Failure storing remote log." 101 | **network group management** | ------- | ------- 102 | network group | 40000 | "Cannot move network. No networkgroup destination specified." 103 | network group | 40001 | "Cannot move network. There are still user-permissions for this network." 104 | network group | 40002 | "Cannot move network. Network with given id does not exist." 105 | network group | 40003 | "Network group not found." 106 | network group | 40004 | "Cannot remove networkgroup. There are still networks in this group. First remove or move the networks." 107 | network group | 40005 | "In this account there is already a networkgroup with this name. Choose another name." 108 | network group | 40006 | "Not permitted by networkgroup id" 109 | network group | 40007 | "Must have Network Admin privileges" 110 | network group | 40008 | "Invalid networkgroup name length." 111 | **node management** | ------- | ------- 112 | nodes | 17000 | "Mac ($mac$) already exists in your network." 113 | nodes | 17001 | "Mac ($mac$) already exists in another of your networks, but you have not set 'confirm_transfer' true." 114 | nodes | 17002 | "Mac ($mac$) already exists in another network." 115 | nodes | 17003 | "Could not delete access point (id $id$)." 116 | nodes | 17004 | "Could not reboot access point (id $id$)." 117 | nodes | 17005 | "Could not reset encrypt key access point (id $id$)." 118 | nodes | 17006 | "Could not enable pairing for access point (id $id$)." 119 | nodes | 17007 | "Could not expedite upgrade for access point (id $id$)." 120 | **permissions** | ------- | ------- 121 | permissions | 30000 | "The needed user-role is not defined." 122 | permissions | 30001 | "No permissions for this request." 123 | permissions | 30002 | "No permissions to access this resource." 124 | **user management** | ------- | ------- 125 | users | 20000 | "Email already exists." 126 | users | 20001 | "Could not verify user." 127 | users | 20002 | "User is not verified." 128 | users | 20003 | "Wrong password." 129 | users | 20004 | "User not found." 130 | users | 20005 | "No permission for this component and method." 131 | users | 20006 | "Only one parameter of role or custom_permission is allowed in one request." 132 | users | 20007 | "Password size must be between 8 and 50 characters." 133 | users | 20008 | "Wrong verification error." 134 | users | 20009 | "User is already verified." 135 | users | 20010 | "E-Mail is not in a valid format." 136 | users | 20011 | "Something went wrong with generating error/secret." 137 | users | 20012 | "Cannot create user with the given error." 138 | users | 20013 | "Wrong resource-id for user." 139 | users | 20014 | "cannot create and add user to account. The role of the new user must be specified." 140 | users | 20015 | "cannot create and add user to account. Not permitted." 141 | users | 20016 | "cannot add user with given user-role to networkgroup. Not permitted." 142 | users | 20017 | "Cannot add user to networkgroup. No Role specified." 143 | users | 20018 | "Cannot grant user-permission for network. No Role specified." 144 | users | 20019 | "Cannot revoke user-permission from networkgroup. No permission found for user-id and networkgroup-id." 145 | users | 20020 | "cannot revoke user-permission from networkgroup. Not permitted." 146 | users | 20021 | "Cannot revoke user-permission from network. No permission found for user-id and network-id." 147 | users | 20022 | "cannot revoke user-permission from network. Not permitted." 148 | users | 20023 | "Cannot delete user. Not permitted." 149 | users | 20024 | "Cannot delete yourself." 150 | users | 20025 | "Cannot delete user. User has permissions to more networks." 151 | users | 20026 | "Cannot retrieve user-data. Not permitted" 152 | users | 20027 | "Cannot add user to networkgroup. User is not in same account." 153 | users | 20028 | "Cannot add user to network. User is not in same account." 154 | users | 20029 | "Not permitted by user ID." 155 | users | 20030 | "Password required." 156 | users | 20031 | "Password updates are only permitted by the user." 157 | users | 20032 | "Token required." 158 | users | 20033 | "Too many attempts. Please contact support." 159 | users | 20034 | "User disabled." 160 | users | 20035 | "Cannot delete the account creator." 161 | users | 20036 | "User name is required." 162 | users | 20037 | "Email is required." 163 | users | 20038 | "User is not in the same account." 164 | users | 20039 | "User must be an account admin." 165 | users | 20040 | "User must be the account owner." 166 | users | 20041 | "User has reached maximum login attempts. Reset password." 167 | users | 20042 | "API users must be created via an Account Owner or Account Admin, not an Application API key." 168 | users | 20043 | "API key does not belong to the specified user." 169 | users | 20044 | "User has too many API keys." 170 | users | 20045 | "API users cannot have passwords." 171 | users | 20047 | "User must be able to login before being assigned as the owner." 172 | -------------------------------------------------------------------------------- /api/history.md: -------------------------------------------------------------------------------- 1 | # History endpoints 2 | 3 | These endpoints aggregate traffic-based statistical data for a specified network over a given time period. This provides a historical overview of traffic changes over time. These statistics and time-sliced data can be viewed from several different perspectives: 4 | 5 | * for all ssid's in the network 6 | * by node (either singly or per all) 7 | * by client (either singly or per all) 8 | 9 | functionality | method | endpoint 10 | --- | --- | --- 11 | [retrieve historical traffic statistics for all ssid's](#network) | GET | `/history/network/` 12 | [retrieve historical traffic statistics for all clients](#clients) | GET | `/history/network//clients` 13 | [retrieve historical traffic statistics for a single client](#client) | GET | `/history/network//client/` 14 | [retrieve historical traffic statistics for all nodes](#nodes) | GET |` /history/network//nodes` 15 | [retrieve historical traffic statistics for a single node](#node) | GET | `/history/network//node/` 16 | 17 | #### Reporting time span and sampling frequency 18 | Each of the calls allows an optional query-string argument, `period=`, to specify the overall time span during which statistics are to be gathered. Allowable values are: `2hours`, `day`, `week`, and `month`. If the query-string is omitted, `day` is assumed. 19 | 20 | The number of samples gathered per reporting period and the per-each-sample time span depend on the reporting period, as follows: 21 | 22 | 23 | ##### samples and intervals per reporting period 24 | reporting period | number of samples | sample span 25 | ----- | ----- | ----- 26 | `2hours` | 24 | 5 minutes 27 | `day` | 288 | 5 minutes 28 | `week` | 288 | 35 minutes 29 | `month` | 288 | 155 minutes 30 | 31 | 32 | 33 | ### network 34 | `GET /history/network/?period=` 35 | 36 | This endpoint provides an aggregation of traffic statistics by ssid for all ssid's in the specified network over the given time period. 37 | 38 | 39 | ##### query-string arguments 40 | argument | allowable values | required | note 41 | ---- | ---- | ---- | ----- 42 | `period` | `2hours`, `day`, `week`, `month` | optional | default is `day` 43 | 44 | ##### example request 45 | `GET /history/network/12554?period=2hours` 46 | 47 | ##### example output 48 | Output for the above query. For this endpoint, the sample array is partitioned into "rows" of smaller "time slices" (only two out of 24 being shown here). Each row is a snapshot of both upload and download data sampled over the time span of the sample. 49 | 50 | The "clients" object contains client counts for each SSID. The "all" field contains the total unique clients across the entire access point. 51 | 52 | In this and the other API calls below, "bup" and "bdown" stand for bytes up and bytes down, respectively (total bytes uploaded and downloaded during the time slice), while "rup" and "rdown" stand for rate up and rate down, respectively (ie, upload and download throughput during the same time span). 53 | 54 | 55 | ````json 56 | { 57 | "clients": { 58 | "ssid1": 5, 59 | "ssid2": 0 60 | }, 61 | "rows": [ 62 | [ 63 | "2013-11-24T13:50:00Z", 64 | { 65 | "ssid1": { 66 | "traffic": { 67 | "udp": { 68 | "bup": 278, 69 | "bdown": 193, 70 | "rup": 278, 71 | "rdown": 193 72 | }, 73 | "tcp": { 74 | "bup": 278, 75 | "bdown": 193, 76 | "rup": 278, 77 | "rdown": 193 78 | } 79 | }, 80 | "users": 1 81 | }, 82 | "ssid2": { 83 | "traffic": { 84 | "netbios": { 85 | "bup": 1872, 86 | "bdown": 0, 87 | "rup": 1872, 88 | "rdown": 0 89 | } 90 | }, 91 | "users": 1 92 | } 93 | } 94 | ], 95 | [ 96 | "2013-11-24T13:55:00Z", 97 | { 98 | "ssid1": { 99 | "traffic": { 100 | "udp": { 101 | "bup": 278, 102 | "bdown": 193, 103 | "rup": 278, 104 | "rdown": 193 105 | }, 106 | "tcp": { 107 | "bup": 278, 108 | "bdown": 193, 109 | "rup": 278, 110 | "rdown": 193 111 | }, 112 | "users": 1 113 | } 114 | } 115 | } 116 | ] 117 | ] 118 | } 119 | ```` 120 | 121 | 122 | ### clients 123 | `GET /history/network//clients?period=` 124 | 125 | This endpoint returns a list of all client devices connected to the specified network, with traffic statistics aggregated over the selected time span. 126 | 127 | ##### query-string arguments 128 | argument | allowable values | required | note 129 | ---- | ---- | ---- | ----- 130 | `period` | `2hours`, `day`, `week`, `month` | optional | default is `day` 131 | 132 | ##### example request 133 | `GET https://api.cloudtrax.com/history/network/12345/clients?period=week` 134 | 135 | ##### example output 136 | ````json 137 | { 138 | "clients": [ 139 | { 140 | "1c:99:4c:7b:ae:fe": { 141 | "name": "android-77b812edd3097a81", 142 | "name_override": "test overwrite", 143 | "cid": "31792", 144 | "signal": { 145 | "antenna1": -75, 146 | "antenna2": -76 147 | }, 148 | "mcs": { 149 | "tx": 3 150 | }, 151 | "bitrate": { 152 | "rx": 6, 153 | "tx": 60 154 | }, 155 | "channel_width": "40", 156 | "band": "5.8", 157 | "last_seen": "2014-02-10T20:20:17Z", 158 | "last_name": "Garage OM5P", 159 | "last_node": "ac:86:74:00:0b:f0", 160 | "os": "android", 161 | "os_version": "", 162 | "blocked": 0, 163 | "link": "unknown", 164 | "traffic": { 165 | "ssid1": { 166 | "bup": 40, 167 | "bdown": 50 168 | }, 169 | "ssid2": { 170 | "bup": 28278, 171 | "bdown": 90538 172 | } 173 | } 174 | } 175 | }, 176 | { 177 | "00:23:15:b3:dc:94": { 178 | "name": "Owner-PC", 179 | "cid": "49671", 180 | "signal": { 181 | "antenna1": -75, 182 | "antenna2": -76 183 | }, 184 | "mcs": { 185 | "tx": 3 186 | }, 187 | "bitrate": { 188 | "rx": 6, 189 | "tx": 60 190 | }, 191 | "channel_width": "40", 192 | "band": "5.8", 193 | "last_seen": "2014-02-10T20:20:17Z", 194 | "last_name": "Garage OM5P", 195 | "last_node": "ac:86:74:00:0b:f0", 196 | "os": "android", 197 | "os_version": "", 198 | "blocked": 1, 199 | "link": "wired", 200 | "traffic": { 201 | "ssid1": { 202 | "bup": 40, 203 | "bdown": 50 204 | }, 205 | "ssid2": { 206 | "bup": 28278, 207 | "bdown": 90538 208 | } 209 | } 210 | } 211 | } 212 | ] 213 | } 214 | ```` 215 | 216 | 217 | ### client 218 | `GET /history/network//client/?period=` 219 | 220 | This endpoint returns traffic statistics for the specified client device on the specified network aggregated over the selected time span. 221 | 222 | ##### query-string arguments 223 | argument | allowable values | required | note 224 | ---- | ---- | ---- | ----- 225 | `period` | `2hours`, `day`, `week`, `month` | optional | default is `day` 226 | 227 | 228 | ##### example output 229 | 230 | ````json 231 | { 232 | "cid": "543", 233 | "traffic": [ 234 | { 235 | "time": "2014-02-10T09:14:25Z", 236 | "ssid1": { 237 | "rup": 40, 238 | "rdown": 50 239 | }, 240 | "ssid2": { 241 | "rup": 28278, 242 | "rdown": 90538 243 | } 244 | }, 245 | { 246 | "time": "2014-02-10T09:19:25Z", 247 | "ssid1": { 248 | "rup": 30, 249 | "rdown": 10 250 | } 251 | } 252 | ] 253 | } 254 | ```` 255 | 256 | ### nodes 257 | `GET /history/network//nodes?period=` 258 | 259 | This endpoint returns a list of all nodes connected to the specified network, with the traffic statistics for each node aggregated over the selected time span. 260 | 261 | ##### query-string arguments 262 | argument | allowable values | required | note 263 | ---- | ---- | ---- | ----- 264 | `period` | `2hours`, `day`, `week`, `month` | optional | default is `day` 265 | 266 | ##### example request 267 | `https://api.cloudtrax.com/history/network/12345/nodes?period=2hours` 268 | 269 | ##### example output 270 | 271 | This sample output shows two time slices out of 24 for the above example request. The JSON consists of an array of nodes, with each node element displaying data on the upload and download traffic volume for each ssid on the node, as well as time-sliced arrays containing "metrics" and "checkins" data for each node. The "metrics" part will not be reported if the node is a gateway. Specifically, 272 | 273 | * "traffic": reports bytes uploaded and downloaded per ssid, aggregated over the preceding 24 hours. 274 | * "metrics": measure how fast traffic over the mesh was moving at the timestamped time, sampled over a 5-minute time span. These are aggregated over the last hour. The unit is bytes/s. 275 | * "checkins": timestamped instances of the nodes' attempts to report traffic statistics and other state back to the CloudTrax servers and to obtain updated configurations from them. Status can be "repeater", "gateway" and "pairing". An outage occured if no status is reported. 276 | 277 | 278 | ````json 279 | { 280 | "nodes": [{ 281 | "31792": { 282 | "traffic": { 283 | "ssid1": { 284 | "bup": 40, 285 | "bdown": 50, 286 | "users": 1 287 | }, 288 | "ssid2": { 289 | "bup": 28278, 290 | "bdown": 90538, 291 | "users": 7 292 | } 293 | }, 294 | "metrics": [{ 295 | "time": "2014-07-29T11:20:00Z", 296 | "speed": 11476 297 | }, { 298 | "time": "2014-07-29T11:25:00Z", 299 | "speed": 11786 300 | }], 301 | "checkins": [{ 302 | "time": "2014-07-28T12:20:00Z", 303 | "status": "repeater" 304 | }, { 305 | "time": "2014-07-28T12:25:00Z", 306 | "status": "gateway" 307 | },{ 308 | "time": "2014-07-28T12:20:00Z" 309 | }] 310 | } 311 | }] 312 | } 313 | ```` 314 | 315 | 316 | ### node 317 | `GET /history/network//node/?period=` 318 | 319 | This endpoint returns traffic statistics for the specified node aggregated every 5 minutes over the selected time span. 320 | 321 | ##### query-string arguments 322 | argument | allowable values | required | note 323 | ---- | ---- | ---- | ----- 324 | `period` | `2hours`, `day`, `week`, `month` | optional | default is `day` 325 | 326 | ##### example request 327 | `https://api.cloudtrax.com/history/network/12345/node/295?period=week` 328 | 329 | ##### example output 330 | 331 | This JSON sample shows the array of time-sliced traffics statistics (upload and download speeds) for the node with id ("nid") 295. This example shows only a single sample. 332 | 333 | ````json 334 | { 335 | "nid": "295", 336 | "traffic": [ 337 | { 338 | "time": "2014-02-10T09:14:25Z", 339 | "ssid1": { 340 | "rup": 40, 341 | "rdown": 50 342 | }, 343 | "ssid2": { 344 | "rup": 28278, 345 | "rdown": 90538 346 | } 347 | }, 348 | { 349 | "time": "2014-02-10T09:19:25Z", 350 | "ssid1": { 351 | "rup": 30, 352 | "rdown": 10 353 | } 354 | } 355 | ] 356 | } 357 | ```` 358 | -------------------------------------------------------------------------------- /api/network_management.md: -------------------------------------------------------------------------------- 1 | # Network Management endpoints 2 | 3 | This API component provides a large number of endpoints for creating, listing, and deleting networks, as well as viewing and editing network settings, checking for network existence, determining whether upgrades are necessary, and uploading and deleting splash pages. 4 | 5 | functionality | method | endpoint 6 | --- | --- | --- 7 | [create network](#create-network) | POST | `/network` 8 | [delete network](#delete-network) | DELETE | `/network/` 9 | [search networks by name](#search-network) | GET | `/network/search?by_name=` 10 | [list networks](#list-networks) | GET | `/network/list` 11 | [get allowed channels](#get-allowed-channels) | GET | `/network//allowed_channels` 12 | [get network settings](#get-settings) | GET | `/network//settings` 13 | [set network settings](#set-settings) | PUT |`/network//settings` 14 | [upload splash pages](#upload-splash) | POST | `/network//file` 15 | [delete splash pages](#delete-splash) | DELETE | `/network//file/` 16 | 17 | 18 | ### create network 19 | `POST /network` 20 | 21 | Create a new network and associate it with the user sending the `create` request. 22 | 23 | Characteristics of the new network are defined by the JSON package that comprises the body of the HTTP request. At a minimum, the following four fields are required. The password must be at least 8 characters long: 24 | 25 | * `name` 26 | * `password` 27 | * `timezone` 28 | * `country` 29 | 30 | ##### example request 31 | 32 | ```` 33 | POST https://api.cloudtrax.com/network 34 | POST https://api-v2.cloudtrax.com/network/networkgroup/ 35 | ```` 36 | 37 | ##### input example 38 | ```` json 39 | { 40 | "name" : "my-test-network-#2", 41 | "password" : "some_password", 42 | "email" : "support@open-mesh.com", 43 | "location" : "Portland", 44 | "timezone" : "America/Los_Angeles", 45 | "country_code" : "US" 46 | } 47 | ```` 48 | 49 | ##### output 50 | 51 | The API either returns HTTP status code 200 (success) or an HTTP error and JSON describing the error. On success, the API returns the id of the newly created network. 52 | 53 | ##### example output 54 | ```` json 55 | { 56 | "id" : 123456 57 | } 58 | ```` 59 | 60 | 61 | ### delete network 62 | Delete a network. 63 | 64 | ```` 65 | DELETE /network/ 66 | ```` 67 | 68 | ##### example request 69 | 70 | ```` 71 | DELETE https://api.cloudtrax.com/network/12345 72 | ```` 73 | ##### example output 74 | 75 | Note the use of error code 1009 to indicate success. 76 | 77 | ````json 78 | { 79 | "code": 1009, 80 | "message": "Success.", 81 | "context": "delete_network", 82 | "values": { 83 | 84 | } 85 | } 86 | ```` 87 | 88 | 89 | ### search networks by name 90 | ```` 91 | GET /network/search?by_name= 92 | ```` 93 | 94 | ##### example request 95 | 96 | ```` 97 | GET https://api.cloudtrax.com/network/search?by_name=%25unittest%25 98 | ```` 99 | 100 | ##### output 101 | 102 | The output returns an array of networks, with each network identified by name and id. 103 | 104 | ##### example output 105 | 106 | ```` json 107 | { 108 | "networks": [ 109 | { 110 | "name": "unittest1", 111 | "id": 114338 112 | }, 113 | { 114 | "name": "unittest2", 115 | "id": 114339 116 | }, 117 | { 118 | "name": "unittest3", 119 | "id": 17275 120 | } ] 121 | } 122 | ```` 123 | 124 | 125 | ### list networks 126 | ```` 127 | GET /network/list 128 | ```` 129 | 130 | ##### example request 131 | 132 | ```` 133 | GET https://api.cloudtrax.com/network/list 134 | GET https://api-v2.cloudtrax.com/network/list 135 | ```` 136 | 137 | ##### output 138 | 139 | The API returns the list of network name/id pairs, or an empty list if no network is associated with the user sending the request. 140 | 141 | ##### example output 142 | 143 | ```` json 144 | { 145 | "networks": [ 146 | { 147 | "name": "hk_network", 148 | "id": 135587, 149 | "network_id": 135587, /* api-v2. new property name. same as "id" */ 150 | "networkgroup_id": 1, /* api-v2 */ 151 | "networkgroup_name": "Network Group #1", /* api-v2 */ 152 | "role_id": "Network Group #1", /* api-v2. role ID for the requester */ 153 | "latitude": 49.44026050000000083, 154 | "longitude": -123.6724020000000053, 155 | "down_repeater": 1, 156 | "down_gateway": 0, 157 | "spare_nodes": 0, 158 | "node_count": 2 159 | } 160 | ] 161 | } 162 | ```` 163 | 164 | 165 | ### get allowed channels 166 | `GET /network//allowed_channels` 167 | 168 | Returns all allowed channels for a network for a particular country, defaulting to the country code for the network that's stored in the database. You can specify a different country with the query-string "country" parameter, using one of the two-letter country codes specified by [ISO 3166](http://www.iso.org/iso/home/standards/country_codes.htm). 169 | 170 | You can also query this information on a per-node basis; see [Node Management API](./node_management.md#get-allowed-channels-for-node). 171 | 172 | ##### example request 173 | 174 | ```` 175 | GET https://api.cloudtrax.com/network/135587/allowed_channels 176 | ```` 177 | ##### example output 178 | 179 | ```` json 180 | { 181 | "channels": { 182 | "2_4GHz": [ 183 | { 184 | "channel": 10, 185 | "ht_modes": [ 186 | "HT40-", "HT20" 187 | ] 188 | }, 189 | { 190 | "channel": 9, 191 | "ht_modes": [ 192 | "HT40-", "HT20" 193 | ] 194 | }, 195 | { 196 | "channel": 8, 197 | "ht_modes": [ 198 | "HT40-", "HT20" 199 | ] 200 | }, 201 | { 202 | "channel": 7, 203 | "ht_modes": [ 204 | "HT40+", "HT40-", "HT20" 205 | ] 206 | }, 207 | { 208 | "channel": 6, 209 | "ht_modes": [ 210 | "HT40+", "HT40-", "HT20" 211 | ] 212 | }, 213 | { 214 | "channel": 5, 215 | "ht_modes": [ 216 | "HT40+", "HT40-", "HT20" 217 | ] 218 | }, 219 | { 220 | "channel": 4, 221 | "ht_modes": [ 222 | "HT40+", "HT20" 223 | ] 224 | }, 225 | { 226 | "channel": 3, 227 | "ht_modes": [ 228 | "HT40+", "HT20" 229 | ] 230 | }, 231 | { 232 | "channel": 11, 233 | "ht_modes": [ 234 | "HT40-", "HT20" 235 | ] 236 | }, 237 | { 238 | "channel": 2, 239 | "ht_modes": [ 240 | "HT40+", "HT20" 241 | ] 242 | }, 243 | { 244 | "channel": 1, 245 | "ht_modes": [ 246 | "HT40+", "HT20" 247 | ] 248 | } 249 | ], 250 | "5GHz": [ 251 | { 252 | "channel": 161, 253 | "ht_modes": [ 254 | "HT40-", "HT20" 255 | ] 256 | }, 257 | { 258 | "channel": 153, 259 | "ht_modes": [ 260 | "HT40-", "HT20" 261 | ] 262 | }, 263 | { 264 | "channel": 149, 265 | "ht_modes": [ 266 | "HT40+", "HT20" 267 | ] 268 | }, 269 | { 270 | "channel": 48, 271 | "ht_modes": [ 272 | "HT40-", "HT20" 273 | ] 274 | }, 275 | { 276 | "channel": 46, 277 | "ht_modes": [ 278 | "HT20" 279 | ] 280 | }, 281 | { 282 | "channel": 165, 283 | "ht_modes": [ 284 | "HT20" 285 | ] 286 | }, 287 | { 288 | "channel": 42, 289 | "ht_modes": [ 290 | "HT20" 291 | ] 292 | }, 293 | { 294 | "channel": 157, 295 | "ht_modes": [ 296 | "HT40+", "HT20" 297 | ] 298 | }, 299 | { 300 | "channel": 40, 301 | "ht_modes": [ 302 | "HT40-", "HT20" 303 | ] 304 | }, 305 | { 306 | "channel": 38, 307 | "ht_modes": [ 308 | "HT20" 309 | ] 310 | }, 311 | { 312 | "channel": 44, 313 | "ht_modes": [ 314 | "HT40+", "HT20" 315 | ] 316 | }, 317 | { 318 | "channel": 36, 319 | "ht_modes": [ 320 | "HT40+", "HT20" 321 | ] 322 | } 323 | ] 324 | } 325 | } 326 | ```` 327 | 328 | 329 | ### get network settings 330 | `GET /network//settings` 331 | 332 | This API is documented in [Network Settings endpoints](network_settings.md#get-settings). 333 | 334 | 335 | ### set network settings 336 | `PUT /network//settings` 337 | 338 | This API is documented in [Network Settings endpoints](network_settings.md#set-settings). 339 | 340 | 341 | ### upload splash pages 342 | `POST /network//file` 343 | 344 | Used by CloudTrax to move internally-hosted splash pages (HTML and related files, including image assets) up to the Amazon S3 instance. 345 | 346 | ##### example request 347 | ```` 348 | POST https://api.cloudtrax.com/network/12345/file 349 | ```` 350 | 351 | ##### input 352 | Details of the file to be moved, base64encoded. 353 | 354 | ##### example input 355 | ```` json 356 | "ssid_id":1, 357 | "name":"logo1.gif", 358 | "role":"image", 359 | "file":"R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=" 360 | ```` 361 | 362 | ##### output 363 | The API either returns `200 Success` or an HTTP error and JSON describing the error on failure. 364 | On success, the api returns the file-id and the S3 location if the transfer was successful. 365 | 366 | ##### example output 367 | ```` json 368 | { 369 | "file_id": 168, 370 | "url": "https://s3.amazonaws.com/my-cloudtrax-files/ct4/133314/splashpage/1/image/logo1.gif" 371 | } 372 | ```` 373 | 374 | 375 | ### delete splash pages 376 | `DELETE /network//file/` 377 | 378 | Delete internal splash pages from the Amazon S3 instance. 379 | 380 | ##### example request 381 | 382 | ```` 383 | DELETE https://api.cloudtrax.com/network/12345/file/102 384 | ```` 385 | 386 | 387 | The API either returns `200 Success` or an HTTP error and JSON describing the error on failure. 388 | On success, the api returns the file-id and the S3 location if the transfer was successful. 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | -------------------------------------------------------------------------------- /api/networkgroup_management.md: -------------------------------------------------------------------------------- 1 | # Network Group Management endpoints 2 | 3 | This set of endpoints is for managing network groups in the new CloudTrax user system. 4 | 5 | functionality | method | endpoint 6 | --- | --- | --- 7 | [Create a network group](#post-networkgroup) | POST | `/networkgroup` 8 | [Get a network group](#get-networkgroup-id) | GET | `/networkgroup/` 9 | [List network groups](#get-networkgroup-list) | GET | `/networkgroup/list` 10 | [Edit a network group](#put-networkgroup) | PUT | `/networkgroup/` 11 | [Delete a network group](#delete-networkgroup-id) | DELETE | `/networkgroup/` 12 | [Edit the network group for a network](#put-networkgroup-id-network-id) | PUT | `/networkgroup//network/` 13 | 14 | 15 | ### Create a network group 16 | `POST /networkgroup` 17 | 18 | Create a network group. 19 | 20 | ##### Account Permissions 21 | Role | Permitted 22 | -|- 23 | Account Admin | X 24 | Group Manager | X 25 | Network User | 26 | 27 | ##### Resource Permissions 28 | N/A 29 | 30 | ##### Example request 31 | 32 | ```` json 33 | { 34 | "name":"New Network Group" 35 | } 36 | ```` 37 | 38 | ##### Example response 39 | 40 | ```` json 41 | { 42 | "networkgroup_id":1 43 | } 44 | ```` 45 | 46 | #### Error codes 47 | 48 | 30002 49 | 40008 50 | 51 | 52 | ### Get a network group 53 | `GET /networkgroup/` 54 | 55 | Get more information about the specified network group. Includes a list of all networks within the specified network group. 56 | 57 | ##### Account Permissions 58 | Role | Permitted 59 | -|- 60 | Account Admin | X 61 | Group Manager | X 62 | Network User | 63 | 64 | ##### Resource Permissions 65 | Role | Permitted 66 | -|- 67 | Network Editor | X 68 | Network Viewer | 69 | Voucher Editor | 70 | 71 | ##### Example request 72 | 73 | ```` 74 | GET https://api-v2.cloudtrax.com/networkgroup/123 75 | ```` 76 | 77 | ##### Example response 78 | 79 | ```` json 80 | { 81 | "networkgroup_id":123, 82 | "creator_user_id":1, 83 | "name":"Network Group #1", 84 | "account_id":4, 85 | "networks":[ 86 | { 87 | "network_id":98, 88 | "name":"Some Network" 89 | } 90 | ] 91 | } 92 | ```` 93 | 94 | #### Error codes 95 | 96 | 30002 97 | 40003 98 | 40006 99 | 100 | 101 | ### List network groups 102 | `GET /networkgroup/list` 103 | 104 | Get a list of network groups accessible to the requester. 105 | 106 | ##### Account Permissions 107 | Role | Permitted 108 | -|- 109 | Account Admin | X 110 | Group Manager | X 111 | Network User | X 112 | 113 | ##### Resource Permissions 114 | N/A 115 | 116 | ##### Example request 117 | 118 | ```` 119 | GET https://api-v2.cloudtrax.com/networkgroup/list 120 | ```` 121 | 122 | ##### Example response 123 | 124 | ```` json 125 | { 126 | "networkgroups":[ 127 | { 128 | "networkgroup_id":1, 129 | "name":"Network Group #1", 130 | "role_id":3 /* the role ID the requester has on the network group */ 131 | ] 132 | } 133 | ```` 134 | 135 | #### Error codes 136 | N/A 137 | 138 | 139 | ### Edit a network group 140 | `PUT /networkgroup/` 141 | 142 | Edit a network group. 143 | 144 | ##### Account Permissions 145 | Role | Permitted 146 | -|- 147 | Account Admin | X 148 | Group Manager | X 149 | Network User | 150 | 151 | ##### Resource Permissions 152 | Role | Permitted 153 | -|- 154 | Network Editor | X 155 | Network Viewer | 156 | Voucher Editor | 157 | 158 | ##### Example request 159 | 160 | ```` 161 | PUT https://api-v2.cloudtrax.com/networkgroup/123 162 | ```` 163 | 164 | ```` json 165 | { 166 | "name":"New Network Group Name" 167 | } 168 | ```` 169 | 170 | ##### Example response 171 | 172 | ```` json 173 | { 174 | } 175 | ```` 176 | 177 | #### Error codes 178 | 179 | 30002 180 | 40003 181 | 40006 182 | 40008 183 | 184 | 185 | ### Delete a network group 186 | `DELETE /networkgroup/` 187 | 188 | Delete a network group. All networks within the network group must first be removed. 189 | 190 | ##### Account Permissions 191 | Role | Permitted 192 | -|- 193 | Account Admin | X 194 | Group Manager | X 195 | Network User | 196 | 197 | ##### Resource Permissions 198 | Role | Permitted 199 | -|- 200 | Network Editor | X 201 | Network Viewer | 202 | Voucher Editor | 203 | 204 | ##### Example request 205 | 206 | ```` 207 | DELETE https://api-v2.cloudtrax.com/networkgroup/123 208 | ```` 209 | 210 | ##### Example response 211 | 212 | ```` json 213 | { 214 | } 215 | ```` 216 | 217 | #### Error codes 218 | 219 | 30002 220 | 40006 221 | 40004 222 | 223 | 224 | ### Edit the network group for a network 225 | `POST /networkgroup//network/` 226 | 227 | Edit the network group for a network. 228 | 229 | Note that the requester must have Network Editor permissions for all associated resources (network, current network group, new network group). 230 | 231 | ##### Account Permissions 232 | Role | Permitted 233 | -|- 234 | Account Admin | X 235 | Group Manager | X 236 | Network User | X 237 | 238 | ##### Resource Permissions 239 | Role | Permitted 240 | -|- 241 | Network Editor | X 242 | Network Viewer | 243 | Voucher Editor | 244 | 245 | ##### Example request 246 | 247 | ```` 248 | PUT https://api-v2.cloudtrax.com/networkgroup/123/network/456 249 | ```` 250 | 251 | ##### Example response 252 | 253 | ```` json 254 | { 255 | } 256 | ```` 257 | 258 | #### Error codes 259 | 260 | 12000 261 | 12047 262 | 30002 263 | 40006 264 | -------------------------------------------------------------------------------- /api/node_management.md: -------------------------------------------------------------------------------- 1 | # Node Management endpoints 2 | 3 | Create, list, update, delete, and test for characteristics of nodes. 4 | 5 | functionality | method | endpoint 6 | --- | --- | --- 7 | [list nodes](#list-nodes) | GET | `/node/network//list` 8 | [create node](#create-node) | POST | `/node/network/` 9 | [update node](#update-node) | PUT | `/node/` 10 | [get node](#get-node) | GET | `/node/` 11 | [delete node](#delete-node) | DELETE | `/node/` 12 | [reboot node](#reboot-node) | GET | `/node//reboot` 13 | [get allowed channels for node](#get-allowed-channels-for-node) | GET | `/node//allowed-channels` 14 | [reset encryption key for node](#reset-encryption-key-for-node) | GET | `/node//reset_encrypt_key` 15 | [enable pairing for node](#enable-pairing-for-node) | GET | `/node//enable_pairing` 16 | [expedite upgrade for node](#expedite-upgrade-for-node) | GET | `/node//expedite_upgrade` 17 | [does an AP with address MAC already exist?](#does-ap-mac-already-exist) | GET | `/node/does_mac_exist?mac=` 18 | 19 | 20 | 21 | ### list nodes 22 | 23 | `GET /node/network//list` 24 | 25 | Retrieve all nodes belonging to the given network. The returned JSON is a dictionary, where each key is a node id, and detailed information for the node is contained in its corresponding value object. That information will differ slightly, depending on whether the node is a gateway (an Access Point connected directly to the Internet) or a repeater. 26 | 27 | ##### Lifecycle 28 | Lifecycle information about devices can be found when retrieving a device or a list of devices. We currently return this information for devices that can be added to Datto Network Manager. Learn more at [Datto Networking: End of Life policy](https://networkinghelp.datto.com/help/Content/kb/Networking/General%20Information/KB370000000038.htm). 29 | 30 | The lifecycle property has the following possible values: 31 | 32 | - **null**: Indicates that the device model does not have end-of-sale and end-of-life dates. 33 | - **Object with null values**: Indicates that the device model is ambiguous and the customer needs to visit the Datto Networking Article to determine the end-of-sale and/or end-of-life dates. 34 | - **Object with Date values**: Indicates the device model's end-of-sale and/or end-of-life dates, where the date is a string type in RFC 3339 format. Available properties: *end_of_life*, *end_of_sale*. 35 | 36 | ##### Subscription 37 | 38 | Subscription information is provided when retrieving a list of devices or information for a specific device (except of summary). 39 | 40 | The `subscription` property has the following possible values: 41 | 42 | - **null**: Indicates that the device does not have any subscription information. 43 | 44 | - **Object** 45 | In case when an object is returned, it always contains the following fields: 46 | 47 | Field name | Type | Can be null? | Description 48 | --- | --- | --- | --- 49 | is_active | boolean | No | Subscription status for a particular device 50 | start_date | string | No | Start date of subscription, Format: Y-m-d\TH:i:s\Z, e.g. “2023-05-05T00:00:00Z” 51 | end_date | string | Yes | End date of subscription. Format: Y-m-d\TH:i:s\Z, e.g. “2026-05-05T00:00:00Z” 52 | term | string | No | Indicates the term of renewal of subscription. Can be either “monthly” for evergreen subscriptions or “yearly” for fixed term subscriptions 53 | term_length | integer | No | Indicates the number of months the devices has subscription for. E.g. 36 for a fixed term subscription or 1 (one) for an evergreen subscription. 54 | is_evergreen | boolean | No | Indicates if the subscription if evergreen, or in other words, never-ending, renewing every month and not having an end date 55 | 56 | ##### example request 57 | `GET https://api.cloudtrax.com/node/network/12345/list` 58 | 59 | ##### example output 60 | 61 | The example shows a two-node network with nodes 529813 and 525849. The former is a repeater, while the latter is a gateway. 62 | 63 | ````json 64 | { 65 | "nodes": { 66 | "529813": { 67 | "mac": "ac:86:74:3b:7a:82", 68 | "name": "Front office", 69 | "ip": "6.59.122.128", 70 | "description": "deleted & re-added", 71 | "role": "repeater", 72 | "firmware_version": "6.0.0", 73 | "firmware_version_full": 60000005, 74 | "firmware_version_id": "56b1193bbbb0dd6ce3db169349d00cc412e771fc", 75 | "firmware_version_release": "beta 5", 76 | "custom_sh_errors": [ 77 | "error1" 78 | ], 79 | "custom_sh_names": [ 80 | "ng6-0-wifi-upgrade" 81 | ], 82 | "mesh_version": "batman-adv", 83 | "last_checkin": "2015-05-19T16:20:59Z", 84 | "uptime": "2h 22m", 85 | "hardware": "OM5P_AN", 86 | "memfree": 34840, 87 | "load": 0.1000000000000000056, 88 | "spare": false, 89 | "flags": "0x0000", 90 | "latitude": 49.53025200000000098, 91 | "longitude": -123.0024159999999984, 92 | "down": true, 93 | "lifecycle": null, 94 | "subscription": { 95 | "is_active": false, 96 | "start_date": "2021-01-01T00:00:00Z", 97 | "end_date": null, 98 | "term": "monthly", 99 | "term_length": 1, 100 | "is_evergreen": true 101 | }, 102 | "selected_gateway": { 103 | "name": "Back office", 104 | "ip": "6.59.122.192", 105 | "mac": "ac:86:74:3b:7a:c0", 106 | "hops": 1, 107 | "latency": 0.9699999999999999734 108 | }, 109 | "channels": { 110 | "2_4GHz": 5, 111 | "5GHz": 44 112 | }, 113 | "ht_modes": { 114 | "2_4GHz": "HT20", 115 | "5GHz": "HT40+" 116 | }, 117 | "neighbors": [ 118 | { 119 | "nid": 525849, 120 | "rssi": { 121 | "2_4GHz": -47, 122 | "5GHz": -57 123 | } 124 | } 125 | ], 126 | "gateway_path": [ 127 | 525849 128 | ] 129 | }, 130 | "525849": { 131 | "mac": "ac:86:74:33:7a:c0", 132 | "name": "Back office", 133 | "ip": "6.59.122.192", 134 | "description": "main AP", 135 | "role": "gateway", 136 | "firmware_version": "fw-ng-r575", 137 | "mesh_version": "batman-adv", 138 | "last_checkin": "2015-06-17T13:35:43Z", 139 | "uptime": "21d 15h 23m", 140 | "hardware": "OM5P_AN", 141 | "memfree": 29004, 142 | "load": 0.02999999999999999889, 143 | "spare": false, 144 | "flags": "0x0000", 145 | "latitude": 49.53026900000000069, 146 | "longitude": -123.002387999999998, 147 | "down": false, 148 | "lifecycle": { 149 | "end_of_sale": "2021-05-31T00:00:00Z", 150 | "end_of_life": "2026-05-31T00:00:00Z", 151 | }, 152 | "subscription": { 153 | "is_active": true, 154 | "start_date": "2024-03-01T00:00:00Z", 155 | "end_date": "2025-03-01T14:50:12Z", 156 | "term": "yearly", 157 | "term_length": 12, 158 | "is_evergreen": false 159 | }, 160 | "lan_info": { 161 | "lan_ip": "192.168.0.9", 162 | "wan_ip": "24.211.105.169" 163 | }, 164 | "channels": { 165 | "2_4GHz": 5, 166 | "5GHz": 44 167 | }, 168 | "ht_modes": { 169 | "2_4GHz": "HT20", 170 | "5GHz": "HT40+" 171 | } 172 | } 173 | } 174 | } 175 | ```` 176 | 177 | 178 | ### create node 179 | `POST /node/network/` 180 | 181 | Create a new node in the specified network, with characteristics defined by the JSON package that comprises the body of the HTTP Request. The node-id assigned to the node by CloudTrax will be returned in the case of a successful request. 182 | 183 | There's a special case if you have "master" access where this endpoint can be used to transfer an existing node from one of your networks to another. To do so, construct the URL using the `network-id` of the destination network, but set the MAC (the "mac" field) of the "new" node to the MAC of the existing node you're moving from its original network. You also need to set "confirm_transfer" to true. This special case makes error handling slightly trickier; see [below](#errors). 184 | 185 | A new node needs to contain, at minimum, a MAC ("mac") and a "name". 186 | 187 | 188 | ##### error handling 189 | * an error will be returned if the node you're creating has the same MAC as an existing node in the specified network. 190 | * if you've "master" access and the node has the same MAC as an existing node in *another* of your networks and "confirm_transfer" is true, the existing node will be transferred to the network specified in the URL. 191 | * otherwise an error will be returned. 192 | 193 | ##### example request 194 | `POST https://api.cloudtrax.com/node/network/12345` 195 | 196 | ##### example input 197 | 198 | ````json 199 | { 200 | "mac": "ac:86:74:aa:cc:ee", 201 | "name": "new test node #1", 202 | "description": "added for test", 203 | "latitude": 49.00112233, 204 | "longitude": -123.00112233, 205 | "confirm_transfer": "true" 206 | } 207 | ```` 208 | 209 | ##### example output 210 | ````json 211 | { 212 | "node_id" : 548430 213 | } 214 | ```` 215 | 216 | 217 | ### update node 218 | `PUT /node/` 219 | 220 | Update an existing node, with characteristics defined by the JSON package in the body of the HTTP Request. Only those fields specified in the JSON package will be updated; all other node state will remain unchanged. 221 | 222 | ##### example request 223 | `PUT https://api.cloudtrax.com/node/548430` 224 | 225 | ##### example input 226 | 227 | Change a single field ("description") for node 54830 228 | 229 | ````json 230 | { 231 | "description": "UPDATE: changing description only" 232 | } 233 | ```` 234 | 235 | ##### example output 236 | 237 | Note the use of error code 1009 to indicate success. 238 | 239 | ````json 240 | { 241 | "code": 1009, 242 | "message": "Success.", 243 | "context": "update_node", 244 | "values": { 245 | 246 | } 247 | } 248 | ```` 249 | 250 | 251 | ### delete node 252 | `DELETE /node/` 253 | 254 | Delete an existing node. 255 | 256 | ##### example request 257 | `DELETE https://api.cloudtrax.com/node/123456` 258 | 259 | ##### example output 260 | 261 | Note the use of error code 1009 to indicate success. 262 | 263 | ````json 264 | { 265 | "code": 1009, 266 | "message": "Success.", 267 | "context": "update_node", 268 | "values": { 269 | 270 | } 271 | } 272 | ```` 273 | 274 | ### get node 275 | `GET /node/` 276 | 277 | Retrieve a node. 278 | 279 | ##### example request 280 | 281 | `GET https://api.cloudtrax.com/node/549365` 282 | 283 | ##### example output 284 | 285 | ````json 286 | { 287 | "active_clients": 0, 288 | "alerts": { 289 | "enabled": True 290 | }, 291 | "allow_dfs": True, 292 | "anonymous_ip": False, 293 | "antenna_type": None, 294 | "autotx_tx_powers": None, 295 | "channel_overrides": { 296 | "2_4GHz": 2, 297 | "5GHz": 36 298 | }, 299 | "channel_utilization": { 300 | "2_4GHz": [], 301 | "5GHz": [] 302 | }, 303 | "channels": {}, 304 | "config_seqno": 73, 305 | "connection_keeper_status": "disconnected", 306 | "country_code": "US", 307 | "current_seqno": 72, 308 | "current_seqno_used_since": "2025-03-03T22:47:09Z", 309 | "custom_sh_approved": True, 310 | "description": "", 311 | "disable_led": False, 312 | "down": True, 313 | "enable_dfs": False, 314 | "enable_dhcp": False, 315 | "enable_troubleshooting_until": None, 316 | "expedite_upgrade": False, 317 | "firmware_flags": [], 318 | "firmware_version": "7.0.22-cdb46dd42ccbc8f108b94c1d8", 319 | "firmware_version_full": 70000022, 320 | "firmware_version_id": "cdb46dd42ccbc8f108b94c1d8ee4d9e13f88a81b", 321 | "firmware_version_release": "", 322 | "firmware_version_semantic": { 323 | "build": "cdb46dd42ccbc8f108b94c1d8ee4d9e13f88a81b", 324 | "major": 7, 325 | "minor": 0, 326 | "patch": 22 327 | }, 328 | "flags": "0x0000", 329 | "hardware": "A42", 330 | "hardware_revision": "", 331 | "has_scanning_support": False, 332 | "ht_mode_overrides": { 333 | "2_4GHz": "HT20", 334 | "5GHz": "HT20" 335 | }, 336 | "ht_mode_overrides_autorf": { 337 | "2_4GHz": "HT20", 338 | "5GHz": "HT20" 339 | }, 340 | "ht_modes": { 341 | "2_4GHz": "?", 342 | "5GHz": "?" 343 | }, 344 | "ip": "10.20.40.104", 345 | "is_triband": False, 346 | "lan_info": { 347 | "lan_ip": "192.168.10.20", 348 | "wan_ip": "1.2.3.4" 349 | }, 350 | "last_checkin": "2025-03-03T22:47:37Z", 351 | "latitude": 45.522700, 352 | "lifecycle": { 353 | "end_of_life": "2026-05-31T00:00:00Z", 354 | "end_of_sale": "2021-05-31T00:00:00Z" 355 | }, 356 | "lldp_negotiation_delay": False, 357 | "load": 1.06, 358 | "locate_mode": False, 359 | "longitude": -122.67862, 360 | "mac": "ac:86:74:aa:aa:aa", 361 | "memfree": 162916, 362 | "mesh_version": "batman-adv", 363 | "name": "AP42_upstairs", 364 | "network_first_add": "2025-01-28T01:55:35Z", 365 | "outdoor": False, 366 | "role": "gateway", 367 | "serial_no": "A1234567890", 368 | "spare": False, 369 | "subscription": None, 370 | "supported_country": True, 371 | "supports_dfs": True, 372 | "underpowered": False, 373 | "upgrade_status": "none", 374 | "uptime": "2m", 375 | "uptime_seconds": 155, 376 | "warranty_first_checkin": "2018-10-24T18:33:19Z" 377 | } 378 | ```` 379 | 380 | 381 | ### reboot node 382 | `GET /node//reboot` 383 | 384 | Reboot an Access Point. 385 | 386 | ##### example request 387 | `GET https://api.cloudtrax.com/node/123456/reboot` 388 | 389 | ##### output 390 | 391 | *API in flux at this point.* 392 | 393 | 394 | ### get allowed channels for node 395 | `GET /node//allowed-channels` 396 | 397 | Returns all allowed channels for a node for a particular country, defaulting to the country code for the node that's stored in the database. You can specify a different country with the query-string "country" parameter, using one of the two-letter country codes specified by [ISO 3166](http://www.iso.org/iso/home/standards/country_codes.htm). 398 | 399 | ##### example request 400 | `GET https://api.cloudtrax.com/node/12345/allowed_channels` 401 | 402 | ##### example output 403 | ````json 404 | { 405 | "channels": { 406 | "2_4GHz": [ 407 | { 408 | "channel": 10, 409 | "ht_modes": [ 410 | "HT40-", 411 | "HT20" 412 | ] 413 | }, 414 | { 415 | "channel": 9, 416 | "ht_modes": [ 417 | "HT40-", 418 | "HT20" 419 | ] 420 | }, 421 | { 422 | "channel": 8, 423 | "ht_modes": [ 424 | "HT40-", 425 | "HT20" 426 | ] 427 | }, 428 | { 429 | "channel": 7, 430 | "ht_modes": [ 431 | "HT40+", 432 | "HT40-", 433 | "HT20" 434 | ] 435 | }, 436 | { 437 | "channel": 6, 438 | "ht_modes": [ 439 | "HT40+", 440 | "HT40-", 441 | "HT20" 442 | ] 443 | }, 444 | { 445 | "channel": 5, 446 | "ht_modes": [ 447 | "HT40+", 448 | "HT40-", 449 | "HT20" 450 | ] 451 | }, 452 | { 453 | "channel": 4, 454 | "ht_modes": [ 455 | "HT40+", 456 | "HT20" 457 | ] 458 | }, 459 | { 460 | "channel": 3, 461 | "ht_modes": [ 462 | "HT40+", 463 | "HT20" 464 | ] 465 | }, 466 | { 467 | "channel": 11, 468 | "ht_modes": [ 469 | "HT40-", 470 | "HT20" 471 | ] 472 | }, 473 | { 474 | "channel": 2, 475 | "ht_modes": [ 476 | "HT40+", 477 | "HT20" 478 | ] 479 | }, 480 | { 481 | "channel": 1, 482 | "ht_modes": [ 483 | "HT40+", 484 | "HT20" 485 | ] 486 | } 487 | ] 488 | } 489 | } 490 | ```` 491 | 492 | 493 | ### reset encryption key for node 494 | `GET /node//reset_encrypt_key` 495 | 496 | Resets the encryption key used by an Access Point. This should be done when a Access Point need to be re-paired. 497 | 498 | ##### example request 499 | `GET https://api.cloudtrax.com/node/123456/reset_encrypt_key` 500 | 501 | 502 | ### enable pairing for node 503 | `GET /node//enable_pairing` 504 | 505 | Newly added Access Points need to be paired with the network. Normally CloudTrax does this automatically, but in case an Access Point has been "re-flashed", it might be necessary to call this endpoint. 506 | 507 | ##### example request 508 | `GET https://api.cloudtrax.com/node/123456/enable_pairing` 509 | 510 | 511 | ### expedite upgrade for node 512 | `GET /node//expedite_upgrade` 513 | 514 | Assuming that firmware upgrades are enabled for a network, CloudTrax will attempt to upgrade any Access Points needing firmware upgrades during the specified maintenance window. Setting the `expedite_upgrade` flag asks CloudTrax to attempt the upgrade as soon as possible, disregarding the settings for the maintenance window. 515 | 516 | 517 | ### does an AP with address MAC already exist? 518 | `GET /node/does_mac_exist?mac=` 519 | 520 | Call this endpoint as a double-check when a user is entering the MAC of an Access Point into the system, prior to the node actually being physically attached and checked in. 521 | 522 | ##### example request 523 | 524 | `GET https://api.cloudtrax.com/node/does_mac_exist?mac=ac:86:74:aa:aa:aa` 525 | 526 | ##### example output 527 | ````json 528 | { 529 | "node_exists" : false 530 | } 531 | ```` 532 | 533 | -------------------------------------------------------------------------------- /api/site_survey.md: -------------------------------------------------------------------------------- 1 | # Site Survey endpoints 2 | 3 | These endpoints are used to determine which Access Points are present in the specified network and their characteristics. 4 | 5 | functionality | method | endpoint 6 | --- | --- | --- 7 | [trigger scan for neighboring Access Points](#scan) | GET | `/sitesurvey/network//scan` 8 | [retrieve stored scan results](#scan-results) | GET | `/sitesurvey/network/` 9 | 10 | 11 | ### trigger scan for neighboring Access Points 12 | GET `/sitesurvey/network//scan` 13 | 14 | Scan nodes in a network to determine neighboring Access Points and their signal strengths. The results of the scan are returned to the CloudTrax server on the next AP checkin, generally within five minutes. 15 | 16 | ##### example output 17 | 18 | ````json 19 | { 20 | "status" : 200 21 | } 22 | ```` 23 | 24 | 25 | ### retrieve stored scan results 26 | GET `/sitesurvey/network/` 27 | 28 | Obtain the results of the previous scan that was carried out using the `scan` API above. 29 | 30 | ##### example output 31 | 32 | ````json 33 | { 34 | "access_points": [ 35 | { 36 | "ssid": "blubberlutsch", 37 | "bssid": "B0:B2:DC:DA:AE:29", 38 | "mode": "11b/g", 39 | "encryption": "wpa2", 40 | "vendor": "Netgear", 41 | "seen_by_node": [ 42 | { 43 | "mac": "02:aa:bb:cc:dd:ee", 44 | "name": "garage", 45 | "time": "2014-07-29T13:52:33Z", 46 | "signal": -43 47 | }, 48 | { 49 | "mac": "02:aa:bb:cc:dd:43", 50 | "name": "kitchen", 51 | "time": "2014-07-29T13:54:33Z", 52 | "signal": -70 53 | } 54 | ], 55 | "channel_width": 40, 56 | "channel": [ 57 | 5, 58 | 11 59 | ] 60 | } 61 | ] 62 | } 63 | ```` 64 | 65 | -------------------------------------------------------------------------------- /api/time.md: -------------------------------------------------------------------------------- 1 | # Time endpoints 2 | 3 | The single endpoint in this collection is useful if you don't have a reliable time source. It helps ensure that your local time is in synch with that of the API server, since authentication uses a time window, outside of which authentication will fail. 4 | 5 | ### time 6 | 7 | ```` 8 | GET /time 9 | ```` 10 | 11 | ##### example request 12 | `GET https://api.cloudtrax.com/time` 13 | 14 | ##### output 15 | 16 | The current time is returned in ISO 8601 format (UTC). 17 | 18 | ##### example output 19 | 20 | ```` json 21 | { 22 | "time" : "2015-08-24T13:52:00Z" 23 | } 24 | ```` 25 | -------------------------------------------------------------------------------- /api/user_management.md: -------------------------------------------------------------------------------- 1 | # User Management endpoints 2 | 3 | This set of endpoints is for managing users in the new CloudTrax user system. 4 | 5 | functionality | method | endpoint 6 | --- | --- | --- 7 | [Edit a user](#put-user-id) | PUT | `/user/` 8 | [Disable a user](#put-user-id-disable) | PUT | `/user//disable` 9 | [Enable a user](#put-user-id-enable) | PUT | `/user//enable` 10 | [Delete a user](#delete-user-id) | DELETE | `/user/` 11 | [Get a user](#get-user-id) | GET | `/user/` 12 | [Create a user API key](#post-user-id-key) | POST | `/user//key` 13 | [Create a user API key](#post-user-id-key) | POST | `/user/key` 14 | [List user API keys](#get-user-id-key-list) | GET | `/user//key/list` 15 | [Delete a user API key](#delete-user-id-key-id) | DELETE | `/user//key/` 16 | [Delete a user API key](#delete-user-id-key-id) | DELETE | `/user/key/` 17 | [Create a user](#post-user) | POST | `/user` 18 | [List users](#get-user-list) | GET | `/user/list` 19 | [Edit a user password](#put-user-id-password) | PUT | `/user//password` 20 | [Create a user password reset token](#post-user-id-password_set) | POST | `/user//password_set` 21 | [Get a user network permission](#get-user-id-network-id) | GET | `/user//network/` 22 | [Get a user account permission](#get-user-id-account) | GET | `/user//account` 23 | [Get a user service agreement status](#get-user-id-service_agreement) | GET | `/user//service_agreement` 24 | [Edit a user service agreement status](#put-user-id-service_agreement) | PUT | `/user//service_agreement` 25 | [Create a permission for a user on a network](#put-network-id-user-id) | PUT | `/network//user/` 26 | [Delete a permission for a user on a network](#delete-network-id-user-id) | DELETE | `/network//user/` 27 | [Create a permission for a user on a network group](#put-networkgroup-id-user-id) | PUT | `/networkgroup//user/` 28 | [Delete a permission for a user on a network group](#delete-networkgroup-id-user-id) | DELETE | `/networkgroup//user/` 29 | 30 | 31 | ### Edit a user 32 | `PUT /user/` 33 | 34 | Edit details about a user. 35 | 36 | ##### Account Permissions 37 | Role | Permitted 38 | -|- 39 | Account Admin | X 40 | Group Manager | X 41 | Network User | 42 | 43 | ##### Resource Permissions 44 | N/A 45 | 46 | ##### Example request 47 | 48 | ```` 49 | PUT https://api-v2.cloudtrax.com/user/123 50 | ```` 51 | 52 | ```` json 53 | { 54 | "name":"foo", 55 | "notes":"district manager. watch out for him." 56 | } 57 | ```` 58 | 59 | ##### Example response 60 | 61 | ```` json 62 | { 63 | } 64 | ```` 65 | 66 | #### Error codes 67 | 68 | 30002 69 | 20000 70 | 20038 71 | 72 | 73 | ### Disable a user 74 | `PUT /user//disable` 75 | 76 | Disable a user. User will not be able to login or make any requests to the API after this endpoint has been successfully called. User information and permissions will be retained, but API keys will be removed. 77 | 78 | ##### Account Permissions 79 | Role | Permitted 80 | -|- 81 | Account Admin | X 82 | Group Manager | X 83 | Network User | 84 | 85 | ##### Resource Permissions 86 | N/A 87 | 88 | ##### Example request 89 | 90 | ```` 91 | PUT https://api-v2.cloudtrax.com/user/123/disable 92 | ```` 93 | 94 | ##### Example response 95 | 96 | ```` json 97 | { 98 | } 99 | ```` 100 | 101 | #### Error codes 102 | 103 | 30002 104 | 20038 105 | 20029 106 | 107 | 108 | ### Enable a user 109 | `PUT /user//enable` 110 | 111 | Enable a user. User will be able to login and make requests to the API after this endpoint has been successfully called. User information and permissions will be restored, but new API keys will have to be generated. 112 | 113 | ##### Account Permissions 114 | Role | Permitted 115 | -|- 116 | Account Admin | X 117 | Group Manager | X 118 | Network User | 119 | 120 | ##### Resource Permissions 121 | N/A 122 | 123 | ##### Example request 124 | 125 | ```` 126 | PUT https://api-v2.cloudtrax.com/user/123/enable 127 | ```` 128 | 129 | ##### Example response 130 | 131 | ```` json 132 | { 133 | } 134 | ```` 135 | 136 | #### Error codes 137 | 138 | 30002 139 | 20038 140 | 20029 141 | 142 | 143 | ### Delete a user 144 | `DELETE /user/` 145 | 146 | Delete a user. All user information (including permissions and API keys) will be permanently removed. 147 | 148 | ##### Account Permissions 149 | Role | Permitted 150 | -|- 151 | Account Admin | X 152 | Group Manager | X 153 | Network User | 154 | 155 | ##### Resource Permissions 156 | N/A 157 | 158 | ##### Example request 159 | 160 | ```` 161 | DELETE https://api-v2.cloudtrax.com/user/123 162 | ```` 163 | 164 | ##### Example response 165 | 166 | ```` json 167 | { 168 | } 169 | ```` 170 | 171 | #### Error codes 172 | 173 | 30002 174 | 20004 175 | 20038 176 | 20029 177 | 178 | 179 | ### Get a user 180 | `GET /user/` 181 | 182 | Get a user's information. This is information that is likely only of interest to Account Admins and Group Managers and not Network Users. For example, "notes" about a user may not be something administrators want to share. Or, "permissions" are only relevant to administrators, because a Network User cannot change their own. 183 | 184 | ##### Account Permissions 185 | Role | Permitted 186 | -|- 187 | Account Admin | X 188 | Group Manager | X 189 | Network User | 190 | 191 | ##### Resource Permissions 192 | N/A 193 | 194 | ##### Example request 195 | 196 | ```` 197 | GET https://api-v2.cloudtrax.com/user/123 198 | ```` 199 | 200 | ##### Example response 201 | 202 | ```` json 203 | { 204 | "email":"foo@bar.com", 205 | "name":"foo", 206 | "notes":"he is a bar", 207 | "role_id":2, /* account role ID */ 208 | "account_id":123, 209 | "enabled":true, 210 | "verified":true, 211 | "permissions":{ 212 | /* networks that were assigned explicitly and via network groups */ 213 | "all_networks":[ 214 | {"network_id":1, "role_id":3}, 215 | {"network_id":2, "role_id":3}, 216 | {"network_id":3, "role_id":3} 217 | ], 218 | /* only specifically assigned networks */ 219 | "assigned_networks":[ 220 | {"network_id":3, "role_id":3} 221 | ], 222 | /* only specifically assigned network groups */ 223 | "assigned_networkgroups":[ 224 | {"networkgroup_id":1, "role_id":3} /* contains network IDs 1 and 2 */ 225 | ], 226 | } 227 | } 228 | ```` 229 | 230 | #### Error codes 231 | 232 | 30002 233 | 20004 234 | 20038 235 | 236 | 237 | ### Create a user API key 238 | `POST /user//key` 239 | `POST /user/key` 240 | 241 | Create an API key for the specified user or the user making the request (if the `` is not specified). API keys have identical API permissions to a logged in user, but do not require entering the user's email and password. They should still be kept secret, even though the user's password is never accessible. 242 | 243 | Limit 10 keys generated via this endpoint per user at once. 244 | 245 | The "key" property must be stored after creation, because it is stored securely and cannot be retrieved again. 246 | 247 | ##### Account Permissions 248 | Role | Permitted 249 | -|- 250 | Account Admin | X 251 | Group Manager | X 252 | Network User | X 253 | 254 | ##### Resource Permissions 255 | N/A 256 | 257 | ##### Example request 258 | 259 | ```` 260 | POST https://api-v2.cloudtrax.com/user/123/key 261 | POST https://api-v2.cloudtrax.com/user/key 262 | ```` 263 | 264 | ##### Example response 265 | 266 | ```` json 267 | { 268 | "user_key_id":123, 269 | "key":"abc", 270 | "secret":"def", 271 | "created":"1970-01-01T00:00:00Z" 272 | } 273 | ```` 274 | 275 | #### Error codes 276 | 277 | 30002 278 | 20004 279 | 20038 280 | 20044 281 | 282 | 283 | ### List user API keys 284 | `GET /user//key/list` 285 | 286 | Get a list of API keys for the specified user that were generated via the [Create user API key](#post-user-id-key) endpoint. 287 | 288 | The "key" is not returned, because it is stored securely. If you lose the "key", you must generated a new API key. 289 | 290 | ##### Account Permissions 291 | Role | Permitted 292 | -|- 293 | Account Admin | X 294 | Group Manager | 295 | Network User | 296 | 297 | ##### Resource Permissions 298 | N/A 299 | 300 | ##### Example request 301 | 302 | ```` 303 | GET https://api-v2.cloudtrax.com/user/123/key/list 304 | ```` 305 | 306 | ##### Example response 307 | 308 | ```` json 309 | { 310 | "keys":[ 311 | { 312 | "user_key_id":123, 313 | "secret":"def", 314 | "created":"1970-01-01T00:00:00Z" 315 | } 316 | ] 317 | } 318 | ```` 319 | 320 | #### Error codes 321 | 322 | 30002 323 | 20004 324 | 20038 325 | 326 | 327 | ### Delete a user API key 328 | `DELETE /user//key/` 329 | `DELETE /user/key/` 330 | 331 | Delete an API key (generated via the [Create user API key](#post-user-id-key) endpoint) for the specified user or the user making the request (if the `` following `/user/` is not specified). 332 | 333 | ##### Account Permissions 334 | Role | Permitted 335 | -|- 336 | Account Admin | X 337 | Group Manager | 338 | Network User | 339 | 340 | ##### Resource Permissions 341 | N/A 342 | 343 | ##### Example request 344 | 345 | ```` 346 | DELETE https://api-v2.cloudtrax.com/user/123/key/456 347 | DELETE https://api-v2.cloudtrax.com/user/key/456 348 | ```` 349 | 350 | ##### Example response 351 | 352 | ```` json 353 | { 354 | } 355 | ```` 356 | 357 | #### Error codes 358 | 359 | 30002 360 | 20004 361 | 20038 362 | 20043 363 | 364 | 365 | ### Create a user 366 | `POST /user` 367 | 368 | Create a user in the same account as the requester. 369 | 370 | ##### Account Permissions 371 | Role | Permitted 372 | -|- 373 | Account Admin | X 374 | Group Manager | X 375 | Network User | 376 | 377 | ##### Resource Permissions 378 | N/A 379 | 380 | ##### Example request 381 | 382 | ```` 383 | POST https://api-v2.cloudtrax.com/user 384 | ```` 385 | 386 | ```` json 387 | { 388 | "email":"foo@bar.com", 389 | "name":"foo", 390 | "role_id":1, /* account role ID */ 391 | "notes":"foo needs bar" 392 | } 393 | ```` 394 | 395 | ##### Example response 396 | 397 | ```` json 398 | { 399 | "user_id":2, 400 | "account_id":123, 401 | "token":"abc", /* used to verify the user */ 402 | "email":"foo@bar.com", 403 | "name":"foo", 404 | "role_id":1 /* account role ID */ 405 | } 406 | ```` 407 | 408 | #### Error codes 409 | 410 | 30002 411 | 20000 412 | 20010 413 | 20012 414 | 20014 415 | 20015 416 | 20029 417 | 20036 418 | 419 | 420 | ### List users 421 | `GET /user/list` 422 | 423 | Get a list of users on the requester's account. 424 | 425 | ##### Account Permissions 426 | Role | Permitted 427 | -|- 428 | Account Admin | X 429 | Group Manager | X 430 | Network User | 431 | 432 | ##### Resource Permissions 433 | N/A 434 | 435 | ##### Example request 436 | 437 | ```` 438 | GET https://api-v2.cloudtrax.com/user/list 439 | ```` 440 | 441 | ##### Example response 442 | 443 | ```` json 444 | { 445 | "users":[ 446 | { 447 | "user_id":2, 448 | "email":"fizz@buzz.com", 449 | "name":"fizz", 450 | "verified":true, 451 | "enabled":true, 452 | "notes":"fizzbuzz", 453 | "role_id":7, 454 | "created":"2017-06-08T00:00:00Z", 455 | "highest_network_role_id":4 /* only applicable to users with assigned roles, i.e., not Account Admins */ 456 | } 457 | ] 458 | } 459 | ```` 460 | 461 | #### Error codes 462 | 463 | 30002 464 | 465 | 466 | ### Edit a user password 467 | `PUT /user//password` 468 | 469 | Update the requester's password. 470 | 471 | ##### Account Permissions 472 | Only the requester may access this endpoint. 473 | 474 | ##### Resource Permissions 475 | N/A 476 | 477 | ##### Example request 478 | 479 | ```` 480 | PUT https://api-v2.cloudtrax.com/user/123/password 481 | ```` 482 | 483 | ```` json 484 | { 485 | "password_current":"L33T", 486 | "password_new":"$P34K" 487 | } 488 | ```` 489 | 490 | ##### Example response 491 | 492 | ```` json 493 | { 494 | } 495 | ```` 496 | 497 | #### Error codes 498 | 499 | 20003 500 | 20004 501 | 20029 502 | 20030 503 | 20038 504 | 20045 505 | 506 | 507 | ### Create a user password reset token 508 | `POST /user//password_set` 509 | 510 | Create a token for resetting a password. This is meant for administrators to be able to reset a user's password without having to see the password; rather they can just deliver the token to the user and have the user set their own password. 511 | 512 | Limit 10 tokens per user at once. 513 | 514 | ##### Account Permissions 515 | Role | Permitted 516 | -|- 517 | Account Admin | X 518 | Group Manager | X 519 | Network User | 520 | 521 | ##### Resource Permissions 522 | N/A 523 | 524 | ##### Example request 525 | 526 | ```` 527 | POST https://api-v2.cloudtrax.com/user/123/password_set 528 | ```` 529 | 530 | ##### Example response 531 | 532 | ```` json 533 | { 534 | "user_id":2, 535 | "token":"abc" 536 | } 537 | ```` 538 | 539 | #### Error codes 540 | 541 | 30002 542 | 20004 543 | 20029 544 | 20033 545 | 20038 546 | 20045 547 | 548 | 549 | ### Get a user network permission 550 | `GET /user//network/` 551 | 552 | Get the role ID for the given network for the requester. 553 | 554 | ##### Account Permissions 555 | Only the requester may access this endpoint. 556 | 557 | ##### Resource Permissions 558 | N/A 559 | 560 | ##### Example request 561 | 562 | ```` 563 | GET https://api-v2.cloudtrax.com/user/123/network/456 564 | ```` 565 | 566 | ##### Example response 567 | 568 | ```` json 569 | { 570 | "role_id":4 571 | } 572 | ```` 573 | 574 | #### Error codes 575 | 576 | 12047 577 | 20029 578 | 579 | 580 | ### Get a user account permission 581 | `GET /user//account` 582 | 583 | Get the account role ID for the given network for the requester. 584 | 585 | ##### Account Permissions 586 | Only the requester may access this endpoint. 587 | 588 | ##### Resource Permissions 589 | N/A 590 | 591 | ##### Example request 592 | 593 | ```` 594 | GET https://api-v2.cloudtrax.com/user/123/account 595 | ```` 596 | 597 | ##### Example response 598 | 599 | ```` json 600 | { 601 | "account_id":1, 602 | "role_id":2 603 | } 604 | ```` 605 | 606 | #### Error codes 607 | 608 | 30002 609 | 20004 610 | 20038 611 | 20043 612 | 613 | 614 | ### Get a user service agreement status 615 | `GET /user//service_agreement` 616 | 617 | Get the status of the requester's acceptance of the service agreement. 618 | 619 | ##### Account Permissions 620 | Only the requester may access this endpoint. 621 | 622 | ##### Resource Permissions 623 | N/A 624 | 625 | ##### Example request 626 | 627 | ```` 628 | GET https://api-v2.cloudtrax.com/user/123/service_agreement 629 | ```` 630 | 631 | ##### Example response 632 | 633 | ```` json 634 | { 635 | "valid":true 636 | } 637 | ```` 638 | 639 | #### Error codes 640 | 641 | 20029 642 | 643 | 644 | ### Edit a user service agreement status 645 | `PUT /user//service_agreement` 646 | 647 | Accept the service agreement for the requester. 648 | 649 | ##### Account Permissions 650 | Only the requester may access this endpoint. 651 | 652 | ##### Resource Permissions 653 | N/A 654 | 655 | ##### Example request 656 | 657 | ```` 658 | PUT https://api-v2.cloudtrax.com/user/123/service_agreement 659 | ```` 660 | 661 | ##### Example response 662 | 663 | ```` json 664 | { 665 | } 666 | ```` 667 | 668 | #### Error codes 669 | 670 | 20029 671 | 672 | 673 | ### Create a permission for a user on a network 674 | `PUT /network//user/` 675 | 676 | Create a permission for the specified user on the specified network. 677 | 678 | ##### Account Permissions 679 | Role | Permitted 680 | -|- 681 | Account Admin | X 682 | Group Manager | X 683 | Network User | 684 | 685 | ##### Resource Permissions 686 | Role | Permitted 687 | -|- 688 | Network Editor | X 689 | Network Viewer | 690 | Voucher Editor | 691 | 692 | ##### Example request 693 | 694 | ```` 695 | PUT https://api-v2.cloudtrax.com/network/1/user/123 696 | ```` 697 | 698 | ```` json 699 | { 700 | "role_id":4 701 | } 702 | ```` 703 | 704 | ##### Example response 705 | 706 | ```` json 707 | { 708 | } 709 | ```` 710 | 711 | #### Error codes 712 | 713 | 12047 714 | 20000 715 | 20016 716 | 20018 717 | 20027 718 | 719 | 720 | ### Delete a permission for a user on a network 721 | `DELETE /network//user/` 722 | 723 | Delete the specified user's permission the specified network. 724 | 725 | ##### Account Permissions 726 | Role | Permitted 727 | -|- 728 | Account Admin | X 729 | Group Manager | X 730 | Network User | 731 | 732 | ##### Resource Permissions 733 | Role | Permitted 734 | -|- 735 | Network Editor | X 736 | Network Viewer | 737 | Voucher Editor | 738 | 739 | ##### Example request 740 | 741 | ```` 742 | DELETE https://api-v2.cloudtrax.com/network/1/user/123 743 | ```` 744 | 745 | ##### Example response 746 | 747 | ```` json 748 | { 749 | } 750 | ```` 751 | 752 | #### Error codes 753 | 754 | 30002 755 | 20019 756 | 20020 757 | 40006 758 | 759 | 760 | ### Create a permission for a user on a network group 761 | `PUT /networkgroup//user/` 762 | 763 | Create a permission for the specified user on the specified network group. 764 | 765 | ##### Account Permissions 766 | Role | Permitted 767 | -|- 768 | Account Admin | X 769 | Group Manager | X 770 | Network User | 771 | 772 | ##### Resource Permissions 773 | Role | Permitted 774 | -|- 775 | Network Editor | X 776 | Network Viewer | 777 | Voucher Editor | 778 | 779 | ##### Example request 780 | 781 | ```` 782 | PUT https://api-v2.cloudtrax.com/networkgroup/1/user/123 783 | ```` 784 | 785 | ```` json 786 | { 787 | "role_id":4 788 | } 789 | ```` 790 | 791 | ##### Example response 792 | 793 | ```` json 794 | { 795 | } 796 | ```` 797 | 798 | #### Error codes 799 | 800 | 30002 801 | 20000 802 | 20016 803 | 20017 804 | 20027 805 | 40006 806 | 807 | 808 | ### Delete a permission for a user on a network group 809 | `DELETE /networkgroup//user/` 810 | 811 | Delete the specified user's permission the specified network group. 812 | 813 | ##### Account Permissions 814 | Role | Permitted 815 | -|- 816 | Account Admin | X 817 | Group Manager | X 818 | Network User | 819 | 820 | ##### Resource Permissions 821 | Role | Permitted 822 | -|- 823 | Network Editor | X 824 | Network Viewer | 825 | Voucher Editor | 826 | 827 | ##### Example request 828 | 829 | ```` 830 | DELETE https://api-v2.cloudtrax.com/networkgroup/1/user/123 831 | ```` 832 | 833 | ##### Example response 834 | 835 | ```` json 836 | { 837 | } 838 | ```` 839 | 840 | #### Error codes 841 | 842 | 30002 843 | 20019 844 | 20020 845 | 40006 846 | -------------------------------------------------------------------------------- /api/vouchers.md: -------------------------------------------------------------------------------- 1 | # Vouchers endpoints 2 | 3 | This API component provides endpoints for creating, listing, and editing vouchers, which provide a mechanism that allows individuals and groups of individuals to access networks with preset restrictions on usage time and bandwidth, both paid and for free. 4 | 5 | functionality | method | endpoint 6 | --- | --- | --- 7 | [list all vouchers for a given network](#list-vouchers) | GET | `/voucher/network//list` 8 | [list specific vouchers for a given network](#list-specific-vouchers) | GET | `/voucher/network/` 9 | [create vouchers](#create-vouchers) | POST | `/voucher/network/` 10 | [update individual voucher settings](#update-individual) | PUT | `/voucher/network//update` 11 | [update multiple vouchers' settings](#update-multiple) | PUT | `/voucher/network//` 12 | 13 | 14 | ### list all vouchers for a given network 15 | `GET /voucher/network//list` 16 | 17 | List all vouchers for the given network, with current status and usage information. 18 | 19 | Deleted and expired vouchers remain in the system indefinitely, with their `status` field marked appropriately, and are only expunged completely once the period `purge_days` has expired. Vouchers that have been manually cancelled (i.e., their `cancelled` field has been set to true) similarly remain in the system until expunged. 20 | 21 | ##### example request 22 | 23 | ```` 24 | GET https://api.cloudtrax.com/voucher/network/123456/list 25 | ```` 26 | 27 | ##### output 28 | 29 | The API either returns HTTP status code 200 (success) or an HTTP error and JSON describing the error. On success, the API returns all vouchers for the given network. 30 | 31 | ##### example output 32 | The following output from this endpoint lists two vouchers that were specified during a "Create Vouchers" session in the CloudTrax Dashboard. For an explanation of the meaning of the individual fields, refer to the table [JSON detail](#json-detail-list) below. 33 | 34 | ```` json 35 | { 36 | "vouchers": [{ 37 | "code": "64dd7fc", 38 | "type": 2, 39 | "created": "2016-05-23T23:05:17Z", 40 | "duration": 1, 41 | "users": [], 42 | "max_users": 2, 43 | "down_limit": 1, 44 | "up_limit": 1, 45 | "comment": "howard's voucher test #1", 46 | "cancelled": false, 47 | "purge_days": 90, 48 | "tx_ids": [], 49 | "remaining": 0, 50 | "status": "unused" 51 | }, { 52 | "code": "f673d28", 53 | "type": 2, 54 | "created": "2016-05-23T23:05:17Z", 55 | "duration": 1, 56 | "users": [], 57 | "max_users": 2, 58 | "down_limit": 1, 59 | "up_limit": 1, 60 | "comment": "howard's voucher test #1", 61 | "cancelled": false, 62 | "purge_days": 90, 63 | "tx_ids": [], 64 | "remaining": 0, 65 | "status": "unused" 66 | }] 67 | } 68 | ```` 69 | 70 | ##### JSON detail 71 | 72 | field | type | description | example value 73 | --- | --- | --- | ---- 74 | `code` | string | The code used to activate this voucher. | `"abc123"` 75 | `type` | int | The voucher type. | `2` 76 | `created` | string | The timestamp corresponding to when the voucher was created. | `"2016-05-23T23:05:17Z"` 77 | `duration` | int | The amount of usage time remaining for the voucher, in hours. | `8` 78 | `users` | array of strings | An array of the MAC's of all devices that have used this voucher. | `"["00:AA:BB:11:22:33","00:AA:CC:22:33:44"]"` 79 | `max_users` | int | Number of users (i.e., devices) that can share this voucher. | `2` 80 | `down_limit` | float | The maximum download speed possible with this voucher, in Mbits/sec. | `10.0` 81 | `up_limit` | float | The maximum upload speed possible with this voucher, in Mbits/sec. | `10.0` 82 | `comment` | string | The comment associated with this voucher. | `"Free guest Wi-Fi"` 83 | `cancelled` | bool | Indicates whether the voucher has been cancelled manually. | false 84 | `purge_days` | int | The number of days a voucher will remain in the system before being automatically expunged. | `90` 85 | `tx_ids` | array of strings | All the Paypal tx_ids associated with this voucher. | `"["0000012345ABC","1111154321CBA"]"` 86 | `remaining` | int | The hours of usage left on the voucher. | `20` 87 | `status` | string | The current condition of the voucher, one of: `"unused"`, `"deleted"`, `"active"`, or `"expired"` | `"unused"` 88 | 89 | 90 | ### list specific vouchers for a given network 91 | `GET /voucher/network/?code=` 92 | 93 | Retrieve the information for a single voucher in a network. The voucher code included as a query parameter must be URL encoded. The response is a single voucher. The output, behavior, and description of returned fields is identical to that of the endpoint for listing all vouchers for the given network [described above](#list-vouchers). 94 | 95 | ##### example request 96 | 97 | ```` 98 | GET https://api.cloudtrax.com/voucher/network/123456?code=Room%201 99 | ```` 100 | 101 | ##### example output 102 | ```` json 103 | { 104 | "code": "f673d28", 105 | "type": 2, 106 | "created": "2016-05-23T23:05:17Z", 107 | "duration": 1, 108 | "users": [], 109 | "max_users": 2, 110 | "down_limit": 1, 111 | "up_limit": 1, 112 | "comment": "howard's voucher test #1", 113 | "cancelled": false, 114 | "purge_days": 90, 115 | "tx_ids": [], 116 | "remaining": 0, 117 | "status": "unused" 118 | } 119 | ```` 120 | 121 | 122 | ### create vouchers 123 | `POST /voucher/network/` 124 | 125 | The POST body is a JSON array of voucher objects, one for each voucher to be created. The array is named "desired_vouchers". 126 | 127 | ##### example request 128 | 129 | ```` 130 | POST https://api.cloudtrax.com/voucher/network/123456 131 | ```` 132 | 133 | ##### example input 134 | 135 | ```` json 136 | { 137 | "desired_vouchers": [{ 138 | "code": "Room 231", 139 | "duration": 1, 140 | "max_users": 1, 141 | "up_limit": 10, 142 | "down_limit": 20, 143 | "comment": "Free access for guests", 144 | "purge_days": 2 145 | }, { 146 | "code": "Room 232", 147 | "duration": 24, 148 | "max_users": 4, 149 | "up_limit": 20, 150 | "down_limit": 40, 151 | "comment": "Courtesy of management (4 guests)", 152 | "purge_days": 5 153 | }] 154 | } 155 | ```` 156 | 157 | 158 | ##### JSON detail 159 | 160 | As stated, the name of the topmost object is `"desired_vouchers"`. The fields of the individual voucher objects are as follows: 161 | 162 | fields | type | description | required 163 | ----- | ----- | ----- | ----- 164 | `code` | string | Desired voucher code, up to 16 characters if user-entered. System-generated if left blank.
:small_orange_diamond:Example value: `"Hotel Ritz"`
:small_orange_diamond:Allowed chars: `0-9, a-z, A-Z, and all other ASCII values up through decimal 126 (tilde) ` | optional 165 | `duration` | int | Number of hours the voucher will be usable once it's been submitted. Between 1 and 8760 (number of hours in one year).
:small_orange_diamond:Example value: `24`
:small_orange_diamond:Allowed chars: `0-9` | required 166 | `max_users` | int | Number of users (i.e., devices) that can share this voucher, between 0 and 9. The number 0 has a special significance; see [Note: max_users](#0-special-meaning).
:small_orange_diamond:Example value: `1`
:small_orange_diamond:Allowed chars: `0-9` | required 167 | `up_limit` | float | Maximum upload speed for all devices sharing this voucher, between 0.056 and 100 Mbits/sec.
:small_orange_diamond:Example value: `10.0`
:small_orange_diamond:Allowed chars: `0-9 and .` | required 168 | `down_limit` | float | Maximum download speed for all devices sharing this voucher, between 0.056 and 100 Mbits/sec.
:small_orange_diamond:Example value: `24.0`
:small_orange_diamond:Allowed chars: `0-9 and .` | required 169 | `comment` | string | User comments associated with the voucher(s), 64 characters max.
:small_orange_diamond:Example value: `"24 hours free access compliments of the hotel"`
:small_orange_diamond:Allowed chars: `any` | optional 170 | `purge_days` | int | Number of days a voucher will remain in the system until automatically expunged, between 1 and 999.
:small_orange_diamond:Example value: `90`
:small_orange_diamond:Allowed chars: `0-9` | required 171 | 172 | 173 | ##### Note: `max_users` 174 | The number 0 in the `max_users` field has a special meaning to the system, indicating a voucher that has no limit on the number of users/devices that can use it. When a user enters a 0 in this field via the Dashboard, the system posts a warning: "This voucher is set to allow unlimited devices. CloudTrax does not track individual users when this setting is applied." 175 | 176 | 177 | ### update individual voucher settings 178 | `PUT /voucher/network/` 179 | 180 | The accompanying JSON body is an array of one or more vouchers, identified by their `code` fields, along with their updated contents. The topmost array object is named "vouchers". All fields are required except for `"comment"` (see the note below). The order of fields is not significant. 181 | 182 | ##### example request 183 | 184 | ```` 185 | PUT https://api.cloudtrax.com/voucher/network/123456/update 186 | ```` 187 | 188 | ##### example input 189 | 190 | ```` json 191 | { 192 | "vouchers": [{ 193 | "code": "Room 231", 194 | "duration": 1, 195 | "max_users": 1, 196 | "up_limit": 10, 197 | "down_limit": 20, 198 | "comment": "Free access for guests", 199 | "purge_days": 2 200 | }] 201 | } 202 | ```` 203 | 204 | ##### output 205 | 206 | On success the API returns HTTP status code 200. No JSON body is returned. 207 | 208 | On failure the API return a 400 and JSON listing the vouchers that could not be updated. If any of the vouchers specified in the request's JSON body do not correspond to the network ID used in the request, the API will return a 403 and none of the vouchers will be updated. 209 | 210 | 211 | ##### JSON detail 212 | 213 | As stated, the topmost array object is named `"vouchers"`. The fields of the individual voucher objects are as follows: 214 | 215 | field | type | description | example value 216 | --- | --- | --- | ---- 217 | `code` | string | The voucher code, up to 16 characters if user-entered, system-generated if left blank.
:small_orange_diamond:Example value: `"Hotel Ritz"`
:small_orange_diamond:Allowed chars: `0-9, a-z, A-Z, and all other ASCII values up through decimal 126 (tilde)` | required 218 | `duration` | int | Number of hours the voucher will be usable once it's been submitted. Between 1 and 8760 (number of hours in one year).
:small_orange_diamond:Example value: `24`
:small_orange_diamond:Allowed chars: `0-9` | required 219 | `max_users` | int | Number of users (i.e., devices) that can share this voucher, between 0 and 9. The number 0 has a special significance; see [Note: max_users](#0-special-meaning).
:small_orange_diamond:Example value: `1`
:small_orange_diamond:Allowed chars: `0-9` | required 220 | `up_limit` | float | Maximum upload speed for each device sharing this voucher, between 0.056 and 100 Mbits/sec.
:small_orange_diamond:Example value: `10.0`
:small_orange_diamond:Allowed chars: `0-9 and .` | required 221 | `down_limit` | float | Maximum download speed for each device sharing this voucher, between 0.056 and 100 Mbits/sec.
:small_orange_diamond:Example value: `24.0`
:small_orange_diamond:Allowed chars: `0-9 and .` | required 222 | `comment` | string | User comments associated with the voucher(s), 64 characters max. The comment field may be omitted; if it is, the original comment (if any) will be truncated to length zero.
:small_orange_diamond:Example value: `"24 hours free access, compliments of the hotel"`
:small_orange_diamond:Allowed chars: `any` | optional 223 | `purge_days` | int | Number of days a voucher will remain in the system until automatically expunged, between 1 and 999.
:small_orange_diamond:Example value: `90`
:small_orange_diamond:Allowed chars: `0-9` | required 224 | 225 | 226 | ### update multiple vouchers' settings 227 | `PUT /network//` 228 | 229 | The action specified in `` (see [actions table below](#actions)) is applied to all vouchers listed in the array of voucher-code objects in the PUT body. The array is named "vouchers". 230 | 231 | 232 | ##### example request 233 | 234 | ```` 235 | PUT https://api.cloudtrax.com/voucher/network/123456/reset 236 | ```` 237 | 238 | ##### example input 239 | 240 | ```` json 241 | { 242 | "vouchers" : [ "Room 1", "Room 2", "Room 3", "Room 5" ] 243 | } 244 | ```` 245 | 246 | ##### output 247 | 248 | The API either returns HTTP status code 200 (success) or an HTTP error 400 and JSON describing the error. On success, the API returns all vouchers operated on as part of the request. If any of the vouchers specified in the request's JSON body do not correspond to the network ID used in the request, the API will return a 403 and no action will be performed on any of the vouchers. 249 | 250 | 251 | ##### actions table 252 | 253 | The specified action is applied to each voucher listed in the voucher-code array in the PUT body. 254 | 255 | action | description 256 | ----- | ----- 257 | `restore` | Undelete (i.e., remove the deleted status) of all vouchers in the "vouchers" array. 258 | `renew` | Undelete all vouchers in the "vouchers" array, reset their creation dates to `now`, and either set the date of first use to `now` if there are any users, or clear that field entirely if there aren't. 259 | `reset` | Undelete all vouchers in the "vouchers" array, clear any existing users, reset the creation dates to `now`, and clear the date of first use. 260 | `delete` | Mark all vouchers in the "vouchers" array as deleted. 261 | -------------------------------------------------------------------------------- /captive_portal/README.md: -------------------------------------------------------------------------------- 1 | # Captive Portal 2 | 3 | A captive portal acts as a controlled gateway to the web. Any attempt to browse the web over a WiFi connection controlled by the portal is intercepted; code running on the portal can then regulate which users are allowed web access, to which pages, and under which conditions. Typically users are initially redirected to a special splash page for logging in. 4 | 5 | With CloudTrax, the actions of the portal are defined by Open-Mesh code code running on an Access Point (AP). An attempt to browse the web via the Access Point will trigger the portal code and initiate the HTTP message flow below. 6 | 7 | The CloudTrax Captive Portal lets you configure two types of [Splash Pages](./splash_pages), hosted internally on CloudTrax or externally using UAM. These are shown in the box labeled "Frontend" in the diagram below. 8 | 9 | These splash pages can be used with a variety of [Authentication Services](./authentication), including a built-in vouchering system, RADIUS servers, and backend HTTP API-based Authentication Servers of your own. These services are shown in the box labeled "Backend" in the diagram below. 10 | 11 | This sequence diagram provides an architectural overview, both of the front and back-end components that are available, as well as the HTTP transactions flowing between them, the client's browser, and CloudTrax Access Points. 12 | 13 |
14 | 15 | ![architectural overview](./images/2014-05-20_architecture.png) 16 | 17 | -------------------------------------------------------------------------------- /captive_portal/authentication/README.md: -------------------------------------------------------------------------------- 1 | # Authentication Services 2 | 3 | This section describes facilities for authenticating users and devices connecting to a CloudTrax Access Point. These range from the built-in internal CloudTrax service that authenticates users based on [vouchers](./built_in), to third-party services such as [RADIUS servers](./radius), to a backend authentication server of your own devising ([HTTP Authentication](./http)). 4 | -------------------------------------------------------------------------------- /captive_portal/authentication/built_in/README.md: -------------------------------------------------------------------------------- 1 | # Voucher-based authentication 2 | 3 | CloudTrax supports a built-in authentication system using vouchers. General information on the vouchering system can be found in the help document, [Using Vouchers in CloudTrax](https://help.cloudtrax.com/hc/en-us/articles/202521890-Using-Vouchers-in-CloudTrax) (*currently applicable to CloudTrax CT3 only*). 4 | 5 | If your users are going to be logging in from a CloudTrax-hosted "custom" splash page, you'll need to make sure that splash page contains a voucher-compatible login form. Refer to [Custom Splash Pages](../../splash_pages/custom) for details. 6 | 7 | -------------------------------------------------------------------------------- /captive_portal/authentication/http/README.md: -------------------------------------------------------------------------------- 1 | 2 | # HTTP Authentication API 3 | 4 | *This is preliminary documentation. It applies only to networks running under CloudTrax 4.* 5 | 6 | 7 | #### Contents of this document #### 8 | 9 | * [Overview](#overview) 10 | * [HTTP Request and Response](#tech-details) 11 | * [Example request](#example-request) 12 | * [Example response](#example-response) 13 | * [Configuration](#config) 14 | * [Providing a login panel](#login-panel) 15 | * [Other configuration parameters](#other-config-params) 16 | * [Request Authenticator](#ra) 17 | * [Password decoding](#password) 18 | * [API functions](#api-operations) 19 | * [status](#status) 20 | * [login](#login) 21 | * [acct](#acct) 22 | * [logout](#logout) 23 | 24 | --- 25 | 26 | ### Introduction ### 27 | 28 | This document describes the CloudTrax **HTTP Authentication API** (**HTTP API** for short), a simple and straightforward authentication protocol that allows your Access Point code to hand off the task of authenticating new users to a backend authentication server of your own devising, (referred to as the **Authentication Server** in the following). The Authentication Server needs to implement the protocol described in this document to tell your Access Points who is allowed to use the service and what some of the basic operating parameters should be. 29 | 30 | CloudTrax offers several types of protocols for third-party authentication. The HTTP API is one. Another is the RADIUS API protocol, which hands off the task of backend authentication to a RADIUS server. The difference between RADIUS and the HTTP Authentication API is that all RADIUS-based authentication parameters need to be preconfigured directly on the RADIUS server. The HTTP Authentication API, on the other hand, allows you to handle all aspects of the authentication process at runtime yourself, giving you total control over that process. 31 | 32 | --- 33 | 34 | 35 | 36 | ### Overview ### 37 | 38 | The protocol details three types of interaction between an **Access Point** ("AP") and the backend Authentication Server. These three interactions, from a high-level perspective, correspond to the following sequence of events in the authentication lifecycle: 39 | 40 | * Code on an Access Point detects a user device attempting to make gain access through one of its SSID's that has been configured using CloudTrax to use this protocol (see [Configuration](#config).) The AP sends a **Status Request** as part of a "preauthentication check" to the Authentication Server, asking if the device has been previously authenticated. If the Authentication Server responds that the device has not (ie, it is unknown), the AP posts the splash screen containing a login panel. This would have also been previously configured. 41 | * Once the user enters his or her credentials and presses the Login button, the AP issues a **Login Request**, passing those credentials to the Authentication Server. If the server doesn't recognize the credentials, it lets the Access Point know that the authentications/login request has been rejected, and that's the end of it. 42 | * If the server accepts the credentials, it lets the AP know that the user/device is now authenticated. The Access Point will then either redirect to the web page the user was originally attempting to access, or redirect the user to an alternate "redirect" web page that was specified in the CloudTrax configuration for HTTP Authentication. The response from the Authentication Server to the Login Request also specifies to the Access Point the amount of time in seconds the user is to be considered authenticated. Following the expiration of that time, the AP is expected to reissue a Status Request, to start the entire process all over again. 43 | * During the session, the AP will periodically issue **Accounting Request** messages to the Authentication Server. These keep the Authentication Server informed of the number of bytes uploaded and downloaded by the client during the current session, as well as the amount of time that has passed since the user was authenticated. 44 | 45 | 46 | ### HTTP Request and Response ### 47 | 48 | The HTTP API uses standard **HTTP Requests** to communicate between the Access Point and the Authentication Server. HTTPS is not currently supported. The base HTTP URL of the Authentication Server is specified in the CloudTrax [Configuration](#config). Query string parameters are added to this URL by the Access Point when making requests to report or request specific information from Authentication Server. 49 | 50 | A query-string contains one or more name-value pairs of the form `param-name=param-value`. All HTTP Requests issued by this protocol use the HTTP GET method. All query-string parameters must be url-encoded. (Refer to [RFC 3986](http://www.rfc-editor.org/rfc/rfc3986.txt) for details.) 51 | 52 | The Authentication Server responds to the request by returning information in a standard **HTTP Response**. This consists of an **HTTP message body**, a multiline body of text encoded as `text/plain`, each line of which contains a name-value pair similar to the parameters in the Request's query-string, although in a slightly different format. 53 | 54 | 55 | ### Example request ### 56 | 57 | Here's a sample ***Login Request*** asking that user "TEST.USER", with a password encoded by the Access Point, be authenticated and logged-in to use the Open-Mesh WiFi network. The fact that this is a Login Request is indicated by the `type=login` name-value pair in its query-string. 58 | 59 | ```` 60 | GET http://exampleauthserver.com/auth.php?type=login&username=TEST.USER&password=87BC4E314689b55d89B&ra=949689087314689b55d89b1980aeff3f&mac=02:BA:DE:AF:FE:01&node=02:BA:DE:AF:FE:01 61 | ```` 62 | 63 | The URL of the backend server ("http://exampleauthserver.com/auth.php") was stored by CloudTrax during the initial [Configuration](#config) process, so that CloudTrax knows how to address the server during API calls. The URL's *path* ("/auth.php" in the above) provides the path on the server to the code that implements the backend component of this protocol. 64 | 65 | The `username` and `password` data transmitted as part of this request ("TEST.USER" and "87BC4E314689b55d89B" respectively) were provided by the user entering their credentials in the splash screen that was posted by the Access Point. (See [Providing a login panel](#login-panel) for instructions on how to configure the login form). 66 | 67 | The `mac` and `node` parameters in the query-string are two other parameters to the Login Request and are explained in the section on [logging in](#login). The `ra` parameter, standing for [Request Authenticator](#ra), is particularly important for maintaining security during handshaking under this protocol and is transmitted as a part of all authentication requests. 68 | 69 | ### Example response ### 70 | 71 | Given the above Login Request, the Login Response's message body might look something like the following. Note that the parameter name and its value on each line are both enclosed in double quotes, and that both names and values must be url-encoded. 72 | 73 |
 74 | "CODE" "ACCEPT"
 75 | "RA" "1fb341292217da85e097f05f3c75b672"
 76 | "SECONDS" "3600"
 77 | "DOWNLOAD" "2000"
 78 | "UPLOAD" "800"
 79 | 
80 | 81 | The name-value pair on the first line of the message body: 82 | 83 | ```` 84 | "CODE" "ACCEPT" 85 | ```` 86 | 87 | indicates that the user has been authenticated. The "DOWNLOAD" and "UPLOAD" parameters indicate the maximum throughputs in the indicated direction in kilobytes/second, while "SECONDS" indicates the number of seconds the login is to remain valid. These are discussed in detail in the section [below](#login). 88 | 89 | If the authentication server did not recognize the user's credentials, the response might look like this: 90 | 91 | ```` 92 | "CODE" "REJECT" 93 | "RA" "82f1e02d2a635dfa5dd459ede4395c3e" 94 | "BLOCKED_MSG" "Invalid%20username%20or%20password" 95 | ```` 96 | The applicable name-value pairs for both Authentication Requests and Responses are given below for all [API functions](#api-operations). 97 | 98 | 99 | ## Configuration ## 100 | 101 | If you intend to use HTTP Authentication, you need to provide several key pieces of information to CloudTrax in advance. You'll do this in the Configuration panel that's displayed in the CloudTrax dashboard when configuring for authentication. 102 | 103 | 104 | #### Providing a login panel #### 105 | If your users are going to be logging in from a CloudTrax-hosted "custom" splash page, you'll need to make sure that splash page contains an HTTP-Authentication-compatible login form. Refer to [Custom Splash Pages](../../splash_pages/custom) for details. 106 | 107 | 108 | #### Other configuration parameters #### 109 | Here's the rest of the Configuration panel you'll need to update when configuring for HTTP Authentication, with an explanation of the fields you'll be editing: 110 | 111 | ![configuration panel](./images/config-panel.png "Config Panel") 112 | 113 | 114 | ##### URL ##### 115 | The full URL that is used in the authentication API call is formed by appending the query-string appropriate to that request to the URL specified above. The URL must point to a standard HTTP server. HTTPS is not currently supported. For a Login Request for example, we have: 116 | 117 | Configured URL: `http://exampleauthserver.com/auth.php`
118 | Full URL:`GET http://exampleauthserver.com/auth.php?type=login&username=TEST.USER&password=87BC4E314689b55d89B&ra=949689087314689b55d89b1980aeff3f&mac=02:BA:DE:AF:FE:01&node=02:BA:DE:AF:FE:01` 119 | 120 | ##### secret ##### 121 | On entry here, this secret is shared between CloudTrax and the backend Authentication Server. Both sides maintain identical copies; it is *never* transmitted in either direction between them during handshaking, encoded or otherwise. 122 | 123 | ##### Failed authentication block ##### 124 | The value "Block duration of XX minutes" specifies how often the password challenge is cycled. We suggest setting this to at least 10 minutes, otherwise you may experience passwords that are decrypted incorrectly. 125 | 126 | 127 | ## Request Authenticator ## 128 | All Authorization Requests contain an `ra` parameter. "ra" stands for **Request Authenticator**, a unique 128-bit (16-byte) string, which is used to help protect against so-called Man-in-the-Middle attacks. The Request Authenticator that is passed by CloudTrax to the Authentication Server needs to be transformed to a new RA by the algorithm discussed below and returned as part of the HTTP Response so that CloudTrax can determine that the Response is in accord with the original Request. 129 | 130 | The RA of the Response packet is produced by calculating the `md5` hash of the concatenated string consisting of the Authentication Response's `CODE` value, followed by the original RA and finally by the **secret** shared between CloudTrax and the Authentication Server, which was provided to CloudTrax during [Configuration](#config). Keep in mind the RA must be decoded from the hex string provided, into a binary format. In pseudocode, it would look something like this: 131 | 132 | ```` 133 | oldRequestAuthenticator = hexToBinaryFunction(oldRequestAuthenticatorHexString) 134 | responseAuthenticator = md5(codeValue, oldRequestAuthenticator, secret) 135 | ```` 136 | 137 | In PHP, it would look like this: 138 | 139 | ````php 140 | $ra = hex2bin($dict['RA']); 141 | $response_ra = hash('md5', $code . $ra . $secret); 142 | ```` 143 | See [PHP example code](code/php/example_server.php) for full details. 144 | 145 | ---- 146 | 147 | 148 | ## Password decoding ## 149 | The password that is passed to the backend Authentication Server via a Login Request (see [Logging in](#login) for details) is encoded by the AP and needs to be decoded by the Authentication Server before it can be used to determine that this is a known user with valid credentials. In other words, you'll need to write the code so that your Authentication Server can take the encoded password it receives and reconstruct the original password from it. See the document [Password decoding](password_decoding.md) for details. 150 | 151 | 152 | 153 | ## API functions ## 154 | The **HTTP API** protocol comprises three different types of Requests (alternatively *functions*, *messages*, or simply *calls*), differentiated by the `type` parameter in the HTTP Request's query-string: 155 | 156 | * [Status Request](#status) (`type=status`) 157 | * [Login Request](#login) (`type=login`) 158 | * [Accounting Request](#acct) (`type=acct`)

159 | 160 | 161 | 162 | #### Status Request `(type=status)` #### 163 | 164 | A Status Request (also known as a Preauthentication Request) is issued to the Authentication Server on detection of either a new device attempting to perform transactions through an SSID configured to use this API, or on the timing-out of an existing session. The request includes, at a minimum, the `type=status` name-value pair, a [Request Authenticator](#ra) and the Ethernet MAC address of the device, which the Authentication Server can use to determine if the connecting device is already known to it. This phase of the authentication handshaking process is also known as preauthentication. 165 | 166 | The AP may issue one or more Status Requests in sequence. The first one issued will generally contain a new and unique session id, which can be used to identify the session in subsequent Accounting Requests. 167 | 168 | 169 | ##### Status Request parameters ##### 170 | 171 | ###### example: ###### 172 | ```` 173 | GET http://exampleauthserver.com/auth.php?type=status&ra=B83DB5D253017788463892C5D45C035B&session=5e13015&mac=65%3A76%3ABA%3A8A%3AD3%3A58 174 | ```` 175 | 176 | param-name | param-value | requirement 177 | ---------------- | --------------- | ------------------ 178 | `type` | `status` | must include 179 | `ra` | a [Request Authenticator](#ra) generated by the AP | must include 180 | `mac` | Ethernet MAC address of the device requesting (pre)authentication. Format: six hex bytes separated by colons (":") | must include 181 | `node` | Ethernet MAC address of the Access Point (AP) to which the user is attempting to connect | should 182 | `ipv4` | IPv4 address of the device | may include 183 | `session` |a string unique to this node identifying the session | may include 184 | 185 | *NOTE*: Entries in this and following tables in "code-block" format (` like this `) are literals to be entered exactly as shown, including quotes when present. 186 | 187 | ##### Status Response parameters ##### 188 | 189 | ###### example: ###### 190 | ```` 191 | "CODE" "REJECT" 192 | "RA" "7b40dfe92d85298b11c32d9a4e4d79c8" 193 | "BLOCKED_MSG" "Unknown Client" 194 | ```` 195 | 196 | param-name | param-value | requirement 197 | ---------------- | --------------- | ----------------------- 198 | `"CODE"` | `"ACCEPT"` for an already authenticated client; `"REJECT"` otherwise | must include 199 | `"RA"` | the computed [Request Authenticator](#ra) to be returned to the AP. | must include 200 | `"SECONDS"` | number of seconds this login will remain valid | must include (if `"ACCEPT"`) 201 | `"DOWNLOAD"` | maximum throughput in kbits/sec from node to device | must include (if `"ACCEPT"`) 202 | `"UPLOAD"` | maximum throughput in kbits/sec from device to node | must include (if `"ACCEPT"`) 203 | `"BLOCKED_MSG"` | a human-readable message why the pre-authentication request was rejected | should include (if `"REJECT"`) 204 | 205 | --- 206 | 207 | 208 | #### Login Request `(type=login)` #### 209 | 210 | The Authentication Server responds to the Login Request by either informing CloudTrax that the user's credentials are correct and that they are authenticated to use the network, or rejecting the login with an optional message indicating why. 211 | 212 | If the user has been authenticated, the server indicates how long the login is valid for and the maximum available speeds for both download and upload. The AP responds to an "ACCEPT" message by showing the webpage the user was attempting to view that invoked the initial Login Request. If "Redirect URL" was specified during [Configuration](#config) , the webpage for that URL will be displayed instead. 213 | 214 | The password retrieved from the splash page's login form is encoded by the AP before being transmitted. The Authentication Server will be able to decode the password using the algorithm discussed in the section [Password decoding](#password). 215 | 216 | ##### example: ##### 217 | ```` 218 | GET http://exampleauthserver.com/auth.php?type=login&username=TEST.USER&password=87BC4E314689b55d89B&ra=949689087314689b55d89b1980aeff3f 219 | ```` 220 | 221 | 222 | ##### Login Request parameters ##### 223 | 224 | The query-string for a Login Request *must* contain: 225 | 226 | param-name | param-value | requirement 227 | ---------------- | --------------- | ------------------ 228 | `type` | `login` | must include 229 | `ra` | a [Request Authenticator](#ra) generated by the Access Point | must include 230 | `username` | name of the user, from the splash page's login form | must include 231 | `password` | from the login form, then encoded by the AP | must include 232 | `mac` | Ethernet MAC address of the device requesting (pre)authentication. Format: six hex bytes separated by colons (":") | should include 233 | `node` | MAC address of the AP to which the user is attempting to connect | should include 234 | `ipv4` | IPv4 address of the device | may include 235 | `session` |a string unique to this node identifying the session | may include 236 | 237 | ##### Login Response parameters ##### 238 | 239 | param-name | param-value | requirement 240 | ---------------- | --------------- | ----------------------- 241 | `"CODE"` | `"ACCEPT"` for an already authenticated client; `"REJECT"` otherwise | must include 242 | `"RA"` | the computed [Request Authenticator](#ra) to be returned to the AP. | must include 243 | `"SECONDS"` | number of seconds this login will remain valid | must include(if `"ACCEPT"`) 244 | `"DOWNLOAD"` | maximum throughput in kbits/sec from node to device | must include (if `"ACCEPT"`) 245 | `"UPLOAD"` | maximum throughput in kbits/sec from device to node | must include (if `"ACCEPT"`) 246 | `"BLOCKED_MSG"` | a human-readable message why the login was rejected | should include (if `"REJECT"`) 247 | 248 | 249 | --- 250 | 251 | 252 | #### Accounting Request `(type=acct)` #### 253 | 254 | The term "Request" is somewhat misleading here, as the purpose of an Accounting Request is to deliver information *to* the Authentication Server, rather than requesting something *from* it. The Access Point issues Accounting Requests periodically to inform the backend server of the user's current bandwidth usage. 255 | 256 | ##### example: ##### 257 | ```` 258 | GET http://exampleauthserver.com/auth.php?type=acct&ra=F565E3F864C904D75A6DFC60B81BD51B&node=AC%3A82%3A74%3A3B%3A7A%3AC0&session=5e13015&mac=64%3A76%3ABB%3A8A%3AD3%3A58&ipv4=11.255.229.138 259 | ```` 260 | 261 | ##### Accounting Request parameters ##### 262 | 263 | param-name | param-value | requirement 264 | ---------------- | --------------- | ------------------ 265 | `type` | `acct` | must include 266 | `ra` | a [Request Authenticator](#ra) generated by the AP | must include 267 | `mac` | MAC address of the device to authenticate against. Format: six hex bytes separated by colons (":") | must include 268 | `node` | MAC address of the node which issued this request | must include 269 | `download` | number of bytes downloaded to the device with this MAC during the current session | should include 270 | `upload` | number of bytes uploaded from the device during this session | should include 271 | `seconds` | number of seconds the session has been active | should include 272 | `ipv4` | IPv4 address of the device | may include 273 | `session` | a string unique to this node during this session identifying the session | may include 274 | 275 | *NOTE*: A device roaming between Access Points may cause additional Accounting Requests to be sent. 276 | 277 | ##### Accounting Response parameters ##### 278 | 279 | param-name | param-value | requirement 280 | ---------------- | --------------- | ----------------------- 281 | `"CODE"` | `"OK"` | must include 282 | `"RA"` | the computed [Request Authenticator](#ra) to be returned to the AP. | must include 283 | 284 | 285 | #### Logout Request `(type=logout)` #### 286 | 287 | The logout message informs the HTTP API server that a user has logged out, and transfers final accounting information. It is sent when a node stops a session for a device. 288 | 289 | The format is the same as for accounting messages, with the exception that "type" is "logout" in the request. 290 | -------------------------------------------------------------------------------- /captive_portal/authentication/http/code/php/README.md: -------------------------------------------------------------------------------- 1 | The files [decode_password.php](decode_password.php) and [example_server.php](example_server.php) are both described in the document [password_decoding.md](../../password_decoding.md). 2 | -------------------------------------------------------------------------------- /captive_portal/authentication/http/code/php/decode_password.php: -------------------------------------------------------------------------------- 1 | 0; $i--) { 39 | if ($password[$i - 1] != "\x00") 40 | break; 41 | else 42 | $j++; 43 | } 44 | 45 | if ($j > 0) { 46 | $password = substr($password, 0, strlen($password) - $j); 47 | } 48 | 49 | return $password; 50 | } 51 | 52 | /* given the fixed inputs: 53 | - $RA (request authenticator) 54 | - $encoded_pw (the encoded password), and 55 | - $secret (the shared secret) 56 | a successful decoding will produce and test for 57 | the expected output ($password) 58 | */ 59 | 60 | function test_decode_password() 61 | { 62 | # assume following passed in via the login request from the Access Point: 63 | $RA = "2590CC8A3930DB222781921A8F8B88B1"; 64 | $encoded_pw = "D8A7B0E4A6122A73705C4640E86CD62EA499201D98C5F436103448C39A537B07"; 65 | 66 | # and the shared secret 67 | $secret = "verysecretstring"; 68 | 69 | echo '$RA = ' . $RA . "
"; 70 | echo '$encoded_pw = ' . $encoded_pw . "
"; 71 | echo 'strlen($encoded) = ' . strlen($encoded_pw) . "
"; 72 | echo '$secret = ' . $secret . "
"; 73 | 74 | $password = decode_password($RA, $encoded_pw, $secret); 75 | echo "decoded password = " . $password; 76 | 77 | # the decoded password for the above params 78 | # should equal "123456abcdefghijklmnopqrs" 79 | 80 | if (strcmp($password, "123456abcdefghijklmnopqrs") == 0) 81 | echo "DECODING WAS SUCCESSFUL"; 82 | else 83 | echo "DECODING FAILED"; 84 | } 85 | 86 | test_decode_password(); 87 | 88 | ?> 89 | -------------------------------------------------------------------------------- /captive_portal/authentication/http/code/php/example_server.php: -------------------------------------------------------------------------------- 1 | 'REJECT', 14 | 'RA' => '0123456789abcdef0123456789abcdef', 15 | 'BLOCKED_MSG' => 'Rejected! This doesnt look like a valid request', 16 | ); 17 | 18 | 19 | /** 20 | * print_dictionary - Print dictionary as encoded key-value pairs 21 | * @dict: Dictionary to print 22 | */ 23 | function print_dictionary($dict) 24 | { 25 | foreach ($dict as $key => $value) { 26 | echo '"', rawurlencode($key), '" "', rawurlencode($value), "\"\n"; 27 | } 28 | } 29 | 30 | /** 31 | * calculate_new_ra - calculate new request authenticator based on old ra, code 32 | * and secret 33 | * @dict: Dictionary containing old ra and code. new ra is directly stored in it 34 | * @secret: Shared secret between node and server 35 | */ 36 | function calculate_new_ra(&$dict, $secret) 37 | { 38 | if (!array_key_exists('CODE', $dict)) 39 | return; 40 | 41 | $code = $dict['CODE']; 42 | 43 | if (!array_key_exists('RA', $dict)) 44 | return; 45 | 46 | if (strlen($dict['RA']) != 32) 47 | return; 48 | 49 | $ra = hex2bin($dict['RA']); 50 | if ($ra === FALSE) 51 | return; 52 | 53 | $dict['RA'] = hash('md5', $code . $ra . $secret); 54 | } 55 | 56 | /** 57 | * decode_password - decode encoded password to ascii string 58 | * @dict: dictionary containing request RA 59 | * @encoded: The encoded password 60 | * @secret: Shared secret between node and server 61 | * 62 | * Returns decoded password or FALSE on error 63 | */ 64 | function decode_password($dict, $encoded, $secret) 65 | { 66 | if (!array_key_exists('RA', $dict)) 67 | return FALSE; 68 | 69 | if (strlen($dict['RA']) != 32) 70 | return FALSE; 71 | 72 | $ra = hex2bin($dict['RA']); 73 | if ($ra === FALSE) 74 | return FALSE; 75 | 76 | if ((strlen($encoded) % 32) != 0) 77 | return FALSE; 78 | 79 | $bincoded = hex2bin($encoded); 80 | 81 | $password = ""; 82 | $last_result = $ra; 83 | 84 | for ($i = 0; $i < strlen($bincoded); $i += 16) { 85 | $key = hash('md5', $secret . $last_result, TRUE); 86 | for ($j = 0; $j < 16; $j++) 87 | $password .= $key[$j] ^ $bincoded[$i + $j]; 88 | $last_result = substr($bincoded, $i, 16); 89 | } 90 | 91 | $j = 0; 92 | for ($i = strlen($password); $i > 0; $i--) { 93 | if ($password[$i - 1] != "\x00") 94 | break; 95 | else 96 | $j++; 97 | } 98 | 99 | if ($j > 0) { 100 | $password = substr($password, 0, strlen($password) - $j); 101 | } 102 | 103 | return $password; 104 | } 105 | 106 | /* copy request authenticator */ 107 | if (array_key_exists('ra', $_GET) && strlen($_GET['ra']) == 32 && ($ra = hex2bin($_GET['ra'])) !== FALSE && strlen($ra) == 16) { 108 | $response['RA'] = $_GET['ra']; 109 | } 110 | 111 | /* decode password when available */ 112 | $password = FALSE; 113 | if (array_key_exists('username', $_GET) && array_key_exists('password', $_GET)) 114 | $password = decode_password($response, $_GET['password'], $secret); 115 | 116 | /* store mac when available */ 117 | $mac = FALSE; 118 | if (array_key_exists('mac', $_GET)) 119 | $mac = $_GET['mac']; 120 | 121 | /* decode request */ 122 | if (array_key_exists('type', $_GET)) { 123 | $type = $_GET['type']; 124 | 125 | switch ($type) { 126 | case 'login': 127 | if ($password === FALSE) 128 | break; 129 | 130 | if ($password == 'ThisIsThePassword' && $_GET['username'] == 'testuser') { 131 | unset($response['BLOCKED_MSG']); 132 | $response['CODE'] = "ACCEPT"; 133 | $response['SECONDS'] = 3600; 134 | $response['DOWNLOAD'] = 2000; 135 | $response['UPLOAD'] = 800; 136 | } else { 137 | $response['BLOCKED_MSG'] = "Invalid username or password"; 138 | } 139 | break; 140 | case 'status': 141 | if ($mac === FALSE) 142 | break; 143 | 144 | if ($mac == '02:ba:de:af:fe:01') { 145 | unset($response['BLOCKED_MSG']); 146 | $response['CODE'] = "ACCEPT"; 147 | $response['SECONDS'] = 120; 148 | $response['DOWNLOAD'] = 3000; 149 | $response['UPLOAD'] = 400; 150 | } else { 151 | $response['BLOCKED_MSG'] = "Unknown Client"; 152 | } 153 | break; 154 | case 'acct': 155 | case 'logout': 156 | if ($mac === FALSE) 157 | break; 158 | unset($response['BLOCKED_MSG']); 159 | $response['CODE'] = "OK"; 160 | break; 161 | }; 162 | } 163 | 164 | /* calculate new request authenticator based on answer and request -> send it out */ 165 | calculate_new_ra($response, $secret); 166 | print_dictionary($response); 167 | 168 | ?> 169 | -------------------------------------------------------------------------------- /captive_portal/authentication/http/images/config-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudtrax/docs/50fe4ac1ec7dc3b10c625f6d33d1f94853135591/captive_portal/authentication/http/images/config-panel.png -------------------------------------------------------------------------------- /captive_portal/authentication/http/images/edit-splash-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudtrax/docs/50fe4ac1ec7dc3b10c625f6d33d1f94853135591/captive_portal/authentication/http/images/edit-splash-page.png -------------------------------------------------------------------------------- /captive_portal/authentication/http/password_decoding.md: -------------------------------------------------------------------------------- 1 | # Password decoding 2 | 3 | ## Important Note 4 | The value "Block duration of XX minutes" within CloudTrax specifies how often the password challenge is cycled. We suggest setting this to at least 10 minutes, otherwise you may experience passwords that are decrypted incorrectly. 5 | 6 | ## Password decoding details 7 | 8 | The password that's passed to the Authentication Server is encoded and needs to be reconstructed by the Authentication server in order to verify the user's credentials. The encoding process on the CloudTrax end uses the Request Authenticator that's passed as part of the Login Request, as well as the shared secret, to encode the password. The Authentication Server likewise needs to use these values in order to decode it. 9 | 10 | The encoded password is a string containing a sequence of hexadecimal digit pairs whose string-length will be an integer multiple of 32 bytes. The first part of the decoding process needs to convert those hex pairs back to the ASCII characters they represent; this new string will be half the length of the old. 11 | 12 | Here's an example of an encoded password, using for the sake of clarity a string of only ten hex digits: 13 | 14 | ` "01626364"` 15 | 16 | The converted string will look like this: 17 | 18 | `"\x01abc"` 19 | 20 | The first character is escaped in this sequence, since there's no ASCII representation for this character. 21 | 22 | A piece of Python doing this decoding would look something like this: 23 | 24 | ````python 25 | p = password 26 | e = encoded password 27 | ra = request authenticator 28 | es[] = e' split in substrings of 16 characters each 29 | c[-1] = ra 30 | res = [] 31 | for i in len(ps): 32 | b[i] = md5(s + c[i - 1]) 33 | c[i] = es[i] 34 | res += es[i] xor b[i] 35 | return res 36 | ```` 37 | 38 | Note the use of the MD5 cryptographic hash function ([RFC 1321](https://www.ietf.org/rfc/rfc1321.txt)), as well as an exclusive-or operation. 39 | 40 | Here's a piece of PHP code that does the same thing. 41 | 42 | ````php 43 | $bincoded = hex2bin($encoded); 44 | $password = ""; 45 | $last_result = $ra; 46 | 47 | for ($i = 0; $i < strlen($bincoded); $i += 16) { 48 | $key = hash('md5', $secret . $last_result, TRUE); 49 | for ($j = 0; $j < 16; $j++) 50 | $password .= $key[$j] ^ $bincoded[$i + $j]; 51 | $last_result = substr($bincoded, $i, 16); 52 | } 53 | ```` 54 | 55 | This is taken from a piece of code, [decode_password.php](./code/php/decode_password.php), (in turn taken from a larger example, [example_server.php](./code/php/example_server.php)), which provides a fairly full-featured example of much of the core functionality of an Authentication Server backend. 56 | 57 | The line 58 | 59 | ```` php 60 | $password .= $key[$j] ^ $bincoded[$i + $j] 61 | ```` 62 | in this snippet shows an example of PHP's bitwise [exclusive-or operator](http://php.net/manual/en/language.operators.bitwise.php) (as opposed to logical), comparable to Python's "xor" operator in the Python code above. 63 | 64 | 65 | #### Testing your decoding routine #### 66 | The function `test_decode_password()` at the bottom of [decode_password.php](./code/php/decode_password.php) provides an example of successfully decoding and testing for an expected output (the password "123456abcdefghijklmnopqrs"), given a particular RA and encoded password passed via the Login Request, as well as a specific shared secret. Your own decoding routine needs to be able to recapitulate this particular result, given the same inputs. 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /captive_portal/authentication/radius/README.md: -------------------------------------------------------------------------------- 1 | # RADIUS-based authentication 2 | 3 | *This is preliminary documentation. It applies only to networks running under CloudTrax 4.* 4 | 5 | CloudTrax may be configured to authenticate users using an external RADIUS server. RADIUS servers are available from the [FreeRADIUS project](http://freeradius.org) and with Microsoft Windows Server, among others. Chapter 2 of the [FreeRADIUS Technical Guide](http://networkradius.com/doc/FreeRADIUS%20Technical%20Guide.pdf) provides a good introduction to RADIUS in general. 6 | 7 | The screenshot below shows the Configuration panel that appears in the CloudTrax Dashboard when you select the "RADIUS" option for "Splash Page Authentication". 8 | 9 | If your users are going to be logging in from a CloudTrax-hosted internal splash page (referred to as a "Custom" splash page in the Dashboard) , you'll need to make sure that splash page contains a RADIUS-authentication-compatible login form. Refer to [Internally Hosted splash pages](../../splash_pages/custom) for details. 10 | 11 | If your users are logging in from an external, UAM-based splash page, refer to the documentation in [Externally Hosted splash pages](../../splash_pages/external). The CloudTrax Help Center also provides a short [tutorial introduction](https://help.cloudtrax.com/hc/en-us/articles/205014660-Externally-Hosted-Splash-Page-with-RADIUS-Authentication-on-5xx-and-later-firmware) to using RADIUS with an Externally Hosted splash page. 12 | 13 | 14 | *Configuration of specific RADIUS servers is outside of the scope of this document.* 15 | 16 |
17 | 18 | ![config screenshot](./images/radius_configuration.png "title") 19 | 20 | ##### Server Address 1 ##### 21 | ##### Server Address 2 ##### 22 | The Hostname or IP address of the RADIUS server. Up to two RADIUS servers may be specified. 23 | 24 | Example: `radius_server.example.com` 25 | 26 | ##### Server Secret ##### 27 | The shared secret maintained independently by both the RADIUS client (the Access Point) and the RADIUS server. 28 | 29 | 30 | ##### NAS ID ##### 31 | A Network Access Server identifier may be provided to supply additional out-of-band information to the RADIUS server during the authentication request. This will typically be your SSID. 32 | -------------------------------------------------------------------------------- /captive_portal/authentication/radius/images/radius_configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudtrax/docs/50fe4ac1ec7dc3b10c625f6d33d1f94853135591/captive_portal/authentication/radius/images/radius_configuration.png -------------------------------------------------------------------------------- /captive_portal/images/2014-05-20_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudtrax/docs/50fe4ac1ec7dc3b10c625f6d33d1f94853135591/captive_portal/images/2014-05-20_architecture.png -------------------------------------------------------------------------------- /captive_portal/splash_pages/README.md: -------------------------------------------------------------------------------- 1 | # Splash Pages 2 | 3 | The CloudTrax Dashboard provides two mechanisms for hosting and managing splash pages: 4 | 5 | * **[Internally Hosted splash pages](./custom/)**, in which HTML is hosted on the Access Point. 6 | * **[Externally Hosted splash pages](./external)**, in which pages are generated and hosted on an external server you configure, using the UAM protocol. 7 | 8 | Both allow you to use CloudTrax' full range of authentication options. 9 | 10 | 11 | -------------------------------------------------------------------------------- /captive_portal/splash_pages/custom/README.md: -------------------------------------------------------------------------------- 1 | # Internally Hosted splash pages 2 | 3 | CloudTrax internally hosted splash pages (sometimes referred to as "custom splash pages" in the documentation) allow you to host your own splash pages, choosing from a number of templates provided by CloudTrax. These templates can be customized, using the full-featured HTML web-page editor that's built into the CloudTrax Dashboard, to provide graphic and textual information about your organization and its WiFi network to your clients. 4 | 5 | One of the special features that's provided is the ability to add or customize an HTML form on your splash page that lets your users log into the particular type of authentication service you've selected. 6 | 7 | You'll choose the type of authentication service you want using the Configuration dialog in the CloudTrax Dashboard (see the [Authentication overview](./../../authentication)), but you'll also need to ensure that your splash page contains a login form that's tailored to that particular service. In short, you'll either need to select a splash-page template that contains the proper form, or use the information that follows to build one yourself. 8 | 9 | One of the key things you'll need to understand in order to be able to do this is the use of so-called "interpolated variables" in CloudTrax splash pages. 10 | 11 | ### Interpolated variables 12 | 13 | All CloudTrax-hosted splash pages contain interpolated-variable "placeholders" in the form `$`, where the placeholder string is replaced at runtime on the Access Point with the string contents of the variable `variable-name`. Interpolated variables are only used with custom splash pages hosted by CloudTrax; they are not used if your splash pages are hosted externally. 14 | 15 | Some interpolated-variable placeholders are used to display information to users who will be viewing your splash page. For example, all CloudTrax splash pages contain a small header element near the top of the document: 16 | 17 | ```` 18 |

Welcome to $gatewayname

19 | ```` 20 | 21 | where `$gatewayname` is replaced at runtime with text describing your network that would have been entered when you originally configured it. It might read, for example, "Welcome to the Hotel California WiFi Network!" You can use this placeholder if and where you see fit. 22 | 23 | Another use of interpolated variables, if you're incorporating an authentication-service login form (see [Login forms for authentication](#login-forms) below), is to provide error-reporting information, whose contents will depend on the type of authentication service and the error messages it returns. If you're attempting to connect to an HTTP Authentication Server and have entered an invalid password, for example, you'll see "Invalid username or password." You'll need to place the following small snippet of HTML somewhere on your splash page: 24 | 25 | ```` 26 |

27 | $error_msg 28 |

29 | ```` 30 | 31 | Finally, there are interpolated-variable placeholders that are used internally by code on the Access Point,. These are used in the HTML forms [shown below](#login-forms) that provide login access to the authentication service you've selected. 32 | 33 | Most of these forms contains three interpolated variables, one to indicate the piece of code on the Access Point used to process the particular type of login, `$authaction,` and two other "hidden" variables that are used by code on the Access Point to track internal state, `$tok` and `$redir.` 34 | 35 | Here's the current list of interpolated variables and the contexts in which they occur. 36 | 37 | variable name | usage | context | required/optional 38 | ---------------- | --------------- | ----------------------- | ---------------------- 39 | $gatewayname | to display network name/description on splash page | all splash pages | optional 40 | $error_msg | to display CT-originated error messages | authentication: voucher passcodes and RADIUS and HTTP API logins | required 41 | $tok | internal CloudTrax use | all splash pages w/ login forms | required 42 | $redir | internal CloudTrax use | all splash pages w/ login forms | required 43 | $authaction | internal CloudTrax use | all splash pages w/ login forms | required 44 | 45 | 46 | ### Login forms for authentication 47 | 48 | Some of the text in the following HTML snippets is optional or editable, for example the `

` elements providing the login form titles. You can style them as you like. Forms can be placed inside `
` elements if you want the forms as a whole to take on default CloudTrax styling. 49 | 50 | It's important to note that it's up to you to ascertain that the login form being shown on a splash page matches the type of authentication service you've configured. 51 | 52 | #### Free access (no authentication required) 53 | 54 | ```` 55 |

Free Access:

56 | 57 |
58 | 59 | 60 | 61 |
62 | ```` 63 | 64 | #### Voucher passcode entry 65 | 66 | ```` 67 |

Passcode Access:

68 |

69 | Enter your access code below: 70 |

71 | 72 | 73 |

74 | $error_msg 75 |

76 | 77 |
78 | 79 | 80 |   81 | 82 |
83 | ```` 84 | 85 | #### CloudTrax HTTP Authentication login 86 | 87 | ```` 88 |

HTTP Authentication:

89 |

90 | Enter your login credentials: 91 |

92 | 93 |
94 | 95 | 96 | Username:   97 | Password:   98 | 99 |
100 | ```` 101 | 102 | #### RADIUS server login 103 | 104 | ```` 105 |

Radius Access:

106 |

107 | Enter your RADIUS credentials: 108 |

109 | 110 |
111 | 112 | 113 | Username:   114 | Password:   115 | 116 |
117 | ```` 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /captive_portal/splash_pages/custom/images/edit-splash-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudtrax/docs/50fe4ac1ec7dc3b10c625f6d33d1f94853135591/captive_portal/splash_pages/custom/images/edit-splash-page.png -------------------------------------------------------------------------------- /captive_portal/splash_pages/external/README.md: -------------------------------------------------------------------------------- 1 | # Externally Hosted splash pages 2 | 3 | * [Introduction](#intro) 4 | * [Overview](#overview) 5 | * [The redirect URL](#redir-url) 6 | * [Pre-login: responding to `'notyet'`](#notyet) 7 | * [Building a log-in form](#building-login-form) 8 | * [userurl](#userurl) 9 | * [Encoding the password](#encoding-pw) 10 | * [Redirecting the encoded password](#build-redir-url) 11 | * [Using 'redir' parameter](#redir) 12 | * [Session handling](#session) 13 | * [Post-authentication: responding to `'success'` and `'failed'`](#post-authentication) 14 | 15 | 16 | ## Introduction 17 | 18 | CloudTrax' external splash-page facility allows you to host your own splash pages and control every aspect of the log-in and authentication process. It requires you to code a splash-page implementation on a server that responds to the protocol described in this document. You also need to use the CloudTrax Dashboard to specify the URL address of your splash-page server code, as well as specify the type of back-end authentication service you are using, either [RADIUS](../../authentication/radius/) or [HTTP Authentication](../../authentication/http). PHP sample code is provided to help clarify the process. 19 | 20 | Here's a sample of what the Dashboard settings might look like for a typical setup: 21 | 22 | ![Dashboard example](./images/cloudtrax_dashboard_uam_config.png) 23 | 24 | 25 | ## Overview 26 | If you're using an externally hosted splash page, code running on the AP (Access Point) makes use of HTTP's *redirection capability* to invoke your splash server's code at several points during the Access Point's log-in/authentication process so that your sever can take appropropriate action. Redirection is the mechanism by which a web server can request a client to redirect its addressing attempt to a different URL than the one it used originally. 27 | 28 | One such use is when an AP recognizes that a client device is attempting to access a web page using an SSID on the AP for the first time. The AP intercepts the initial page request and returns an HTTP `302 Redirect` instruction to the web browser, causing it to redirect itself to the splash page instead. The special [redirect URL](#redir-url) that the AP constructs and provides as part of the Redirect has a query-string containing parameters of interest to the splash page server. In particular, a `'res'` parameter specifies the particular operation the server is being asked to carry out. 29 | 30 | This redirection mechanism is used on several occasions during login and authentication. The first, as mentioned above, is *pre-login*, which occurs on the initial detection of a new client's connection attempt. The other is *post-authentication*, which occurs once the back-end authentication service has signalled to the AP whether authentication was successful or failed. The AP then uses HTTP redirection to deliver a redirect URL to the splash page indicating the authentication status. 31 | 32 | Redirection is also used by the splash page server on one occasion to return control to the AP. See [Redirecting the encoding password](#XXXYYY). 33 | 34 | In both the pre-login and post-authentication case where the AP is redirecting to the splash page server, the redirection URL's query-string `'res'` parameter indicates which case it is. `'notyet'` indicates that the client has not yet been authenticated and is a signal that the splash page server needs to return a login page to the user. `'success'` and `'failed'` indicate the two possible outcomes of the attempt to authenticate, and `logout` is a response to a log-out message from the authentication service, if provided. 35 | 36 | 37 | ## The redirect URL 38 | 39 | Here's an example of the initial redirect URL constructed by the AP and redirected by the web client to your splash page server code to initiate the log-in sequence: 40 | 41 | ```` 42 | http://example_server.com/uam_simple_server.php?res=notyet&uamip=10.255.224.1&uamport=8082&mac=64-76-BA-8A-D3-58&called=AC-86-74-3B-7A-C0&ssid=Howards%20Test%20Network%201&nasid=100.101.102.103&userurl=http%3A%2F%2Fwww.mlsite.net%2Fblog%2F%3Fp%3D1409&challenge=ACC28255A7A0122D682AFE0653F7440F0C19E6E9E89FABC03EA2CA82D791B90C 43 | ```` 44 | 45 | The URL of the splash page host and its server code, http://example.com/uam_server.php, would have been specified by you in the CloudTrax Dashboard. The `'res'` parameter is set to `'notyet'`, telling the splash page server that the client has not yet been logged in, and that it should build and return a log-in form. 46 | 47 | The key-value pairs present in the redirect URL's query-string are as follows. 48 | 49 | parameter | description 50 | ----- | ----- 51 | `res` | One of `'notyet'`, `'success'`, `'failed'`, or `'logoff'`. 52 | `uamip` | Internal address of the AP. Used by the splash page server to build a URL to the AP to return an encoded password to it. See [Redirecting the encoded password](#build-redir-url). 53 | `uamport` | AP port. Used together with `uamip` above to build a URL to the AP. 54 | `mac` | MAC address of the client device . 55 | `called` | MAC address of eth0 on the AP. This is the same MAC address that shows up in CloudTrax. 56 | `ssid` | SSID network name. 57 | `nasid` | ID of the Network Access Server, if one has been specified. 58 | `userurl` | url-encoded web page requested by the user, eg "http%3A%2F%2Fwww.google.com%2F". 59 | `challenge` | Provided by the AP. Used by server's log-in code, along with a shared secret, to produce an encoded password. The length of time the challenge is valid for is linked to the "Block duration of XX minutes" value listed in CloudTrax. The default value is 30 minutes. 60 | 61 | Let's walk through a sequence of messages to the splash page server and its responses as it proceeds through a typical log-in and authentication process. 62 | 63 | 64 | ## Pre-login: responding to `'notyet'` 65 | 66 | Your splash page server code needs to do a few things in response to receiving a `"res=notyet"` message from the AP. `'notyet'` indicates that the user has not yet been authenticated and marks the first step in the logging-in process. In response to receiving this message, your splash-page server needs to: 67 | 68 | 1. [build and return a log-in page to the user](#building-login-form), 69 | 2. [encode the password it gets back](#encoding-pw), and 70 | 3. [build a special redirection URL for the AP](#build-redir-url). 71 | 72 | Let's walk through all three steps in code. 73 | 74 | 75 | #### Building a log-in form 76 | 77 | Here's a snippet of code from [uam_simple_server.php](./code/php/uam_simple_server.php) that checks the contents of the query-string in the *transport URL* shipped to it from the AP, and accordingly builds a log-in form for the user to fill out. 78 | 79 | ```` php 80 | 81 | 82 | 83 | 86 |

Please Log in

87 |
88 |
89 |
90 | "> 91 | "> 92 | "> 93 | "> 94 | 95 |
96 | 101 | 102 | 103 | ```` 104 | 105 | Note the use of hidden fields to hold the key-value pairs that were passed to the server in the transport URL's query-string. These will subsequently be POSTed to the "uam_handle_form.php" code that uses the `"challenge"` to encode the password. 106 | 107 | 108 | #### `'userurl'` 109 | The AP will redirect the user to the requested URL on conclusion of a successfully authenticated login. In that eventuality, the role of the splash-page server is concluded, and it will *not* be called again with the query-string parameter 'res' = 'success'. The `'userurl'` query-string parameter contains that redirection URL. 110 | 111 | You might notice by the way that the `action` parameter in our login form above dictates that we're POSTing to a separate PHP script, [uam_handle_form.php](./code/php/uam_handle_form.php), as already noted. It's not necessary to do this, and if in fact you omit the `action` parameter, you end up with the single-file server solution shown in [splash.php](./code/php/splash.php). Doing it the way we're doing it here makes the code a bit easier to follow in our opinion, but you can choose either implementation on which to base your own server. 112 | 113 | 114 | #### Encoding the password 115 | 116 | The first thing that [uam_handle_form.php](./code/php/uam_handle_form.php) needs to do is make some local copies of the variables that were POSTed to it. This just puts them in a slightly more convenient form for use. The `"challenge"` that was originally generated by the AP is passed into the function that's going to do the encoding, along with the `$uam_secret` at the top of the file. This is the shared secret we made available to CloudTrax when we initially configured the splash server in the Dashboard. Keep in mind that the length of time the challenge is valid for is linked to the "Block duration of XX minutes" value listed in CloudTrax. The user should attempt to login before this timeout is reach. The default value is 30 minutes. 117 | 118 | 119 | ```` php 120 | 0) { 151 | $crypt_secret = md5($hexchall . $secret, TRUE); 152 | $len_secret = 16; 153 | } else { 154 | $crypt_secret = $hexchall; 155 | $len_secret = strlen($hexchall); 156 | } 157 | 158 | /* simulate C style \0 terminated string */ 159 | $plain .= "\x00"; 160 | $crypted = ''; 161 | for ($i = 0; $i < strlen($plain); $i++) 162 | $crypted .= $plain[$i] ^ $crypt_secret[$i % $len_secret]; 163 | 164 | $extra_bytes = 0;//rand(0, 16); 165 | for ($i = 0; $i < $extra_bytes; $i++) 166 | $crypted .= chr(rand(0, 255)); 167 | 168 | return bin2hex($crypted); 169 | } 170 | ```` 171 | 172 | Note also the use of PHP's [bitwise exclusive-or](http://php.net/manual/en/language.operators.bitwise.php) operator, `^`, in the line: 173 | 174 | ```` php 175 | $crypted .= $plain[$i] ^ $crypt_secret[$i % $len_secret]; 176 | ```` 177 | 178 | The full source for [uam_handle_form.php](./code/php/uam_handle_form.php), by the way, contains some additional debugging gear we're not showing here. It might be useful if you want to view the contents of what was POSTed or the composition of the redirection URL it builds to send back to the Access Point. Which coincidentally is our next topic. 179 | 180 | 181 | 182 | #### Redirecting the encoded password 183 | 184 | The last thing your server needs to do in responding to `'notyet'` is to build a special URL containing the username and encoded password and return that URL to the AP so it can forward those on to the back-end authentication server. 185 | 186 | Since this logging-in process was initially started (what seems to be a very long time ago!) by the user attempting to browse to a new web page, the URL we're constructing would normally be returned back to the user's web browser, which would not be very useful. What we really want to do is return the URL to the AP instead. 187 | 188 | We do this by using the same HTTP redirection capability we [discussed earlier](#http-redirection), where a `302 Redirect` was used by the AP to call the splash-page server with a `notyet` message. Here we'll do the same thing but in reverse, allowing the AP to take over the flow of control once again. 189 | 190 | This is the reason for the existence of the two POSTed variables, `$uamip` and `$uamport`, which are used together to build the URL address of the AP, as in 191 | 192 | ```` php 193 | $redirect_url = "http://$uamip:$uamport/logon?" . 194 | "username=" . urlencode($username) . 195 | "&password=" . urlencode($encoded_password); 196 | ```` 197 | This URL, when fully filled out, will look something like this: 198 | 199 | ```` 200 | http://10.255.224.1:8082/logon?username=testuser&password=d14b2343e44f19a5d37fc83f68bc1daae123 201 | ```` 202 | The IP address shown is an *internal* one (it's only for use within your local area network and is not addressable across the greater Internet), and may differ depending on your particular setup. 203 | 204 | Finally, the PHP directive that causes the actual redirection is this: 205 | 206 | ```` php 207 | header('Location: ' . $redirect_url); 208 | ```` 209 | Note this only works correctly if the `Location` header is emitted before any *textual* output. In fact, this is true for *any* HTTP header, as noted in the PHP Manual: ["Send a raw HTTP Header"](http://php.net/manual/en/function.header.php): 210 | 211 | > Remember that header() must be called before any actual output is sent, either by normal HTML tags, 212 | > blank lines in a file, or from PHP. 213 | 214 | 215 | ####Using 'redir' parameter 216 | One final note on using the 'Location:' directive to achieve redirection: You can change the location of the final landing page, if you so desire, by adding a `'redir'` directive to the `$redirect_url` *before* issuing the above `header()` command. For example, 217 | 218 | ```` php 219 | $redirect_url .= "&redir=" . urlencode("http://myportal.example.com"); 220 | ```` 221 | 222 | if you use the `'redir'` parameter, the AP will automatically redirect to that URL and will not call your splash-page server with a 'res' = 'success' parameter (similar to the case [above](#userurl) when we discussed the use of "Redirection URL"). 223 | 224 | 225 | #### Session handling 226 | Our form handler contains some PHP session-handling code, 227 | 228 | ```` php 229 | session_start(); 230 | if(isset($_POST["userurl"])) { 231 | $_SESSION["userurl"] = $_POST["userurl"]; 232 | } else { 233 | unset($_SESSION["userurl"]); 234 | } 235 | session_write_close(); 236 | ```` 237 | 238 | The purpose of this code is to maintain the value of `userurl` across web-server invocations. This makes `userurl` available to the "success"-handling code discussed below, where it is used, along with an HTML **<meta>** tag, to redirect the web browser to its final landing page, if that was specified by the user. 239 | 240 | 241 | ## Post-authentication: responding to `'success'` and `'failed'` 242 | 243 | ```` php 244 | # "res"==="notyet" case discussed above 245 | } 246 | else if ($res === "success") { 247 | $redir = $_SESSION["userurl"]; 248 | if(isset($redir)) { 249 | echo ""; 250 | echo ''; 251 | echo ""; 252 | } 253 | else { 254 | echo "

Log-in successful!

"; 255 | } 256 | } 257 | else if ($res === "failed") { 258 | echo "

Whoops, failed to authenticate

"; 259 | } 260 | else { 261 | # ... logoff case remains 262 | } 263 | ```` 264 | 265 | The two `if ($res === ...)` statements, for "success" and "failed", deliver the two possible results of the back-end authentication. If authentication was successfull, we use an HTML **<meta>** tag to redirect the web browser to its final landing page, getting the value of `userurl` from our `$_SESSION` dictionary. If authentication failed, our server simply notes the fact. In a more realistic scenario, we would have likely posted a log-in form explaining the failure and allowing the user to attempt to log-in again. 266 | 267 | Alternatively, in the event that the splash-page server had determined that the user had made too many attempts to log in at this point, we could have posted a message stating they'd been locked out for a certain amount of time. 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | -------------------------------------------------------------------------------- /captive_portal/splash_pages/external/click_to_enter/README.md: -------------------------------------------------------------------------------- 1 | # UAM Splash Authentication click-to-enter page 2 | 3 | The normal CloudTrax splash page allows a click-to-enter style authentication. UAM Splash Authentication does not directly allow this kind of page because the UAM page is designed to exchange a username and password. This article will show how UAM Splash Authentication can be used for click-to-enter style authentication. 4 | 5 | ## Click-to-Enter page 6 | 7 | A click-to-enter page can have many different elements. For example an e-mail field could be used that gets stored in a database. We assume that it is using an informative text for the user, a field (accept checkbox) which is evaluated by the backend and an submit button. Additional, different or even less fields are possible. 8 | 9 | The user is in this example expected to enable the checkbox and then press the OK button. The user should only get access when the UAM page server verified that the user accepted the TOS. 10 | 11 | ``` 12 | ---------------------------------------- 13 | | | 14 | | IMPORTANT TEXT | 15 | | EVEN MORE IMPORTANT TEXT | 16 | | FINE-PRINT | 17 | | | 18 | | [ ] accept TOS | 19 | | | 20 | | [OK] | 21 | | | 22 | ---------------------------------------- 23 | ``` 24 | 25 | ## Authentication process 26 | 27 | The basic idea is to use the username as auto-generated id and the password as signature for the username. Both can be used together to validate a user login. 28 | 29 | ![alt text](images/2015-08-26_Click-To-Enter_nodb.png) 30 | 31 | The user is still automatically redirected to the remote login page when he tries to access the internet. But this time no username and password fields are shown. Instead a different set of fields are presented which he has to fill out. A press on the ok button submits the content of these fields back to the login server. 32 | 33 | The task of the login server is then to validate the forms. The UAM login page server has to generate a new username and password when the content is validated successfully. The username should be based on the client mac address and the current time. The password on the other hand should be used as signature and hard to guess. A good approach is to create the sha256_hmac of the username together with a unique secret that is only known to the server. 34 | 35 | Both username and password are then returned to the client browser. The browser of the client will submit the username and password automatically to the AP which then tries to validate them. 36 | 37 | The HTTP authentication backend is expected to first validate the username. If the username doesn't contain the mac of the current user or if the timestamp in the username is outside an acceptable range then the user has to be rejected. If the username looks valid then the signature of the username has to be generated with the same algorithm as used by the splash page. The authentication backend must only return a success message when the submitted password matches the generated signature. 38 | 39 | The client browser will then be redirected to the login page for the success message. The UAM remote login page can then display or redirect to any page it wants. 40 | 41 | ## Recommendations 42 | 43 | The accounting messages can be disabled via the udsplash ssid option "disable_accounting". 44 | 45 | ## Example 46 | 47 | ### Configuration in the cloudtrax dashboard: 48 | 49 | ``` 50 | Splash page URL (UAM): http://1.2.3.4/test/uam.php 51 | Splash page secret (UAM): toosecretstring 52 | Splash page authentication URL (HTTP Auth): http://1.2.3.4/test/server.php 53 | Splash page authentication Secret (HTTP Auth): verysecretstring 54 | ``` 55 | 56 | ### Runtime environment: 57 | 58 | ``` 59 | Client MAC: 02:ba:de:af:fe:01 60 | Time (unix timestamp): 1440596666 61 | ``` 62 | 63 | ### Sever configuration: 64 | 65 | ``` 66 | Secret used for the signature: evenmoresecretstring 67 | ``` 68 | 69 | ### UAM splashpage process 70 | 71 | The user should automatically be redirect to the splash page when its browser is trying to access a webpage. 72 | 73 | ``` 74 | http://1.2.3.4/test/uam.php?res=notyet&...&mac=02-BA-DE-AF-FE-01&... 75 | ``` 76 | 77 | The page has to validate its parameters and create a form with hidden fields to store things like the challenge or the client mac address. 78 | 79 | When the user has filled the form and pressed, the splash page has to create a new temporary username and password which will later be used to verify that the user of this client went through the splash page. Here we use SHA256_HMAC. Base64 is chosen instead of the normal hex representation to avoid that the generated password gets larger than the limit of 63 characters. 80 | 81 | ```php 82 | $username = 02-BA-DE-AF-FE-01 . '_' . 1440596666; 83 | $password = base64_encode(hash_hmac('sha256', $username, 'evenmoresecretstring', true)); 84 | ``` 85 | 86 | In this example the splash page would generate: 87 | 88 | ``` 89 | Username: 02-BA-DE-AF-FE-01_1440596666 90 | Password: FuDiZi//mEFgleZkUW67L0ZtoaEjEgugLbi3nHCkZHw= 91 | ``` 92 | 93 | ### Authentication Process 94 | 95 | The AP will try to decode the supplied password and create an authentication request with both username and (re-encrypted) password for the HTTP API server. First the server has to decode the password again. 96 | 97 | ``` 98 | username: 02-BA-DE-AF-FE-01_1440596666 99 | password: FuDiZi//mEFgleZkUW67L0ZtoaEjEgugLbi3nHCkZHw= 100 | mac: 02:BA:DE:AF:FE:01 101 | ``` 102 | 103 | The username has to be split first into a MAC and a timestamp again. The retrieved MAC has to match the MAC address of the authentication request. 104 | 105 | ```php 106 | /* split into mac and timestamp */ 107 | $ret = preg_match('/^(?P([A-F0-9]{2}\-){5}[A-F0-9]{2})_(?P[0-9]+)$/', 108 | $username, $matches); 109 | if (!$ret) 110 | return FALSE; 111 | 112 | /* check if mac is the same */ 113 | if (str_replace(':', '-', $mac) != $matches['mac']) 114 | return FALSE; 115 | ``` 116 | 117 | The time has to be checked to make sure it is not in the future nor too far in the past. A two minute window should be acceptable. 118 | 119 | ```php 120 | /* check if token comes from the future */ 121 | $now = time(); 122 | if ($now < $matches['timestamp']) 123 | return FALSE; 124 | 125 | /* check if token is too old */ 126 | if (120 < ($now - $matches['timestamp'])) 127 | return FALSE; 128 | ``` 129 | 130 | As last step the signature has to be generated and has to be compared with the $password 131 | 132 | ```php 133 | /* generate signature and compare with the password */ 134 | $signature = base64_encode(hash_hmac('sha256', $username, 'evenmoresecretstring', true)); 135 | if ($signature != $password) 136 | return FALSE; 137 | ``` 138 | 139 | A reply as explained in HTTP API documentation has to be sent when everything went fine. The AP will then send the user back to the splashpage which then can take appropriate actions. 140 | 141 | ### Source Code 142 | 143 | A complete example can be found under in the code directory. 144 | 145 | * Splashpage: uam.php 146 | * HTTP API server: server.php 147 | -------------------------------------------------------------------------------- /captive_portal/splash_pages/external/click_to_enter/code/php/server.php: -------------------------------------------------------------------------------- 1 | 'REJECT', 24 | 'RA' => '0123456789abcdef0123456789abcdef', 25 | 'BLOCKED_MSG' => 'Rejected! This doesnt look like a valid request', 26 | ); 27 | 28 | 29 | /** 30 | * print_dictionary - Print dictionary as encoded key-value pairs 31 | * @dict: Dictionary to print 32 | */ 33 | function print_dictionary($dict) 34 | { 35 | foreach ($dict as $key => $value) { 36 | echo '"', rawurlencode($key), '" "', rawurlencode($value), "\"\n"; 37 | } 38 | } 39 | 40 | /** 41 | * calculate_new_ra - calculate new request authenticator based on old ra, code 42 | * and secret 43 | * @dict: Dictionary containing old ra and code. new ra is directly stored in it 44 | * @secret: Shared secret between node and server 45 | */ 46 | function calculate_new_ra(&$dict, $secret) 47 | { 48 | if (!array_key_exists('CODE', $dict)) 49 | return; 50 | 51 | $code = $dict['CODE']; 52 | 53 | if (!array_key_exists('RA', $dict)) 54 | return; 55 | 56 | if (strlen($dict['RA']) != 32) 57 | return; 58 | 59 | $ra = hex2bin($dict['RA']); 60 | if ($ra === FALSE) 61 | return; 62 | 63 | $dict['RA'] = hash('md5', $code . $ra . $secret); 64 | } 65 | 66 | /** 67 | * decode_password - decode encoded password to ascii string 68 | * @dict: dictionary containing request RA 69 | * @encoded: The encoded password 70 | * @secret: Shared secret between node and server 71 | * 72 | * Returns decoded password or FALSE on error 73 | */ 74 | function decode_password($dict, $encoded, $secret) 75 | { 76 | if (!array_key_exists('RA', $dict)) 77 | return FALSE; 78 | 79 | if (strlen($dict['RA']) != 32) 80 | return FALSE; 81 | 82 | $ra = hex2bin($dict['RA']); 83 | if ($ra === FALSE) 84 | return FALSE; 85 | 86 | if ((strlen($encoded) % 32) != 0) 87 | return FALSE; 88 | 89 | $bincoded = hex2bin($encoded); 90 | 91 | $password = ""; 92 | $last_result = $ra; 93 | 94 | for ($i = 0; $i < strlen($bincoded); $i += 16) { 95 | $key = hash('md5', $secret . $last_result, TRUE); 96 | for ($j = 0; $j < 16; $j++) 97 | $password .= $key[$j] ^ $bincoded[$i + $j]; 98 | $last_result = substr($bincoded, $i, 16); 99 | } 100 | 101 | $j = 0; 102 | for ($i = strlen($password); $i > 0; $i--) { 103 | if ($password[$i - 1] != "\x00") 104 | break; 105 | else 106 | $j++; 107 | } 108 | 109 | if ($j > 0) { 110 | $password = substr($password, 0, strlen($password) - $j); 111 | } 112 | 113 | return $password; 114 | } 115 | 116 | /** 117 | * validate_login - check if the login was valid for tos page 118 | * 119 | * Returns TRUE for valid logins, FALSE on errors 120 | */ 121 | function validate_login($username, $password, $mac) 122 | { 123 | global $token_ttl; 124 | global $sigsecret; 125 | 126 | /* split into mac and timestamp */ 127 | $ret = preg_match('/^(?P([A-F0-9]{2}\-){5}[A-F0-9]{2})_(?P[0-9]+)$/', 128 | $username, $matches); 129 | if (!$ret) 130 | return FALSE; 131 | 132 | /* check if mac is the same */ 133 | if (str_replace(':', '-', $mac) != $matches['mac']) 134 | return FALSE; 135 | 136 | /* check if token comes from the future */ 137 | $now = time(); 138 | if ($now < $matches['timestamp']) 139 | return FALSE; 140 | 141 | /* check if token is too old */ 142 | if ($token_ttl < ($now - $matches['timestamp'])) 143 | return FALSE; 144 | 145 | /* generate signature and compare with the password */ 146 | $signature = base64_encode(hash_hmac('sha256', $username, $sigsecret, true)); 147 | if ($signature != $password) 148 | return FALSE; 149 | 150 | return TRUE; 151 | } 152 | 153 | /* copy request authenticator */ 154 | if (array_key_exists('ra', $_GET) && strlen($_GET['ra']) == 32 && ($ra = hex2bin($_GET['ra'])) !== FALSE && strlen($ra) == 16) { 155 | $response['RA'] = $_GET['ra']; 156 | } 157 | 158 | /* decode password when available */ 159 | $password = FALSE; 160 | if (array_key_exists('username', $_GET) && array_key_exists('password', $_GET)) 161 | $password = decode_password($response, $_GET['password'], $secret); 162 | 163 | /* store mac when available */ 164 | $mac = FALSE; 165 | if (array_key_exists('mac', $_GET)) 166 | $mac = $_GET['mac']; 167 | 168 | /* decode request */ 169 | if (array_key_exists('type', $_GET)) { 170 | $type = $_GET['type']; 171 | 172 | switch ($type) { 173 | case 'login': 174 | if ($password === FALSE) 175 | break; 176 | 177 | if ($mac === FALSE) 178 | break; 179 | 180 | if (validate_login($_GET['username'], $password, $mac) === TRUE) { 181 | unset($response['BLOCKED_MSG']); 182 | $response['CODE'] = "ACCEPT"; 183 | $response['SECONDS'] = 3600; 184 | $response['DOWNLOAD'] = 2000; 185 | $response['UPLOAD'] = 800; 186 | } else { 187 | $response['BLOCKED_MSG'] = "Invalid username or password"; 188 | } 189 | break; 190 | case 'status': 191 | if ($mac === FALSE) 192 | break; 193 | 194 | if ($mac == '5C:0A:5B:4E:6A:C5') { 195 | unset($response['BLOCKED_MSG']); 196 | $response['CODE'] = "ACCEPT"; 197 | $response['SECONDS'] = 120; 198 | $response['DOWNLOAD'] = 3000; 199 | $response['UPLOAD'] = 400; 200 | } else { 201 | $response['BLOCKED_MSG'] = "Unknown Client"; 202 | } 203 | break; 204 | case 'acct': 205 | case 'logout': 206 | if ($mac === FALSE) 207 | break; 208 | unset($response['BLOCKED_MSG']); 209 | $response['CODE'] = "OK"; 210 | break; 211 | }; 212 | } 213 | 214 | /* calculate new request authenticator based on answer and request -> send it out */ 215 | calculate_new_ra($response, $secret); 216 | print_dictionary($response); 217 | 218 | ?> 219 | -------------------------------------------------------------------------------- /captive_portal/splash_pages/external/click_to_enter/code/php/uam.php: -------------------------------------------------------------------------------- 1 | 0) { 32 | $crypt_secret = md5($hexchall . $secret, TRUE); 33 | $len_secret = 16; 34 | } else { 35 | $crypt_secret = $hexchall; 36 | $len_secret = strlen($hexchall); 37 | } 38 | 39 | /* simulate C style \0 terminated string */ 40 | $plain .= "\x00"; 41 | $crypted = ''; 42 | for ($i = 0; $i < strlen($plain); $i++) 43 | $crypted .= $plain[$i] ^ $crypt_secret[$i % $len_secret]; 44 | 45 | $extra_bytes = rand(0, 16); 46 | for ($i = 0; $i < $extra_bytes; $i++) 47 | $crypted .= chr(rand(0, 255)); 48 | 49 | return bin2hex($crypted); 50 | } 51 | 52 | /* WARNING better use http_redirect from pecl_http for this */ 53 | function redirect($url, $statusCode = 302) 54 | { 55 | header('Location: ' . $url, true, $statusCode); 56 | } 57 | 58 | function main() 59 | { 60 | global $secret; 61 | global $sigsecret; 62 | 63 | foreach (array('res', 'uamip', 'uamport', 'mac') as $req) { 64 | if (!array_key_exists($req, $_GET)) { 65 | header("Content-Type: text/plain"); 66 | print("No ". $req ." found\n"); 67 | return; 68 | } 69 | } 70 | 71 | switch ($_GET['res']) { 72 | case 'notyet': 73 | case 'failed': 74 | /* continue after this block */ 75 | break; 76 | case 'logoff': 77 | header("Content-Type: text/plain"); 78 | print("Bye\n"); 79 | return; 80 | case 'success': 81 | header("Content-Type: text/plain"); 82 | print("Congrats. You are now logged in\n"); 83 | return; 84 | default: 85 | header("Content-Type: text/plain"); 86 | print("Sorry, I don't understand you\n"); 87 | return; 88 | } 89 | 90 | /* autogenerate a username and password */ 91 | $username = $_GET['mac'] . '_' . time(); 92 | $password = base64_encode(hash_hmac('sha256', $username, $sigsecret, true)); 93 | 94 | if (!array_key_exists('challenge', $_GET)) { 95 | /* this should not happen and is not part of the specification */ 96 | $pw_crypt = urlencode($password); 97 | } else { 98 | $pw_crypt = encode_password_challenge($password, 99 | $_GET['challenge'], 100 | $secret); 101 | } 102 | 103 | if ($pw_crypt === FALSE) { 104 | header("Content-Type: text/plain"); 105 | print("Failed to encode the password\n"); 106 | return; 107 | } 108 | 109 | 110 | /* WARNING this check should use some extra token which changes often 111 | * to avoid users to automate the form submission with zero effort 112 | */ 113 | if (array_key_exists('accept', $_GET) && $_GET['accept'] == 'accept') { 114 | redirect('http://'. $_GET['uamip'] . ':' . $_GET['uamport'] . '/logon?username=' . urlencode($username) . '&password=' . $pw_crypt); 115 | } else { 116 | render_page(); 117 | } 118 | } 119 | 120 | /* WARNING better use proper template system - this is just a quick hack for this example */ 121 | function render_page() 122 | { 123 | ?> 124 | '; ?> 125 | 126 | 127 | 128 | Splashpage 129 | 130 | 131 | 132 | 133 |

TOS

134 | 135 |

Lorem ipsum dolor sit amet, reque tation ad duo, illud 136 | aperiam labores eam ad, legere ignota nostrum ea duo. In sea 137 | etiam congue decore. Illud tantas corrumpit est in, sale prima 138 | error id mei. Ex his fugit aeterno, an natum tincidunt 139 | abhorreant sea, et eos modus convenire signiferumque. Ei eum 140 | iuvaret reprehendunt. Aliquid tacimates quo at, vix dicta 141 | scribentur consequuntur eu.

142 | 143 |

Munere omnesque ei ius. Aliquam nominavi mei te, eum zril 144 | facete qualisque at, dicta hendrerit consequuntur ei nec. Odio 145 | integre intellegebat ius ea, agam diceret nominati eu nec, no 146 | menandri persecuti quaerendum eos. Eu latine explicari 147 | voluptaria vel. Qui mucius luptatum id. Vis in volumus 148 | scribentur, ad eam vulputate definitiones, ei nec exerci 149 | moderatius posidonium.

150 | 151 |
"> 152 |
153 | 154 | 155 | 156 | 157 | 160 | 161 | 164 | 165 | 166 |
167 |
168 | 169 |
170 |
171 | 172 | 173 | 174 | 180 | 181 | -------------------------------------------------------------------------------- /captive_portal/splash_pages/external/click_to_enter/images/2015-08-26_Click-To-Enter_nodb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudtrax/docs/50fe4ac1ec7dc3b10c625f6d33d1f94853135591/captive_portal/splash_pages/external/click_to_enter/images/2015-08-26_Click-To-Enter_nodb.png -------------------------------------------------------------------------------- /captive_portal/splash_pages/external/code/php/README.md: -------------------------------------------------------------------------------- 1 | Files shown here demonstrate two slightly different ways of implementing a simple, externally-hosted splash page implementation using the UAM protocol. See [Externally Hosted Splash Pages](../../README.md). 2 | -------------------------------------------------------------------------------- /captive_portal/splash_pages/external/code/php/splash.php: -------------------------------------------------------------------------------- 1 | 0) { 14 | $crypt_secret = md5($hexchall . $secret, TRUE); 15 | $len_secret = 16; 16 | } else { 17 | $crypt_secret = $hexchall; 18 | $len_secret = strlen($hexchall); 19 | } 20 | 21 | /* simulate C style \0 terminated string */ 22 | $plain .= "\x00"; 23 | $crypted = ''; 24 | for ($i = 0; $i < strlen($plain); $i++) 25 | $crypted .= $plain[$i] ^ $crypt_secret[$i % $len_secret]; 26 | 27 | $extra_bytes = 0;//rand(0, 16); 28 | for ($i = 0; $i < $extra_bytes; $i++) 29 | $crypted .= chr(rand(0, 255)); 30 | 31 | return bin2hex($crypted); 32 | } 33 | 34 | function print_logon_form() { 35 | ?> 36 | 37 | 38 | Example Remote Splash Page 39 | 40 |
41 |
42 |
43 | "> 44 | "> 45 | "> 46 | "> 47 | 48 |
49 | 50 | 51 | 57 | 58 | 59 | 60 | Example Remote Splash Page 61 | '; 64 | } 65 | ?> 66 | 67 | 68 | Welcome! You will be redirected to your destination momentarily

'; 71 | } else { 72 | echo '

Welcome!

'; 73 | } 74 | ?> 75 | 76 | 77 | 82 | 83 | 84 | Example Remote Splash Page 85 |

Authentication Failed

86 | 87 | 92 | 93 | 94 | Example Remote Splash Page 95 |

GoodBye

96 | 97 | 98 | 0) { 15 | $crypt_secret = md5($hexchall . $secret, TRUE); 16 | $len_secret = 16; 17 | } else { 18 | $crypt_secret = $hexchall; 19 | $len_secret = strlen($hexchall); 20 | } 21 | 22 | /* simulate C style \0 terminated string */ 23 | $plain .= "\x00"; 24 | $crypted = ''; 25 | for ($i = 0; $i < strlen($plain); $i++) 26 | $crypted .= $plain[$i] ^ $crypt_secret[$i % $len_secret]; 27 | 28 | $extra_bytes = 0;//rand(0, 16); 29 | for ($i = 0; $i < $extra_bytes; $i++) 30 | $crypted .= chr(rand(0, 255)); 31 | 32 | return bin2hex($crypted); 33 | } 34 | 35 | 36 | $username = $_POST["username"]; 37 | $password = $_POST["password"]; 38 | $uamip = $_POST["uamip"]; 39 | $uamport = $_POST["uamport"]; 40 | $challenge = $_POST["challenge"]; 41 | 42 | $encoded_password = encode_password($password, $challenge, $uam_secret); 43 | 44 | $redirect_url = "http://$uamip:$uamport/logon?" . 45 | "username=" . urlencode($username) . 46 | "&password=" . urlencode($encoded_password); 47 | 48 | # point them toward a different landing page if you want ... 49 | # (couldn't get this working) 50 | #$redirect_url .= "&redir=" . urlencode("http://www.nytimes.com"); 51 | 52 | session_start(); 53 | if(isset($_POST["userurl"])) { 54 | $_SESSION["userurl"] = $_POST["userurl"]; 55 | } else { 56 | unset($_SESSION["userurl"]); 57 | } 58 | session_write_close(); 59 | 60 | 61 | if ($DEBUG) { 62 | echo "userurl: {" . $_SESSION['userurl'] . "}
"; 63 | echo "REDIRECT_URL: {\"" . $redirect_url . "\"}

"; 64 | 65 | echo "POST data:"; 66 | echo "
";
67 | 	print_r($_POST);
68 | 	echo "
" . "
"; 69 | 70 | echo "SERVER data:"; 71 | echo "
";
72 | 	print_r($_SERVER);
73 | 	echo "
"; 74 | } else { 75 | header('Location: ' . $redirect_url); 76 | exit(); 77 | } 78 | 79 | ?> 80 | -------------------------------------------------------------------------------- /captive_portal/splash_pages/external/code/php/uam_simple_server.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 |
"; 12 | echo "res: {\"" . $_GET["res"] . "\"}
"; 13 | echo "request_uri: {\"" . $_SERVER["REQUEST_URI"] . "\"}

"; 14 | 15 | echo "SERVER data:"; 16 | echo "
";
17 | 		print_r($_SERVER);
18 | 		echo "
"; 19 | } 20 | ?> 21 | 22 | 27 |

Please Log in

28 |
29 |
30 |
31 | "> 32 | "> 33 | "> 34 | "> 35 | 36 |
37 | "; 43 | echo ''; 44 | echo ""; 45 | } 46 | else { 47 | echo "

Log-in successful!

"; 48 | } 49 | } 50 | else if ($res === "failed") { 51 | echo "

Whoops, failed to authenticate

"; 52 | } 53 | else if ($res === "logoff") { 54 | echo "

Logging off ...

"; 55 | } 56 | else { 57 | echo "

Oops!, bad 'res' parameter

"; 58 | } 59 | ?> 60 | 61 | 62 | -------------------------------------------------------------------------------- /captive_portal/splash_pages/external/images/2014-06-25_Acct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudtrax/docs/50fe4ac1ec7dc3b10c625f6d33d1f94853135591/captive_portal/splash_pages/external/images/2014-06-25_Acct.png -------------------------------------------------------------------------------- /captive_portal/splash_pages/external/images/2014-06-26_Login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudtrax/docs/50fe4ac1ec7dc3b10c625f6d33d1f94853135591/captive_portal/splash_pages/external/images/2014-06-26_Login.png -------------------------------------------------------------------------------- /captive_portal/splash_pages/external/images/External_splash-page_configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudtrax/docs/50fe4ac1ec7dc3b10c625f6d33d1f94853135591/captive_portal/splash_pages/external/images/External_splash-page_configuration.png -------------------------------------------------------------------------------- /captive_portal/splash_pages/external/images/cloudtrax_dashboard_auth_server_stub_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudtrax/docs/50fe4ac1ec7dc3b10c625f6d33d1f94853135591/captive_portal/splash_pages/external/images/cloudtrax_dashboard_auth_server_stub_success.png -------------------------------------------------------------------------------- /captive_portal/splash_pages/external/images/cloudtrax_dashboard_uam_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudtrax/docs/50fe4ac1ec7dc3b10c625f6d33d1f94853135591/captive_portal/splash_pages/external/images/cloudtrax_dashboard_uam_config.png --------------------------------------------------------------------------------