Introduction ============ Welcome to the StoredSafe API ----------------------------- The StoredSafe API is a way for you to automate your interaction with the StoredSafe system. With the API, you can create your own scripts or applications with most of the functionality you can find inside StoredSafe. The StoredSafe API is RESTlike and HTTP-based. Basically, this means that the communication is made through normal HTTP requests. Open Source and StoredSafe -------------------------- StoredSafe develops and maintains some Open source code projects at `GitHub `__, which can be used as reference when coding. Currently the following projects has been published: * `tokenhandler `__ logins and aquires a token used for subsequent REST API calls to a StoredSafe instance. * `storedsafe-ruby `__ is a transparent Ruby wrapper for the StoredSafe REST-like API. * `storedsafe-python `__ is a transparent Python wrapper for the StoredSafe REST-like API. * `x509-vacuumer `__ locates, retrieves and stores X.509 certificates in StoredSafe. * `csv-importer `__ assists in importing objects via CSV files to StoredSafe. * `ssh-storedsafe `__ let’s a user login to a remote server using SSH after obtaining the required credentials (password) from StoredSafe. * `ansible-storedsafe `__ is a lookup module for information stored in StoredSafe. * `vault-storedsafe-client `__ is an ansible vault password client script helper when using ansible-vault to encrypt playbooks or variables. * `hiera-storedsafe `__ is a Hiera backend to retrieve secrets from Password StoredSafe. Authentication -------------- User credentials ~~~~~~~~~~~~~~~~ Authentication is needed in order to use the StoredSafe API, and for this a StoredSafe account is required. The credentials used for accessing the API are the same used to StoredSafe - in other words your login id and your pass phrase. API Key ~~~~~~~ You will also need an application key - API key (The API keys can be generated by a StoredSafe System Administrator from the system console). You generate your application key inside the StoredSafe control panel. It is supposed to be unique on an application basis, not user basis. This means that if you produce an application and then distribute it to the public, all users of this application should use the same application key (API key). The application key (API key) is provided as part of the authentication data. The authentication method for user credentials is a POST request using JSON data with the required credentials. Upon successful authentication a token will be returned, this token needs be used for all subsequent requests. .. note:: Hence this is a RESTlike API, not RESTful Server may induce token rotation at any time. Token Rotation ~~~~~~~~~~~~~~ The token might be rotated (changed) by the server at any given moment, the StoredSafe token returned in the CALLINFO object should be used for any subsequent calls. +-------------------------+----------------------------------------------------------+ | Attribute | Description | +=========================+==========================================================+ | .CALLINFO.token | Rotated StoredSafe-Token to be used for subsequent calls | +-------------------------+----------------------------------------------------------+ | .DATA.token | StoredSafe-Token as supplied at the call | +-------------------------+----------------------------------------------------------+ Example: ~~~~~~~~ :: GET /api/1.0/template/1001 x-http-token: 7TCRHJy6jPb1qlrZ0r7k3ht29a28c8i5qo433tdr67 { "CALLINFO": { "token": "MarBjhsl8zA4xGNG1nq6qlp7shsj76bb92296mdid6" }, "DATA": { "token": "7TCRHJy6jPb1qlrZ0r7k3ht29a28c8i5qo433tdr67" }, Server indicates that it wants to replace the old token with a new one, (CALLINFO.token != DATA.token), so for next API call, the new token is used: :: GET /api/1.0/template/1 x-http-token: MarBjhsl8zA4xGNG1nq6qlp7shsj76bb92296mdid6 { "CALLINFO": { "token": "MarBjhsl8zA4xGNG1nq6qlp7shsj76bb92296mdid6" }, "DATA": { "token": "MarBjhsl8zA4xGNG1nq6qlp7shsj76bb92296mdid6" }, This time, the server does not impose a token rotation. (CALLINFO.token == DATA.token) Two-factor Authentication (2FA) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ StoredSafe requires each user to use 2FA to authenticate at login. Currently two different methods exists, OTP (Yubico) and TOTP. Yubico OTP ~~~~~~~~~~ A Yubico OTP is a 44-character, one use, secure, 128-bit encrypted Public ID and Password. The OTP is comprised of two major parts: the first 12 characters remain constant and represent the Public ID of the YubiKey device itself. The remaining 32 characters make up a unique passcode for each OTP generated. *Example output from a YubiKey where the button has been pressed three times* :: cccjgjgkhcbbirdrfdnlnghhfgrtnnlgedjlftrbdeut cccjgjgkhcbbgefdkbbditfjrlniggevfhenublfnrev cccjgjgkhcbbcvchfkfhiiuunbtnvgihdfiktncvlhck The passcode is generated from a multitude of random sources, including counters for both YubiKey sessions and OTPs generated. When a Yubico OTP is verified, the session and OTP counter values are compared to last values submitted. If the counters are less than the previously used values the OTP is rejected. Copying an OTP will not allow another user to spoof a YubiKey — the counter value will allow the validation server to know which OTPs have already been used. Example ^^^^^^^ Example: Your account is *foo@example.com*, the chosen pass phrase is *ThisIsAPrettyLousyPassPhrase*, the API key is *My-API-Key* and the OTP is *OhMyCouldThisReallyBeAnOTP*. **Request** :: > POST /api/1.0/auth HTTP/1.1 > Host: api.storedsafe.com > { > "username": "foo@example.com", > "keys": "ThisIsAPrettyLousyPassPhraseMy-API-KeyOhMyCouldThisReallyBeAnOTP" > } **Response** :: < HTTP/1.1 200 OK < Content-Length: 571 < Content-Type: application/json < { < "DATA": { < "username": "foo@example.com", < "keys": "ThisIsAPrettyLousyPassPhraseMy-API-KeyOhMyCouldThisReallyBeAnOTP" < }, < "PARAMS": [ < ], < "CALLINFO": { < "token": "rotated_storedsafe_token", < "fingerprint": "", < "userid": "42", < "password": "ThisIsAPrettyLousyPassPhrase", < "userstatus": "396", < "username": "sven", < "fullname": "Sven Test", < "timeout": 3600000, < "filesupport": 3, < "handler": "AuthHandler", < "status": "SUCCESS" < } < } Python Code Example ^^^^^^^^^^^^^^^^^^^ **“This is too much to read. I just want to get started right now! Give me a simple example!”** Here is a short example of how you can use the API with Python when using Yobico OTP. .. code-block:: python #!/usr/bin/python # -*- coding: utf-8 -*- import httplib import ssl import json loginJson = { 'username':'foo@example.com', 'keys':'ThisIsAPrettyLousyPassPhrase' + 'My-API-Key' + 'OhMyCouldThisReallyBeAnOTP' } c = httplib.HTTPSConnection('api.storedsafe.com') c.request("POST", "/api/1.0/auth", json.dumps(loginJson)) response = c.getresponse() print response.status, response.reason data = response.read() jsonObject = json.loads(data) TOTP ~~~~ Time-based One-Time Password algorithm (`TOTP `__ is an algorithm that computes a one-time password from a shared secret key and the current time. It has been adopted as Internet Engineering Task Force standard RFC 6238, and is a part of Initiative For Open Authentication (OATH). TOTP is used by numerous application, Google Authenticator and Microsoft Authenticator being two common examples. .. _example-1: Example ^^^^^^^ Example: Your account is *foo@example.com*, the chosen pass phrase is *ThisIsAPrettyLousyPassPhrase*, the API key is *My-API-Key* and the TOTP is *123456*. **Request** :: > POST /api/1.0/auth HTTP/1.1 > Host: api.storedsafe.com > { > "username": "foo@example.com", > "passphrase": "ThisIsAPrettyLousyPassPhrase", > "otp": "123456", > "apikey": "My-API-Key", > "logintype": "totp" > } **Response** :: < HTTP/1.1 200 OK < Content-Length: 571 < Content-Type: application/json < { < "DATA": { < "username": "foo@example.com", > "passphrase":"ThisIsAPrettyLousyPassPhrase", > "otp":"123456", > "apikey":"My-API-Key", > "logintype":"totp" < }, < "PARAMS": [ < ], < "CALLINFO": { < "token": "rotated_storedsafe_token", < "fingerprint": "", < "userid": "42", < "password": "ThisIsAPrettyLousyPassPhrase", < "userstatus": "396", < "username": "sven", < "fullname": "Sven Test", < "timeout": 3600000, < "filesupport": 3, < "handler": "AuthHandler", < "status": "SUCCESS" < } < } .. _python-code-example-1: Python Code Example ^^^^^^^^^^^^^^^^^^^ **“This is too much to read. I just want to get started right now! Give me a simple example!”** Here is a short example of how you can use the API with Python when using TOTP. .. code-block:: python #!/usr/bin/python # -*- coding: utf-8 -*- import httplib import ssl import json loginJson = { 'username':'foo@example.com', 'passphrase':"ThisIsAPrettyLousyPassPhrase', 'otp':'123456', 'apikey':'My-API-Key', 'logintype':'totp' } c = httplib.HTTPSConnection('api.storedsafe.com') c.request("POST", "/api/1.0/auth", json.dumps(loginJson)) response = c.getresponse() print response.status, response.reason data = response.read() jsonObject = json.loads(data) Server Address -------------- The base server address is: \https:///api/ Please note that HTTPS is required. You will not be able to connect through unencrypted HTTP. Providing Parameters -------------------- GET requests should provide their parameters as a query string, part of the URL or as HTTP headers. POST, PUT and DELETE requests should provide their parameters in JSON-encoded. This should be part of the body. The encoding of the query string should be standard URL-encoding, as provided by various programming libraries. HTTP Status Code Definitions ---------------------------- The HTTP status code returned by a successful API request is defined in the documentation for that method. Usually, this will be 200 (HTTP2) or 200 OK (HTTP/1.1). If something goes wrong, other codes may be returned. The API uses standard HTTP2 and HTTP/1.1 status codes as defined by RFC 7540 and RFC 2616, respectively. JSON Responses -------------- All responses are sent JSON-encoded. The specific responses (successful ones) are described in the documentation section for each method. However, if something goes wrong, our standard JSON error message (together with an appropriate status code) follows this format: :: { "CALLINFO": { "errorcodes": 1, "errors": 1, "general": [], "handler": "Handler", "status": "FAIL", "token": "rotated_storedsafe_token" }, "DATA": { "token": "your_storedsafe_token" }, "ERRORCODES": { "2000": "Errormessages exists" }, "ERRORS": [ "NO SUCH TEMPLATE" ], "HEADERS": { "Accept": "*/*", "Host": "safe.domain.cc", "User-Agent": "curl/7.64.1", "X-Http-Token": "your_storedsafe_token" }, "PARAMS": [] } The Internet is unreliable -------------------------- Networks in general are unreliable, and particularly one as large and complex as the Internet. Your application should not assume it will get an answer. There may be timeouts. Limitations ----------- - We do not support “``Expect: 100-continue``” header from RFC 7231, section 5.1.1: Expect. - We do not support chunked transfer encoding - “``Transfer-Encoding: chunked``” REST Requests ------------- In the others sections, you will find descriptions and examples for all methods.