General description on API protocol: ========================================================================= All API nodes currently use POST requests for all parameters. All API nodes return JSON data unless explicitly noted , and all JSON results will contain a 'response' element. The 'response' may be set to: * 'ok' on success. Further contents depend on the node requested. * 'invalid' on invalid requests. A 'cause' element should give a keyword denoting what was invalid about the request... * 'failed' on simple failure. A 'cause' element is supplied, with a failure-keyword (e.g. 'nonexistant', 'inuse', 'dbfail'). A final 'detail' is commonly included, with a verbose desctiption. * 'error' on critical failure. Regard this as a soft form of "500 server error"... So, in all Return-specifications it should be assumed that 'response' is set to 'ok' on success. Authentication mechanism: ========================================================================= All API nodes except 'auth/login' and 'auth/logout' require a 'token authentication' based on a session identifier (session) and a session authentication key (auth_key) passed as parameters. The 'session' and 'auth_key' values are provided as response to a successful API login through 'auth/login', and must be included as parameters to all subsequent calls to the API. The 'auth_key' has a limited lifetime (configurabel, defaul 5 minutes), significantly shorter than the session lifetime. The 'auth_kye' must be renewed before the key lifetime expires, by issuing a call to 'auth/ping'. The ping call will provide a new key that replaces the previous 'auth_key' in subsequent calls to the API. API login may be done using either a username/password combination, or using a registered API key tied to the host originating the requests. See documentation for nodes under 'auth/*' for information about adding, removing and maintaining authentication users and keys. Sample session: call auth/login with username and password store session and auth_key for further requests perform API calls with session and auth_key set ... call auth/ping with session and auth_key set update the auth_key used for requests perform API calls with session and auth_key set ... call auth/logout with session set NOTE: that the API also requires COOKIE-support in the user-agent used to communicate with the API. NOTE: See README for information on configuring API authentication backends, and boostrapping the user/key authorizations. Parameter notes: ========================================================================= 'user'/'alias' parameters marked '*' may alternatively be given as username=foo + domain=bar alias_username=foo + alias_domain=bar List of API nodes: ========================================================================= auth/login ------------------- Required: username=authuser password=authpassword Alternate: api_key=API_KEY Description: TODO: DOCUMENT THIS. Return: Returns 'session' set to the allocated session_name and 'auth_key' set to the generated auth_key. These must be used for further access, and the key must be refreshed through 'auth/ping' at intervals. Returns 'failed' with 'cause' = 'unauthorized' if Login failed auth/logout ------------------- Required: session=session_name Description: TODO: DOCUMENT THIS. // De-authenticate/deauthorize the ongoing session. // I.e. destroy session data, remove session cookies. Return: Returns 'ok' on successful logout auth/ping ------------------- Required: session=$session_name auth_key=$auth_key Description: TODO: DOCUMENT THIS. // API clients are required to periodically ping the server // The time between pings (interval) is 5 minutes? // A ping call refreshes cookie lifetimes, then // generates and stores a new auth_key // The ping required a valid session... // A successful ping returns a 'response' => 'pong' // along with the new auth_key. NOTE!: Does not give 'response' => 'ok' ! Return: Returns 'pong' with 'auth_key' set to the new key to be used with the session_name in further requests. auth/new_apikey ------------------- Required: host_ip=10.20.30.40 access=limited_read Description: TODO: DOCUMENT THIS. 'access' may be one of: * limited_read * full_read * read_write Return: Returns 'key', 'host' and 'access'. Returns 'invalid' with 'cause' = 'parameters' on parameter error Returns 'failed' with 'cause' = 'error' on database errors. TODO: Change 'error' to 'dbfail'. auth/remove_apikey ------------------- Required: api_key=$key Description: TODO: DOCUMENT THIS. Return: Returns 'key' set to the removed key on success. Returns 'invalid' on parameter error Returns 'failed' with 'cause' = 'nonexistant' if key does not exist. Returns 'failed' with 'cause' = 'error' on database errors. TODO: Change 'error' to 'dbfail'. auth/list_apikeys ------------------- Description: TODO: DOCUMENT THIS. Return: Returns 'list' auth/authorize_user ------------------- Required: username=authuser access=limited_read Description: TODO: DOCUMENT THIS. // Add or update a valid back-end user in authorization // if the current authentication has write access. // If the authorization does not exist, add it. // If the user is already authorized, replace access level. 'access' may be one of: * limited_read * full_read * read_write Return: Returns 'user' and 'access' when user was successfully added. Returns 'invalid' with 'cause' = 'parameters' on parameter error Returns 'failed' with 'cause' = 'nonexistant' if user does not exist. Returns 'failed' with 'cause' = 'error' on database errors. auth/remove_user ------------------- Required: username=authuser Description: TODO: DOCUMENT THIS. // If the current authentication has write access: // Remove authorization for the given users. // Delete user from backend if backend is read-write. Return: Returns 'user' when user was successfully removed. Returns 'invalid' with 'cause' = 'parameters' on parameter error Returns 'failed' with 'cause' = 'nonexistant' if user does not exist. Returns 'failed' with 'cause' = 'error' on database errors. auth/list_users ------------------- Description: Returns 'list' auth/add_user ------------------- Description: TODO: This is not implemented. Document, write test and implement. Returns 'notimplemented' // Add user to backend if backend is read-write and // the current authentication has write access. // The created user should be added to authorizations // with an access level of "limited_read (1)" auth/update_user ------------------- Description: TODO: This is not implemented. Document, write test and implement. Returns 'notimplemented' // Update the given user in the backend, if the backend // is read-write, and the current authentication has // write access. user/get ------------------- Required: user=user@bar.bz * Description: Return User information, not including hardphone association. Return: On success, 'user' will be set to the user-data set: for all users: type,username,password,domain,authid,registrar, r_port,proxy,p_port,dialplan,displayname,linetext for local users (kamailio), as above, plus: email,ha1,ha1b,rpid,permittedcalls Returns 'failed' with 'cause' = 'nonexistant' if user does not exist.. Returns 'failed' with 'cause' = 'invalid' if SIP address is malformed. user/location ------------------- Required: user=user@bar.bz * Description: Return registered user agent locations for a given user, with contact and user agent information. Return: On success, returns the array 'locations', each element contianing: 'contact' is the registration contact (the location) 'useragent' is the UA string from the client 'expires' is the expiration time of the registration, as unix timestamp Returns 'failed' with 'cause' = 'offline' if user is not registered ... Returns 'failed' with 'cause' = 'invalid' if SIP address is malformed. TODO: Add a return code for non-existant subscriber? user/list ------------------- Optional: search=foo@bar Description: Return an array of users, with 'user' set to SIP-address and 'displayname' set to the users Displayname. If the search parameter is included, a globbing search is performed, and only matches are listed. Return: TODO: Document this user/add_local ------------------- Required: user=foo@bar.bz * displayname=baz email=qux@zef.tld Description: Adds a user account to both Kamailio and provisioning, if the username@domain is nonexistant, and the domain is local (handled by Kamailio). The password for the user is auto-generated. Return: Returns a full user object, the same form as user/get Returns 'failed' with 'cause' = 'exists' if address is taken Returns 'failed' with 'cause' = 'invalid' if SIP address is malformed. Returns 'failed' with 'cause' = 'servfail' if no default servers could be determined for the requested domain. See domain/set_servers. Returns 'failed' with 'cause' = 'dbfail' on database errors.. user/add_remote ------------------- Required: user=foo@bar.bz * password=S3cr3t displayname=baz registrar=sip.example.com Optional: r_port=5060 proxy=sipproxy.example.com p_port=5060 authid=realfoo dialplan=(.*) linetext=lalala Description: Adds a provisioning user for a remote SIP account. This allows locally provisioned hardphones to be associated with non-local SIP accounts. Return: Returns a full user object, the same form as user/get Returns 'failed' with 'cause' = 'domain' if an attempt is made to add_remote for a local domain. Returns 'failed' with 'cause' = 'exists' on any address collisions Returns 'failed' with 'cause' = 'dbfail' on database errors.. user/remove ------------------- Required: user=foo@bar.bz * Description: Removes user account from Kamailio, if present, and removes user from provisioning. Will fail if user has associated hardphones, remove phones before removing user. TODO: Should fail if user has associated aliases. Remove aliases before removing user. Return: TODO: Document additional data on 'result' = 'ok' Returns 'failed' with 'cause' = 'nonexistant' if user does not exist.. Returns 'failed' with 'cause' = 'invalid' if SIP address is malformed. Returns 'failed' with 'cause' = 'inuse' if phone-associations exists TODO: Returns 'failed' with 'cause' = 'inuse' if aliases exist for the address user/change_pw ------------------- Required: user=foo@bar.bz * password=baz Description: Changes the password for the given user, returns 'ok' with 'detail' as a descriptive text on success. In the current implementation, provisioning and kamailio passwords are handled separately, so one may succeed and the other fail. As is, this permits password changes of non-local provision users with minimal effort. Return: Returns 'detail' on success. Returns 'failed' with 'cause' = 'invalid' if SIP address is malformed. Returns 'failed' with 'cause' = 'nonexistant' if user does not exist. Returns 'failed' with 'cause' = 'dbfail' if the database request failed. Note that this may be because the given password was identical to the old. user/change_email ------------------- Required: user=foo@bar.bz * email=user@example.com Description: Changes the email address for the given user, returns 'ok' with 'user' set to the requested username and 'email' set to the email address. This only applies to kamailio local users. Return: Returns 'user' and 'email' on success. Returns 'failed' with 'cause' = 'invalid' if SIP address is malformed. TODO: Returns 'failed' with 'cause' = 'nonexistant' if user does not exist. Returns 'failed' with 'cause' = 'dbfail' if the database request failed. This may be because no update was required ... user/update ------------------- Required: user=foo@bar.bz * optionals: displayname=baz dialplan=(.*) linetext=lalala email=foo@bar.baz Updates user with the given data.... TODO: Extend this description ;) Return: Returns two arrays on success, 'updated' containing updated params and 'skipped' containing specified parameters that did not require any changes. Returns 'failed' with 'cause' = 'invalid' if SIP address is malformed. Returns 'failed' with 'cause' = 'nonexistant' if user does not exist. Returns 'failed' with 'cause' = 'dbfail' if the database request failed. With database errors, the array 'failed' will contain any parameters that may have caused the dbfail. user/available ------------------- user=foo@bar.bz * Description: Tests an address to see if it is available. Return: Returns 'ok' with 'cause' = 'nonexistant' if the address is available. TODO: Returns 'failed' with 'cause' = 'domain' if the domain is not a kamailio domain. Returns 'failed' with 'cause' = 'exists' if the address is in use. Returns 'failed' with 'cause' = 'invalid' if SIP address is malformed. user/gen_pw ------------------- Description: Generates a random password on the same form as that used by user/add_local. NOTE: This node does not use JSON formatting. Return: Return is always a password in plain text, with no formatting. phone/get ------------------- Optional: mac=f00ba2ba5c00 user=foo@bar.bz * Description: Returns a list of associated elements. If MAC address is given as parameter, a list of associated user@domain addresses is retruned. If user@domain is given as parameter, a list of associated mac addresses is returned. Return: Returns 'list' as an array of phone-user mappings on success, where each element of 'list' consists of either usernames or mac-adresses. Returns 'failed' with 'cause' = 'nonexistant' on no matches. Returns 'failed' with 'cause' = 'invalid' if SIP address is malformed. phone/list ------------------- Optional: search=f00ba2 Description: Returns the list of registered MAC addresses. If the (optional) search parameter is included, the list is the result of a globbing search, a subset matching on the MAC addresses. Return: Returns 'list' as a simple array of registered MAC-adresses on success, the list will be limited to addresses matching the search if provided. phone/add ------------------- Required: user=foo@bar.bz * mac=f00ba2ba5c00 Description: Adds a phone-to-user association to Provisioning. The user may be provided as 'username' and 'domain' in separate parameters, or as 'user' in sip-address form. The MAC address must be valid, in plain-hex or colon-separated form, and the user must match a valid user registration. Return: Returns 'mac', 'username' and 'domain' on success. Returns 'failed' with 'cause' = 'invalid' if SIP address is malformed. Returns 'failed' with 'cause' = 'invalid' if MAC address is malformed. Returns 'failed' with 'cause' = 'nonexistant' if user does not exist. Returns 'failed' with 'cause' = 'exists' if user+mac is already reg. Returns 'failed' with 'cause' = 'dbfail' on database failure. phone/remove ------------------- Required: user=foo@bar.bz * mac=f00ba2ba5c00 Description: Deletes a phone-to-user association. Required parameters and formats are identical to the phone/add node. Return: Returns 'mac', 'username' and 'domain' on success. Returns 'failed' with 'cause' = 'invalid' if SIP address is malformed. Returns 'failed' with 'cause' = 'invalid' if MAC address is malformed. Returns 'failed' with 'cause' = 'nonexistant' on no such registration. numbers/list ------------------- Optional: search=1234 random=true limit=n Description: Gives a list of entries in the phone number pool. With no parameters, the entire (ordered) list of available numbers is returned. With 'search', the result of a pre-post globbing search is returned With 'random', a list of entries is returned unordered (random order) For all nodes, 'limit' may be a numeric limit of desired results. Return: Returns the list of numbers as the array 'list'. Returns 'failed' with 'cause' = 'empty' if no numbers were listed... numbers/add_range ------------------- Required: start=+4712345000 end=+4712345678 Description: The start and end must have identical lead-in, and start must be less than end. Both numbers must be given in E164 format, remember to urlencode the plus (+ -> %2B). The numeric component of both will be extracted, and all numbers in the range will be iteratively added to the pool, including the ending number. Return: TODO: Document additional info provided on 'result' = 'ok' Returns 'failed' with 'cause' = 'rejected' with 'detail' set on failure numbers/add ------------------- Required: number=+4712345679 Description: Adds the given number to the pool, after verifying that the number is in valid E164 format, and that the number does not already exist in the pool. Return: Returns "Added " in 'detail' on success. Returns 'failed' with 'cause' = 'rejected' and 'detail' set to "Not a valid E164 number" if the provided number could not pass E164 validation. Returns 'failed' with 'cause' = 'exists' if number is already in the pool. TODO: Returns 'failed' with 'cause' = 'dbfail' on database failure. TODO: Gives 'cause' = 'rejected' with no detail in current code. numbers/remove ------------------- Required: number=+4712345676 Description: Removes/pulls a number from the pool. The number must be a valid e164 number, and must be present in the pool. Return: Returns 'number' set to the number that was pulled from the pool. Returns 'failed' with 'cause' = 'rejected' and 'detail' set to "Not a valid E164 number" if the provided number could not pass E164 validation. Returns 'failed' with 'cause' = 'nonexistant' if number is not the pool. Returns 'failed' with 'cause' = 'dbfail' on database failure. numbers/inpool ------------------- Required: number=+4712345676 Description: Tests if the given E164 number is in the pool. Return: Returns 'number' = '' If the number is in the pool Returns response = 'failed' with 'cause' = 'nonexistant' if not. alias/list ------------------- Optional: alias=foo@bar.bz destination=foo@bar.bz e164=true Description: With no parameters, this will return all defined aliases (potentially a huge list). With the destination parameter set, only aliases for that destination will be listed, and with the e164 option set to true, only an e164 alias will be returned (if one/it exists). The alias parameter gives the same behaviour, but looks up an alias address instead of the destination. The e164 option is not valid for the alias search (naturally). Return: Returns 'ok' on success, with an array 'aliases' containing 'destination' and 'alias' pairs. Returns 'ok' with an empty array if the search gave no results. Returns 'ok' with an empty array if the database search fails. Returns 'failed' with 'cause' = 'invalid' on invalid SIP addresses. alias/add ------------------- Required: alias=foo@bar.bz * destination=bar@qux.zx Description: Add an alias specified by alias_username and alias_domain that points to the destination SIP-adress. Return: On success, 'ok' is returned, with 'alias' and 'destination' set to the resulting alias and destination adresses. Returns 'invalid' with 'cause' = 'destination' or 'alias' if the resulting alias-adress or destination are invalid SIP-adresses. Returns 'failed' with 'cause' = 'nxdomain' if the given alias domain is not a Kamailio domain. Returns 'failed' with 'cause' = 'exists' if the alias is an E164 number and the user already has an E164 alias registered. Returns 'failed' with 'cause' = 'nonexistant' when an alias for a local domain is requested, but the local subscriber does not exist. Returns 'failed' with 'cause' = 'exists' for aliases that already exists, (and aliases that overlaps SIP-accounts - not implemented). alias/remove ------------------- Required: alias=foo@bar.bz * Description: Removes the alias given by alias_username and alias_domain. Return: Returns 'ok' with the removed alias adress as 'alias' on success Returns 'failed' with 'cause' = 'nonexistant' it the alias does not exists. Returns 'invalid' with 'cause' = 'address' if the given alias is not a valid SIP adress. domain/list ------------------- Description: Returns a list of configured and valid domains. May return an empty list if no domains are configured for kamailio. No node is provided to add domains, this is not a task for Hermes, but a kamailio configuration task. Return: Returns a list of domains as 'list' on success. Returns 'failed' on database errors. domain/get_servers ------------------- Required: domain=bar.bz Description: Provides the default registrar/proxy/provisioning server information for the given domain. Return: Returns 'ok' with 'servers' set to a kvp-set on success, containing: 'registrar','r_port','proxy','p_port','prov_url'. Returns 'failed' with 'cause' = 'nonexistant' if lookup of data for given domain results in an empty set (not configured). domain/set_servers ------------------- Required: domain=bar.bz registrar=server.bar.bz r_port=5060 proxy=proxy.bar.bz p_port=5060 prov_url=http://server.bar.bz/hermes/prov Description: Sets the server data for the given domain. All of these parameters are Required: 'domain', 'registrar', 'r_port', 'proxy', 'p_port', 'prov_url'. If no default server data is defined, the configuration is added. If server configuration existed, the default data is updated. Return: Returns 'ok' with 'servers' set as domain/get_servers on success. Returns 'failed' with 'cause' set to 'cause' set to 'parameters' if one of these is true: * Missing parameters * One or more NULL/empty parameters * Non-numeric values for port-numbers. If you need to determine which of these triggered, the 'description' contains a text describing the actual fail. Return 'failed' with 'cause' = 'error' on database failure. permission/get ------------------- Required: user=foo@bar.bz * Description: Gets the permissions value for a user, with no parsing. The permission system is based on bit-flags, with the following values: NOCALLS 1 EMERGENCY 2 INTERNAL 4 VOIP 8 NATIONAL 16 SERVICES 32 INTERNATIONAL 64 OTHERS 128 The user must match a valid user registration. Return: Returns 'permission' set to the applied permission value on success. Returns 'failed' with 'cause' = 'nonexistant' if user does not exist.. Returns 'failed' with 'cause' = 'dbfail' on database failure. permission/set ------------------- Required: user=foo@bar.bz * permission=63 Description: Sets the permissions value for a user directly. The permission system is based on bit-flags, with the following values: NOCALLS 1 EMERGENCY 2 INTERNAL 4 VOIP 8 NATIONAL 16 SERVICES 32 INTERNATIONAL 64 OTHERS 128 To give a subscriber full permissions, set permission to 255. To give a subscriber National calls, all internal and VOIP calls, but not international, sat-phones and other, use 63. To give a user emergency and internal, but no other calls, the permission value becomes 7. The user must match a valid user registration. Return: Returns 'permission' set to the applied permission value on success. Returns 'failed' with 'cause' = 'dbfail' on database failure. BUGS: --------------------- api/alias/list destination=foo@bar.bz e164=true should return an empty array, returns false TODO list: --------------------- user/forward To set up permanent forwarding. Call forward is stored as a user preference value in the kamailio database. Evample of diverting calls to 'jonl@hig.no' to the number +4761135280': insert into usr_preferences (username, domain, attribute, value, last_modified) values ( 'jonl', 'hig.no', 'cfu', '+4761135280', now()); Some TODO's listed above ... Permissions! (user/permissions?user=...) The permissions bit-flag seems to be: NOCALLS 1 EMERGENCY 2 INTERNAL 4 VOIP 8 NATIONAL 16 SERVICES 32 INTERNATIONAL 64 OTHERS 128 can_write() checks! can_read_full() checks! Improve robustness of change_pw: fetch old password for rollback/testing. Check for locations where sql_dbexec_rows is more appropriate/correct than sql_dbexec Perform an evaluation of the security related to the api-key functionality, and replace if the method is evaluated as too weak. I.e. implement strong non-interactive, non-user based authentication.