From a9ebc345ed987d55074470ad2628e400d4413fbc Mon Sep 17 00:00:00 2001 From: terraHDB Date: Mon, 26 Sep 2022 10:28:57 -0500 Subject: [PATCH 1/9] doc migration epic branch --- docs/harperdb-cloud/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/harperdb-cloud/index.md b/docs/harperdb-cloud/index.md index 8f125a56..cfa4d3ca 100644 --- a/docs/harperdb-cloud/index.md +++ b/docs/harperdb-cloud/index.md @@ -1 +1,2 @@ # HarperDB Cloud + From baa7f4061d73ff017fc3a74fa4f6e46d0862dfee Mon Sep 17 00:00:00 2001 From: terraHDB Date: Tue, 27 Sep 2022 10:14:06 -0500 Subject: [PATCH 2/9] 3/4 progress of security --- docs/security/basic-auth.md | 64 +++++++++++++++++++++++ docs/security/configuration.md | 49 +++++++++++++++++ docs/security/index.md | 8 +++ docs/security/jwt-auth.md | 96 ++++++++++++++++++++++++++++++++++ 4 files changed, 217 insertions(+) create mode 100644 docs/security/basic-auth.md create mode 100644 docs/security/configuration.md create mode 100644 docs/security/jwt-auth.md diff --git a/docs/security/basic-auth.md b/docs/security/basic-auth.md new file mode 100644 index 00000000..021021ee --- /dev/null +++ b/docs/security/basic-auth.md @@ -0,0 +1,64 @@ +# Authentication + +HarperDB utilizes Basic Auth to secure our REST requests. In the context of an HTTP transaction, **basic access authentication** is a method for an HTTP user agent to provide a user name and password when making a request. + + + +** ***You do not need to log in separately. Basic Auth is added to each REST request like create_schema, create_table, insert etc… via headers.*** ** + + + +A header is added to each REST request. The header key is **“Authorization”** the header value is **“Basic <>”** + + + + + +## Using HarperDB Studio + +Below is a code sample from HarperDB Studio. On Line 14 you will see where we are adding the authorization header to each request. Again this needs to be added for each and every REST request for HarperDB. + +```bash +function callHarperDB(call_object,operation, callback){ + + var options = { + "method": "POST", + "hostname": call_object.endpoint_url, + "port": call_object.endpoint_port, + "path": "/", + "headers": { + "content-type": "application/json", + "authorization": "Basic " + Buffer.from(call_object.username + ':' + call_object.password).toString('base64'), + "cache-control": "no-cache" + + } + }; + + var http_req = http.request(options, function (hdb_res) { + var chunks = []; + + hdb_res.on("data", function (chunk) { + chunks.push(chunk); + }); + + hdb_res.on("end", function () { + var body = Buffer.concat(chunks); + if (isJson(body)) { + return callback(null, JSON.parse(body)); + } else { + return callback(body, null); + + } + + }); + }); + + http_req.on("error", function (chunk) { + return callback("Failed to connect", null); + }); + + http_req.write(JSON.stringify(operation)); + http_req.end(); + +} +``` \ No newline at end of file diff --git a/docs/security/configuration.md b/docs/security/configuration.md new file mode 100644 index 00000000..54f84a2d --- /dev/null +++ b/docs/security/configuration.md @@ -0,0 +1,49 @@ +# Configuration + +HarperDB was set up to require very minimal configuration to work out of the box. There are, however, some best practices we encourage for anyone building an app with HarperDB. + + + +## CORS + +HarperDB allows for managing [cross-origin HTTP requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS). By default, HarperDB enables CORS for all domains if you need to disable CORS completely or set up whitelisted domains you can do the following: + +1) Open the HarperDB harperdb.config file this can be found in /HDB_ROOT, the location you specified during install. + +2) In harperdb.config there should be 2 entries under `operationsApi.network`: cors and corsWhitelist. + * `cors` + + 1) To turn off change to: `cors: false` + + 2) To turn on change to: `cors: true` + CORS_WHITELIST + The CORS_WHITELIST will only be recognized by the system when CORS_ON=true + To create a whitelist you set CORS_WHITELIST to a comma-separated list of domains. + i.e. CORS_WHITELIST=http://harperdb.io,http://products.harperdb.io + To clear out the whitelist and allow all domains: CORS_WHITELIST= + SSL + HarperDB provides the option to use an HTTP or HTTPS interface. The default port for the server is 9925. + + + +These default ports can be changed by updating the SERVER_PORT value in HDB_ROOT/config/settings.js. + + + +By default HTTPS is turned off and HTTP is turned on. + + + +You can toggle HTTPS and HTTP in the settings file. By setting HTTPS_ON to true/false. When HTTPS_ON is set to false, the server will use HTTP. + + + +HarperDB automatically generates a certificate and a privateKey file which live at HARPERDB_ROOT/keys/. + + + +You can replace these with your own certificate and key. + + + +If any of these settings are changed please make sure to run harperdb stop && harperdb run as they will not take effect until a restart. \ No newline at end of file diff --git a/docs/security/index.md b/docs/security/index.md index 8dbb2f9b..a5c74dbc 100644 --- a/docs/security/index.md +++ b/docs/security/index.md @@ -1 +1,9 @@ # Security + +HarperDB uses role-based, attribute-level security to ensure that users can only gain access to the data they’re supposed to be able to access. Our granular permissions allow for unparalleled flexibility and control, and can actually lower the Total Cost of Ownership compared to other database solutions, since you no longer have to replicate subsets of your data to isolate use cases. + +* [JWT Authentication](https://harperdb.io/docs/security/jwt-authentication/) +* [Basic Authentication](https://harperdb.io/docs/security/authentication/) +* [Configuration](https://harperdb.io/docs/security/configuration/) +* [Users and Roles](https://harperdb.io/docs/security/users-roles/) + diff --git a/docs/security/jwt-auth.md b/docs/security/jwt-auth.md new file mode 100644 index 00000000..6483c55f --- /dev/null +++ b/docs/security/jwt-auth.md @@ -0,0 +1,96 @@ +# JWT Authentication + +HarperDB introduced Token based authentication in version 2.3.0 with JWTs. + + + +This consists of two primary operations `create_authentication_tokens` and `refresh_operation_token`. These generate two types of tokens, as follows: + +* The `operation_token` which is used to authenticate all HarperDB operations in the Bearer Token Authorization Header. The default expiry is one day. + +* The `refresh_token` which is used to generate a new `operation_token` upon expiry. This token is used in the Bearer Token Authorization Header for the `refresh_operation_token` operation only. The default expiry is thirty days. + +The `create_authentication_tokens` operation can be used at any time to refresh both tokens in the event that both have expired or been lost. + +## Create Authentication Tokens + +Users must initially create tokens using their HarperDB credentials. The following POST body is sent to HarperDB. No headers are required for this POST operation. + +```bash +{ +"operation": "create_authentication_tokens", +"username": "username", +"password": "password" +} +``` + +A full cURL example can be seen here: + +```bash +curl --location --request POST 'http://localhost:9925' \ +--header 'Content-Type: application/json' \ +--data-raw '{ +"operation": "create_authentication_tokens", +"username": "username", +"password": "password" +}' +``` + +An example expected return object is: + +```bash +{ +"operation_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjA0OTc4MjAwLCJleHAiOjE2MDUwNjQ2MDAsInN1YiI6Im9wZXJhdGlvbiJ9.MpQA-9CMjA-mn-7mHyUXSuSC_-kqMqJXp_NDiKLFtbtMRbodCuY3DzH401rvy_4vb0yCELf0B5EapLVY1545sv80nxSl6FoZFxQaDWYXycoia6zHpiveR8hKlmA6_XTWHJbY2FM1HAFrdtt3yUTiF-ylkdNbPG7u7fRjTmHfsZ78gd2MNWIDkHoqWuFxIyqk8XydQpsjULf2Uacirt9FmHfkMZ-Jr_rRpcIEW0FZyLInbm6uxLfseFt87wA0TbZ0ofImjAuaW_3mYs-3H48CxP152UJ0jByPb0kHsk1QKP7YHWx1-Wce9NgNADfG5rfgMHANL85zvkv8sJmIGZIoSpMuU3CIqD2rgYnMY-L5dQN1fgfROrPMuAtlYCRK7r-IpjvMDQtRmCiNG45nGsM4DTzsa5GyDrkGssd5OBhl9gr9z9Bb5HQVYhSKIOiy72dK5dQNBklD4eGLMmo-u322zBITmE0lKaBcwYGJw2mmkYcrjDOmsDseU6Bf_zVUd9WF3FqwNkhg4D7nrfNSC_flalkxPHckU5EC_79cqoUIX2ogufBW5XgYbU4WfLloKcIpb51YTZlZfwBHlHPSyaq_guaXFaeCUXKq39_i1n0HRF_mRaxNru0cNDFT9Fm3eD7V8axFijSVAMDyQs_JR7SY483YDKUfN4l-vw-EVynImr4", +"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjA0OTc4MjAwLCJleHAiOjE2MDc1NzAyMDAsInN1YiI6InJlZnJlc2gifQ.acaCsk-CJWIMLGDZdGnsthyZsJfQ8ihXLyE8mTji8PgGkpbwhs7e1O0uitMgP_pGjHq2tey1BHSwoeCL49b18WyMIB10hK-q2BXGKQkykltjTrQbg7VsdFi0h57mGfO0IqAwYd55_hzHZNnyJMh4b0iPQFDwU7iTD7x9doHhZAvzElpkWbc_NKVw5_Mw3znjntSzbuPN105zlp4Niurin-_5BnukwvoJWLEJ-ZlF6hE4wKhaMB1pWTJjMvJQJE8khTTvlUN8tGxmzoaDYoe1aCGNxmDEQnx8Y5gKzVd89sylhqi54d2nQrJ2-ElfEDsMoXpR01Ps6fNDFtLTuPTp7ixj8LvgL2nCjAg996Ga3PtdvXJAZPDYCqqvaBkZZcsiqOgqLV0vGo3VVlfrcgJXQImMYRr_Inu0FCe47A93IAWuQTs-KplM1KdGJsHSnNBV6oe6QEkROJT5qZME-8xhvBYvOXqp9Znwg39bmiBCMxk26Ce66_vw06MNgoa3D5AlXPWemfdVKPZDnj_aLVjZSs0gAfFElcVn7l9yjWJOaT2Muk26U8bJl-2BEq_DSclqKHODuYM5kkPKIdE4NFrsqsDYuGxcA25rlNETFyl0q-UXj1aoz_joy5Hdnr4mFELmjnoo4jYQuakufP9xeGPsj1skaodKl0mmoGcCD6v1F60" +} +``` + +## Using JWT Authentication Tokens + +The `operation_token` value is used to authenticate all operations in place of our standard Basic auth. In order to pass the token you will need to create an Bearer Token Authorization Header like the following request: + +```bash +curl --location --request POST 'http://localhost:9925' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjA0OTc4MjAwLCJleHAiOjE2MDUwNjQ2MDAsInN1YiI6Im9wZXJhdGlvbiJ9.MpQA-9CMjA-mn-7mHyUXSuSC_-kqMqJXp_NDiKLFtbtMRbodCuY3DzH401rvy_4vb0yCELf0B5EapLVY1545sv80nxSl6FoZFxQaDWYXycoia6zHpiveR8hKlmA6_XTWHJbY2FM1HAFrdtt3yUTiF-ylkdNbPG7u7fRjTmHfsZ78gd2MNWIDkHoqWuFxIyqk8XydQpsjULf2Uacirt9FmHfkMZ-Jr_rRpcIEW0FZyLInbm6uxLfseFt87wA0TbZ0ofImjAuaW_3mYs-3H48CxP152UJ0jByPb0kHsk1QKP7YHWx1-Wce9NgNADfG5rfgMHANL85zvkv8sJmIGZIoSpMuU3CIqD2rgYnMY-L5dQN1fgfROrPMuAtlYCRK7r-IpjvMDQtRmCiNG45nGsM4DTzsa5GyDrkGssd5OBhl9gr9z9Bb5HQVYhSKIOiy72dK5dQNBklD4eGLMmo-u322zBITmE0lKaBcwYGJw2mmkYcrjDOmsDseU6Bf_zVUd9WF3FqwNkhg4D7nrfNSC_flalkxPHckU5EC_79cqoUIX2ogufBW5XgYbU4WfLloKcIpb51YTZlZfwBHlHPSyaq_guaXFaeCUXKq39_i1n0HRF_mRaxNru0cNDFT9Fm3eD7V8axFijSVAMDyQs_JR7SY483YDKUfN4l-vw-EVynImr4' \ +--data-raw '{ +"operation":"search_by_hash", +"schema":"dev", +"table":"dog", +"hash_values":[1], +"get_attributes": ["*"] +}' +``` + +## Token Expiration + +`operation_token` expires at a set interval. Once it expires it will no longer be accepted by HarperDB. This duration defaults to one day, and is configurable in [harperdb.conf](https://harperdb.io/docs/reference/configuration-file/). To generate a new `operation_token`, the `refresh_operation_token` operation is used, passing the `refresh_token` in the Bearer Token Authorization Header. A full cURL example can be seen here: + +```bash +curl --location --request POST 'http://localhost:9925' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjA0OTc4MjAwLCJleHAiOjE2MDc1NzAyMDAsInN1YiI6InJlZnJlc2gifQ.acaCsk-CJWIMLGDZdGnsthyZsJfQ8ihXLyE8mTji8PgGkpbwhs7e1O0uitMgP_pGjHq2tey1BHSwoeCL49b18WyMIB10hK-q2BXGKQkykltjTrQbg7VsdFi0h57mGfO0IqAwYd55_hzHZNnyJMh4b0iPQFDwU7iTD7x9doHhZAvzElpkWbc_NKVw5_Mw3znjntSzbuPN105zlp4Niurin-_5BnukwvoJWLEJ-ZlF6hE4wKhaMB1pWTJjMvJQJE8khTTvlUN8tGxmzoaDYoe1aCGNxmDEQnx8Y5gKzVd89sylhqi54d2nQrJ2-ElfEDsMoXpR01Ps6fNDFtLTuPTp7ixj8LvgL2nCjAg996Ga3PtdvXJAZPDYCqqvaBkZZcsiqOgqLV0vGo3VVlfrcgJXQImMYRr_Inu0FCe47A93IAWuQTs-KplM1KdGJsHSnNBV6oe6QEkROJT5qZME-8xhvBYvOXqp9Znwg39bmiBCMxk26Ce66_vw06MNgoa3D5AlXPWemfdVKPZDnj_aLVjZSs0gAfFElcVn7l9yjWJOaT2Muk26U8bJl-2BEq_DSclqKHODuYM5kkPKIdE4NFrsqsDYuGxcA25rlNETFyl0q-UXj1aoz_joy5Hdnr4mFELmjnoo4jYQuakufP9xeGPsj1skaodKl0mmoGcCD6v1F60' \ +--data-raw '{ +"operation":"refresh_operation_token" +}' +``` + +This will return a new `operation_token`. An example expected return object is: + +```bash +{ +"operation_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6eyJfX2NyZWF0ZWR0aW1lX18iOjE2MDQ5NzgxODkxNTEsIl9fdXBkYXRlZHRpbWVfXyI6MTYwNDk3ODE4OTE1MSwiYWN0aXZlIjp0cnVlLCJyb2xlIjp7Il9fY3JlYXRlZHRpbWVfXyI6MTYwNDk0NDE1MTM0NywiX191cGRhdGVkdGltZV9fIjoxNjA0OTQ0MTUxMzQ3LCJpZCI6IjdiNDNlNzM1LTkzYzctNDQzYi05NGY3LWQwMzY3Njg5NDc4YSIsInBlcm1pc3Npb24iOnsic3VwZXJfdXNlciI6dHJ1ZSwic3lzdGVtIjp7InRhYmxlcyI6eyJoZGJfdGFibGUiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl9hdHRyaWJ1dGUiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl9zY2hlbWEiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl91c2VyIjp7InJlYWQiOnRydWUsImluc2VydCI6ZmFsc2UsInVwZGF0ZSI6ZmFsc2UsImRlbGV0ZSI6ZmFsc2UsImF0dHJpYnV0ZV9wZXJtaXNzaW9ucyI6W119LCJoZGJfcm9sZSI6eyJyZWFkIjp0cnVlLCJpbnNlcnQiOmZhbHNlLCJ1cGRhdGUiOmZhbHNlLCJkZWxldGUiOmZhbHNlLCJhdHRyaWJ1dGVfcGVybWlzc2lvbnMiOltdfSwiaGRiX2pvYiI6eyJyZWFkIjp0cnVlLCJpbnNlcnQiOmZhbHNlLCJ1cGRhdGUiOmZhbHNlLCJkZWxldGUiOmZhbHNlLCJhdHRyaWJ1dGVfcGVybWlzc2lvbnMiOltdfSwiaGRiX2xpY2Vuc2UiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl9pbmZvIjp7InJlYWQiOnRydWUsImluc2VydCI6ZmFsc2UsInVwZGF0ZSI6ZmFsc2UsImRlbGV0ZSI6ZmFsc2UsImF0dHJpYnV0ZV9wZXJtaXNzaW9ucyI6W119LCJoZGJfbm9kZXMiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl90ZW1wIjp7InJlYWQiOnRydWUsImluc2VydCI6ZmFsc2UsInVwZGF0ZSI6ZmFsc2UsImRlbGV0ZSI6ZmFsc2UsImF0dHJpYnV0ZV9wZXJtaXNzaW9ucyI6W119fX19LCJyb2xlIjoic3VwZXJfdXNlciJ9LCJ1c2VybmFtZSI6InVzZXJuYW1lIn0sImlhdCI6MTYwNDk3ODcxMywiZXhwIjoxNjA1MDY1MTEzLCJzdWIiOiJvcGVyYXRpb24ifQ.qB4FS7fzryCO5epQlFCQe4mQcUEhzXjfsXRFPgauXrGZwSeSr2o2a1tE1xjiI3qjK0r3f2bdi2xpFlDR1thdY-m0mOpHTICNOae4KdKzp7cyzRaOFurQnVYmkWjuV_Ww4PJgr6P3XDgXs5_B2d7ZVBR-BaAimYhVRIIShfpWk-4iN1XDk96TwloCkYx01BuN87o-VOvAnOG-K_EISA9RuEBpSkfUEuvHx8IU4VgfywdbhNMh6WXM0VP7ZzSpshgsS07MGjysGtZHNTVExEvFh14lyfjfqKjDoIJbo2msQwD2FvrTTb0iaQry1-Wwz9QJjVAUtid7tJuP8aBeNqvKyMIXRVnl5viFUr-Gs-Zl_WtyVvKlYWw0_rUn3ucmurK8tTy6iHyJ6XdUf4pYQebpEkIvi2rd__e_Z60V84MPvIYs6F_8CAy78aaYmUg5pihUEehIvGRj1RUZgdfaXElw90-m-M5hMOTI04LrzzVnBu7DcMYg4UC1W-WDrrj4zUq7y8_LczDA-yBC2-bkvWwLVtHLgV5yIEuIx2zAN74RQ4eCy1ffWDrVxYJBau4yiIyCc68dsatwHHH6bMK0uI9ib6Y9lsxCYjh-7MFcbP-4UBhgoDDXN9xoUToDLRqR9FTHqAHrGHp7BCdF5d6TQTVL5fmmg61MrLucOo-LZBXs1NY" +} +``` + +The `refresh_token` also expires at a set interval, but a longer interval. Once it expires it will no longer be accepted by HarperDB. This duration defaults to thirty days, and is configurable in [harperdb.conf](https://harperdb.io/docs/reference/configuration-file/). To generate a new `operation_token` and a new `refresh_token` the `create_authentication_tokensoperation` is called. + +## Configuration + +Token timeouts are configurable in [harperdb.conf](https://harperdb.io/docs/reference/configuration-file/) with the following parameters: + +* `OPERATION_TOKEN_TIMEOUT`: Defines the length of time until the operation_token expires (default 1d). + +* `REFRESH_TOKEN_TIMEOUT`: Defines the length of time until the refresh_token expires (default 30d). + +A full list of valid values for both parameters can be found here: https://github.com/vercel/ms \ No newline at end of file From f4af55eb2bec75538fbe1f79a3d65d852adae8cf Mon Sep 17 00:00:00 2001 From: terraHDB Date: Tue, 27 Sep 2022 16:48:36 -0500 Subject: [PATCH 3/9] finished configuration.md, began users and roles --- docs/security/configuration.md | 36 +++-- docs/security/users-and-roles.md | 222 +++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+), 14 deletions(-) create mode 100644 docs/security/users-and-roles.md diff --git a/docs/security/configuration.md b/docs/security/configuration.md index 54f84a2d..726d6922 100644 --- a/docs/security/configuration.md +++ b/docs/security/configuration.md @@ -13,32 +13,40 @@ HarperDB allows for managing [cross-origin HTTP requests](https://developer.mozi 2) In harperdb.config there should be 2 entries under `operationsApi.network`: cors and corsWhitelist. * `cors` - 1) To turn off change to: `cors: false` + 1) To turn off, change to: `cors: false` - 2) To turn on change to: `cors: true` - CORS_WHITELIST - The CORS_WHITELIST will only be recognized by the system when CORS_ON=true - To create a whitelist you set CORS_WHITELIST to a comma-separated list of domains. - i.e. CORS_WHITELIST=http://harperdb.io,http://products.harperdb.io - To clear out the whitelist and allow all domains: CORS_WHITELIST= - SSL - HarperDB provides the option to use an HTTP or HTTPS interface. The default port for the server is 9925. + 2) To turn on, change to: `cors: true` + + * `corsWhitelist` + + 1) The `corsWhitelist` will only be recognized by the system when `cors` is `true` + + 2) To create a whitelist you set `corsWhitelist` to a comma-separated list of domains. + + i.e. `corsWhitelist` is `http://harperdb.io,http://products.harperdb.io` + + 3) To clear out the whitelist and allow all domains: `corsWhitelist` is `[null]` + + +## SSL + +HarperDB provides the option to use an HTTP or HTTPS interface. The default port for the server is 9925. -These default ports can be changed by updating the SERVER_PORT value in HDB_ROOT/config/settings.js. +These default ports can be changed by updating the `operationsApi.network.port` value in `HDB_ROOT/harperdb.config` -By default HTTPS is turned off and HTTP is turned on. +By default, HTTPS is turned off and HTTP is turned on. -You can toggle HTTPS and HTTP in the settings file. By setting HTTPS_ON to true/false. When HTTPS_ON is set to false, the server will use HTTP. +You can toggle HTTPS and HTTP in the settings file. By setting `operationsApi.network.https` to true/false. When `https` is set to `false`, the server will use HTTP. -HarperDB automatically generates a certificate and a privateKey file which live at HARPERDB_ROOT/keys/. +HarperDB automatically generates a certificate and a privateKey file which live at `HDB_ROOT/keys/`. @@ -46,4 +54,4 @@ You can replace these with your own certificate and key. -If any of these settings are changed please make sure to run harperdb stop && harperdb run as they will not take effect until a restart. \ No newline at end of file +**If any of these settings are changed please make sure to run `harperdb stop && harperdb run` as they will not take effect until a restart.** \ No newline at end of file diff --git a/docs/security/users-and-roles.md b/docs/security/users-and-roles.md new file mode 100644 index 00000000..df0d2f29 --- /dev/null +++ b/docs/security/users-and-roles.md @@ -0,0 +1,222 @@ +Users & Roles +Role Permissions documentation below for HarperDB version 2.2.0 and above. + + + +HarperDB utilizes a Role-Based Access Control (RBAC) framework to manage access to HarperDB instances. A user is assigned a role that determines the user’s permissions to access database resources and run core operations. + +Roles in HarperDB +Role permissions in HarperDB are broken into two categories – permissions around database manipulation and permissions around database definition. + + + +Database Manipulation: A role defines CRUD permissions against database resources (i.e. data) in a HarperDB instance. + +At the table-level access, permissions must be explicitly defined when adding or altering a role – i.e. HarperDB will assume CRUD access to be FALSE if not explicitly provided in the permissions JSON passed to the add_role and/or alter_role API operations. +At the attribute-level, permissions for attributes in all tables included in the permissions set will be assigned based on either the specific attribute-level permissions defined in the table’s permission set or, if there are no attribute-level permissions defined, permissions will be based on the table’s CRUD set. + + +Database Definition: Permissions related to managing schemas, tables, roles, users, and other system settings and operations are restricted to the built-in super_user role. + + + +Built-In Roles +There are two built-in roles within HarperDB. See full breakdown of operations restricted to only super_user roles here. + +super_user – this role provides full access to all operations and methods within a HarperDB instance, this can be considered the admin role. +This role provides full access to all Database Definition operations and the ability to run Database Manipulation operations across the entire database schema with no restrictions. + +cluster_user – this role is an internal system role type that is managed internally to allow clustered instances to communicate with one another. +This role is an internally managed role to facilitate communication between clustered instances. + + +User-Defined Roles +In addition to built-in roles, admins (i.e. users assigned to the super_user role) can create customized roles for other users to interact with and manipulate the data within explicitly defined tables and attributes. + +Unless the user-defined role is given super_user permissions, permissions must be defined explicitly within the request body JSON. +Describe operations will return metadata for all schemas, tables, and attributes that a user-defined role has CRUD permissions for. + + +Role Permissions +When creating a new, user-defined role in a HarperDB instance, you must provide a role name and the permissions to assign to that role. Reminder, only super users can create and manage roles. + +role name used to easily identify the role assigned to individual users. +Important: starting with 3.0, roles are assigned to users and altered/dropped based on the role name used in and returned from a successful add_role , alter_role, or list_roles operation. +permissions used to explicitly defined CRUD access to existing table data. + + +Example JSON for add_role request + +{ +"operation":"add_role", +"role":"software_developer", +"permission":{ +"super_user":false, +"schema_name":{ +"tables": { +"table_name1": { +"read":true, +"insert":true, +"update":true, +"delete":false, +"attribute_permissions":[ +{ +"attribute_name":"attribute1", +"read":true, +"insert":true, +"update":true +} +] +}, +"table_name2": { +"read":true, +"insert":true, +"update":true, +"delete":false, +"attribute_permissions":[] +} +} +} +} +} + + +Setting Role Permissions +There are two parts to a permissions set: + +super_user – boolean value indicating if role should be provided super_user access. +If super_user is set to true, there should be no additional schema-specific permissions values included since the role will have access to the entire database schema. If permissions are included in the body of the operation, they will stored within HarperDB, but ignored, as super_users have full access to the database. +permissions: Schema tables that a role should have specific CRUD access to should be included in the final, schema-specific permissions JSON. +For user-defined roles (i.e. non-super_user roles, blank permissions will result in the user being restricted from accessing any of the database schema. + + +Table Permissions JSON +Each table that a role should be given some level of CRUD permissions to must be included in the tables array for its schema in the roles permissions JSON passed to the API (see example above). + +{ +table_name: { // the name of the table to define CRUD perms for +"read": boolean, // access to read from this table +"insert": boolean, // access to insert data to table +"update": boolean, // access to update data in table +"delete": boolean, // access to delete row data in table +"attribute_permissions": [ // permissions for specific table attributes +{ +"attribute_name": "attribute_name", // attribute to assign permissions to +"read": boolean, // access to read this attribute from table +"insert": boolean, // access to insert this attribute into the table +"update": boolean // access to update this attribute in the table +} +] +} + + +Important Notes About Table Permissions + +If a schema and/or any of its tables are not included in the permissions JSON, the role will not have any CRUD access to the schema and/or tables. +If a table-level CRUD permission is set to false, any attribute-level with that same CRUD permission set to true will return an error. + + +Important Notes About Attribute Permissions + +If there are attribute-specific CRUD permissions that need to be enforced on a table, those need to be explicitly described in the attribute_permissions array. +If a non-hash attribute is given some level of CRUD access, that same access will be assigned to the table’s hash_attribute, even if it is not explicitly defined in the permissions JSON. +See table_name1’s permission set for an example of this – even though the table’s hash attribute is not specifically defined in the attribute_permissions array, because the role has CRUD access to ‘attribute1’, the role will have the same access to the table’s hash attribute. +If attribute-level permissions are set – i.e. attribute_permissions.length > 0 – any table attribute not explicitly included will be assumed to have not CRUD access (with the exception of the hash_attribute described in #2). +See table_name1’s permission set for an example of this – in this scenario, the role will have the ability to create, insert and update ‘attribute1’ and the table’s hash attribute but no other attributes on that table. +If an attribute_permissions array is empty, the role’s access to a table’s attributes will be based on the table-level CRUD permissions. +See table_name2’s permission set for an example of this. +The __createdtime__ and __updatedtime__ attributes that HarperDB manages internally can have read perms set but, if set, all other attribute-level permissions will be ignored. +Please note that DELETE permissions are not included as a part of an individual attribute-level permission set. That is because it is not possible to delete individual attributes from a row, rows must be deleted in full. +If a role needs the ability to delete rows from a table, that permission should be set on the table-level. +The practical approach to deleting an individual attribute of a row would be to set that attribute to null via an update statement. +Role-Based Operation Restrictions +The table below includes all API operations available in HarperDB and indicates whether or not the operation is restricted to super_user roles. + + + +Keep in mind that non-super_user roles will also be restricted within the operations they do have access to by the schema-level CRUD permissions set for the roles. + +Schemas and Tables Restricted to Super_Users +describe_all +describe_schema +describe_table +create_schema X +drop_schema X +create_table X +drop_table X +create_attribute +drop_attribute X +NoSQL Operations Restricted to Super_Users +insert +update +upsert +delete +search_by_hash +search_by_value +search_by_conditions +SQL Operations Restricted to Super_Users +select +insert +update +delete +Bulk Operations Restricted to Super_Users +csv_data_load +csv_file_load +csv_url_load +import_from_s3 +Users and Roles Restricted to Super_Users +list_roles X +add_role X +alter_role X +drop_role X +list_users X +user_info +add_user X +alter_user X +drop_user X +Clustering Restricted to Super_Users +add_node X +update_node X +remove_node X +cluster_status X +Custom Functions Restricted to Super_Users +custom_functions_status X +get_custom_functions X +get_custom_function X +set_custom_function X +drop_custom_function X +add_custom_function_project X +drop_custom_function_project X +package_custom_function_project X +deploy_custom_function_project X +Registration Restricted to Super_Users +registration_info +get_fingerprint X +set_license X +Jobs Restricted to Super_Users +get_job +search_jobs_by_start_date X +Logs Restricted to Super_Users +read_log X +read_transaction_log X +delete_transaction_logs_before X +Utilities Restricted to Super_Users +delete_records_before X +export_local +export_to_s3 +system_information X +restart X +restart_service X +get_configuration X +configure_cluster X +Token Authentication Restricted to Super_Users +create_authentication_tokens +refresh_operation_token +Error: Must execute as User +You may have gotten an error like, Error: Must execute as <>? + +This means that you installed HarperDB as <>. Because HarperDB stores files natively on the operating system, we only allow the HarperDB executable to be run by a single user. This prevents permissions issues on files. + + + +For example if you installed as user_a, but later wanted to run as user_b. User_b may not have access to the hdb files HarperDB needs. This also keeps HarperDB more secure as it allows you to lock files down to a specific user and prevents other users from accessing your files. \ No newline at end of file From 95599394e56234eecd2e753e60c5e8986aa74286 Mon Sep 17 00:00:00 2001 From: terraHDB Date: Mon, 3 Oct 2022 11:38:33 -0600 Subject: [PATCH 4/9] users and roles migrated; still needs updates --- docs/security/users-and-roles.md | 391 ++++++++++++++++++------------- 1 file changed, 222 insertions(+), 169 deletions(-) diff --git a/docs/security/users-and-roles.md b/docs/security/users-and-roles.md index df0d2f29..a1973a2f 100644 --- a/docs/security/users-and-roles.md +++ b/docs/security/users-and-roles.md @@ -1,219 +1,272 @@ -Users & Roles -Role Permissions documentation below for HarperDB version 2.2.0 and above. +# Users & Roles + +*Role Permissions documentation below for HarperDB version 2.2.0 and above.* HarperDB utilizes a Role-Based Access Control (RBAC) framework to manage access to HarperDB instances. A user is assigned a role that determines the user’s permissions to access database resources and run core operations. -Roles in HarperDB +## Roles in HarperDB + Role permissions in HarperDB are broken into two categories – permissions around database manipulation and permissions around database definition. -Database Manipulation: A role defines CRUD permissions against database resources (i.e. data) in a HarperDB instance. +**Database Manipulation**: A role defines CRUD permissions against database resources (i.e. data) in a HarperDB instance. + +1) At the table-level access, permissions must be explicitly defined when adding or altering a role – *i.e. HarperDB will assume CRUD access to be FALSE if not explicitly provided in the permissions JSON passed to the `add_role` and/or `alter_role` API operations.* + +2) At the attribute-level, permissions for attributes in all tables included in the permissions set will be assigned based on either the specific attribute-level permissions defined in the table’s permission set or, if there are no attribute-level permissions defined, permissions will be based on the table’s CRUD set. -At the table-level access, permissions must be explicitly defined when adding or altering a role – i.e. HarperDB will assume CRUD access to be FALSE if not explicitly provided in the permissions JSON passed to the add_role and/or alter_role API operations. -At the attribute-level, permissions for attributes in all tables included in the permissions set will be assigned based on either the specific attribute-level permissions defined in the table’s permission set or, if there are no attribute-level permissions defined, permissions will be based on the table’s CRUD set. +**Database Definition**: Permissions related to managing schemas, tables, roles, users, and other system settings and operations are restricted to the built-in `super_user` role. -Database Definition: Permissions related to managing schemas, tables, roles, users, and other system settings and operations are restricted to the built-in super_user role. +**Built-In Roles** +There are two built-in roles within HarperDB. See full breakdown of operations restricted to only super_user roles [here](https://harperdb.io/docs/security/users-roles/). -Built-In Roles -There are two built-in roles within HarperDB. See full breakdown of operations restricted to only super_user roles here. +* `super_user` – this role provides full access to all operations and methods within a HarperDB instance, this can be considered the admin role. -super_user – this role provides full access to all operations and methods within a HarperDB instance, this can be considered the admin role. -This role provides full access to all Database Definition operations and the ability to run Database Manipulation operations across the entire database schema with no restrictions. + * This role provides full access to all Database Definition operations and the ability to run Database Manipulation operations across the entire database schema with no restrictions. +* `cluster_user` – this role is an internal system role type that is managed internally to allow clustered instances to communicate with one another. -cluster_user – this role is an internal system role type that is managed internally to allow clustered instances to communicate with one another. -This role is an internally managed role to facilitate communication between clustered instances. + * This role is an internally managed role to facilitate communication between clustered instances. -User-Defined Roles +**User-Defined Roles** In addition to built-in roles, admins (i.e. users assigned to the super_user role) can create customized roles for other users to interact with and manipulate the data within explicitly defined tables and attributes. -Unless the user-defined role is given super_user permissions, permissions must be defined explicitly within the request body JSON. -Describe operations will return metadata for all schemas, tables, and attributes that a user-defined role has CRUD permissions for. +* Unless the user-defined role is given `super_user` permissions, permissions must be defined explicitly within the request body JSON. +* Describe operations will return metadata for all schemas, tables, and attributes that a user-defined role has CRUD permissions for. -Role Permissions -When creating a new, user-defined role in a HarperDB instance, you must provide a role name and the permissions to assign to that role. Reminder, only super users can create and manage roles. -role name used to easily identify the role assigned to individual users. -Important: starting with 3.0, roles are assigned to users and altered/dropped based on the role name used in and returned from a successful add_role , alter_role, or list_roles operation. -permissions used to explicitly defined CRUD access to existing table data. +**Role Permissions** +When creating a new, user-defined role in a HarperDB instance, you must provide a role name and the permissions to assign to that role. *Reminder, only super users can create and manage roles.* -Example JSON for add_role request +* `role` name used to easily identify the role assigned to individual users. + *Important: starting with 3.0, roles are assigned to users and altered/dropped based on the role name used in and returned from a successful `add_role` , `alter_role`, or `list_roles` operation.* + +* `permissions` used to explicitly defined CRUD access to existing table data. + + +Example JSON for `add_role` request + +```bash { -"operation":"add_role", -"role":"software_developer", -"permission":{ -"super_user":false, -"schema_name":{ -"tables": { -"table_name1": { -"read":true, -"insert":true, -"update":true, -"delete":false, -"attribute_permissions":[ -{ -"attribute_name":"attribute1", -"read":true, -"insert":true, -"update":true -} -] -}, -"table_name2": { -"read":true, -"insert":true, -"update":true, -"delete":false, -"attribute_permissions":[] -} -} -} -} + "operation":"add_role", + "role":"software_developer", + "permission":{ + "super_user":false, + "schema_name":{ + "tables": { + "table_name1": { + "read":true, + "insert":true, + "update":true, + "delete":false, + "attribute_permissions":[ + { + "attribute_name":"attribute1", + "read":true, + "insert":true, + "update":true + } + ] + }, + "table_name2": { + "read":true, + "insert":true, + "update":true, + "delete":false, + "attribute_permissions":[] + } + } + } + } } +``` +**Setting Role Permissions** -Setting Role Permissions There are two parts to a permissions set: -super_user – boolean value indicating if role should be provided super_user access. -If super_user is set to true, there should be no additional schema-specific permissions values included since the role will have access to the entire database schema. If permissions are included in the body of the operation, they will stored within HarperDB, but ignored, as super_users have full access to the database. -permissions: Schema tables that a role should have specific CRUD access to should be included in the final, schema-specific permissions JSON. -For user-defined roles (i.e. non-super_user roles, blank permissions will result in the user being restricted from accessing any of the database schema. +* `super_user` – boolean value indicating if role should be provided super_user access. + *If `super_user` is set to true, there should be no additional schema-specific permissions values included since the role will have access to the entire database schema. If permissions are included in the body of the operation, they will stored within HarperDB, but ignored, as super_users have full access to the database.* -Table Permissions JSON -Each table that a role should be given some level of CRUD permissions to must be included in the tables array for its schema in the roles permissions JSON passed to the API (see example above). +* `permissions`: Schema tables that a role should have specific CRUD access to should be included in the final, schema-specific `permissions` JSON. + *For user-defined roles (i.e. non-super_user roles, blank permissions will result in the user being restricted from accessing any of the database schema.* + + +**Table Permissions JSON** + +Each table that a role should be given some level of CRUD permissions to must be included in the `tables` array for its schema in the roles permissions JSON passed to the API (*see example above*). + +```bash { -table_name: { // the name of the table to define CRUD perms for -"read": boolean, // access to read from this table -"insert": boolean, // access to insert data to table -"update": boolean, // access to update data in table -"delete": boolean, // access to delete row data in table -"attribute_permissions": [ // permissions for specific table attributes -{ -"attribute_name": "attribute_name", // attribute to assign permissions to -"read": boolean, // access to read this attribute from table -"insert": boolean, // access to insert this attribute into the table -"update": boolean // access to update this attribute in the table -} -] + table_name: { // the name of the table to define CRUD perms for + "read": boolean, // access to read from this table + "insert": boolean, // access to insert data to table + "update": boolean, // access to update data in table + "delete": boolean, // access to delete row data in table + "attribute_permissions": [ // permissions for specific table attributes + { + "attribute_name": "attribute_name", // attribute to assign permissions to + "read": boolean, // access to read this attribute from table + "insert": boolean, // access to insert this attribute into the table + "update": boolean // access to update this attribute in the table + } + ] } +``` -Important Notes About Table Permissions +**Important Notes About Table Permissions** -If a schema and/or any of its tables are not included in the permissions JSON, the role will not have any CRUD access to the schema and/or tables. -If a table-level CRUD permission is set to false, any attribute-level with that same CRUD permission set to true will return an error. +1) If a schema and/or any of its tables are not included in the permissions JSON, the role will not have any CRUD access to the schema and/or tables. +2) If a table-level CRUD permission is set to false, any attribute-level with that same CRUD permission set to true will return an error. -Important Notes About Attribute Permissions -If there are attribute-specific CRUD permissions that need to be enforced on a table, those need to be explicitly described in the attribute_permissions array. -If a non-hash attribute is given some level of CRUD access, that same access will be assigned to the table’s hash_attribute, even if it is not explicitly defined in the permissions JSON. -See table_name1’s permission set for an example of this – even though the table’s hash attribute is not specifically defined in the attribute_permissions array, because the role has CRUD access to ‘attribute1’, the role will have the same access to the table’s hash attribute. -If attribute-level permissions are set – i.e. attribute_permissions.length > 0 – any table attribute not explicitly included will be assumed to have not CRUD access (with the exception of the hash_attribute described in #2). -See table_name1’s permission set for an example of this – in this scenario, the role will have the ability to create, insert and update ‘attribute1’ and the table’s hash attribute but no other attributes on that table. -If an attribute_permissions array is empty, the role’s access to a table’s attributes will be based on the table-level CRUD permissions. -See table_name2’s permission set for an example of this. -The __createdtime__ and __updatedtime__ attributes that HarperDB manages internally can have read perms set but, if set, all other attribute-level permissions will be ignored. -Please note that DELETE permissions are not included as a part of an individual attribute-level permission set. That is because it is not possible to delete individual attributes from a row, rows must be deleted in full. -If a role needs the ability to delete rows from a table, that permission should be set on the table-level. -The practical approach to deleting an individual attribute of a row would be to set that attribute to null via an update statement. -Role-Based Operation Restrictions -The table below includes all API operations available in HarperDB and indicates whether or not the operation is restricted to super_user roles. +**Important Notes About Attribute Permissions** + +1) If there are attribute-specific CRUD permissions that need to be enforced on a table, those need to be explicitly described in the `attribute_permissions` array. + +2) If a non-hash attribute is given some level of CRUD access, that same access will be assigned to the table’s `hash_attribute`, even if it is not explicitly defined in the permissions JSON. + + *See table_name1’s permission set for an example of this – even though the table’s hash attribute is not specifically defined in the attribute_permissions array, because the role has CRUD access to ‘attribute1’, the role will have the same access to the table’s hash attribute.* +3) If attribute-level permissions are set – *i.e. attribute_permissions.length > 0* – any table attribute not explicitly included will be assumed to have not CRUD access (with the exception of the `hash_attribute` described in #2). + *See table_name1’s permission set for an example of this – in this scenario, the role will have the ability to create, insert and update ‘attribute1’ and the table’s hash attribute but no other attributes on that table.* + +4) If an `attribute_permissions` array is empty, the role’s access to a table’s attributes will be based on the table-level CRUD permissions. + + *See table_name2’s permission set for an example of this.* + +5) The `__createdtime__` and `__updatedtime__` attributes that HarperDB manages internally can have read perms set but, if set, all other attribute-level permissions will be ignored. + +6) Please note that DELETE permissions are not included as a part of an individual attribute-level permission set. That is because it is not possible to delete individual attributes from a row, rows must be deleted in full. + + * If a role needs the ability to delete rows from a table, that permission should be set on the table-level. + + * The practical approach to deleting an individual attribute of a row would be to set that attribute to null via an update statement. + +## Role-Based Operation Restrictions + +The table below includes all API operations available in HarperDB and indicates whether or not the operation is restricted to super_user roles. -Keep in mind that non-super_user roles will also be restricted within the operations they do have access to by the schema-level CRUD permissions set for the roles. - -Schemas and Tables Restricted to Super_Users -describe_all -describe_schema -describe_table -create_schema X -drop_schema X -create_table X -drop_table X -create_attribute -drop_attribute X -NoSQL Operations Restricted to Super_Users -insert -update -upsert -delete -search_by_hash -search_by_value -search_by_conditions -SQL Operations Restricted to Super_Users -select -insert -update -delete -Bulk Operations Restricted to Super_Users -csv_data_load -csv_file_load -csv_url_load -import_from_s3 -Users and Roles Restricted to Super_Users -list_roles X -add_role X -alter_role X -drop_role X -list_users X -user_info -add_user X -alter_user X -drop_user X -Clustering Restricted to Super_Users -add_node X -update_node X -remove_node X -cluster_status X -Custom Functions Restricted to Super_Users -custom_functions_status X -get_custom_functions X -get_custom_function X -set_custom_function X -drop_custom_function X -add_custom_function_project X -drop_custom_function_project X -package_custom_function_project X -deploy_custom_function_project X -Registration Restricted to Super_Users -registration_info -get_fingerprint X -set_license X -Jobs Restricted to Super_Users -get_job -search_jobs_by_start_date X -Logs Restricted to Super_Users -read_log X -read_transaction_log X -delete_transaction_logs_before X -Utilities Restricted to Super_Users -delete_records_before X -export_local -export_to_s3 -system_information X -restart X -restart_service X -get_configuration X -configure_cluster X -Token Authentication Restricted to Super_Users -create_authentication_tokens -refresh_operation_token -Error: Must execute as User -You may have gotten an error like, Error: Must execute as <>? +*Keep in mind that non-super_user roles will also be restricted within the operations they do have access to by the schema-level CRUD permissions set for the roles.* + +| Schemas and Tables | Restricted to Super_Users | +|--------------------|---------------------------| +| describe_all | | +| describe_schema | | +| describe_table | | +| create_schema | X | +| drop_schema | X | +| create_table | X | +| drop_table | X | +| create_attribute | | +| drop_attribute | X | + + +| NoSQL Operations | Restricted to Super_Users | +|----------------------|---------------------------| +| insert | | +| update | | +| upsert | | +| delete | | +| search_by_hash | | +| search_by_value | | +| search_by_conditions | | + +| SQL Operations | Restricted to Super_Users | +|-----------------|---------------------------| +| select | | +| insert | | +| update | | +| delete | | + +| Bulk Operations | Restricted to Super_Users | +|------------------|---------------------------| +| csv_data_load | | +| csv_file_load | | +| csv_url_load | | +| import_from_s3 | | + +| Users and Roles | Restricted to Super_Users | +|-----------------|---------------------------| +| list_roles | X | +| add_role | X | +| alter_role | X | +| drop_role | X | +| list_users | X | +| user_info | | +| add_user | X | +| alter_user | X | +| drop_user | X | + +| Clustering | Restricted to Super_Users | +|----------------|---------------------------| +| add_node | X | +| update_node | X | +| remove_node | X | +| cluster_status | X | + +| Custom Functions | Restricted to Super_Users | +|----------------------------------|---------------------------| +| custom_functions_status | X | +| get_custom_functions | X | +| get_custom_function | X | +| set_custom_function | X | +| drop_custom_function | X | +| add_custom_function_project | X | +| drop_custom_function_project | X | +| package_custom_function_project | X | +| deploy_custom_function_project | X | + +| Registration | Restricted to Super_Users | +|-------------------|---------------------------| +| registration_info | | +| get_fingerprint | X | +| set_license | X | + +| Jobs | Restricted to Super_Users | +|----------------------------|---------------------------| +| get_job | | +| search_jobs_by_start_date | X | + +| Logs | Restricted to Super_Users | +|--------------------------------|---------------------------| +| read_log | X | +| read_transaction_log | X | +| delete_transaction_logs_before | X | + +| Utilities | Restricted to Super_Users | +|-----------------------|---------------------------| +| delete_records_before | X | +| export_local | | +| export_to_s3 | | +| system_information | X | +| restart | X | +| restart_service | X | +| get_configuration | X | +| configure_cluster | X | + +| Token Authentication | Restricted to Super_Users | +|------------------------------|---------------------------| +| create_authentication_tokens | | +| refresh_operation_token | | + +## Error: Must execute as User + +**You may have gotten an error like,** `Error: Must execute as <>`. This means that you installed HarperDB as <>. Because HarperDB stores files natively on the operating system, we only allow the HarperDB executable to be run by a single user. This prevents permissions issues on files. From ab9e432bf55f0595ddece579b17b8aa528605351 Mon Sep 17 00:00:00 2001 From: terraHDB Date: Mon, 3 Oct 2022 11:59:23 -0600 Subject: [PATCH 5/9] updated user and roles operations --- docs/security/users-and-roles.md | 207 ++++++++++++++++--------------- 1 file changed, 107 insertions(+), 100 deletions(-) diff --git a/docs/security/users-and-roles.md b/docs/security/users-and-roles.md index a1973a2f..a3a4ca5c 100644 --- a/docs/security/users-and-roles.md +++ b/docs/security/users-and-roles.md @@ -163,106 +163,113 @@ The table below includes all API operations available in HarperDB and indicates *Keep in mind that non-super_user roles will also be restricted within the operations they do have access to by the schema-level CRUD permissions set for the roles.* -| Schemas and Tables | Restricted to Super_Users | -|--------------------|---------------------------| -| describe_all | | -| describe_schema | | -| describe_table | | -| create_schema | X | -| drop_schema | X | -| create_table | X | -| drop_table | X | -| create_attribute | | -| drop_attribute | X | - - -| NoSQL Operations | Restricted to Super_Users | -|----------------------|---------------------------| -| insert | | -| update | | -| upsert | | -| delete | | -| search_by_hash | | -| search_by_value | | -| search_by_conditions | | - -| SQL Operations | Restricted to Super_Users | -|-----------------|---------------------------| -| select | | -| insert | | -| update | | -| delete | | - -| Bulk Operations | Restricted to Super_Users | -|------------------|---------------------------| -| csv_data_load | | -| csv_file_load | | -| csv_url_load | | -| import_from_s3 | | - -| Users and Roles | Restricted to Super_Users | -|-----------------|---------------------------| -| list_roles | X | -| add_role | X | -| alter_role | X | -| drop_role | X | -| list_users | X | -| user_info | | -| add_user | X | -| alter_user | X | -| drop_user | X | - -| Clustering | Restricted to Super_Users | -|----------------|---------------------------| -| add_node | X | -| update_node | X | -| remove_node | X | -| cluster_status | X | - -| Custom Functions | Restricted to Super_Users | -|----------------------------------|---------------------------| -| custom_functions_status | X | -| get_custom_functions | X | -| get_custom_function | X | -| set_custom_function | X | -| drop_custom_function | X | -| add_custom_function_project | X | -| drop_custom_function_project | X | -| package_custom_function_project | X | -| deploy_custom_function_project | X | - -| Registration | Restricted to Super_Users | -|-------------------|---------------------------| -| registration_info | | -| get_fingerprint | X | -| set_license | X | - -| Jobs | Restricted to Super_Users | -|----------------------------|---------------------------| -| get_job | | -| search_jobs_by_start_date | X | - -| Logs | Restricted to Super_Users | -|--------------------------------|---------------------------| -| read_log | X | -| read_transaction_log | X | -| delete_transaction_logs_before | X | - -| Utilities | Restricted to Super_Users | -|-----------------------|---------------------------| -| delete_records_before | X | -| export_local | | -| export_to_s3 | | -| system_information | X | -| restart | X | -| restart_service | X | -| get_configuration | X | -| configure_cluster | X | - -| Token Authentication | Restricted to Super_Users | -|------------------------------|---------------------------| -| create_authentication_tokens | | -| refresh_operation_token | | +| Schemas and Tables | Restricted to Super_Users | +|--------------------|:---------------------------:| +| describe_all | | +| describe_schema | | +| describe_table | | +| create_schema | X | +| drop_schema | X | +| create_table | X | +| drop_table | X | +| create_attribute | | +| drop_attribute | X | + + +| NoSQL Operations | Restricted to Super_Users | +|----------------------|:---------------------------:| +| insert | | +| update | | +| upsert | | +| delete | | +| search_by_hash | | +| search_by_value | | +| search_by_conditions | | + +| SQL Operations | Restricted to Super_Users | +|-----------------|:---------------------------:| +| select | | +| insert | | +| update | | +| delete | | + +| Bulk Operations | Restricted to Super_Users | +|------------------|:---------------------------:| +| csv_data_load | | +| csv_file_load | | +| csv_url_load | | +| import_from_s3 | | + +| Users and Roles | Restricted to Super_Users | +|-----------------|:---------------------------:| +| list_roles | X | +| add_role | X | +| alter_role | X | +| drop_role | X | +| list_users | X | +| user_info | | +| add_user | X | +| alter_user | X | +| drop_user | X | + +| Clustering | Restricted to Super_Users | +|-----------------------|:---------------------------:| +| cluster_set_routes | X | +| cluster_get_routes | X | +| cluster_delete_routes | X | +| add_node | X | +| update_node | X | +| cluster_status | X | +| remove_node | X | +| configure_cluster | X | + + +| Custom Functions | Restricted to Super_Users | +|----------------------------------|:---------------------------:| +| custom_functions_status | X | +| get_custom_functions | X | +| get_custom_function | X | +| set_custom_function | X | +| drop_custom_function | X | +| add_custom_function_project | X | +| drop_custom_function_project | X | +| package_custom_function_project | X | +| deploy_custom_function_project | X | + +| Registration | Restricted to Super_Users | +|-------------------|:---------------------------:| +| registration_info | | +| get_fingerprint | X | +| set_license | X | + +| Jobs | Restricted to Super_Users | +|----------------------------|:---------------------------:| +| get_job | | +| search_jobs_by_start_date | X | + +| Logs | Restricted to Super_Users | +|--------------------------------|:---------------------------:| +| read_log | X | +| read_transaction_log | X | +| delete_transaction_logs_before | X | +| read_audit_log | X | +| delete_audit_logs_before | X | + +| Utilities | Restricted to Super_Users | +|-----------------------|:---------------------------:| +| delete_records_before | X | +| export_local | | +| export_to_s3 | | +| system_information | X | +| restart | X | +| restart_service | X | +| get_configuration | X | +| configure_cluster | X | + +| Token Authentication | Restricted to Super_Users | +|------------------------------|:---------------------------:| +| create_authentication_tokens | | +| refresh_operation_token | | ## Error: Must execute as User From 303e35fc8000a022ece12b8fd7626cc0de3eb1f3 Mon Sep 17 00:00:00 2001 From: terraHDB Date: Thu, 20 Oct 2022 10:54:06 -0500 Subject: [PATCH 6/9] corrections to security docs --- docs/security/basic-auth.md | 22 +++++++------- docs/security/configuration.md | 24 ++++++++-------- docs/security/index.md | 2 +- docs/security/jwt-auth.md | 49 +++++++++++++++----------------- docs/security/users-and-roles.md | 14 ++++----- 5 files changed, 52 insertions(+), 59 deletions(-) diff --git a/docs/security/basic-auth.md b/docs/security/basic-auth.md index 021021ee..a3ca2147 100644 --- a/docs/security/basic-auth.md +++ b/docs/security/basic-auth.md @@ -1,27 +1,27 @@ # Authentication -HarperDB utilizes Basic Auth to secure our REST requests. In the context of an HTTP transaction, **basic access authentication** is a method for an HTTP user agent to provide a user name and password when making a request. +HarperDB utilizes Basic Auth to secure our HTTP requests. In the context of an HTTP transaction, **basic access authentication** is a method for an HTTP user agent to provide a user name and password when making a request. -** ***You do not need to log in separately. Basic Auth is added to each REST request like create_schema, create_table, insert etc… via headers.*** ** +** ***You do not need to log in separately. Basic Auth is added to each HTTP request like create_schema, create_table, insert etc… via headers.*** ** -A header is added to each REST request. The header key is **“Authorization”** the header value is **“Basic <>”** +A header is added to each HTTP request. The header key is **“Authorization”** the header value is **“Basic <>”** -## Using HarperDB Studio +## Authentication in HarperDB Studio -Below is a code sample from HarperDB Studio. On Line 14 you will see where we are adding the authorization header to each request. Again this needs to be added for each and every REST request for HarperDB. +Below is a code sample from HarperDB Studio. On Line 14 you will see where we are adding the authorization header to each request. This needs to be added for each and every HTTP request for HarperDB. -```bash -function callHarperDB(call_object,operation, callback){ +```javascript +function callHarperDB(call_object, operation, callback){ - var options = { + const options = { "method": "POST", "hostname": call_object.endpoint_url, "port": call_object.endpoint_port, @@ -34,15 +34,15 @@ function callHarperDB(call_object,operation, callback){ } }; - var http_req = http.request(options, function (hdb_res) { - var chunks = []; + const http_req = http.request(options, function (hdb_res) { + let chunks = []; hdb_res.on("data", function (chunk) { chunks.push(chunk); }); hdb_res.on("end", function () { - var body = Buffer.concat(chunks); + const body = Buffer.concat(chunks); if (isJson(body)) { return callback(null, JSON.parse(body)); } else { diff --git a/docs/security/configuration.md b/docs/security/configuration.md index 726d6922..708cd573 100644 --- a/docs/security/configuration.md +++ b/docs/security/configuration.md @@ -6,26 +6,26 @@ HarperDB was set up to require very minimal configuration to work out of the box ## CORS -HarperDB allows for managing [cross-origin HTTP requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS). By default, HarperDB enables CORS for all domains if you need to disable CORS completely or set up whitelisted domains you can do the following: +HarperDB allows for managing [cross-origin HTTP requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS). By default, HarperDB enables CORS for all domains if you need to disable CORS completely or set up an access list of domains you can do the following: -1) Open the HarperDB harperdb.config file this can be found in /HDB_ROOT, the location you specified during install. +1) Open the harperdb-config.yaml file this can be found in /, the location you specified during install. -2) In harperdb.config there should be 2 entries under `operationsApi.network`: cors and corsWhitelist. +2) In harperdb-config.yaml there should be 2 entries under `operationsApi.network`: cors and corsAccessList. * `cors` 1) To turn off, change to: `cors: false` 2) To turn on, change to: `cors: true` - * `corsWhitelist` + * `corsAccessList` - 1) The `corsWhitelist` will only be recognized by the system when `cors` is `true` + 1) The `corsAccessList` will only be recognized by the system when `cors` is `true` - 2) To create a whitelist you set `corsWhitelist` to a comma-separated list of domains. + 2) To create an access list you set `corsAccessList` to a comma-separated list of domains. - i.e. `corsWhitelist` is `http://harperdb.io,http://products.harperdb.io` + i.e. `corsAccessList` is `http://harperdb.io,http://products.harperdb.io` - 3) To clear out the whitelist and allow all domains: `corsWhitelist` is `[null]` + 3) To clear out the access list and allow all domains: `corsAccessList` is `[null]` ## SSL @@ -34,11 +34,11 @@ HarperDB provides the option to use an HTTP or HTTPS interface. The default port -These default ports can be changed by updating the `operationsApi.network.port` value in `HDB_ROOT/harperdb.config` +These default ports can be changed by updating the `operationsApi.network.port` value in `/harperdb-config.yaml` -By default, HTTPS is turned off and HTTP is turned on. +By default, HTTPS is turned off and HTTP is turned on. It is recommended that you never directly expose HarperDB's HTTP interface through a publicly available port. HTTP is intended for local or private network use. @@ -46,7 +46,7 @@ You can toggle HTTPS and HTTP in the settings file. By setting `operationsApi.ne -HarperDB automatically generates a certificate and a privateKey file which live at `HDB_ROOT/keys/`. +HarperDB automatically generates a certificate and a privateKey file which live at `/keys/`. @@ -54,4 +54,4 @@ You can replace these with your own certificate and key. -**If any of these settings are changed please make sure to run `harperdb stop && harperdb run` as they will not take effect until a restart.** \ No newline at end of file +**If any of these settings are changed please make sure to run `harperdb restart` as they will not take effect until a restart.** \ No newline at end of file diff --git a/docs/security/index.md b/docs/security/index.md index a5c74dbc..9bba60da 100644 --- a/docs/security/index.md +++ b/docs/security/index.md @@ -1,6 +1,6 @@ # Security -HarperDB uses role-based, attribute-level security to ensure that users can only gain access to the data they’re supposed to be able to access. Our granular permissions allow for unparalleled flexibility and control, and can actually lower the Total Cost of Ownership compared to other database solutions, since you no longer have to replicate subsets of your data to isolate use cases. +HarperDB uses role-based, attribute-level security to ensure that users can only gain access to the data they’re supposed to be able to access. Our granular permissions allow for unparalleled flexibility and control, and can actually lower the total cost of ownership compared to other database solutions, since you no longer have to replicate subsets of your data to isolate use cases. * [JWT Authentication](https://harperdb.io/docs/security/jwt-authentication/) * [Basic Authentication](https://harperdb.io/docs/security/authentication/) diff --git a/docs/security/jwt-auth.md b/docs/security/jwt-auth.md index 6483c55f..a7ff19ea 100644 --- a/docs/security/jwt-auth.md +++ b/docs/security/jwt-auth.md @@ -1,8 +1,5 @@ # JWT Authentication - -HarperDB introduced Token based authentication in version 2.3.0 with JWTs. - - +HarperDB uses token based authentication with JSON Web Tokens, JWTs. This consists of two primary operations `create_authentication_tokens` and `refresh_operation_token`. These generate two types of tokens, as follows: @@ -16,11 +13,11 @@ The `create_authentication_tokens` operation can be used at any time to refresh Users must initially create tokens using their HarperDB credentials. The following POST body is sent to HarperDB. No headers are required for this POST operation. -```bash +```json { -"operation": "create_authentication_tokens", -"username": "username", -"password": "password" + "operation": "create_authentication_tokens", + "username": "username", + "password": "password" } ``` @@ -30,18 +27,18 @@ A full cURL example can be seen here: curl --location --request POST 'http://localhost:9925' \ --header 'Content-Type: application/json' \ --data-raw '{ -"operation": "create_authentication_tokens", -"username": "username", -"password": "password" + "operation": "create_authentication_tokens", + "username": "username", + "password": "password" }' ``` An example expected return object is: -```bash +```json { -"operation_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjA0OTc4MjAwLCJleHAiOjE2MDUwNjQ2MDAsInN1YiI6Im9wZXJhdGlvbiJ9.MpQA-9CMjA-mn-7mHyUXSuSC_-kqMqJXp_NDiKLFtbtMRbodCuY3DzH401rvy_4vb0yCELf0B5EapLVY1545sv80nxSl6FoZFxQaDWYXycoia6zHpiveR8hKlmA6_XTWHJbY2FM1HAFrdtt3yUTiF-ylkdNbPG7u7fRjTmHfsZ78gd2MNWIDkHoqWuFxIyqk8XydQpsjULf2Uacirt9FmHfkMZ-Jr_rRpcIEW0FZyLInbm6uxLfseFt87wA0TbZ0ofImjAuaW_3mYs-3H48CxP152UJ0jByPb0kHsk1QKP7YHWx1-Wce9NgNADfG5rfgMHANL85zvkv8sJmIGZIoSpMuU3CIqD2rgYnMY-L5dQN1fgfROrPMuAtlYCRK7r-IpjvMDQtRmCiNG45nGsM4DTzsa5GyDrkGssd5OBhl9gr9z9Bb5HQVYhSKIOiy72dK5dQNBklD4eGLMmo-u322zBITmE0lKaBcwYGJw2mmkYcrjDOmsDseU6Bf_zVUd9WF3FqwNkhg4D7nrfNSC_flalkxPHckU5EC_79cqoUIX2ogufBW5XgYbU4WfLloKcIpb51YTZlZfwBHlHPSyaq_guaXFaeCUXKq39_i1n0HRF_mRaxNru0cNDFT9Fm3eD7V8axFijSVAMDyQs_JR7SY483YDKUfN4l-vw-EVynImr4", -"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjA0OTc4MjAwLCJleHAiOjE2MDc1NzAyMDAsInN1YiI6InJlZnJlc2gifQ.acaCsk-CJWIMLGDZdGnsthyZsJfQ8ihXLyE8mTji8PgGkpbwhs7e1O0uitMgP_pGjHq2tey1BHSwoeCL49b18WyMIB10hK-q2BXGKQkykltjTrQbg7VsdFi0h57mGfO0IqAwYd55_hzHZNnyJMh4b0iPQFDwU7iTD7x9doHhZAvzElpkWbc_NKVw5_Mw3znjntSzbuPN105zlp4Niurin-_5BnukwvoJWLEJ-ZlF6hE4wKhaMB1pWTJjMvJQJE8khTTvlUN8tGxmzoaDYoe1aCGNxmDEQnx8Y5gKzVd89sylhqi54d2nQrJ2-ElfEDsMoXpR01Ps6fNDFtLTuPTp7ixj8LvgL2nCjAg996Ga3PtdvXJAZPDYCqqvaBkZZcsiqOgqLV0vGo3VVlfrcgJXQImMYRr_Inu0FCe47A93IAWuQTs-KplM1KdGJsHSnNBV6oe6QEkROJT5qZME-8xhvBYvOXqp9Znwg39bmiBCMxk26Ce66_vw06MNgoa3D5AlXPWemfdVKPZDnj_aLVjZSs0gAfFElcVn7l9yjWJOaT2Muk26U8bJl-2BEq_DSclqKHODuYM5kkPKIdE4NFrsqsDYuGxcA25rlNETFyl0q-UXj1aoz_joy5Hdnr4mFELmjnoo4jYQuakufP9xeGPsj1skaodKl0mmoGcCD6v1F60" + "operation_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjA0OTc4MjAwLCJleHAiOjE2MDUwNjQ2MDAsInN1YiI6Im9wZXJhdGlvbiJ9.MpQA-9CMjA-mn-7mHyUXSuSC_-kqMqJXp_NDiKLFtbtMRbodCuY3DzH401rvy_4vb0yCELf0B5EapLVY1545sv80nxSl6FoZFxQaDWYXycoia6zHpiveR8hKlmA6_XTWHJbY2FM1HAFrdtt3yUTiF-ylkdNbPG7u7fRjTmHfsZ78gd2MNWIDkHoqWuFxIyqk8XydQpsjULf2Uacirt9FmHfkMZ-Jr_rRpcIEW0FZyLInbm6uxLfseFt87wA0TbZ0ofImjAuaW_3mYs-3H48CxP152UJ0jByPb0kHsk1QKP7YHWx1-Wce9NgNADfG5rfgMHANL85zvkv8sJmIGZIoSpMuU3CIqD2rgYnMY-L5dQN1fgfROrPMuAtlYCRK7r-IpjvMDQtRmCiNG45nGsM4DTzsa5GyDrkGssd5OBhl9gr9z9Bb5HQVYhSKIOiy72dK5dQNBklD4eGLMmo-u322zBITmE0lKaBcwYGJw2mmkYcrjDOmsDseU6Bf_zVUd9WF3FqwNkhg4D7nrfNSC_flalkxPHckU5EC_79cqoUIX2ogufBW5XgYbU4WfLloKcIpb51YTZlZfwBHlHPSyaq_guaXFaeCUXKq39_i1n0HRF_mRaxNru0cNDFT9Fm3eD7V8axFijSVAMDyQs_JR7SY483YDKUfN4l-vw-EVynImr4", + "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjA0OTc4MjAwLCJleHAiOjE2MDc1NzAyMDAsInN1YiI6InJlZnJlc2gifQ.acaCsk-CJWIMLGDZdGnsthyZsJfQ8ihXLyE8mTji8PgGkpbwhs7e1O0uitMgP_pGjHq2tey1BHSwoeCL49b18WyMIB10hK-q2BXGKQkykltjTrQbg7VsdFi0h57mGfO0IqAwYd55_hzHZNnyJMh4b0iPQFDwU7iTD7x9doHhZAvzElpkWbc_NKVw5_Mw3znjntSzbuPN105zlp4Niurin-_5BnukwvoJWLEJ-ZlF6hE4wKhaMB1pWTJjMvJQJE8khTTvlUN8tGxmzoaDYoe1aCGNxmDEQnx8Y5gKzVd89sylhqi54d2nQrJ2-ElfEDsMoXpR01Ps6fNDFtLTuPTp7ixj8LvgL2nCjAg996Ga3PtdvXJAZPDYCqqvaBkZZcsiqOgqLV0vGo3VVlfrcgJXQImMYRr_Inu0FCe47A93IAWuQTs-KplM1KdGJsHSnNBV6oe6QEkROJT5qZME-8xhvBYvOXqp9Znwg39bmiBCMxk26Ce66_vw06MNgoa3D5AlXPWemfdVKPZDnj_aLVjZSs0gAfFElcVn7l9yjWJOaT2Muk26U8bJl-2BEq_DSclqKHODuYM5kkPKIdE4NFrsqsDYuGxcA25rlNETFyl0q-UXj1aoz_joy5Hdnr4mFELmjnoo4jYQuakufP9xeGPsj1skaodKl0mmoGcCD6v1F60" } ``` @@ -54,24 +51,24 @@ curl --location --request POST 'http://localhost:9925' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjA0OTc4MjAwLCJleHAiOjE2MDUwNjQ2MDAsInN1YiI6Im9wZXJhdGlvbiJ9.MpQA-9CMjA-mn-7mHyUXSuSC_-kqMqJXp_NDiKLFtbtMRbodCuY3DzH401rvy_4vb0yCELf0B5EapLVY1545sv80nxSl6FoZFxQaDWYXycoia6zHpiveR8hKlmA6_XTWHJbY2FM1HAFrdtt3yUTiF-ylkdNbPG7u7fRjTmHfsZ78gd2MNWIDkHoqWuFxIyqk8XydQpsjULf2Uacirt9FmHfkMZ-Jr_rRpcIEW0FZyLInbm6uxLfseFt87wA0TbZ0ofImjAuaW_3mYs-3H48CxP152UJ0jByPb0kHsk1QKP7YHWx1-Wce9NgNADfG5rfgMHANL85zvkv8sJmIGZIoSpMuU3CIqD2rgYnMY-L5dQN1fgfROrPMuAtlYCRK7r-IpjvMDQtRmCiNG45nGsM4DTzsa5GyDrkGssd5OBhl9gr9z9Bb5HQVYhSKIOiy72dK5dQNBklD4eGLMmo-u322zBITmE0lKaBcwYGJw2mmkYcrjDOmsDseU6Bf_zVUd9WF3FqwNkhg4D7nrfNSC_flalkxPHckU5EC_79cqoUIX2ogufBW5XgYbU4WfLloKcIpb51YTZlZfwBHlHPSyaq_guaXFaeCUXKq39_i1n0HRF_mRaxNru0cNDFT9Fm3eD7V8axFijSVAMDyQs_JR7SY483YDKUfN4l-vw-EVynImr4' \ --data-raw '{ -"operation":"search_by_hash", -"schema":"dev", -"table":"dog", -"hash_values":[1], -"get_attributes": ["*"] + "operation":"search_by_hash", + "schema":"dev", + "table":"dog", + "hash_values":[1], + "get_attributes": ["*"] }' ``` ## Token Expiration -`operation_token` expires at a set interval. Once it expires it will no longer be accepted by HarperDB. This duration defaults to one day, and is configurable in [harperdb.conf](https://harperdb.io/docs/reference/configuration-file/). To generate a new `operation_token`, the `refresh_operation_token` operation is used, passing the `refresh_token` in the Bearer Token Authorization Header. A full cURL example can be seen here: +`operation_token` expires at a set interval. Once it expires it will no longer be accepted by HarperDB. This duration defaults to one day, and is configurable in [harperdb-config.yaml](https://harperdb.io/docs/reference/configuration-file/). To generate a new `operation_token`, the `refresh_operation_token` operation is used, passing the `refresh_token` in the Bearer Token Authorization Header. A full cURL example can be seen here: ```bash curl --location --request POST 'http://localhost:9925' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjA0OTc4MjAwLCJleHAiOjE2MDc1NzAyMDAsInN1YiI6InJlZnJlc2gifQ.acaCsk-CJWIMLGDZdGnsthyZsJfQ8ihXLyE8mTji8PgGkpbwhs7e1O0uitMgP_pGjHq2tey1BHSwoeCL49b18WyMIB10hK-q2BXGKQkykltjTrQbg7VsdFi0h57mGfO0IqAwYd55_hzHZNnyJMh4b0iPQFDwU7iTD7x9doHhZAvzElpkWbc_NKVw5_Mw3znjntSzbuPN105zlp4Niurin-_5BnukwvoJWLEJ-ZlF6hE4wKhaMB1pWTJjMvJQJE8khTTvlUN8tGxmzoaDYoe1aCGNxmDEQnx8Y5gKzVd89sylhqi54d2nQrJ2-ElfEDsMoXpR01Ps6fNDFtLTuPTp7ixj8LvgL2nCjAg996Ga3PtdvXJAZPDYCqqvaBkZZcsiqOgqLV0vGo3VVlfrcgJXQImMYRr_Inu0FCe47A93IAWuQTs-KplM1KdGJsHSnNBV6oe6QEkROJT5qZME-8xhvBYvOXqp9Znwg39bmiBCMxk26Ce66_vw06MNgoa3D5AlXPWemfdVKPZDnj_aLVjZSs0gAfFElcVn7l9yjWJOaT2Muk26U8bJl-2BEq_DSclqKHODuYM5kkPKIdE4NFrsqsDYuGxcA25rlNETFyl0q-UXj1aoz_joy5Hdnr4mFELmjnoo4jYQuakufP9xeGPsj1skaodKl0mmoGcCD6v1F60' \ --data-raw '{ -"operation":"refresh_operation_token" + "operation":"refresh_operation_token" }' ``` @@ -79,18 +76,18 @@ This will return a new `operation_token`. An example expected return object is: ```bash { -"operation_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6eyJfX2NyZWF0ZWR0aW1lX18iOjE2MDQ5NzgxODkxNTEsIl9fdXBkYXRlZHRpbWVfXyI6MTYwNDk3ODE4OTE1MSwiYWN0aXZlIjp0cnVlLCJyb2xlIjp7Il9fY3JlYXRlZHRpbWVfXyI6MTYwNDk0NDE1MTM0NywiX191cGRhdGVkdGltZV9fIjoxNjA0OTQ0MTUxMzQ3LCJpZCI6IjdiNDNlNzM1LTkzYzctNDQzYi05NGY3LWQwMzY3Njg5NDc4YSIsInBlcm1pc3Npb24iOnsic3VwZXJfdXNlciI6dHJ1ZSwic3lzdGVtIjp7InRhYmxlcyI6eyJoZGJfdGFibGUiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl9hdHRyaWJ1dGUiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl9zY2hlbWEiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl91c2VyIjp7InJlYWQiOnRydWUsImluc2VydCI6ZmFsc2UsInVwZGF0ZSI6ZmFsc2UsImRlbGV0ZSI6ZmFsc2UsImF0dHJpYnV0ZV9wZXJtaXNzaW9ucyI6W119LCJoZGJfcm9sZSI6eyJyZWFkIjp0cnVlLCJpbnNlcnQiOmZhbHNlLCJ1cGRhdGUiOmZhbHNlLCJkZWxldGUiOmZhbHNlLCJhdHRyaWJ1dGVfcGVybWlzc2lvbnMiOltdfSwiaGRiX2pvYiI6eyJyZWFkIjp0cnVlLCJpbnNlcnQiOmZhbHNlLCJ1cGRhdGUiOmZhbHNlLCJkZWxldGUiOmZhbHNlLCJhdHRyaWJ1dGVfcGVybWlzc2lvbnMiOltdfSwiaGRiX2xpY2Vuc2UiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl9pbmZvIjp7InJlYWQiOnRydWUsImluc2VydCI6ZmFsc2UsInVwZGF0ZSI6ZmFsc2UsImRlbGV0ZSI6ZmFsc2UsImF0dHJpYnV0ZV9wZXJtaXNzaW9ucyI6W119LCJoZGJfbm9kZXMiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl90ZW1wIjp7InJlYWQiOnRydWUsImluc2VydCI6ZmFsc2UsInVwZGF0ZSI6ZmFsc2UsImRlbGV0ZSI6ZmFsc2UsImF0dHJpYnV0ZV9wZXJtaXNzaW9ucyI6W119fX19LCJyb2xlIjoic3VwZXJfdXNlciJ9LCJ1c2VybmFtZSI6InVzZXJuYW1lIn0sImlhdCI6MTYwNDk3ODcxMywiZXhwIjoxNjA1MDY1MTEzLCJzdWIiOiJvcGVyYXRpb24ifQ.qB4FS7fzryCO5epQlFCQe4mQcUEhzXjfsXRFPgauXrGZwSeSr2o2a1tE1xjiI3qjK0r3f2bdi2xpFlDR1thdY-m0mOpHTICNOae4KdKzp7cyzRaOFurQnVYmkWjuV_Ww4PJgr6P3XDgXs5_B2d7ZVBR-BaAimYhVRIIShfpWk-4iN1XDk96TwloCkYx01BuN87o-VOvAnOG-K_EISA9RuEBpSkfUEuvHx8IU4VgfywdbhNMh6WXM0VP7ZzSpshgsS07MGjysGtZHNTVExEvFh14lyfjfqKjDoIJbo2msQwD2FvrTTb0iaQry1-Wwz9QJjVAUtid7tJuP8aBeNqvKyMIXRVnl5viFUr-Gs-Zl_WtyVvKlYWw0_rUn3ucmurK8tTy6iHyJ6XdUf4pYQebpEkIvi2rd__e_Z60V84MPvIYs6F_8CAy78aaYmUg5pihUEehIvGRj1RUZgdfaXElw90-m-M5hMOTI04LrzzVnBu7DcMYg4UC1W-WDrrj4zUq7y8_LczDA-yBC2-bkvWwLVtHLgV5yIEuIx2zAN74RQ4eCy1ffWDrVxYJBau4yiIyCc68dsatwHHH6bMK0uI9ib6Y9lsxCYjh-7MFcbP-4UBhgoDDXN9xoUToDLRqR9FTHqAHrGHp7BCdF5d6TQTVL5fmmg61MrLucOo-LZBXs1NY" + "operation_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6eyJfX2NyZWF0ZWR0aW1lX18iOjE2MDQ5NzgxODkxNTEsIl9fdXBkYXRlZHRpbWVfXyI6MTYwNDk3ODE4OTE1MSwiYWN0aXZlIjp0cnVlLCJyb2xlIjp7Il9fY3JlYXRlZHRpbWVfXyI6MTYwNDk0NDE1MTM0NywiX191cGRhdGVkdGltZV9fIjoxNjA0OTQ0MTUxMzQ3LCJpZCI6IjdiNDNlNzM1LTkzYzctNDQzYi05NGY3LWQwMzY3Njg5NDc4YSIsInBlcm1pc3Npb24iOnsic3VwZXJfdXNlciI6dHJ1ZSwic3lzdGVtIjp7InRhYmxlcyI6eyJoZGJfdGFibGUiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl9hdHRyaWJ1dGUiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl9zY2hlbWEiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl91c2VyIjp7InJlYWQiOnRydWUsImluc2VydCI6ZmFsc2UsInVwZGF0ZSI6ZmFsc2UsImRlbGV0ZSI6ZmFsc2UsImF0dHJpYnV0ZV9wZXJtaXNzaW9ucyI6W119LCJoZGJfcm9sZSI6eyJyZWFkIjp0cnVlLCJpbnNlcnQiOmZhbHNlLCJ1cGRhdGUiOmZhbHNlLCJkZWxldGUiOmZhbHNlLCJhdHRyaWJ1dGVfcGVybWlzc2lvbnMiOltdfSwiaGRiX2pvYiI6eyJyZWFkIjp0cnVlLCJpbnNlcnQiOmZhbHNlLCJ1cGRhdGUiOmZhbHNlLCJkZWxldGUiOmZhbHNlLCJhdHRyaWJ1dGVfcGVybWlzc2lvbnMiOltdfSwiaGRiX2xpY2Vuc2UiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl9pbmZvIjp7InJlYWQiOnRydWUsImluc2VydCI6ZmFsc2UsInVwZGF0ZSI6ZmFsc2UsImRlbGV0ZSI6ZmFsc2UsImF0dHJpYnV0ZV9wZXJtaXNzaW9ucyI6W119LCJoZGJfbm9kZXMiOnsicmVhZCI6dHJ1ZSwiaW5zZXJ0IjpmYWxzZSwidXBkYXRlIjpmYWxzZSwiZGVsZXRlIjpmYWxzZSwiYXR0cmlidXRlX3Blcm1pc3Npb25zIjpbXX0sImhkYl90ZW1wIjp7InJlYWQiOnRydWUsImluc2VydCI6ZmFsc2UsInVwZGF0ZSI6ZmFsc2UsImRlbGV0ZSI6ZmFsc2UsImF0dHJpYnV0ZV9wZXJtaXNzaW9ucyI6W119fX19LCJyb2xlIjoic3VwZXJfdXNlciJ9LCJ1c2VybmFtZSI6InVzZXJuYW1lIn0sImlhdCI6MTYwNDk3ODcxMywiZXhwIjoxNjA1MDY1MTEzLCJzdWIiOiJvcGVyYXRpb24ifQ.qB4FS7fzryCO5epQlFCQe4mQcUEhzXjfsXRFPgauXrGZwSeSr2o2a1tE1xjiI3qjK0r3f2bdi2xpFlDR1thdY-m0mOpHTICNOae4KdKzp7cyzRaOFurQnVYmkWjuV_Ww4PJgr6P3XDgXs5_B2d7ZVBR-BaAimYhVRIIShfpWk-4iN1XDk96TwloCkYx01BuN87o-VOvAnOG-K_EISA9RuEBpSkfUEuvHx8IU4VgfywdbhNMh6WXM0VP7ZzSpshgsS07MGjysGtZHNTVExEvFh14lyfjfqKjDoIJbo2msQwD2FvrTTb0iaQry1-Wwz9QJjVAUtid7tJuP8aBeNqvKyMIXRVnl5viFUr-Gs-Zl_WtyVvKlYWw0_rUn3ucmurK8tTy6iHyJ6XdUf4pYQebpEkIvi2rd__e_Z60V84MPvIYs6F_8CAy78aaYmUg5pihUEehIvGRj1RUZgdfaXElw90-m-M5hMOTI04LrzzVnBu7DcMYg4UC1W-WDrrj4zUq7y8_LczDA-yBC2-bkvWwLVtHLgV5yIEuIx2zAN74RQ4eCy1ffWDrVxYJBau4yiIyCc68dsatwHHH6bMK0uI9ib6Y9lsxCYjh-7MFcbP-4UBhgoDDXN9xoUToDLRqR9FTHqAHrGHp7BCdF5d6TQTVL5fmmg61MrLucOo-LZBXs1NY" } ``` -The `refresh_token` also expires at a set interval, but a longer interval. Once it expires it will no longer be accepted by HarperDB. This duration defaults to thirty days, and is configurable in [harperdb.conf](https://harperdb.io/docs/reference/configuration-file/). To generate a new `operation_token` and a new `refresh_token` the `create_authentication_tokensoperation` is called. +The `refresh_token` also expires at a set interval, but a longer interval. Once it expires it will no longer be accepted by HarperDB. This duration defaults to thirty days, and is configurable in [harperdb-config.yaml](https://harperdb.io/docs/reference/configuration-file/). To generate a new `operation_token` and a new `refresh_token` the `create_authentication_tokensoperation` is called. ## Configuration -Token timeouts are configurable in [harperdb.conf](https://harperdb.io/docs/reference/configuration-file/) with the following parameters: +Token timeouts are configurable in [harperdb-config.yaml](https://harperdb.io/docs/reference/configuration-file/) with the following parameters: -* `OPERATION_TOKEN_TIMEOUT`: Defines the length of time until the operation_token expires (default 1d). +* `operationsApi.authentication.operationTokenTimeout`: Defines the length of time until the operation_token expires (default 1d). -* `REFRESH_TOKEN_TIMEOUT`: Defines the length of time until the refresh_token expires (default 30d). +* `operationsApi.authentication.refreshTokenTimeout`: Defines the length of time until the refresh_token expires (default 30d). A full list of valid values for both parameters can be found here: https://github.com/vercel/ms \ No newline at end of file diff --git a/docs/security/users-and-roles.md b/docs/security/users-and-roles.md index a3a4ca5c..68ec76c7 100644 --- a/docs/security/users-and-roles.md +++ b/docs/security/users-and-roles.md @@ -1,9 +1,5 @@ # Users & Roles -*Role Permissions documentation below for HarperDB version 2.2.0 and above.* - - - HarperDB utilizes a Role-Based Access Control (RBAC) framework to manage access to HarperDB instances. A user is assigned a role that determines the user’s permissions to access database resources and run core operations. ## Roles in HarperDB @@ -12,7 +8,7 @@ Role permissions in HarperDB are broken into two categories – permissions arou -**Database Manipulation**: A role defines CRUD permissions against database resources (i.e. data) in a HarperDB instance. +**Database Manipulation**: A role defines CRUD (create, read, update, delete) permissions against database resources (i.e. data) in a HarperDB instance. 1) At the table-level access, permissions must be explicitly defined when adding or altering a role – *i.e. HarperDB will assume CRUD access to be FALSE if not explicitly provided in the permissions JSON passed to the `add_role` and/or `alter_role` API operations.* @@ -48,14 +44,14 @@ When creating a new, user-defined role in a HarperDB instance, you must provide * `role` name used to easily identify the role assigned to individual users. - *Important: starting with 3.0, roles are assigned to users and altered/dropped based on the role name used in and returned from a successful `add_role` , `alter_role`, or `list_roles` operation.* + *Roles can be altered/dropped based on the role name used in and returned from a successful `add_role` , `alter_role`, or `list_roles` operation.* * `permissions` used to explicitly defined CRUD access to existing table data. Example JSON for `add_role` request -```bash +```json { "operation":"add_role", "role":"software_developer", @@ -107,9 +103,9 @@ There are two parts to a permissions set: Each table that a role should be given some level of CRUD permissions to must be included in the `tables` array for its schema in the roles permissions JSON passed to the API (*see example above*). -```bash +```json { - table_name: { // the name of the table to define CRUD perms for + "table_name": { // the name of the table to define CRUD perms for "read": boolean, // access to read from this table "insert": boolean, // access to insert data to table "update": boolean, // access to update data in table From 223284b5985a972936afdafb5b5fe88f86c02551 Mon Sep 17 00:00:00 2001 From: terraHDB <93155880+terraHDB@users.noreply.github.com> Date: Fri, 28 Oct 2022 14:31:11 -0500 Subject: [PATCH 7/9] CORE-1825 Add/Update "install harpedb" subtopics (#1) * added mac, added/updated windows, added/updated node version requirements, and added offline docs * go requirements * docs adjustments * testing variable usage * testing variable usage * Update other.md Update installation for other operating systems. * added link to download * node version * exact replica download button * functional download link * deleted old link * node version Co-authored-by: Kris Zyp --- docs/install-harperdb/index.md | 7 ++++--- docs/install-harperdb/mac.md | 17 +++++++++++++++++ docs/install-harperdb/node-ver-requirement.md | 7 +++++++ docs/install-harperdb/offline.md | 14 ++++++++++++++ docs/install-harperdb/other.md | 9 +++++++++ docs/install-harperdb/windows.md | 13 +++++++++++++ images/xcode.png | Bin 0 -> 50262 bytes 7 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 docs/install-harperdb/mac.md create mode 100644 docs/install-harperdb/node-ver-requirement.md create mode 100644 docs/install-harperdb/offline.md create mode 100644 docs/install-harperdb/other.md create mode 100644 docs/install-harperdb/windows.md create mode 100644 images/xcode.png diff --git a/docs/install-harperdb/index.md b/docs/install-harperdb/index.md index 1c8841fc..84de45de 100644 --- a/docs/install-harperdb/index.md +++ b/docs/install-harperdb/index.md @@ -6,6 +6,7 @@ This section contains technical details and reference materials for installing H * [Linux](linux.md) * [Docker](docker.md) -* Mac -* Windows (WSL2) -* Offline +* [Mac](mac.md) +* [Windows](windows.md) +* [Offline](offline.md) +* [Other](other.md) \ No newline at end of file diff --git a/docs/install-harperdb/mac.md b/docs/install-harperdb/mac.md new file mode 100644 index 00000000..e4f97bea --- /dev/null +++ b/docs/install-harperdb/mac.md @@ -0,0 +1,17 @@ +# Mac Development Requirements + +MacOS is not recommended as a production operating system, but it is great for development. For more information on standard HarperDB installation visit the HarperDB Command Line Interface guide. This page details the unique cases you may run into when installing on Mac. + + + +Please note, HarperDB suggests Node.js v18.12.0 as a prerequisite to installation, but we allow Node.js versions 14.0.0 & higher. [Learn more here](https://harperdb.io/docs/install-harperdb/node-version/). + +--- + +To install HarperDB please visit the npm install guide: https://www.npmjs.com/package/harperdb. + +or from the command line: +```bash +npm install -g harperdb +harperdb +``` diff --git a/docs/install-harperdb/node-ver-requirement.md b/docs/install-harperdb/node-ver-requirement.md new file mode 100644 index 00000000..ae4e6e0b --- /dev/null +++ b/docs/install-harperdb/node-ver-requirement.md @@ -0,0 +1,7 @@ +# Node Version Requirement + +HarperDB installations suggest Node.js v18.12.0, but we allow Node.js versions 14.0.0 & higher, in order to be installed. We recommend using Node Version Manager (nvm) to manage your Node.js installations, an example of how to install HarperDB using nvm can be found on our Linux Install doc. + + + +HarperDB 4.0.0, our current release, suggests Node.js v18.12.0. \ No newline at end of file diff --git a/docs/install-harperdb/offline.md b/docs/install-harperdb/offline.md new file mode 100644 index 00000000..b1ff72de --- /dev/null +++ b/docs/install-harperdb/offline.md @@ -0,0 +1,14 @@ +# Offline Install + +If you need to install HarperDB on a device that doesn't have an Internet connection, you can download the npm package and install it directly (you’ll still need Node.js and NPM): + +Download Install Package + + +Once you’ve downloaded the .tgz file, run the following command from the directory where you’ve placed it: + +```bash +npm install -g harperdb-X.X.X.tgz harperdb install +``` + +For more information visit the [HarperDB Command Line Interface](https://harperdb.io/docs/administration/harperdb-cli/) guide. \ No newline at end of file diff --git a/docs/install-harperdb/other.md b/docs/install-harperdb/other.md new file mode 100644 index 00000000..9fc7a500 --- /dev/null +++ b/docs/install-harperdb/other.md @@ -0,0 +1,9 @@ +# Other Install Information + + +If you are installing on a platform other than linux-amd64, linux-arm64, darwin-amd64, darwin-arm64, windows-amd64, you will need to ensure that you have installed: +* [Go](https://go.dev/dl/): version 1.19.1 +* GCC +* Make +* Python v3.7, v3.8, v3.9, or v3.10 + diff --git a/docs/install-harperdb/windows.md b/docs/install-harperdb/windows.md new file mode 100644 index 00000000..d9713b1e --- /dev/null +++ b/docs/install-harperdb/windows.md @@ -0,0 +1,13 @@ +# Install HarperDB on Windows + +Running HarperDB on Windows 10 or 11 is a great way to test and develop with HarperDB. We do not, though, recommend HarperDB on Windows for production workloads. + +--- + +Install HarperDB on Windows + +```bash +npm install -g harperdb +harperdb +```` +HarperDB will automatically start after installation. For more information visit the [HarperDB Command Line Interface guide](https://harperdb.io/docs/administration/harperdb-cli/). diff --git a/images/xcode.png b/images/xcode.png new file mode 100644 index 0000000000000000000000000000000000000000..eaaa35263f17de94917add46ca1294a216edecc3 GIT binary patch literal 50262 zcmbTcXH=6<*DoAEKtu%T9g!;4fYJ$|RH=%9N|k_gB1LK_0*Z8`_aIg2AXQ564uiT{gJH~r)P5}U+03CI;mwr||i@x=iC#gby*Fa^LhEj(QA5zO};&l200@UPH z)+6d7_#=z}5rC-gp}nM0_tYsqst?Pv-j>O5jAa;R1*2VE(?y%hE#H0^(K9rxR6g_S zPNR)1y%9WP0t(sru&=zq;;AszeuF)Z}zr!h!gPGFcqqROor|}{k zpvP)=#>2P&Nc0i1oXdWc{A-ZX&z7morwzh)@x(VaHfBiKKfBE#BbJqw(_UsDYj%qv#%b{e^xltZy1e1_+{`3Dc`MBLWjCVAWc(Tn> zs2L{`?7cZ&Ou_zd;)(N;Tvae4mTe3ka-B<=Q$ZTH`24i|{|NI}q8Bue{v(?~A-m(` zznSK0J|6x4?{x}sOXQDEQ_DIF|2K-igb#lFUoOU_&;NgJ^Z$|i1N)Tff419tRQ6Hx zf1`aoHR<${Ebc!loB00!X4Fo~zJ8}w(d^&4n0i^VNE+vi{tt0U(6_&9lml=xI#VS7 z0h;Bb@!!DxH>rZYx3??*^4_H9xx{LnMW4peRgATU zYBs3-TWK#R0*n9O=KF65|0m4;rw0BsXkV6B{cklaiT>sNA9Mc8&h6yvl|i?hUHpaG z%Ps#6rat*~w`6PZ9mYb} znxUH4SC@sp`%B~mE9L9`sBwpL+@|p;ES`4yr7@EK&ga`~FS4C5OlDy{p{7dC4q$<@z`u8tKt^*6L+5>t*Nyu&;wFh*RWL>4) z?4_f$g}1b{C1ltrYhB~^t z1yb&w(hu-tTup{*lE&WP{Fe-+>Rpkv2~Y3H7Xeroe|?B4DJfxhoy1X=ss!Zo8WB%- z&#Tb;xjuk6sb1EHHb%EO<>Wr!=DxejC)oY{G>SaV)yQk0lj215yYIH*rf(-0ITLc5 zVy2?{KmT!JwX;z6%~b zceH3^t}Z8dT7#|us_5KSlA+4|BIiI+6LN-x_0uVzKtrbdG06MdSQUN>T6XTIk@d)$ z6>wqDWD2Wnux2bwNs(DxUIN42At$Jx&9Tc+qHYZ59c$fR#!kuH%Q~kK187xa(!z-K z^?OCWBdz-{8>8(0E6e>?_R9%6o%Q(h+My@wv5W1=)>N&}?%b;;cgq~V)hLayOdwd3 zGYB_!IIb(%<5JzuJdiFa866IcovyZq-)GZ#f=IF0rQpvmgrGJ-F}_KluFo_vCgr9~ zlS@n741#*Xe)1OwUB1k`Ze9j!f!xWrr3r*b2_rj^LTjy!e!qRs_7#A zV;lUBG59SrI6JM%#rs$`=@P7q6;y~8#E}2;#7fYMU8OoGoy1^HXrK0*ae!WSvDIP8 zP_qYm)eClR0bLihjyfJzgp|fNuqpYyKg`|>5(4jOr(WLs@H`Dw3hb42p8OtWh#U_d zNE44SHdKDhEMdVK$sOSMJQPnIJW$6j`{6~}&)aL`CAb%jA5KpcFU62L%eo~g;{LLq zQTQ|EAl{Mf^z`mE05~m3`23z18`5vCxO$Csc%!8*VFTjD4~Z|W2p-8(R_ws{NFr!M z@LkQzVbnfbnEdcNB6DlavmnBTBIa=icyoIA!|q%|J`T2%9-wda;@aD)(c~!4=Mf7R zm)9WhkT4Pg>NsEAM?^D;M$2#B^SvHtMs#WIg(__LEuqvJV^b%i2vSQ@Lg^3OoE$#E z2WVZZlu>P%ESJ@7>+668=OzX+2adV?mOmz6&VUzn)2EI0nP`$Umj(Z z@_WhmIzciNfu)kR{U=2p=tNxc!0u=OgZ91xKP;`v;cEE6Yeku33f_7oa=)5nNb8Fs zYE)AFt(m7-Nll0 z6hh)FttrfjuPC0zXY0(ixNf(ilZ=?zqJ*iNjyon|y5DiSRkiN@dcqCLjK71Mn! zcw=G7eskhzYE$fLs@vr~T**M!h~;^Pl_;G*kytvDf%q?kO{ zY@+b|{f(mJ2?NE-?+lO*!n?Q0W&iRy0DJqqj}em+zsir9Mv^!aHF(F^&tejVtn6Ez zhkJ)->iaj?gU)3WaiN;|wA|d`k?|6th%aM`p@<9}FWZF_0`M)SAVcOFo6+v4A*OV? zmhoY&78pFd0(8BYbacUOPXI&~&3!}Tw$@Y5blDhxkhR`S>TN$;GG`qBi?m$$zM;~g zy2nn(`JQO#Cli`clF;{!Q4B)Nidvm*VipQ+l+Cwh8p{2&sx zn}i>wc_OC0ASR=7jM(@1a|yaw^3a($U@)@|)o9zTJan*-b+51J{itM^GnvscA4B^g zp_;(>s?KfkR|CKncDdsK-pgtE#N#CEf3&tL``%8;&A7yp(Bc1r;&`;MqxbCS*#axR zM<}y5O@rUeKg_D2{cn``o=%Na(RJW5A344Frl|8dXy&(XfQ^bqf>``!^=^NvDB288 zoHC&6`b=EW`c2q|Uu}~{?x?*`g%;uep%2=c_`5b+zZcF?J*ti2fSB zL_b)RH^aiSde+OAAU5|7^r*ytlgj!cmJ9nd`$omZ3yxqlNuS`sl~+@!6C}$Km*E%j zGgQd+lP1jMlE=lU+nnsl*Ou(~19a12U$!d4wOu!?vDdw6(b69o|8B~PhcT%0?@=#n zQowbuaCi5d+aemz)uJ@k>gI=Y=DPcIp2Gliox!T?;eMMH^zHd1rTPfw?8PLC>d~8X z8WEDXuRy=<3`ZWt$C~Xlj|7KbaT>ql;))zr6cij*R49>n&Gs|?4b!n{4tpe*mu)Pp zAs@gw6nCXhM!{6KqiEPF7@y-Pn|xQx?q`g|ukXSK5()2x4TRoe$sBLu{;TfRSkV{( zox4J}jA79<95Rfs#j2_eC zShWnbRt+A?W<{{trdCXw6qOv8YiFE1XsbS+Fbb zEL+U&1eGg4UWVC=v{5OlI;O?3uSF}Y?XNf6@vAZ9Be)ceCk5%t268JA1##jYG6Bn1 zl&crVBa%DccBMy@7(%YkLU`a;C)F{zf~|VEBBP`5{h<9ojaqNchadc-;wP%uuIWWk zEmy>C>U_;o?_!=nJHqiXdw@~ny_lI%16C? zpY#vb>wb9rskXN6jiYT_3OZXPuWia$o5#NsI;|N%o^i1lGe3Za4ijqpXTq$+O?SQ= zy*|oBLUC{1f31hae_)h!$nnrz30Fm+E*DWu=<#rTKGOQ?DE(8M`>)NCe>B(ZS*~<6 z!8PQa3(wo$

@Wk$uX{YjT@6#V%f862Vx0@DL{gC@7CLzd~~$$Xz{eE3|G`wcfg# z4ZZD2lLrgAB0Mqlr+QJbxsemx>x({kz1E5^)@o!yUHBZX_Gmy?(%~z)$`-4W6yKZI z)68#i0BZ($Y|1E6}`J*z&M$dt~nQN z5^`3Oh8>@T1L)h?y-uCCSu6u-V-vU+B(o^E*eP*N2_-9WOozBa+s!Q(bhp=j!qkQe z`-T9zm4OjYP3BA=JfwzuXq<2|;5Rk8MCgpjPM zyfKFP9=Lx2y*f%RgoqU=2hY__*ls=75kl^yy4WrqmUbWf_*tk0|LtKjveeT|9?j)w zu1|k$c|QcsUvneQX06S%hW?&VzwM^9nGNUQI)hE8uOer3t{ni--1N_01bB{ZL?#=+ z0{Aq%M8S?oV-?^-YY*?;{R)-uuoJ5HjS{qcag2ji-_q=dUWsvRPOW3=v7eqBoip=#r^QDKFS;?2q;QDsW;5L*y8o; zZ=2zUI-jA4qec9jwo({-!f-b2K7F_yzf)qD9R25JGCBcesT!80AlWKXfFs~OeM;?# zK%t>16VVqQ3H~8!6CNz!5_ITfrSrzqb2H*{!px{@P2jOcgg#C>&r%wylh4r@m^88Slu`>!5UcF$4)V#^MI7uRNdY#fj^%n<=8VHe>_kF$^KDdXlO@ z>kYqUZ(}!i% z=VUjAp86a|^VO>D+X3z!YQk=Ycx(9t7i0)e+BsWe;H^KbG1S{lJOtiRbVidNL_aq$XzxzKfMn}txEZp;?_c6}6cs3L zXLB%=!q|VZK%$K$nq?*0FMH%R=7iArWA^XR^>$$J-c;>Q5hXfF6vV@)RT(XzjMOsr zIzOjm52D<=!QS;bA$p9Vgh>^=ladMamvWaZsPpXy>M9*(f2%(;y+p z8UClc2y7TLD^!)hAoGasd>mb1?-=Nj>{vb^)#J0k!|eFpfJ*=9WuhF=ftzwQe^t_)9|w%VKb{FA=FA+bdCCEPUwCy(eIByvl0Gk zBQW0AsnD|xSE5h3@^;dbohOx#uDZnFc+s~R&U-x|nHAJ%dor}bT z_yI-szx7cMAkj`Vf6l`zD?5luz(+Rke&LMp=j$WM>leLby$AN7-`3a|?wRkm9x*Ni zk+6IBJ8*4utukWs`GPB}NlPK5b^*W++l?b`YblDL%1|dEm5#qoiLgfA<5R6 zw>AuIs~m8D+Pm+Egs|EAI9bY$ZfX=JyCuJmYkJ|Zi{~K{LLh^!nL!a2e*MtCkXYS) zD!JH!bSB8fE7!gc7rkwr@cmp@!HZhLK4Q%=Ck63R{v!V73Q9ldv_I2Vbx zzktFud-9+ET#Yc6h@qAY7OO&CH8i`=yLjyNSbzDzVI55_w7~qdG>0dWyG0!FNLBN^ zUUW6Xm^+Fl8|R9)Fq967x|8Ng z1v-&Aa-sn2rEN5i{vg*V^yRh&SwnEd438rMaJ&21mIy2d4}F?OjHi}r?UQt&m-m8f zmzPnSCubVxw;svK8?EfQ8i+-aaYTc(SZp}IdWksF-z)X8pkr1D3_N+~N>X)j%N=<{ ztlKw5`Bxf|_JwNDmk{_RKBo3stk#`~%_ga-(LEx4#zCcZ)MQ)tkJ<8#pLVv7A-6w8 zOYpyhV)^_@$>}5c`FM-;qJOH3&+2d^NJ%+`0y78S1_uDmNM-MlstjkEk-nwY(Y1n0|{3lZBGp%Z5!um5yPVHZz6nS_i z^HRy<$H$_-RI|g7AvoER=I45NwoDTFUjt7z0@)meABE-}&PFP&8mXDQAFLE!3Eznp zr9&dTGtN|ml`j5dKwt5Oz*WOzqqAT2@PRl>wSF}uhYAG|1W{5BbLK0nrGABqwcE*t z>q9ku1%(S=`vUbs%^Rh7xN0k#--#Zh*=~~E9IWQbom=OxmboC66GO2&yDldTVO0m` z?R7%vgaF*_9MZedX2fq~Xptq`B-n~M=ux8#4yo1s@zvNJm752Gg&qfhh(j8$iFA3U zVC2y|S1-WL9X;exp2v|VTi`g4@`R2=9`_Tmb(|yH%h>a(7T>2_Bynk1;Xb5sBwfhvgW6>9 zcq{?vWd&w>6zIXJus=iUIB3x(Qz9w3fT^$O9&YKn};9nQw$#&ZL+k$>rPuMbvQzeblqW)Z}W?HDUvSQKLt8o_b4f zC)(TU$_P3LE2q&>eg9yIRD1ai`KC+oz5X2Ht*V8-+WU!iSdHJmVWCZ7iS@@xMwy=3 zIaNoVA0x8O_ip9$3qD}e&ELSUJ*96_e=2byDjXVJ^mN4ZEsn$$D+I(>VYMG6ar*P$ z3ov}Ib%skPkGsT?V-G55M94ZXA}KAmBab9}1JWiB??>r`dS{hu!a|uIs>?zJF6jX; zZu7em9b7hMd9YFmPrw5iVm<<~Zc2R_RwN+g3<+)Y?W+@Xwd%L9B-#G))U^SYP}bvj z9?2P-OBOp~+;|nbuZnjNMw4FKZ$fIH?7I#&Vy1`7Uck4ClVf#$bxE49!ayfz4onft zDakS(P{5N;if880SzNzVGF`YJlZ>WYY_rb)c5{UZ;;E7+rFpWVGPlZrA2U@vUKfa+ z41XnpYAS5r80yT-?$x2h^=gIM2leI3hRi>gp><&C8V zTyE|-&IDm)`R6DakSQ%%2*i*tB1@V(mO$Ou6VYc5-s$E5JD&exmD&2ts)U(ios*OC z-Z}vWyaO{yuhhM2IQ=2FTcK5es-ckLPyz+uX3yYBrXMvY=0~~?qAtR_Rdk57986EO z4C6f}SQur)xP2zGUcFyv0d8a*+hS^25V%xxThDL=SF60B1YE8pw?-aQ`HTOK%E~`- z>pL#s+5Thj>ZAsZ&10osCkuzuEfyMk;=5}t1YcRZceN~|rtVRuOml6<7QB7gY65g0 zU30vh)z*`bUHA?v?f8X}i4l1>n{Pex(V}{EL)%JJE9^zx%bKI4u+Sts0g{i-#rGfH zaIAuoFhX@WO;btDq^1qtQyKKVI!bS5yc5M|)@^q$h9Gc%S>02Ol*^D{c(d*1niSP5 z&#~Mh@gLN#tu%Jpmv6p_Mg*H{Xltv6d1&z!8qII{mruK;ebZ1-dewjf0$OfRIlntXG|@VV`nHQ{%4u3 z`RQ9xupOzr3s_jhd#`C#8}4Uk_KBexEfihcf(>07?7{dYYJZfLF}di|f^;&$zJ3uf z?L-bUsEmFN!qVrjQ{G}UA1(ezXtR#xviW5pJUH~zKTcH^m&Y{3ceFhd*&J~y;>q|TsOs|+c6qLb=U5kI4ISk4le&zb5V#({!F zX6o0=GvoHGP{7NbYFhI#@$)gmCG>Bh?ow94NWx}Bu&0ptwf_C8A&1*ia>Gq{4Ma01 zQj5eq7@>)1sIfIWEaJwrvr;`eNIA>+`Y9Hp9g+{Z8Zq@T-yK{@SUQ4*;5!+vSDV(r zq5=K6QjIGN{y&wiE@eW(96ZM#&w8r$gzSDVqMd2E+v++9Vk^HW&LaEU6z>eBAboUa zYwo3m!seK--)&Biq-kz|LrxtgDnt3~6f5iKMI!Y%0S?sEhYM&9H9tUb`DoqF_6)ks z?(6S5v!Lo%z8{^Y<`ttWdA0iHQg~mWC2I3i=j?8t;E3`F!o!%Q_5}^8qaN8_e+IJ*-^Ejr(%^CcjIJbpLUMYyb}E zp!6~P{m(1^sd!c<+SJagVGzXJUpg>-B@V%LYI!W+c2QjBz;e>7*dwG9SD-6u= zDOtRCn^nb&4~E7}n8Dk$!nu#>^_EyGc-k=r23EoOu(r2f-I<4>y>0)5iQhV!14n<=xaB;_=YGA*>jvtrSrNV$p?d}xsT9yXG}&vvi^E_ z$4FG@Rf=4-1B96emyt!B$`s|Ep5)>>({;#%a2z*=Zg;S?@Lm@^Vq*W48hK7H!-1Mu?&dMB&`J zhBNHSZ)nB$!Vi?jbZ zSf568GfnqQnoJ{c$yEt0LT+(Bmm@#Nk1Z|m zzd10U8`55N9H#b+ib9LD4eN+RO$uoBX^f3oJ$!FKQmCp%07R>%d}pK`-f7Ec&Y?=Y zacnT})9FILbmM7(ck2yIueyWzWZA3tH(#pdiITbT1J(ElBKP8xpJ!V*x$xEMzKm}1 z@Fcfc|2qAu55lSRz{tOS(!ok9>E+>538`_*On#=KnGCHLX`}p4yf8xqI*W)_P1FWosQu3*L!0+q4Z+G4SYO`D^K5kU0@o!nEq6dd0nr~ zQ-=6m*^qwsUZcPpf|}LCQ^qWq*Tt=3?iO5 zOl5}{Qe@2ByQb>WB2h=qyi@_}lwDY=phtl!eT&pk5p^8zk7Q~?+{iS)UCy){ z{Q%b|oPWRp)eRA7C`jdyOLCYkX7MPAbZIT5t@wJ~;O6_ZR$)~#l@dPr`yWUwM2IcM zE`x3A4o&gOy)Jq^C<~&TCfxS3__0;(jc2Zag#)3h3YFZN2csfs-JhBtr4tA$qp9f= z^|IWV01CpBs|l1vrtGFEhr9qxz?M6JT)nru>8L|ewF{`)r9{Qrn|9W^91fY#UxM+` zuCh3GvT%_a+CEvR<9U7T39CKq$vX-?T{Y?$;EHQ|`2e%_Bww8ed0+@35{AvNjsNB8 zK{l#j${EL)rJ%V2-GoL>ULNksaEG2Sfpi2y92UbD-yw?7N3nxTWkV5@9JZ6U>y?S| zEh3sDZSshbC2o#JC@+SmLEz4ovP{4!4}JTaY!vw;dP|4oM{Q2krW`eP<;&(6A0gURk5iSy&$&2J7m z#&xT`q?UBIOnYJ{M7il2gOegj-A#t~;z>wJ0!^{ZXQ`U8Bcp>S>lXsk+NBW}HR15xf+BpcE%{>^sQfKOjb4}f2_oc3{ z*I~cI^U1~dm3ycpG4~4PkL!#x0kRvLqYIeM1e1r#I#z2dF409rPb1|it@s(hE=Vi? zS$?#^ed(&T@_dnhTy2%mNzZEw;1`g#u-}@u9itL+?1i5kpli`pNC{R2Ylx<}DwZZo_( z5-CD6zwv7Tx7hy_}&i^!$h)Wy$ZVmI0l}8a%#I5#CbIuMH>43he^8#4F=$ z=cVpvQF5{yV=5AktNrBTU!S}pk(v{-;z5@zau*7s?(zvP-c80RF*3YEae*CB^weo8 zbQ_jLPd%8g0q2a%OAj4BnBW@<4t@PwwUiz-U-IJi7Xu$_j36%bgU&T{uSt61V1#&j z!{eRt$);vWbfVReBI+%vom^A+8((On{{sGt?Oq2TOZOz-aBQK$JiCx3UR%tVxv@v5W|Hrnw8x$@dud?0P>^$#aJA4<}^P7gtdwX6vi}LhKRXgbJkel+7 zhtrQybAdxe(>vee=TaW1nPw^|ShuSuisHrSr##NoXN8bS_~Z*8P{M{qhpx6in0Xu) z+w#U6q;pRm^;K-W*7$S0_7h=a6GN`eRy}%`q$|9XLtc7K5$Ua%r?tjFKK+~VDVQ(w@0 z`iJL#`l@g*{wOZ%(&8ny#pXw~m_k#**$cBxd?CHH{g(q*pQI>L++K1cId#0>f_9JF z_TyKCena+R<(fp>(>81Cf{zk`v&Y{{f^R7e8i9znK0+rZ*>=&4))yTyIvK; zCA2pjZ)1-Y^T*~O+Bj#YgsPyDZiHxFRPr!UPcPS3x8ATr0-@g`PZ8TDqp9Cket5pb z3Of8l(c3JJ7;Xcl!HA9)&_xz7E+shz-YD}p^mAlT>Tk`%s;?M|UPlg7BjbT(M<+68 zcG2UHe1#)_d!zy!BL~<&N^-w&Y>fJsTFkJ@r%0x=IJxOUd+oyn=2i~HP)(QG*0FMu zB&Fj*Efm!lWNjfs?tD$oqbs*|b)gl!l*K_^0}LGEqDdSB=#-+ECjKljX;rl=nnGOf zO}Wt`zi<95K<0xyw2!aG6!Oh**opPCJCCjsBj9>6I`Q&CU&>TN#ilGH^p>V-pKpEV zyysuVzQFZ7zP9{o{doT|oi7q%lBaywt{B{pEZgwu8Rb$cq0ku-*D^YO-DPTKOl>&kVDc({&h-O;7Cr{m_} z!Pv?%&<7X2oIvy2u5gjL(#1L9)w7<3Lhgju1AB2EUR7ThMy~8!e-R0qA62A&$uwvg zqwe+?JRRu>Vz(h{LD{~?IdQSI2PDMr>4OV>kBhKN4lfShSBD40N09}@#$VoHqo;g4 zEK8HcOg2aB0*K8VEB3d*am&g7s*jD@3qqLhE zXL4LTi=y>GqdCqga3e_2gc6!=CAx8)A)M=PrV92|Gfw@i3MV{)$|2ht%lIuFVVc#Z_>My|~>vNQcHk&s)7l3nZN+)czD8b8tzFL9OsJ-uE z4*ofdU*+lVjfL##+2x5OnB~$IFZz%_Qdm`SvnVd(6l23AF3r>ZLf9F`o2&GFWjncahtbaygDPd(pscmFXY zE|o=@?$`{)`Ez=KNqTyL<>wy|>#;$IFf+_QL`;s5HA7q;078lXkh*ftEEN9snrO8W zG^2_4-E0U45W)GL|42eqOy_r>#XFM@>~k^E!`Otm8aBU_MOE}`om_NK*k(4x`#no+ zkes{=UneLeRqM4nw@MZ@r!s8uM3>ch7a1art@qQ*0;;#x+sE^EG9W?*1f}eMM4%8i zBZ+tRiFOj`)b+A{b4+R3$QGeaY2Cm4Vkf!`ZL%@Xuv>PWCw!9KCdj&d^CERaWF*t= z?DMop=l6IRIPK)sdU5|qmGJz#>m#|TMy?@L=A@G@GHaj<1kcZ7hc7bhs zI7FmmaGo8Eag}tx;8Zz7WO(B%f$vUfYIo{OSNj!1n6@PdwmCsPF}13VaKfuX}heZQ)gXt9+ECl*j1rsj@v9W#~+1z-8g^Oih^swR8!@X;q;p#UBRCLRL#NTMm;^f(v z`k_`>=m0;(D%jIYVTcEwQv~>BV!CQED^=?+3fMJ$|TA@B{`?jN&h1 zZC=p@n9Tdg6o;cVYWAxDDfSh_zN$6D*U9)Yc}^Dfbs0hyAs+D{p{bOxL~DX9;6NR^ ze#)ATQ=FsN7k<6x4wZ6{dT>@<)!{o80{NjtD?ihD>ecbOdB@l3x^1i`Yv%M*k~K>D zb=n42+nSBm{b~ZPfaw_De;Q(6Ac`iXvwOzoS^6abZ}lhl_(EX)$5G0fMa#ofxX^wJ z;QLmIg6WYt7mOCKcZF?HGW(FD{M{0Xju}RHyE(bF^PLDnbsFRtJ$;r&@CD|P-*N^a7=SkY^1u(X zf7|yv^XOlJARevUMu99!!-Tvbq|B|W8oBRI?{`pv>KC)W!_gzOP6>aW>w+;!F{^7o zd1754f#QuzC+*EEOFZ4X377ZX1257&y2wgT98cjS$#qQEx z-*bl+d0fRqx^s%tk=C3_;-~ux0(bW01p^)>d@1SJ?a8)_Y3gtE6M(uL&Q3zKJs`;Z zLoaaO1M}4|j=Th834-3BBB4X>DAo`e_5l{%^#??+RTWqd;NWNk8MjFm`<~Johk}uOn_MJ&8`=4InG&cHs(0|ha4qnY~#S}WpVHst8ZTpai4eD%;;RXNbH%#^E z@y#0`H50d*YV3oY_z^Qh;s{Nja(;?h&vS@uBlc62*Z05cXC5Szr@9+Q5*|KAeq1_g zo~rGa?|~43pTW!&n@=A&y!JJbKk|LuWd9E}>{K}@f3CF9|1>Q&(<2M|q1!O;I{*7b zsOD1sY+=jCNy^8!QFu_mT-&Rr%`~}_FfA}Ih(!6yHHRMXuwQY0TV*(865ugD`IbR_h*#q=A$K9Mc{YJ6aIk28Lgxxaqj%>`S zJNL`bJJ&Xwe@d-A9ynkldOV02t<~{)IMcu|^!^?5WA#DnYwtnDOX(h88#HbJ7B?Z$ zBJ@UZ=!;=}BXcXCeY;8cw`XDfsM-tiXP)$ogfp3tjq=8Q*QCkg2ayEaz%VI0+r44? zyJt;LLrPO|6&5~B+O7ZgiZW7~6e?FUuiYrv7NJd18Rpr?OzPNC~6I|Re)@XCWqYpLKEULxOy3kz1*sPcJ=(e zRZorW(7P+r%mfvobi>1x>Plo}N9%ER(c)olM`XX7?^H)&%S?{=#nhy=;NsIHav*Y% zrEE(`P2?F&#JE8vbU-WTSdY0y^|sud8wABl_}x}y@f^##SENy}P{M{Kb_3Ffzh`ab zIm?k>E85?N4g3&$aTy%?8N{4>f%Hnmmh^Y=geh=2dOL019ePTsIil8O`)52E9jnU| z%V*f}bEbz?YB$Xh=4BR}?dwom=rQ>N7CQhTz7w#^ya!rRcpPvC#%4(oANAIl zg)v=tp?p(y`g=s234=v=VhDVLxV_kGwxwClg>hgtP(?vy65&EO_)&73STJGQbp668(jBLT% zn_sv+(N!0;?2g`(9SWvZulOI5o3}j(ox0yv?&0%6>47gNBjEbQK@Bf0)zR&6XH-0K zKWb*wOBp-+^A5kO*Mr2Xu91xk2n?Fk>Y#^*!-a-Pm8R62t0O2SZ>*i=0ar=XueSP5 zxhzGxVeklI>1W$lToCKce>qj%7O$ae5uG0ix*e1{2UL7R6I@}RxJgO?xBB5vx3^okTy03wy%Lp4R59PmZksHX-3Sd#`@8`L z0N1^oV$R4O^KmP zOXj%1jK-+Yb(JhKPOzYuSe6ckQ4ogSQ3#a`M@K9rQ_gJ>| z7mW=S7V428-n8879=W4I$?iHYIJ1!DY5R=dnIw3Zrn;w(X0#SVgdl!QLqUJsLLTf9 z`XyJZ(nH?txI*!T6X=lhXu=GkO- zlJ{j}(~CQi3~!g6g5>NoboWx&^9|0L@FN`)f(r-g>};3^sQnvm9x^v=VQ27 zGOt;W-cs#1CYeIJIq7lG4L;MHmT2z<-;I8v_poozP*;IcT2`f(Z;!*$^m3~ut)cp$ zmYNv@@EP}ksLbF>;Fd69Fs$3ayM#`D4~ht71(twYyi$*R>d8nf{9Y7gtkB{xEBbcF zxEI71-O6nAHfZ!_vfo3=U??$HYE9{fAyGfjSxX}HDHB4ueal8SnU7t9_k$E}X0lLpE6*e4`yZZL*?HQ>y&a8zx^!$;?8mgl^U0?+L9RD+pTKxD3GK|_P+JB>+o_aX&F*C$ZoH^&PQtoI$ zb~Qp=C1IlRQZBv)S6=Hkkq#zcB#|EJAwW||paA#zl?rl5@|yoMSBE@k+|w26cm4Ws zkK9a8kUzkr=*q6*{FMx$)%9DU47W0%b*19o!-78u zgZ|^M1r!X`>h-_Fx)GW3?-jBPhX-dHHl&F+?YsgaYPBSFK@za#sN**+=z%Q-*!|u< z0g<4};GhwkuXZ?K2k=H*=z%%?=?!JDUBT3J;YVd3_TCpa5Bgz!Pu_hRv%vo`%y-&Q zvf`30{YR(CTQeAKmxvyS?qyKwdmDRshJug6PH~V=VX_;?_GWg8oSUQmqNYAS3r>pE z8XpMpAL&%tJudafhPq45uFP}d5*$SRE;kiXO-cLK+Vh#&TnCVy8zx-Or%aj&$|=6RC_H75EAze)npr8)7M+HF>Vh zh!J};H^ve2_qFcU*f_ob?YwHo7k~~#43Vz30&yb)An$>YBQ92|lDdo-A+PP^Uwoy9 zeaWiDdM5d5K35LzUIkL4Myi|q^UDvYBmIZKAn2+g)c&w1;YpgC@`-T{#yqaU5cQ3c zXy(`FVu1V@f5XFxC1B7$?l*Ea(%__v&x*j&F`E%T!7}(10_`oWyn^oQGY209J}JA} z`ADnOOTJ1?$dmcap*ncB5wXJeQO=o}=JAHFz2ojuFF{b(*49(IbI=0@iEyv_+;;R6Qton|yI zr?CRJF&Q2^#^f^@b@-2R+eeV@OcoUeJ_IL6dthvIW7@YKd#aHdxuR;WI5iJd*y&95 z-@3RwyrjqagS^t;1H=Dsq3i^-JEHNx5l4{qXzmz$mNxoyCE~o?-*c*`PPBu`OA_xn z2-+_PI&?Ly9#AjEe&PtG^BRHj-BCG1dhFQ4qfN}#Ln}@q@B@3gXvts0BAuTpgZ*Rub#>v`d57gY`D0S|e?2n% z5)4j=NtOk1Z9qb#nR5>ZK zcvYnszgfaL(;DgUxh?>pmcIY+u99g^PUcfgAVbc8uY#Yq%*IguAYjFt)TlFSWy0``k_U2zbxaMC{;O z&gEM+Z>yn`&D}2-$BbsM$u#^Bu&y`Z&$RP`>7_o0((-hm)e#pjG;YN2n#^BGB6G0u zQoylQW=F(BF@0aU&$oV*@_g*BQ8$A7_n^v5;^k&%pi_k=*NCs;0YjgeV&~_@URR4> zLVVnSR_9fIS(J@2sB3sf`x@7e3cUkd1`gw2e8G-D_62ngVt4{PMm_;!_s6DJha9vL zpjD(as<&zYgWXS<91qlL)k6?NO^l+i2!b!$RxHJmBx4Nr00;gX^~ZZ`zs5iWW(V%a zlH9YsLRUGm>0z0zbhnsW2Hlj-oR()H9YPyl#5XR|L;*HFGNu?>w9w6buTC`!q26WV z)sGU4R{k0l>w(dU`|-8pnP(J^iBBzRc&|GnE$g67Uoz0?ggW^E%26q;alX(L0v*I| zS{60XE!o?3?e#U0pVFl(A!v2T&+`UM+N|TOC&QmAiKj+LzK211y&Qemn=W0Q{_%6p zz4)Q8{)#A3d)F&1vzEdaap`5(9cEkJ`D2yOLn-S=qNX;MxP@|p{}*X*{nzyS#Se>! zScr&(bfL(B;X<__CNZl6%i)wFgx=w^8r$`jL*ipDYFXEXJ?J{F8 z@B-Joc@_Sh4#HcMalVlPUtJKj>S7eV!LoKQLfyLCPa!N~!`L@WK9;26=J-8nha0yq zJ3m>w|2#ynMv36uGXhSLe|OE0k(|O{ej^u%L{;D_qWgYwOYvG3{j2k`S%ixXsqJo!je>OfT_g&E>GuQ|H=?x^4E%}Ovk5y`&1P{4hc_BbeB zn;go9=IbJIF}?jaBn(WjiF>z6%D2~}CZrP!`uQ_;zg3keEyq`~d0J*`l~w6#oH5BZ zO$`cB-P3XdtpB>2d>bU)xW*ZLdJtul^ga+?ul`bV`@J`u{sAC9oF@PFw);S2taXta zd{lE~YvFZW_{T!-(O=rbFbhQQ0sz|uct&;)=0(CbYf2LxuSmP3rTpNo1wG(Rl(OIo zx7@3HTAmL1ztuDRxB(tI9C7yOp4mJ1=lQ?QKTptV@pgP6XG@Z&889T}xT$l$O%psk z0lmV6+*T9*K7e`p@`*!te4BO;kz|uB5?Uuv*5fjToCU5KhNCN!A5hFX4ZkGw6#=`4b6H?rr`^D6glLK>tDixv@`c`*{N~JT}FFN zyZhp|ylN*To}(!;yaat_C6HPgwznwmts>SUqW#r5d|y2MwbzATOMs9X{tkp&51zE2 zZRoQPxm>odr#-yoG0NW3?-o*64pM#ChL(`Kg*ofDyP~`yB|Gbu+7Lsj&?>o8Zmd-cGi(RqsIzy3QAV%-)!86xplj1ip3KVB_zTz&AdG# zEwVhqZB2T=S;HHWLGr^bXG!$F+t=Gms{h3}--0$(Z#DS&bcah9*t`X`+xYCywDaLT zFKte?hIqR}xA<)g`8*x`oO(Dw%od0~mEMUV@W!-VAtdHOva}ZG<g zFfOFn=n5zv8eEsQZtyg*plfU~sCrvX@oC7X+jlDNj9;-?tLw54bJ(%m=V_W810*2UiYPw>7ygYNBnN0AYCxPOzCyv^6<;SKKTe}U{1pvN}ej_bn{ z=g-uv=!(YgbKfTuGXAki8ywHo!u7!;-dL~3<0b44x!mmMT+a4bC7_&I?Dbgc*P|3J zCD{5)*7xx%N^9dhz??*N=+ZOt@81T`x`mx>x^uTvR6{;9cHi5M|Bw4_gwbbqsmxJ2 z87>ptGs<#H92e#Ne(75@ssA2CH^1nbzOG??AFlfsu2Ak?@#!DsezY%WOahLHsNphI zH_AgX?&}<_TT#kQFfvaxa7Ekwdq4I^mSqmPJJX-aTEn?i+je`vs3kvSrk>J2@m^#ZiK=qhQ8x}(>Eicj`Hr%C-jq&IWahXT>5 z3m~t~A6!ATXi*d&+xnUnC=S8jye34#Z4trS7JgQIYpf2lVfOB^>v9cI%SZFY1C@P% z3B#YdphZ^t|lsdm(b&VaRs1!pa(c}_Z9L9%FH_|7#bCfa*2_+vRf-DWGb-Z;5|FKG zzbENuQGIhA1g^iSerR=GM*h_|qYt zXc`B-vK49-DSFY0AQ3-|R#!#Rcp59PY+0NJFf|><(NBUw;^(RF%_Vorw?h{`e%*6YM z5GlF^DkjmgjOwb!ey(Um8M{yC6P^qR-}szm1XzKbF&j06*eFJ}twfBj1znG70E?+y zBd9k}&!_!cr7j|F?3Mk8M-1-DOpt3A_c3{7)b}k3#h&?Mt6}x*i0{QglC&9T%1jM- z{qE4dq9H)5-%^KhA6<1kQM5e!1sM|dmtv0h>E_?tf>H4tZ1(l5vrLH-n;Ue)|K`Zr zKMgsR#|O&}Ld*S3KEKWul(kZSqx5g_YhTB<>qpNc?a#~v1s4WC}KK?#&$pU$|zI7}7 zw)H}KUOnZh6mHuJq%LIK>&4@6o9St)=n2=wY0)^^QAC|lAvW1&JKFV%3Q+&0--7tIrCoa!s1JJ`+MeJh6TW`bn8^MS8jK0SUpap6uM?;*cwNx z@-=xkjm@)^^SQE`sg!~6EL zpV@d3)cW&iDR@F~cJ|7QKYm0i$Wax!t|xa4E#9{y36}ElJYeyAX}6myw0(~603R?J;Pn2jf@_yT)hEtfT5m&}8|fd5<{uP6q(JnKyC%h0@6JH1a33KANGYraIQ?5*ioB_%Gj>ftLc0(#QoJ z7JFz?dyzWpaieQ>p*JHDdG<%8h8busNg^KkchpY#1{tn`?)Z%j#VG-h|V{ve3*xLrG*0WUS`D{&tlDO{e@H=fvu?tg8H9LZ&b z(C0JUvO4QI`de{;2-gSP5W0Ir-o5C!krEADsAkxLvs#H9)s$)o1fKOQu8(t|aZv0yFi1E!{Pq&o+U2gL`WqaZ^|EAE~ zvt;=*+ZS!y-6% ztClIw<8H1WI?#);ztQ4Y{TrXZ(qO?Cv0MCW+n9`Dvu5bgr*(_#k6Jm00;4apetR5@ zH4i&*s@;)NsC-s2>tXZhTqM?`A*XrQ=|zD_D}S6bQEQmYJE#uB=$kM}8PnLCoIe1Y z{kUEIK)jFbL|uBbkh6A0_sP}}?0Wri>NNhoSz7X8$Wu05*Br{NTTj6Et=EGTKwn1&{5|FZ z$ry%tNBfweF~N72Y>BPJuK>Kd?|v|Ow=7T=#`=A6`27^#D@$tQwv%B_2oRJ`={Fph zHJb!DA@`x9_HWUtVk7;cB>G(>pbygYkK6|l{NeQ$@a)gK#sfg0Dv39#Ha{Cziq(~Jfj z71GxqUfmtVynXa~OO_(|B!T%;{)vp&M7pRSYEN_*Ew$FnOHg&jef(Qg{Z4D?I+WgH z)%VNjcVrYQE1bS%qjzj$oi1-9kuGnGm#C zE6_yg2EH-^Vys_S`n#SnD!fW#(6O@1_ltMT?Yg`xE?xYGgN-{E`y(WM_Kd2+NH`;= z%R}d0TAljcM(#@VoetUAAqSd-WS!{TV-NdY=?1H>xuVi<)Vn8|;p-YfDN4U?GscJ3aBHAqjXgx| zuEryf@$Hg5{gAL&MnDTgk@0=0Q2n=YgHNqTIRiH|(kQqhcMGXobE{u~r!;xJ1pG(E zevz#-YuTQw_Vy$P2hq)V{VBrsn76DATTuGm@bbN1e+3q!TbBUWs>jB^SQzJKZkaO- zDJX6qGt_%2g(EfmIEvfMzU6ROo01IVIzF{P7YO3EjoubRf(`Vn>$GO)Z&CPo9=kP# zEpJob8`M-%Uq8A3EtZth%N`t@i2JKT>+#x%ZBDbZ!t!-Zq_M$l=z)h*di%Z!6e&+a z5&WGpfn6kXR03FN+>O1 zq#)uSxG#|W95^@*YXoUw!yyO~2KV&K*_Hcl&YWShF<>-M?~CDZ|A0;~`@#xy^8jQF zn@-Bm2aV_K6^ii`2FV#!5;+LKYobUJ{a(w`1E^*0^S^!-6)*7mONRSY-5BI7e$B>V zXb6AUf-)7&8SfbRhEYCk#|$V4uKm7pUXZUw=%zUoG$-tAg%uw|9?%Si{pm+5Af5LpPrU8V$_>RK%? zA)>pRa2(f_1W-NjB0>5vHT-83gr!3Y#}$xu86cj0bxvPf71ASu(HWH4n%WMUb#WiX z%A!y|mKY$S{+`%k65hYSi>&cm49i>#^S2nFzgb32h8iitS%wZoz1GmuJCVA*X)ZwM z_hNv>zWw0GD}zB9`06KK1zN#jzn8|YbY21PuaJB9V@@}Wia*h$JpJi^r(F4bgn%7+ zCz@c7qb=E#y1PYgAPu*QCyL_J|LJpxSFy$3;c~K`d7JY6OTMzr>u@hKiQB_0Q)qsBs_-&YHK2CBDR8s-PD--iE!ET8 zQCQ*XbY6Nf#E~L(R=pK$(dFcR_)dbLWqFhNPP$pH9c$}MkEo=$0&oT9qc&)<)Hpm~ z>NhB}qw2nv+<0`whOnk?uO(;I`otkxC(k-MgT=}o{rD_Z^O>$Fjhe>9v-V;7CbZ!? zoHc;A7-JJi_LgZ~!)U*MZgU{8>K1m}NWgSCGuZ_=oP$KSHUyS2P@eo26H~MZc2_T2nas+?U>X z!tP2`UeAUM&kDN5Z$x2R9SM^UEozdOx{LOIL;8j;ci4{XM2kT}M(A4UtT_d@e#v5s z+&iAwD15Z`5kF^8e;mep5PJpt>4Covnjj|?my{>Au}1zba>M>s6jF947GBaSTJ5^~ z_RN1#J&j2 zIycoqo~gWO*@xO&__#4OWAK<+D&HQwXB8B|&?c!cZfDB0Xh%M3?|3lxUjclqP1nqw z)M=oE^UGDy4^+j1WfbQ$K%Z!T3iy9Si8k;`?S)woFvI@@CmOIj9+q+Sr;)jJ;RF=p zOG?qLv4-Me(xB>dLVf*uDj5D%^SD-Mt1+NYcAG3X&)G1!Tfo@+`hE)NoA=xm*AsP1 z_pL?0CzCA}Zd;091`tl0IMVp)Z#hEsLp7B#cgR@Co-Q9NWnmH0R~}Ik#=oDcF4J?9 zNWX~%_2Uz-S}NzPVt@G82;9M7lnUP z#BQZn?k19};WMd~0E=JlP5Zh&T=xf(KD~N7!EAkhh#X3se_+De>Z9ZFmyY$F7sVin zz_r=;k1nUli*x;dw401#daRYUu8eLou|aiH7Mc|!TlySqQrz{2-6FXOKb%$6d7Q*_U?t@slkbodz=FlnaWdbzm~h^fz4 zrQFOVeP_m0V?~Q@zmrRUTg#_x{HV5Mexn~z#~aL9_vJ5G@!m)14#-mDcH-$AQ_HmK&i=X>!jETw`Imo-yOaq=)X*1cNBe2e6kJox0Hl~ z_2M$z$*uMpi+;-AsF^RZfYs1wFJ%`0G5^;z*{R4^Zewz`6 z;^jm*4JBUMb{T_9X+EgV-&o65Avi&(D~+)(UP$cew$Ez%SipvE-oWNeUhg|(bT=h| zrxAPrM8!yP=->wd@3IHr%#g;bwv$ys@H5ty&8;!ekcid${E_vC$Z$hnto9?BEB zlBbXDkC%h~xr_x)zdMNlpGNssHG2`c%X>&B`Sf-bxO~8)RK|zXOAL%hPQj^S3XG>Z zfv;^@E|uMP)_;ELG3BKZR?g*y?|j#h{3 z_tiRKexpATv14wMNB8X2D!h_>_XHn#ZGx_4pZ8faUEejWcywADe03FXh1mdLvmc+R zZTZjDYOS+z&SB!3*L|<(mKR+#92kZ$*Q$~flV zN${iISzX4qcWCX+w067V*Faj&Yq6g)CqLoE)kJRUW4gA}y2X!^F$!me02xKR=2bmP zb$E+=3v_WmyFk`sOtyu@3{+TcfFllnVSO?>>%MbmG{ti=RP=nCnK<~npB#lvM?BY@ zUZhv*5s4^j9OKRz<7=1@^+REd>7XV>oo$P)r0h!1_LhlSThqsLtEy zXYCoS1VBCEPfm6Tj3;Y=cNtgXfbL)?I7>$(;RSyGI=lBr)r|u-c>wZ6$PE0%N)iH? z4niY(`|2#i8_z|0*n0uA9k3SJYi4&Fai}E=`MGOwq(UV{7NcT*_3fp0)P|GNX7`|> z)Zao%1dVFemGcYp8*>tI+w9RC6l=79xhwQMoi%)HBDuS>k~H5AD9a%0dT)tl^u4)E zBG*r|`Ih(#SvSRJDLZ}{W^Hm|^#fo3bjsA}daiR0WDcm3@=Cv#V;T{aDL(z^HU2q) zQvGSQf6$C5YAbReWoKW;*3~UtHCEad3{F!wFzfY*T9%$?)X$d7UdZz6qMqY63%3l3 zW6PWwHkmJ6RKWEhYPcvp3!SeEwMwIWF@yz#=Z)zCsLY`!O?T_mCE=-Y%PgZ*g4X<> zmiC%g=P4+etoAfbGs%~aIqa(5LsS1rc`Cv$Wc@`#qw6budj4bDc>Jti$VYN&xVh(8 zHsQr`^afMbvBP4$C>)JvLotrfUl&!L`k~__`<#ZJKkVU4@Qn9W*zf1gXm8QKIa7j- zU!*3pOz=)zgg>6(aVV~TH$@l!fv!h0h+YiGl5~*Ss=cCUd`2WqPw1Mtj4vB9%Ov|u z_ZEA`tQr#5E#8c0u}W8W{KgTc(IJ!CTgqWPN}MuVd@||=Pq-f3)`-vG=Imq-ClUQ@q-8}*M!ielBfa?_;j%jI z`$BE{588lOtDxp;J3ydurD;Q%V`bn>l|{O^JMxpw__VElg3I|96K;CnB_tCcr8Pj> z>}vJh$%u8luF?nF^AG*^G(N^@lXH|(#$0M)US@A=c3zEt;{#pH5GDHn)|9DF%|_Y@@9PAd3tSn zx)yEwu>p^kcfwWn;Ifl>M?N%X9eP4x2a{9fSw=rd9xhEUxw6}haTVh~ zJMsV`f!YvLe3h`tiEq+JypA%*vccTkh=9|fr+~?Mm@V_qtVFM4s~vP3z6whN(QmO4 z*#;Q37ZK{P5{4$Hjb+H=koaQxDzL1AL7l(u?%L{2lE)m_hH`#0T&1w6E6McgsjB$+ zIA?Ke_osd@Wz?U+%`&85D>d>sFF>nZf9U$q;4~y~GLN7gV_(nMJ|ARVGFo^%1@eQcbp5dnuEGA~gKouQ&Dov4* zoQVQB#O)PdekSP+|KRRjcLv6RFlgLfu>i^oeKQmE-tM96yD8STmz6Fs4?pW}=29vi zg#22-yu03k5!2J1BcO8WE&Gl2*JCeK$ zVy^@AO}O+WIN%p#7>h%W!TYpJ&o== zWvek=w+ugJ&U_CiLl<$4tApgy&R|@b)V`%e8~RtkaFAt}5j;~8%Vv@q&?)HTjsledpa;yDXAvLK*v=sYUeiBE zdKx4p%jZ5AYk1(a{J#6#P0$n7R@QpXSttxrU-AjcDH_Pq-~AStNX-sew=_KJSmzp3 z(;4wl<`fL=98s~lYJFK=x_g;bS-J=GP|jCJWI9TIYT{(5yz8pJ@TUCf6Q~C7zcA$Q z?oPMRysB>UR!T@tkE|CH-Wxs39J@r5g5rx{lD%{~KVB{FP5rO<&3~T-68Qs%8Bs9u zxV-+$)q%@5xS{*qnW>b(tcFyoN1ROHK+@Y>F)4d}zhnggnXqQ7{7Sx=nXZ{$z)_b- z7b2?<96`MEsJ9u41g5JGDlDhgRr{4;Cr>i{`g9r&n8{|r-<9(}t;A3(qf+U|N_^w(3(%V?J`{!Pl7{DB#~c@}GEHGE zx=>X>0j;rosjFv}G2z{a!%!E%k&r&Vy9J*TLaKRY-B?;&Fze$OVSJZibct=NB}!1n zn6lN@WJ9|{rQO<1e~M*n+2-P9QV|Qt*y;k2YZ-{the}kJpj(>^{JR#)T1uZx`GCrz zy?duV&)hgR9MWtI`x$uKDinQ|@P7ZM*fm`L%OYHT^>hLV0@60)?w^k*%Au>C)JcUCLRa z#Xk-u`8=_WPBM~ekPH1&iGY_m8Cj*;~Bv!nbgCW?HwmoVWN&I#4YEY+pYNR?LQ>^2F=idmO z?tuS9_N#-FFsN>fHVo5zYLofkC#Bi!prJV+(A?^x9vvAfg8Y#UFZiD+kMlBUx9z3T z_!3RF-1MSFl!HNb((4oZn7#X)u(rijQ@H&LLzTjd{=N!`2Q+K}E?1uRL9pRHxK7k@ z43a+|)22Gq1IVM*&ob)$I8r);ML3*YbDH{%*tLGS)SO3G)t_dMpV=XdFBg`Lj+C;? zcd}59@SEm&!g+J+L4XCWv7_LA6VuGPeD&^p8^BCU7I-%&WVT~yr+T|3_wuG0+LKYJ&h zc9v0D&Y3sn{im~JxQ%IyZ@W!tNO-B@F{!b`+WtWqvfWp?tgPiZ^vsz7@YZ%rnRMCO5240=`qvUpV+ zN%|_*Vq86Mt~`0i%XdtIfW-77zha`6yeJMVX_ z)4=4OP*mk_#7r(?tWoHrG2-yzV(h<%Z|Xeq+OUqTzrMI^G|^XHB)b^h@R`(U+V#qa zl#TqcV)%uX&~7;w)b6-(I-fhPt&%zWo6UBE|-NZ2~fegQn zN~$2&f(Ib&*Jru)#0vD389L6=Tm@8PNq4ZfBMnt_QRrpKzhCK-9%q(Nvugt{m-G+i zK19?Lsw^8HKV96*t1#nBur!lMr_kT~>@?}n|Hd1>$?u(cQ+qA!uM}#8sQl-v&UpbY z>y+v!rn*7S)Q1}kf>jtik9yNCq#XL|#_Q<{!qc~}=@2t`-Gc4J#(~=GF6?jFem54&#R%x-5OXy)7-Ot?wwY%<*~m*A|(Ug zT>2fv-~W?=f;kv!Bp7z4WJ?#0CDIyqpEn?|v{Oe-OH|N+j;1p8>hl6g6uVv@ zjC|O~{^LSDQx4U!^=(o3_cFGp=()p>c&|r@gs!TOW6o~YiadT|UyV!lHRB(h<P6E~y_KeCs9WGhe&i$gdt=pf9_h$I#by1C(V2w(++fvl|H0M-I_mP+&&|`v+(6mQu|9JE+k+*D7`4j7jjO-+Ay(*H$pctR04HIM~icmV~V^x|VyeVy-7 z+#&VoI(4xmx6x;mEctTEYDvAmMBF9@(>=+o{{MFue5D{j2PGiL;SbR!Fk16%^{adO z|MP3>lWtYugyikoxxlG_S=$EY8r@2jrJ~JzH>0SNrn1ZZ8*%rc&l}?OIkgVwOk--g z1D;5k&njHnY-{~hjViugV34w{LksjGX-t%iNAqxqf2d(y+~QW zWWfz!s-hS%hL$RH%DPdRz+dQEoHmJwZ>2&UJH@iM!}5W9HR1M)r!8KnlW>Ffvko5l zn+6`hY2ju&CUg^VoVf|WHEb$ee5X}7PTgF@wi+#6#T>?j_u+aA$u5IRKbyfE`zS<8 zh71>ymM|X-`>HC}&!VZt7#uV-hYU3Wlxz(4sswEhXHuMwe37q9=d2Pq85;6|;VLk2 zxx|-Q4#Ob_(???%o38P>NKS^}Pi}RT__fjr2Oz>~!IE5#&bEtbp(f5^Z}#&cam9vb z8++~jN3XBY!%~=Y-8%rHL0l0H0(Wt`T1<3by5GcxvhTq`MEt0uK*%BS3L0rd(?QPk z5=}_eVL+GYz$`yxxcDn*2q1Njh(Tl8uU9xpg@g-X7tHmuA0K!bHUwt$km^wqHXEc= z|7HV-$wT;U5#{{7j6mn}=zC(Hx=7gVjUHr}T9|x5W9AVUyE;|a#4{y};S+126Sn=-}0kM)nTtboI-Ff*y zNB3_V)p&hIBu0ZAo+ydHw-~YF%%fmI`To_Q^0^TRGgV@>_||c;Df*1g^lWx3E%3OR zcKIT0dhzNfi@#axDTTw@vak53E~;}AOmNv{YCd{up}B$U-*f)&QE&1Cig~sKWEyH1 z8k*mF8Uw0wCn(GGvbD>m0_)1oW=qnRrpx_d=M4YN(OS$q6}{5m6O^qB$i-1Vp|GWy z6@q=d0)*j=HfXwPs=zTzl)3z z9o!1`D>5r$Ws6|kHBZt9vnKqu9wwm<$17(08a#JiGr-eBO=o1AfX2t+a zkn&4~iEY_s_Zxeypfk=P5L&-7GQ2YkqE-%r{r=I zH=;OOA}`inoY728JWU&}-StzUqEpPLOrwKEL$~rd6|}!!d-#K^X^cS8rm}K=jKJhU zZcmrdHhM@^-0>!zq2bg5x_k<^l{?Gal*3ynze@SX$MyWw68qs{Oi*dV5urpVTiJRQ z#afTbfBS2u2g(ZG8+O635a%EcUz*fVv_} z!mrnlmdoxgapa9zm2)yo$yVz{SxVU-A4AL1k=4q|p%y~-v2wL+eW$BJ;pEn&=uSKa zq#F#=KKaZNeutIF{5#cS?zdJhfy1lXl?npei^@P`SAkHcbN7s z;Fxk~FLy;;u=w#C=8MozKa_0Q*;oe^U!LBISP=|<^7!8Em6YcX;eHQ3ujYRMRi)_q zp3aiBTtv7i@D5%qKDj@9vdS_IQY_e?n11qPB2-oAUB?XTf40ZOYuNwOP0-v-Iu0sKF}GS|+AbH< zQa`re{?qGqPN7?VX``8FRto(fXrOQVz2CL+Zq(ZZ&+^?ri}Tk_8f1=A4uw4~5CdaY zKMP~zNh7WNMW~UShy`IEmUjAg%M^4bTPcSRHNxWpUGTk^^SH6g*3;AQPaq-rL5|U* zf`xoS8vt+A2Dq`51zZ^|0IpSJ72bSNn`@!OGGJ-hE zyZe}V&6dt@zjN!)m9i+({gVY*9pe*97H^ZlmjhIu} z#BV2YS3JpiMzd-r?^4w!%GKyp(9N)|{P06{`O!=>H_UNxU%qUq8S!eIQBbI~b^L?i zyG{vR{yBevbTe&&VN0XOYs%^0-=%h2Wi7||L*4AhIJ2EL#Ky&y_!rmz5LT;ibKzsQc*#tc*A=xbPGsXL8U(cIMW{p zjO7SOME)FlHfs#n#_PhzT#kbBma;*)%wy-7*+In?de_=+nu3$t33_cPg-lLsucP7b zhT2@*#hyQbsvOLe=1e_p*!iUxsw!;U@pV~as3dQ4|82axWcb}c^W*Jrz3deYD-R1n ziVuzpHOBvgij$F%wH`DsFpaIqIVqTIw|wfnEj2;H|K=`%{dp`+zg2$K$2waXap&35 zbH;^;$sb7G|M&eG=5;cJ79>8h*_iTii+Pmw>#ebnLWwZSx8$_IY%<`_0kN*%3_@I+ z2feS8>c|llyC}0ov1@3$RTZpb!|e^+l{xu27;I|GcPKYJ4TTX&rjI&I>wM)az7zAV zU0#LJB09umPd?IND6YExaOkN1aOr62QFjO>d^(gB;vH{~J&@fw5rK^6XG5yn6tKmv z_;pR(`Ggw>h%WB21Bwx8e|4I@bIps--6NKd$zbqe30+9VKUv~Lxt#JO)8d43&8x{> zfpmj^L)MA9)teW$_!H&_gB*O$5=j{VTkaCO^~*!H;`Ru=^97gpb;FF41>JHDd(x`7 zAA^RL4Mnciv3U_a_iOdu{;$G`#e$kr5>&a`9sWrCenejPtbMv)>yBITOHwrc=BQ1T z>j@RywjV`14g4iZntCz+!iHqp>A2@%@b6h?Rcsqci2a@aonT_b{+vc^2~6)G(=b)g z^<>D@`$w30oo;2XtMOK;ZyByWaB8!RbXVFPao0SkZFBKBF)SKMPlgnB6^WdTc62r3 z>ts; zGHF7({|>S0yPYlngX>oWt=kVpIlt3yP3k62pR1eDKKEcBW<~NrS7=xFt>e`$=Bwb? z7Z$CPE+WI;EQK#8I`c*vUFKisb#gup|GWM#f%)G`C9PU`P-)UfsG|7)dC+To#P_^u zI9HnL3V3A8<0x*QUn4d{Y@jMRN_j+?0^z!qY|rZsjc1$=-C+hr6#;KgUHwcc%(V2b z&WsMz+1I_5IXPaGCON8n%P$iyGx_MykXE)_m@jTSq*Dn#qglcq!sdO>9=gb1H2AFea=(Jx3OGGqFgpKm%O!D}kc9&7^l$7@ zL%huI+rsU=;5;0x#s@Ptr>vvn2h~o0zXg?L0N$&odNch~+ekR6uYF7QrelSYCZd2i{Bz?OP9Z_n-i+x_xdpB~Q%!+qnKzQG!Qv zAsRmrKUpHdZjeh^;ySm>dGB?`N_lK9kDFZ1*Fy`#+z(~KS|jf@Hd?Ay{X&bgBEc~@q97osX(`t6P&lx{H2lzbyL-bp_4tHA~EIHmL#8!P4Y6Ow!3^0ykg+PGmlDUen|*qkIj;8BvvrQ*jrnx^sOAq zSkM`llmLqCb_s1S)d|nzf_apx#|wnW7xNR(M?eJ)Ban*oUiet+3}Pyb=oL17*MIFC zu-5Z)&`7ZmOxu7s6LKKIys6_l&PVR*r=&bcYX*O=*TB&59#RF0z-O2%2y8(N8o$Kj z+swoI?WD%KQxB#f`$pO+fofKEOL-2Ohn`%h`3BWV|7`_gDNDa$!PN*_w6_$kvg+L9 zA>rzo8D;@jd7hNU-xMIvU06tnH^GEcQM_+_dzCOF>D{p+Auybz818OM;sAEF|b- z(q4T;<`$x|d<8M2@n7_#dt_W)v;l8FNpB|CV>w3p9v|P2L9+h8h~=I4nNinpP|J8O zgYUgjS!MlQ3-5zX-YKj3N&)%~)yiU)5548A?~`{fTcqB<)@@bdq*bW;C&+4i1NKa` z$gk`#p89Y#S*B6dml+uL>hr_t-?wflQ>iPzF%T>gXaSKW@f*K^2q)(e_|l62i4k8d zp|oWS-UTW`OSxA~fYL%r=CR1G-ugo~H^;q!Bd41&Sp9htW&P@R--aDGH~YO@x`wNm zVsXeR2}L&C0NP67y$8U%eV4-p&OJ(s=nt_z4*cA0jw?0SHWZ>Sic7fM>;DKVu|fzP z9>fpW$rVv0uztY#NHu@Y_Fiz&8~V9tSUhDq&o4{665EKSiGyA7jht2;gc&EDLP1TPtI-TxOgA3t z@y}nQl%A|Har?0SOYOJ-7k{ZUnT5HeI-QC&ih!-Dz#pu=zy)?YYLp@^8v>#4C#a|= zJvZM%&Eapll9zMXXR3_|Edvt&O@HxzuJF;Nl~UglENGGP!GdrkQhE)Gnm9{-AVbyJ z=Lxg#=P6cE=Cq*WF&@uO*eg*%4TuYm2c8*TmcQAXa-6ejGd*7{{Tjo_T*LR5;;<j5J%bWfmvR{Bi8J(&Ag z_Fe`^NQ$d|D9|){{IN~^^Hu(Cpf<4#KVLnp?`oL*kDMcqgrin;#}=CE8Pzj1pDTpgpm1v&nO{$^sW8xQUNO5FcJbS za0v-=JWNG!(fwb~gB9sSe6zoG+q%1gsfn%rZDGK*(v?QpoNIGc3Ts>h8TphGTKGJr zUA3bcuFcGQpuZ3iIAx2~ZaphIV34wd^$fv9IT#ZROMMo?oeeyZ&qADE=97%6*|A$G zOU4f*>R-LuFg(oj5b$)D!L?{T$BlH;?ZN0Q3J9Cc<}$d;?{qtW|EIe53~RFa7Jeaw z5PDO32}GLGO9-9NQL2E5bm`K&^xmWe0Ribnib{uwARTGaYiQDf6afJN>AdlI{(J9h zzaP#yU(fx8a9zpF+_PrZtXb=~W^Ojyk6lA0wRY}O52d{cI4UMX@i zH7R{PnDGIlrWqA$Em+@~&99z?|9;v!&6Ynac zyARZDU7hTE9+*bE=rnS6FIi<^zC3d&(}M{=QWFM@{~h#T_^9p+ET;h7vvW(<5%yTL ziXbwY*5_;~-E!nCwSPFdy!CUe9F zs73ty#;4B-X!PFjrIrZWOd7OF2dL;j@$k6&i8E5O?|zAO%Un|07uy%pdoRR`$J;h- zr|&<-*zzA`SGq;wh3kIqRPe3R{KYtp1qC*y89&tdIiF-q1#;LJUY;*-y0g1W9>m7F z`m)N_a`OcGVM8fcFQotk`>cfg-78^iOSr{lhFgEU&CR1uNqneVFsv7~%apz`B3q3j znkSzsIbRU7JPh{EbBmDFSS)=)jruT{B+2%hM3EO z;(&x$dYZ1mfI~VHo0{f;SM`JftnR@RuSh7Ft!!Y)y|5Am{qpju60dh~BMCC;?>GZ4 zAKYO-UzCnq|8O|^csW%xDLL)Zj6L`u)WXf=-Rg;pZL#|134`FN?X;6DBINy;{H*V2Ur6}1yfyVJo;R!2WL9y}`G$zc0ya*KJK{!oy`u)pLX*=C zeHI&HJ0(-^HJ22$W_0w`2E6m%Y!0U-q#eh;EcqN&Y~&Qh!UmPta1@z@trgs4O zbZWRKi=Q>W_?jS{K3ZOtwphv_=-=3D9+bg=GI>8g{8H($nlO$CgS6y8rMN&dN-6i! zHMT}Ao$(~0-=H$qUJ1y_rMa!UG+96SPz(0ouXiB8#sI_4DYtqC-O!JjdS*Ox>ie13 zLic}6>t(F!i3QH6c<0qT+pC`c2s3oiOJ#d7?a@!v$c)n8i{{h0WbFr;>jX8k)pgXwIz%8`gWTiT_a_wDBONv z<9oVluux;gl%%UJ;j@=dpFMe1_W`rBmUi!2g6LR%nz3S|BW_3BI~NO;A@)?O-L?ch zK0hPNtLAJi{b19c^+k!j^7YTpPf@k+{K!>K`hI97xlRTQfK{maD%;>n+$*zj3_r z{j^uxPUEwJ=)Dff@3Nh&+0*O0d674JPTJ zDWB&rtGfL6QKxIw&Y3JbFw_Z;Ro-AdQ|L#cN5w-s; zNorP76+!dfbkpI*lvDYO>Fv^;#swZe5iL=zA^RNMtCh&(n8CN!P*j z%lu3G7I?w=HST1eagJGix@~+H&uFfL+ps>H*M(EDDhk%65=~i&3Hk>8Ltd`dX~Pwj z#KJojK0gr*f|ha7+QyZz>#}m)+1eLU7Vn9g%?o?6q782OX7ctepMXK4t*L%xLF;=l zr+4>H-d-t*zJK!I%6^WpZnX^%@cQMQHCiR8Y)eY#&oUzyT*nt6V(p(F9#`_e_1QBs zg;~Z+9vhW}S&4tx97}h}PoR-0{ot1oH|Pp`n!e*>_#%B~@cs9P+kT`5LiMEq>!2VA zJUc! z$y;vv#rU_Bg!dU1`#l^TJVY8cG1`&H1voth62+-#txCR* z<<+m6L=;y&tB}p$^D3VxX!rQt^5BDts?8IJcfu9tSi`B#%knXYY0fis+9wu_zr8r# z*)%(&wfTX=JjSuhka39o<>m-Y0yqtLOnYm>M8heEyd2VyT{qTDh z#H@Y!exUK?EUB&U?UgJ2>DF-U`{~h(kSkpirmrX2^-;fvliaVKcnQ2;l3x_Zez=xd z$)O|-Atl%Z@7xvkgx2XA*+9G8-DygSOOLX>pUmD^O*yYTZQK%;*OWRoSbDN#@WF_! zv;1*;)X|Qpu3506!TTqw>Fh-*xyIWiP$6rZA8_;^!&kl9vy~!8aD4Wj2UWE9>>gD6 zc)ruWKc>uce(}gdGhMx6^?Q!`aQP3G(VW10)RggEjoi^`u_pI;M5Mx@4C-506hF0pXlIJ5GloQncY6Sy^CeSQpIbUGioL*C>jdx3HyX4D2W9jPBFK+7&x+#tFXlo@XZXN#F*{ltHQe13 zk3lxY#77@VhhU}c__9en0yhZhqqn(^GavnqzG$96Gk&5oe)l_1lne~qx@Xgff9Fe_ z79qpvkn5CbF%uluC;y%kJLaq$zVr09P5^KjDR{FypVffQ_e^9I_c13q^?vt#D!EOa zKJ<8tA%E%B!hpqfqWR?LQJ=G=j{<9|a?9Jw+JJhjM+5bUX{vidw=|J-hl*9zy=TwW zYTgTf#vE=p8}ImNWRhlzlrhWs)oX&WQ721QC+kHW(0W?nOfOS$`-$OCybeqjUxkQ= z6D;NU3C;Gd|6)6GWWLnq+Z(ybIe95`rUKb@OWYl2H_s9xWlP)R{x?9S90UT`EbnWZ!V`Xx^k`wTR)CdIe)cb@J_|xgc)u4n|641 zf2pO>c9=mx^q5>J`otn?q-(sDz9okaGGqiPAJ) z3i#E!Gg+3L>N_kpYkzUP&2MkaT3a_h9xmaZWhK_988ob(#UCukmHe)cr|MbbV7h}d zRdv9kmb8@Bgy}sr55p%lb4fUy*AhnNFxHQD_Hy!=-&0YtXmZqy{zR<) z)Am=_FWNZY9A%@up2%+Vk=97!^zQT~GUl59-diYCB*0FlZ|BXO5d=T0TJ}Azdv+YG zzF~Sd`n-jM0?%>n2sU%N^fU(i^JmzdgP3H7q0yYdZ)mu#DgCxV8VhCN$A%&B_MnD#BJ$%Eoq!c4AJZTPE4sgOv1)N}=`Mo$5=UpCbXkJd`3jPyU9N3c8k^- zzS=NP`Hn~ubdflt7l}_!9I4L80wj^#U&?i)mnkMpN@qg1NJ(l&kiLWPu%an3Io-Jwvb4+tQiw>!^C`iQ_kvlZ8;jgxNq7bkN zJ_$Yp+VlrMS!^&*-haHhAk`NmDmslOOxA$VlibIqR_uN>*A(>-zJHg+?wxXZ`EJgg zn5iLa8;nh}c3+vQ&4IrY+d%&f$9OFE@J8;B^)hX>^r^3@5@+@n?EB1p?SJtFOnn~_sDu@WIU*TnvC>%Fcec-zZE|s@chSaOT+q6o!gufWBYt-ex)rz* z_)%XHbFQ@Y)Ca>+(uW{+Klozn)u; zwg&uZZV{0rtPRD$FS!MR}dgljA#H%1;h3qMslTEgzdF=d%T2UQT7&A*$!R& zQ&Fdd&U-EkUaFk4IDN9?f-zc*BH6fwjluZ4x-sdBtSe-xH_t^F4ll-JkJ;*>K@k_@ zr)kP^^0YfTQwgB!_=X17>&P2bId*bXN4q5f@k;P*5xJ$^{Mlh2t8cyLRQ9PVO&|KM zANtNU_&qh3mY4HDljW2B(*;-Gpl`jg_`F`FuHRp-Css*UY3E7LB^p$Xt2$rJ?b7>n zu{7{p56x5O+DD|yihe+PJ(inbCvJJJT2+iET#M^nkgQAQ17Mouch|8CR)3W1pt|Ge zjJ-<1yhG>|p=u#wycOrfSlv)8^IgHNm)|*=B(|&Kida(6st3;dq8B_zE_YS)0eZCk zC_%zHg{7s((p!8?4%eaLg8-Hu_-mXUOiCu^Teud77NOtaKR{+>udmEl<;a_vZKNhF z_`ZHKx^p4YLnxmBhRZBkA>p|9bG&)3%8gb_EmVX_qO75fHT-!u?q^EulrOD44)UbL zERU2n4ERh45V>w8SPQ}fFR!zYxdV?}dZjd-5H#d(;5>kY1TTw(VZpSzd4+}nIoy?yubLFt6G;V5_eO}Y=5ySdR@kA3z<#Xv5IB@??2y7 zN{2KBUHWW3eR=Hpdsw_B)MHXpT%xSqZj@yc4hC)!`ze@D6bRU?m7m=%dX7p2xtYP0 zj*19GIEPLt)ZFCZO`G{4-z6S14SUa;ZkBfN^%t@>3-LD@tCA0L$R#Bdg!7Hxo!fdv zt@=hHct<_Ry$T(DjE*>lD+jBq(j)I3NA6MtHZ5#O1MNG*@wws{&$)@0YBsUsPt0fc zsjmj4n6x&ck=Ta~&X&JA6tq{2I$hFW6sv!>wuTzWQ*Ooa+ zlPc9lKW#MP-+d%uqRMJZoQYpRZutn~MkaBr$nr}^dnYEbg8cjp|CnP3bBX6wsyzl` zntX$1WYmL6LXuJMTm_4jvZQ==-GplitUyxIzlTJ$W?pkV%F({3_MEtC9M)etsynNm z%K5ngXb9%_DXX66B`K&vPGhxM(-o2wurrh;a)L?KmSnEQ^)JehxhT0lceP4JLC_Q8 z^j0t{1giq;Pels33mlurWP1um{hQKt$oO0&;F%dO~gHIK=?dJ%F# z)qX7D6`3PQGamQlM>0~G6~oXLTANIEMsz_UqMcxL#S?@Uc(5|NoRRNSTDy`;pj7s*<5bftVE4oO%Uq&km;I%~H+moNl&$ci+S$xL*## zE#VOmz(hdyTG;)E=Z_)xkvm-RRyBQiQ`ANjqyXBmOh^Zm;kb)`k*D=!#(1BfC?=MP z=^ldqa&Da=T==9{LYI7yT?Uu6Ox*}gsrvAIv2(6L&budN731!<6c|IN1}UTX@Hg^WCwM zsCzYRj52gw4P`0R$0%4AO#@rdP;bhB-AAf>+nboO>T%nTY=xw?F(-0PUGP%V1H9vu zvi#ur>7`y)M7|_Dxx^MCx18~$jFjFGBjbNu?#PH?@aqcRA~fmxZD_vY-m0_@FH8|*?soGdC>ZA~e*JMZSiu6}agy zeqE1suUh0#P~z@pDJ&qrC6t8=gJj9lExJw1Dk%#ztaUlt;Z{s|VeuVyt_uaUzMfd+ zwBZ8WL|nUv!s!6-CF(NFgXi!$kEk(jvY4-*3s`+3)7HL}KfsOm1fF26@|GjQ@~H?O zuN(-hzzJ|?@6~V(k8kHzmImC;w+%pBu&B+#y39`B({}=p3|IkkEn7+S zp*o{S0{^j`8qHD7-AM8U!3TH})=q^FtSQ>VnCbJ6A6ZY>PG!SvnZ|$B@Z63M(`-zb zZ+!wfUvT&f{^*Z4g>a=g{g0B|QA!NY={6zUZS6SW2BL?dCVI&zqQ!hAKq&gzt? z1=QTJvnDbH)4^S>51$Ez;i|?syX~V1x%#|!Uuo9h>aC`!2k#^Y+GdQUdSVn^)i^xB z3P7F!>2RxFcdDmn%ABwm`KR)ts;~HgPjdujw`tKysP5ItJTx7ZIz`!N0oVD!Pij)z z*%~|%98BX)sEIMo052|Xm~e^>CSCHm@qU5>*G^0l@OF(Q7ro0I8EQ3H%sRiOZ2NPI z013Lt8m=1i-BvyJyubx!Sg5epHp(rYFBJ1%2$f{jDz=E^jb#bh19WwCgnILkDOLy!B}R!+dxALRH#c?s~~%yPb&rhOFT;UAg1Jd=Z}t zYQF;k3X zc)`qoaW$OwE0HLHWiG0?v0`oVz4mJw=cQUrz6F%@xvbP=JDdxQhet{frDqemn&V`O zuLazE^o$9Rlzht7v`XP_3nv6IZ@RxIu{u~!b0-6W!s$l9eewlR^m>n*K3oI9l5{sF z+U2x2c25~=x^Qi~-v>SkhivF^){SQrgcg_NuU)zOPepmtz$JEKs6TCPpaFfI%&@PbF1OR{BC3Krot?uBKl-in2-_DIq+{;s=U#!4^b5BbLZJ>G#Xz;uTndrG3%7P8|8yum41xNciVP*^Kgc})nu+6dlC0zW&l(~ z7M&5Slsi62V^|~(3qN=&SRzL~v?bDjd!2U^E3@S|MVf8N6<$63knS)XLm8Rk3KLvL z7&Qph6Tp5{a7m7HZ3&mw}N zI^tEj0`sA=q=kj9ZbN=MYDf+lRH3sIDS@OssD{rfOei52@@tBDj)EWRlE8B`&e_1) zJlU30rddmH1G-!xsA05XZC75HgYr&h9RkL=UtmwTdCV{Jm`RVUS(g=iWWH6(Z3F#Y zq{h{~dfv1FF(;C_o*!xPX6RV~ft6V{ibUYSH#t3QvKD&^c(+i_(RaMqXJ&7h83#79gXb?8vnoHz0`x6S1!q zoI8h{teeFT=P2b(AdnV}i?`@Buwp~8{^ezZ!n2$^CAg~5lbbGpm`E!wQpDXprvRRn zNOzU3Ze}L3*DFdC9qNw_0h0B&a0VtyNsqqnJA&3>9ZsX&6?kOdX@rX&_$xeg`-0>L zFq90ja23${D5~yr?4JIy;VpKCdLzJCIaC62FFOcRKa&XL3A-FH9u4d2ir(q ziBT^xNlPvO&&gMs2A++H?~6jzO6sG)}m84u8ob5G+x>^xLbX_fD zQbw?{X-ZU}*GAG<{{vVt$qu=C7mNZj4`B362y`MEU&$25G%wS2N^m4u0^tQK;uWf>a+|S1+_}@65qfMh zA9te;v1ZaCP@}UVq@TI*5!YxLr$NV^M!vLg-L+@zvlO_%N96oOJ*ANvccs$enstk*Kb5R(>R}|fmGRndw#Nwc6J@Ew7Iio^VOB~$U3vgDwEg_?Da8hMVd2IN;VSox=%y=L9&b-rfka%aY1eRV=~&Z#&N>-@%SP`C&cOuqHE0miitGTUbY#uHas)w=UYob* z^<}Lg2`5at=T{Z#+nwF2jmafNbiVKd{@;Eq8S>eFN+spe>x8@$aRvW z+l(yc+FErRNKWlJUmbJbM@I(IY&p;86{wFvnSDcsELPyPgh}K9a|^_9+ui8m;vjp~ zeN;KEF!E80{6jDUnuKv)B*@Y6NSDhP{Y^kAw$3fDq`2BbG1vjmi%kI49gQYv$!bee z49Xma*jtfjyOe4d;_ZF2q7IOj5AeusQ)gvE7f3k}+7IOvP&*Jln~H0T*gI6mDANL1 zgHn%PH5YRTQjXOy!VvtOv_YS7N{G|R8VT!%~&ub(f-k#X<-`qEGtSyO4!! zhcn8ZLAnl=2DZKg8>bkpQ|`N_V;QpQtOa>jeJ&00mF_~U3TE@KVED(x?bicVr-cGK zh8NVQmP2~xn|4s}y`b|<{oL06ExYPdTrit$+byT^n6k20c40nBO=6Wvk3J2@mtK-FZ=9~rlpMsE<%2;-h>p8ZgiKy}PUGQY)o zuz>dnn%()3LX`lM$pSOB*eIlS7)uT;;mF0;sVYw1C`*Fd4E|u)=2a7({^qx1ul2@r zE5-?fX1=p2VTYhJWfOm*IzOoH8IF?Dk~K-baOOyZ@04pWLk``3FXaEDoIh zZXsi-r}U^SN*FFW(b1OHQ9#Ph*SQ~br7BQQ*oP51-_mZaCdj3%+un6J5<6@QeduDb#-=m^zdijFQ zVJz39Hv2a%*Zeq`bkm86(*9g5fGykHS`S4@@*B5g{mY3FBO|81EtDU1c=xvQ*W1d< zDl~33`sEKgqqG4bhYIppxb_^1tm`Tg>zl7lG#$}hE((|1?2m8Tat$8oWj!C}ny;2| zfibT4tvPpl{~QhW!)+(qWF9|T2Gw6yW3%NzZDFD}5}x78+pGE7Y1QST%I_X%1?zer z+T>kxNqPcU`pxik-iOqo&7;R^9bFozFmSd$j2vC7AY|;g< z{N-DM3+|Hzw~3^0c4z42TovuCeUoJV9Cws|AB%Z4Ds^MCCe!p?Z~Z$y<2ygrZ@~v% zQZ!7Ht2i*>^$&J#`oKhX6}mb zHz%Q<@nf90xa~^+Q!xr6NO={=kz(ga9_WV=&}uUk*vTDR$6E3CTEJX@N?>xzW%p}oF>^fNm2G!MKR_q zeYWKFQQNfIR?V5x#?to0^gN@xhKu2X=C?0p*1XG16Lx!&E3cY(+Y^d((wzU|Zo%LF z(}ps+)TKVwb^;mYfIch{I<25}Ptz+^HyHOvM@L25S9#zcn7MoiqgId}@CE#>(tLiA%qAx@1Xr zYzEtR+tO55#d^h@s{^_3H0}~lSYe}p!&4rz!(cbb?Y~VL5d)0F`ws9_*`KnstpDK8 z3WA_Ty388yCE}mF%)ap0HMx4bQJ;mIWu!)t|iKwFLz0ip?TB9a_WdXucmCW z2@lMg%8>mQaCYkd{L7cKglI}Y727v6#z-|C8fyZ-FSDa8gs2Avl<7@3?sd93l|yFk z0cVo#vU%T~=s($g(UR3D?QCbkg3SlUivP-64G4JM$~bC^zyWk%F9-kRyXY5^JN$0# z*Cwq2%dZ>}>gNG0Xci*SAXF|}tJeLsbE&eKEzZkXX7=aFtVl|N&IK4wT`L8{{w%MKr(M5~3(F8S6mLbqM)Aqb!HLuCG9UZ0}nMMs3L zI#R$uX`gkg=&&Ww9-i*5lU?>~%>!u5DT2(2(c-9Xou!3EK?|K0-7!>^0(1e$wmg{s zLyr755~{+2=AlyP4(T8Z+wJPB^1pihMxF?r_1a@DCB^jac+?UR+dU*cj3&IHCZ4T@ zunr2nMs{dR?)37%HnIEka93$88eWslD#cm%6@HTRYpQ=g4E2+v`7zgyro5f8^^VdK z&c)*pYrT-88X+plnn!H@xIR=EV-l3Z8)pV(DR~_# zEXndJ*Kjr+sfF%UpKFB%aliV#_&N0r-;i^*8%sMjIaLd1$bdJ;ka?3cPQMl3PW=!LbKE@ zQGm~*vX>t}$RV;TE5>niCHj0;`(yMeKURn@&_h$5k>lCFAZ{9rJ#nCWiO%J73R$X; zDM&GhhvhAmze3dt9xNZ!xW3nk4vKu$j7=q~;F=wtIW?sPL+T$;+-WNgyRH#CDK^FX zOD~=+hyJ&SRm0gkU&;5RNiE-5Db@hOPk(du*TK_%QM!H7SouNrmL5NxPlQDJA%rwC zl%9-5@t~D7oE|}u4YgC+5QV zF(JvR(~)#6w1NxaET}hlGHk5CC*SuWNIf#iB1XyA1QVUSz&gJMD@Sf81lKL$ zDLV8Ec6(vT8QRAas4-y1#?hge2CGtPz(Ef7A~ZF*=RG#jM>;o2?tm>m%J82K|Kb} z(O9Q`N*E0cY*QOplDQWlVt*G_FU4mULIw{;3ZMYa@}B2v&myD*x5HvMI6MRn#lHdvPvH z&7U1Y0K)YIr}mMm^gvEytBxtDl5qJxed(~o?STEA&)u6TgdX66eieQMvX2BP!=ml> zX@nV;%+U00^N{#F3m1Y@&k}Ai3Z)C@tj?)JkIzIE;MJ1=J!}otVtA`O#gkpqzIw}> z*{XmqP@_?<0wW0Cr;`@>-YUK{?~dT0ES$RTA>wPs34D4*R$C8EcYAKPl6`1!BD%$k_JAE4V0?rWgl3+HtL(<3&ERg?3s z0%0Y1b+{>3hdjfOsQ}4KWSr zxaF-Rkg(uEVp{ycr16YdJQk=h3$pQ*eP((j4wP*wamB^8c_)bj1ivXUS`hD&p3BQw*IA^YtbO8|^~9aJi)pf#z)H}+^8RO? z#qtoA0jlV?_&2}H!=*g;BMD7_>@3Nd8sT^9nSu9N!q7#UfV;C<_!gW1s?HGOkM`Bp zNA}@Hc;Wm3=3{c;Xe4+7cW{M_Wx~RdPOUFjHtgdOWI`a>{ua=@Omsk>hHoJ@Y#@B4NGzPCr&`HOaeCuyL{znV|!xdV|a__ ze%EJ_Pjiv~CbtS;F<|%yi&y!ojHl?nfAk>5!KDI(0oU%X^p54L<+jgj=H!2I_Ol?Y z^X8u)g@!)Hb|>z?&pWIw@YmkNJ`YL%1N{SmX$=3ti@{)SvHxUoQBdf-_(vLlQSpK) zidbx`|9L9@e_L>UE zPyqk8^W5HaB!= z_IBune19O;#nyHm{~uY|5dxbY@qboo^*@=2rG$zGo*jhhS&S7RcBU#)ccwpVvUs1J z^70_Vc&*9k1XpM`@UNJPRT3ZP%LYw-G!mKqX!M&F8+i)6q>cGk;v-bg8te;$uP-FH zzJGExH}&6s#{s|qUr*Fpse13t8(>?fm&_uwD26%xwY+@IF2$d3;m#lGvK2P6)Z%-q z97KgHEYFNc<6(`Pf?GPBuOlV)CGqow@q5OZ*%~SGm1yKyJ+(mH`Q|Rjns* zSle^zD`CarZI{Wi#{z0^v4<1HezTu`KP?ce#C~@A-~G$6qD@E#1O;DrJq4=D{WE{A-*612bu80(T z&3)UfA4vqP6y(vHfGy%qc{xBnE0Vf1oVk5CTU>6PuEz=Fk>YUl2r&7T{M4OlWs`@n z#z;L5w3+2cCu|*incTV!9Rg)7n(G-_T$33|yBx*+@Au36ouvIK0|+DuCgjDUNs~De zo}xB`cT|Ppn*>tCXjxo1^HZZ6KRoY)(R2BX@nTiS@gij{)qJ&k;R9@$D5trj1S{my zMlr3&b;nyUMzQ)Ok_&7V00ji+y~d$emXQ>?W5cSp*b_%BhQ&ftVfm8Y?`?-N-5>8) zS3C&qpBR}UUpW82#)I2;pevLi7@`hjx=A-QtML9gvz;WhkLAr|N>o{crkDlH+f1x_ z6Wq5GfD}MNwI^@z)rr{`@jc5J$W0FAA2JL!$XnsYVy}ya+wR3q$Nj`K9&eqTttUI4 z?#{J?)8Z^nWQ~=IowHF|Rnwj1ALA&v7^cp98C|_jA~f8;?<6mc&y65hp6Xd8zlPhL zHa6ZZ**JUaST)|@FsX%t`VE8nabQ2gm}~{?Z?Q2;BP^KU75NvVnGM4uFbNT44qV_2 zpPoIIH~ia8*Z}RnI;z7c9G^bK`Rg{IuibT7R_6BVcanwET%EG-l6OYl%QeE*i=7Y6 z+ojniD?&#Wn8*&L0`{k9X1JtVQq;Tt#;A}-zRd4epnsBP-STkCoWo)vt5ScIo_={* zo5UjXK87_IEt3T4r#SWt1kAnsk!82IBVuOebRf4cv{ADmT zKczmSLMWM-+ET#-vl!`*@6$_G_VU8W*Mp-jQGv@qqQ;hTdqye_wc6~wG<|8rbH zA0;yDL{W_D_iZXts0at{#edXZ9&T9>r;-)+Ol5zpf!AQs8J;?_9DZGA^KFs&^o!r` z#U&Wx7MrLo>9d>pJ?M{V>(Sr?7g$d?`F39Lg@fudUebHfMwBxl1zbOTYhM*?A|o|Mo|=>&{nN^Qnn*$UoYo?-+>Ov~?uNxsU4IeYj>2VXU1N3GF4?ks-yX*t+9 zKtAhn6j$zufBIHFcAI zcRYOQ@Nz_klI7(JF6~fLJqHd&|DHtqu0f=Zxn^qx#+@Ke<1Pp8N-? z87qIV^PtJ|FQ0=*>=Q(i&M;G$WFpu|F}MH#IER8@AN?81g~J}vfrT{h`|b^J<$H-R z0-r$dFUDP-s7Jm;Zca~~`DzD~#@Zd?JbR2JFJKfQKoaee_(i<5AOJ^KW_`xKuqos2 zQp+rU5SZ}vYaWuoNu@=eD3i1^xZh%rv=3v6iu38*m-5}0o_nJL-^N2S$Aan^(>g;2 z$^M#QkQHALlpcQcS~9q!nZy>7Sy>~_jI^p2o}VW+)`$DbxLf$v@ZvUkWlF{tXu5XlJ|SiP2IJ><+9$Cj{5!TaX}yXucFe z_lS}up9N=^((pb`3c8U6E1G_wbqXqS=zx_uv-bHD{Lk2aPEN($mUq1XwzmaoM>XaC zYvRDQE$*$)hPRlOZV+niEqwAtgsx~I*>UC<*h)D1DKTtR6rg<%Kc%jaQgjBd(OUT3 zdK%Pes5c%^84L_doFm-N`)rGwZ(-+1g*?ZO!X_z?{ep9H$~DSRK6`2EBFnJc^}oYV zITJ~^^H9Ez^D5>Z#j$fPakCZeJV{aK5W(qRK4@|1j)>CGJs~`s6Pp<#c7Q_xuNu@D zSIF8HZ%l=PVPyQxe~dHu*~pg?3!?A+VU@pEl4i^lH&TcYNd;PCc8Q>$;LJg;mqM*0 zlBD}>evFYF{Tea&cd#H$9+J{l6C4eMO)MC5Y#rWy@`XuMK0wfIr~SEMdHMz3)#_rj3<0_5YT5B6&QcdCjrVKPO!bwj+(4MHMdpqOTp!3Q|t2c^S}1> zsq$#`Qz9(n!I>fI_Bba9!RK~Gca_9q>Bh;VO}o8_W(kx4sGcNvP#b{{M+`n&=ym3^ zLXzhZ4pd5DpM_?wa$omI!??W#=8*g*3&Ean7{!i;Mf7 z4re4)s-pOoh&%Wx2q{DJd#BmIG>%{W@_hO7jQ0Fy1U1^HS~x~QxgEQ!#ubH;XM!h1 ztKr2(!QCSXwAmmmZrpI$9E!_<42$FVrz?b1OmZWPx}jt8yh?clBgsgqO<~F2L$=3m zN#LxP52Gl(F_922@Ot0+TefgGYfJ*%vgi8`8HV<|3~1>{(6_265bWy!6dd~$>QmoFh5AT1;4*rg`a3%mOc+3!)@O^BuEz=IkwnZOBD`VRxR;eIoI z!oV0+i5v>(%1i&KAYPunV3gI1Xf*=uY=}}OLnn{N$G1v@o0KxEB4}-TCgDdty6{%@ zEd~4I%j*fACsCl;A+koo)tXvT1*lWP-g%Wqypjw4My3lyo4x4n;gQI$a<=8<%WA^e zABj~=WzwISau{80ZL-zw?hQyV74N|_PK%stX!pAAXkrree=gBPMaTB%ghmi?F$m1R zy~{rC`KDt4@bgQ}&rb^LCnt?wIh2V5WMZj5sr47!S-^!Ue`dq%acBIduiz=T-Mh;` z?C;f7m8L>Dpeu{D-tpn2PQ#h%brz#ah;+66QqIw za^^`7P$mYtb_-sLBmNo3hx_rFe@2*FNC=E&WMyyMti3IMQ!psO^78Tq9odc{#PJ$c zs)ET1#iW#u|AY{RJDuV0n@ z_^O7d3}|SwO!AeX!6&rYON)!#qDo4(x%Kt+b={oHp*?c1@xv&s($A*#yDsBNVX45U?5P>k|k z=c5X|tcyP-AzE(1F|bISkw|B^@N z?DZN8mxlic@iVjvnlB*yt)VsKzY6$&`~thM*d1&||KI+o5Rod!#Ky)p6keqMel$8d z`gL#5`EyuPY{W-aX69?Sk&=>9Ns?K$ArlkRokxa-hF`JLuWfHPe-)panp(8{DH-{uk?jzhBWby11=|%gXYXY4ZyR zJPM=K<5Us(TLUR3M1e;XCN3Vow6rAr{_yx%{}tJc(f6gLIBerIfc|T%tEB4G(5++e;G{aIlZMvX)Ynf<^fM0qiNM AKmY&$ literal 0 HcmV?d00001 From 3fb02cefacaffa7e614bc6c07e6716ad8ffb31be Mon Sep 17 00:00:00 2001 From: terraHDB <93155880+terraHDB@users.noreply.github.com> Date: Mon, 31 Oct 2022 10:29:24 -0500 Subject: [PATCH 8/9] Core 1826 add/update harperdb studio topic and subtopics (#2) * added mac, added/updated windows, added/updated node version requirements, and added offline docs * 1/3 through studio subtopics * 2/3 through studio subtopics * 3/3 through studio subtopics * corrected harperdb operations api * removed icon descriptions and fixed typos * removed icon descriptions * primary key vs hash_attribute change * primary key vs hash attribute qualifier --- docs/harperdb-studio/create-account.md | 22 +++ docs/harperdb-studio/enable-mixed-content.md | 7 + docs/harperdb-studio/index.md | 10 ++ .../harperdb-studio/instance-configuration.md | 115 ++++++++++++ docs/harperdb-studio/instance-example-code.md | 58 ++++++ docs/harperdb-studio/instance-metrics.md | 15 ++ docs/harperdb-studio/instances.md | 137 ++++++++++++++ docs/harperdb-studio/login-password-reset.md | 38 ++++ docs/harperdb-studio/manage-charts.md | 75 ++++++++ docs/harperdb-studio/manage-clustering.md | 90 ++++++++++ docs/harperdb-studio/manage-functions.md | 167 ++++++++++++++++++ docs/harperdb-studio/manage-instance-roles.md | 72 ++++++++ docs/harperdb-studio/manage-instance-users.md | 59 +++++++ .../manage-schemas-browse-data.md | 128 ++++++++++++++ docs/harperdb-studio/organizations.md | 101 +++++++++++ docs/harperdb-studio/query-instance-data.md | 49 +++++ docs/harperdb-studio/resources.md | 39 ++++ images/ave-age-per-owner-ex.png | Bin 0 -> 38173 bytes 18 files changed, 1182 insertions(+) create mode 100644 docs/harperdb-studio/create-account.md create mode 100644 docs/harperdb-studio/enable-mixed-content.md create mode 100644 docs/harperdb-studio/instance-configuration.md create mode 100644 docs/harperdb-studio/instance-example-code.md create mode 100644 docs/harperdb-studio/instance-metrics.md create mode 100644 docs/harperdb-studio/instances.md create mode 100644 docs/harperdb-studio/login-password-reset.md create mode 100644 docs/harperdb-studio/manage-charts.md create mode 100644 docs/harperdb-studio/manage-clustering.md create mode 100644 docs/harperdb-studio/manage-functions.md create mode 100644 docs/harperdb-studio/manage-instance-roles.md create mode 100644 docs/harperdb-studio/manage-instance-users.md create mode 100644 docs/harperdb-studio/manage-schemas-browse-data.md create mode 100644 docs/harperdb-studio/organizations.md create mode 100644 docs/harperdb-studio/query-instance-data.md create mode 100644 docs/harperdb-studio/resources.md create mode 100644 images/ave-age-per-owner-ex.png diff --git a/docs/harperdb-studio/create-account.md b/docs/harperdb-studio/create-account.md new file mode 100644 index 00000000..b39a2a03 --- /dev/null +++ b/docs/harperdb-studio/create-account.md @@ -0,0 +1,22 @@ +# Create a Studio Account +Start at the [HarperDB Studio sign up page](https://studio.harperdb.io/sign-up). + +1) Provide the following information: + * First Name + * Last Name + * Email Address + * Subdomain + + *Part of the URL that will be used to identify your HarperDB Cloud Instances. For example, with subdomain “demo” and instance name “c1” the instance URL would be: https://c1-demo.harperdbcloud.com.* + * Coupon Code (optional) +2) Review the Privacy Policy and Terms of Service. +3) Click the sign up for free button. +4) You will be taken to a new screen to add an account password. Enter your password. + *Passwords must be a minimum of 8 characters with at least 1 lower case character, 1 upper case character, 1 number, and 1 special character.* +5) Click the add account password button. + +You will receive a Studio welcome email confirming your registration. + + + +Note: Your email address will be used as your username and cannot be changed. \ No newline at end of file diff --git a/docs/harperdb-studio/enable-mixed-content.md b/docs/harperdb-studio/enable-mixed-content.md new file mode 100644 index 00000000..1c2e9674 --- /dev/null +++ b/docs/harperdb-studio/enable-mixed-content.md @@ -0,0 +1,7 @@ +# Enable Mixed Content + +Enabling mixed content is required in cases where you would like to connect the HarperDB Studio to HarperDB Instances via HTTP. This should not be used for production systems, but may be convenient for development and testing purposes. Doing so will allow your browser to reach HTTP traffic, which is considered insecure, through an HTTPS site like the Studio. + + + +A comprehensive guide is provided by Adobe here: https://experienceleague.adobe.com/docs/target/using/experiences/vec/troubleshoot-composer/mixed-content.html. \ No newline at end of file diff --git a/docs/harperdb-studio/index.md b/docs/harperdb-studio/index.md index a90590dc..6db82fe6 100644 --- a/docs/harperdb-studio/index.md +++ b/docs/harperdb-studio/index.md @@ -1 +1,11 @@ # HarperDB Studio +HarperDB Studio is the web-based GUI for HarperDB. Studio enables you to administer, navigate, and monitor all of your HarperDB instances in a simple, user friendly interface without any knowledge of the underlying HarperDB API. It’s free to sign up, get started today! + +[Sign up for free!](https://studio.harperdb.io/sign-up) + +--- +## How does Studio Work? +While HarperDB Studio is web based and hosted by us, all database interactions are performed locally. The HarperDB Studio loads in your browser, at which point you login to your HarperDB instances. Credentials are stored in your browser cache and are not transmitted back to HarperDB. All database interactions are made via the HarperDB Operations API directly from your browser to your instance. + +## What type of instances can I manage? +HarperDB Studio enables users to manage both HarperDB Cloud instances and privately hosted instances all from a single UI. All HarperDB instances feature identical behavior whether they are hosted by us or by you. \ No newline at end of file diff --git a/docs/harperdb-studio/instance-configuration.md b/docs/harperdb-studio/instance-configuration.md new file mode 100644 index 00000000..de88c7f0 --- /dev/null +++ b/docs/harperdb-studio/instance-configuration.md @@ -0,0 +1,115 @@ +# Instance Configuration + +HarperDB instance configuration can be viewed and managed directly through the HarperDB Studio. HarperDB Cloud instances can be resized in two different ways via this page, either by modifying machine RAM or by increasing drive storage. User-installed instances can have their licenses modified by modifying licensed RAM. + + + +All instance configuration is handled through the **config** page of the HarperDB Studio, accessed with the following instructions: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/organizations) page. + +2) Click the appropriate organization that the instance belongs to. + +3) Select your desired instance. + +4) Click config in the instance control bar. + +*Note, the **config** page will only be available to super users and certain items are restricted to Studio organization owners.* + +## Instance Overview + +The **instance overview** panel displays the following instance specifications: + +* Instance URL + +* Instance Node Name (for clustering) + +* Instance API Auth Header (this user) + + *The Basic authentication header used for the logged in HarperDB database user* + +* Created Date (HarperDB Cloud only) + +* Region (HarperDB Cloud only) + + *The geographic region where the instance is hosted.* + +* Total Price + +* RAM + +* Storage (HarperDB Cloud only) + +* Disk IOPS (HarperDB Cloud only) + +## Update Instance RAM + +HarperDB Cloud instance size and user-installed instance licenses can be modified with the following instructions. This option is only available to Studio organization owners. + + + +Note: For HarperDB Cloud instances, upgrading RAM may add additional CPUs to your instance as well. Click here to see how many CPUs are provisioned for each instance size. + +1) In the **update ram** panel at the bottom left: + + * Select the new instance size. + + * If you do not have a credit card associated with your account, an **Add Credit Card To Account** button will appear. Click that to be taken to the billing screen where you can enter your credit card information before returning to the **config** tab to proceed with the upgrade. + + * If you do have a credit card associated, you will be presented with the updated billing information. + + * Click **Upgrade**. + +2) The instance will shut down and begin reprovisioning/relicensing itself. The instance will not be available during this time. You will be returned to the instance dashboard and the instance status will show UPDATING INSTANCE. + +3) Once your instance upgrade is complete, it will appear on the instance dashboard as status OK with your newly selected instance size. + +*Note, if HarperDB Cloud instance reprovisioning takes longer than 20 minutes, please submit a support ticket here: https://harperdbhelp.zendesk.com/hc/en-us/requests/new.* + +## Update Instance Storage + +The HarperDB Cloud instance storage size can be increased with the following instructions. This option is only available to Studio organization owners. + +Note: Instance storage can only be upgraded once every 6 hours. + +1) In the **update storage** panel at the bottom left: + + * Select the new instance storage size. + + * If you do not have a credit card associated with your account, an **Add Credit Card To Account** button will appear. Click that to be taken to the billing screen where you can enter your credit card information before returning to the **config** tab to proceed with the upgrade. + + * If you do have a credit card associated, you will be presented with the updated billing information. + + * Click **Upgrade**. + +2) The instance will shut down and begin reprovisioning itself. The instance will not be available during this time. You will be returned to the instance dashboard and the instance status will show UPDATING INSTANCE. + +3) Once your instance upgrade is complete, it will appear on the instance dashboard as status OK with your newly selected instance size. + +*Note, if this process takes longer than 20 minutes, please submit a support ticket here: https://harperdbhelp.zendesk.com/hc/en-us/requests/new.* + +## Remove Instance + +The HarperDB instance can be deleted/removed from the Studio with the following instructions. Once this operation is started it cannot be undone. This option is only available to Studio organization owners. + +1) In the **remove instance** panel at the bottom left: + * Enter the instance name in the text box. + + * The Studio will present you with a warning. + + * Click **Remove**. + +2) The instance will begin deleting immediately. + +## Restart Instance + +The HarperDB Cloud instance can be restarted with the following instructions. + +1) In the **restart instance** panel at the bottom right: + * Enter the instance name in the text box. + + * The Studio will present you with a warning. + + * Click **Restart**. + +2) The instance will begin restarting immediately. \ No newline at end of file diff --git a/docs/harperdb-studio/instance-example-code.md b/docs/harperdb-studio/instance-example-code.md new file mode 100644 index 00000000..e942f07f --- /dev/null +++ b/docs/harperdb-studio/instance-example-code.md @@ -0,0 +1,58 @@ +# Instance Example Code + +Example code prepopulated with the instance URL and authorization token for the logged in database user can be found on the **example code** page of the HarperDB Studio. Code samples are generated based on the HarperDB API Documentation Postman collection. Code samples accessed with the following instructions: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/organizations) page. + +2) Click the appropriate organization that the instance belongs to. + +3) Select your desired instance. + +4) Click **example code** in the instance control bar. + +5) Select the appropriate **category** from the left navigation. + +6) Select the appropriate **operation** from the left navigation. + +7) Select your desired language/variant from the **Choose Programming Language** dropdown. + +8) Copy code from the sample code panel using the copy icon. + +## Supported Languages + +Sample code uses two identifiers: **language** and **variant**. + +* **language** is the programming language that the sample code is generated in. + +* **variant** is the methodology or library used by the language to send HarperDB requests. + +The list of available language/variants are as follows: + +| Language | Variant | +|--------------|---------------| +| C# | RestSharp | +| cURL | cURL | +| Go | Native | +| HTTP | HTTP | +| Java | OkHttp | +| Java | Unirest | +| JavaScript | Fetch | +| JavaScript | jQuery | +| JavaScript | XHR | +| NodeJs | Axios | +| NodeJs | Native | +| NodeJs | Request | +| NodeJs | Unirest | +| Objective-C | NSURLSession | +| OCaml | Cohttp | +| PHP | cURL | +| PHP | HTTP_Request2 | +| PowerShell | RestMethod | +| Python | http.client | +| Python | Requests | +| Ruby | Net:HTTP | +| Shell | Httpie | +| Shell | wget | +| Swift | URLSession | + + diff --git a/docs/harperdb-studio/instance-metrics.md b/docs/harperdb-studio/instance-metrics.md new file mode 100644 index 00000000..2dcd80c7 --- /dev/null +++ b/docs/harperdb-studio/instance-metrics.md @@ -0,0 +1,15 @@ +# Instance Metrics + +The HarperDB Studio display instance status and metrics on the instance status page, which can be accessed with the following instructions: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/organizations) page. + +2) Click the appropriate organization that the instance belongs to. + +3) Select your desired instance. + +4) Click **status** in the instance control bar. + +Once on the instance browse page you can view host system information, [HarperDB logs](https://harperdb.io/docs/reference/logging/), [HarperDB jobs](https://harperdb.io/docs/reference/job-logs/), and [HarperDB Cloud alarms](https://harperdb.io/docs/harperdb-cloud/alarms/) (if it is a cloud instance). + +*Note, the **status** page will only be available to super users.* \ No newline at end of file diff --git a/docs/harperdb-studio/instances.md b/docs/harperdb-studio/instances.md new file mode 100644 index 00000000..a5180e69 --- /dev/null +++ b/docs/harperdb-studio/instances.md @@ -0,0 +1,137 @@ +# Instances + +The HarperDB Studio allows you to administer all of your HarperDB instances in one place. HarperDB currently offers the following instance types: + +* **HarperDB Cloud Instance** +Managed installations of HarperDB, what we call [HarperDB Cloud](https://harperdb.io/docs/harperdb-cloud/). +* **5G Wavelength Instance** +Managed installations of HarperDB running on the Verizon network through AWS Wavelength, what we call [5G Wavelength Instances](https://harperdb.io/docs/harperdb-cloud/verizon-5g-wavelength/). *Note, these instances are only accessible via the Verizon network.* +* **User-Installed Instance** +Any HarperDB installation that is managed by you. These include instances hosted within your cloud provider accounts (for example, from the AWS or Digital Ocean Marketplaces), privately hosted instances, or instances installed locally. + +All interactions between the Studio and your instances take place directly from your browser. HarperDB stores metadata about your instances, which enables the Studio to display these instances when you login. Beyond that, all traffic is routed from your browser to the HarperDB instances using the standard [HarperDB API](https://harperdb.io/docs/harperdb-api/). + +## Organization Instance List +A summary view of all instances within an organization can be viewed by clicking on the appropriate organization from the [HarperDB Studio Organizations](https://studio.harperdb.io/organizations) page. Each instance gets their own card. HarperDB Cloud and user-installed instances are listed together. + +## Create a New Instance +A new instance can be created as follows: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/organizations) page. +2) Click the appropriate organization for the instance to be created under. +3) Click the **Create New HarperDB Cloud Instance + Register User-Installed Instance** card. +4) Select your desired Instance Type. +5) For a HarperDB Cloud Instance or a HarperDB 5G Wavelength Instance, click **Create HarperDB Cloud Instance**. + + 1) Fill out Instance Info. + 1) Enter Instance Name + + *This will be used to build your instance URL. For example, with subdomain “demo” and instance name “c1” the instance URL would be: https://c1-demo.harperdbcloud.com. The Instance URL will be previewed below.* + + 2) Enter Instance Username + + *This is the username of the initial HarperDB instance super user.* + + 3) Enter Instance Password + + *This is the password of the initial HarperDB instance super user.* + + 2) Click **Instance Details** to move to the next page. + 3) Select Instance Specs + + 1) Select Instance RAM + + *HarperDB Cloud Instances are billed based on Instance RAM, this will select the size of your provisioned instance. [More on instance specs](https://harperdb.io/docs/harperdb-cloud/instance-size-hardware-specs/).* + + 2) Select Storage Size + + *Each instance has a mounted storage volume where your HarperDB data will reside. Storage is provisioned based on space and IOPS. [More on IOPS Impact on Performance](https://harperdb.io/docs/harperdb-cloud/iops-impact-on-performance/).* + + 3) Select Instance Region + + *The geographic area where your instance will be provisioned.* + + 4) Click **Confirm Instance Details** to move to the next page. + 5) Review your Instance Details, if there is an error, use the back button to correct it. + 6) Review the [Privacy Policy](https://harperdb.io/legal/privacy-policy/) and [Terms of Service](https://harperdb.io/legal/harperdb-cloud-terms-of-service/), if you agree, click the **I agree** radio button to confirm. + 7) Click **Add Instance**. + 8) Your HarperDB Cloud instance will be provisioned in the background. Provisioning typically takes 5-15 minutes. You will receive an email notification when your instance is ready. + +6) For a user-installed instance, click **Register User-Installed Instance**. + + 1) Fill out Instance Info. + + 1) Enter Instance Name + + *This is used for descriptive purposes only.* + 2) Enter Instance Username + + *The username of a HarperDB super user that is already configured in your HarperDB installation.* + 3) Enter Instance Password + + *The password of a HarperDB super user that is already configured in your HarperDB installation.* + 4) Enter Host + + *The host to access the HarperDB instance. For example, `harperdb.myhost.com` or `localhost`.* + 5) Enter Port + + *The port to access the HarperDB instance. HarperDB defaults `9925` for HTTP and `31283` for HTTPS.* + 6) Select SSL + + *If your instance is running over SSL, select the SSL checkbox. If not, you will need to enable mixed content in your browser to allow the HTTPS Studio to access the HTTP instance. If there are issues connecting to the instance, the Studio will display a red error message.* + + 2) Click **Instance Details** to move to the next page. + 3) Select Instance Specs + 1) Select Instance RAM + + *HarperDB instances are billed based on Instance RAM. Selecting additional RAM will enable the ability for faster and more complex queries.* + 4) Click **Confirm Instance Details** to move to the next page. + 5) Review your Instance Details, if there is an error, use the back button to correct it. + 6) Review the [Privacy Policy](https://harperdb.io/legal/privacy-policy/) and [Terms of Service](https://harperdb.io/legal/harperdb-cloud-terms-of-service/), if you agree, click the **I agree** radio button to confirm. + 7) Click **Add Instance**. + 8) The HarperDB Studio will register your instance and restart it for the registration to take effect. Your instance will be immediately available after this is complete. + +## Delete an Instance + +Instance deletion has two different behaviors depending on the instance type. + +* **HarperDB Cloud Instance** +This instance will be permanently deleted, including all data. This process is irreversible and cannot be undone. +* **User-Installed Instance** +The instance will be removed from the HarperDB Studio only. This does not uninstall HarperDB from your system and your data will remain intact. + +An instance can be deleted as follows: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/organizations) page. +2) Click the appropriate organization that the instance belongs to. +3) Identify the proper instance card and click the trash can icon. +4) Enter the instance name into the text box. + + *This is done for confirmation purposes to ensure you do not accidentally delete an instance.* +5) Click the **Do It** button. + +## Upgrade an Instance + +HarperDB instances can be resized on the [Instance Configuration](https://harperdb.io/docs/harperdb-studio/instance-config/) page. + +## Instance Log In/Log Out + +The Studio enables users to log in and out of different database users from the instance control panel. To log out of an instance: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/organizations) page. +2) Click the appropriate organization that the instance belongs to. +3) Identify the proper instance card and click the lock icon. +4) You will immediately be logged out of the instance. + +To log in to an instance: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/organizations) page. +2) Click the appropriate organization that the instance belongs to. +3) Identify the proper instance card, it will have an unlocked icon and a status reading PLEASE LOG IN, and click the center of the card. +4) Enter the database username. + + *The username of a HarperDB user that is already configured in your HarperDB instance.* +5) Enter the database password. + + *The password of a HarperDB user that is already configured in your HarperDB instance.* +6) Click **Log In**. \ No newline at end of file diff --git a/docs/harperdb-studio/login-password-reset.md b/docs/harperdb-studio/login-password-reset.md new file mode 100644 index 00000000..99cca0a6 --- /dev/null +++ b/docs/harperdb-studio/login-password-reset.md @@ -0,0 +1,38 @@ +# Login and Password Reset + +## Log In to Your HarperDB Studio Account + +To log into your existing HarperDB Studio account: + +1) Navigate to the [HarperDB Studio](https://studio.harperdb.io/). +2) Enter your email address. +3) Enter your password. +4) Click **sign in**. + +## Reset a Forgotten Password + +To reset a forgotten password: + +1) Navigate to the HarperDB Studio password reset page. +2) Enter your email address. +3) Click **send password reset email**. +4) If the account exists, you will receive an email with a temporary password. +5) Navigate back to the HarperDB Studio login page. +6) Enter your email address. +7) Enter your temporary password. +8) Click **sign in**. +9) You will be taken to a new screen to reset your account password. Enter your new password. +*Passwords must be a minimum of 8 characters with at least 1 lower case character, 1 upper case character, 1 number, and 1 special character.* +10) Click the **add account password** button. + +## Change Your Password + +If you are already logged into the Studio, you can change your password though the user interface. + +1) Navigate to the HarperDB Studio profile page. +2) In the **password** section, enter: + + * Current password. + * New password. + * New password again *(for verification)*. +4) Click the **Update Password** button. \ No newline at end of file diff --git a/docs/harperdb-studio/manage-charts.md b/docs/harperdb-studio/manage-charts.md new file mode 100644 index 00000000..5f45ac9a --- /dev/null +++ b/docs/harperdb-studio/manage-charts.md @@ -0,0 +1,75 @@ +# Charts + +The HarperDB Studio includes a charting feature within an instance. They are generated in real time based on your existing data and automatically refreshed every 15 seconds. Instance charts can be accessed with the following instructions: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/organizations) page. +2) Click the appropriate organization that the instance belongs to. +3) Select your desired instance. +4) Click **charts** in the instance control bar. + +## Creating a New Chart + +Charts are generated based on SQL queries, therefore to build a new chart you first need to build a query. Instructions as follows (starting on the charts page described above): + +1) Click **query** in the instance control bar. +2) Enter the SQL query you would like to generate a chart from. + + *For example, using the dog demo data from the API Docs, we can get the average dog age per owner with the following query: `SELECT AVG(age) as avg_age, owner_name FROM dev.dog GROUP BY owner_name`.* + +3) Click **Execute**. + +4) Click **create chart** at the top right of the results table. + +5) Configure your chart. + + 1) Choose chart type. + + *HarperDB Studio offers many standard charting options like line, bar, etc.* + + 2) Choose a data column. + + *This column will be used to plot the data point. Typically, this is the values being calculated in the `SELECT` statement. Depending on the chart type, you can select multiple data columns to display on a single chart.* + 3) Depending on the chart type, you will need to select a grouping. + + *This could be labeled as x-axis, label, etc. This will be used to group the data, typically this is what you used in your **GROUP BY** clause.* + + 4) Enter a chart name. + + *Used for identification purposes and will be displayed at the top of the chart.* + + 5) Choose visible to all org users toggle. + + *Leaving this option off will limit chart visibility to just your HarperDB Studio user. Toggling it on will enable all users with this Organization to view this chart.* + + 6) Click **Add Chart**. + + 7) The chart will now be visible on the **charts** page. + +The example query above, configured as a bar chart, results in the following chart: + +![Average Age per Owner Example](../../images/ave-age-per-owner-ex.png) + + +## Downloading Charts +HarperDB Studio charts can be downloaded in SVG, PNG, and CSV format. Instructions as follows (starting on the charts page described above): + +1) Identify the chart you would like to export. +2) Click the three bars icon. + +3) Select the appropriate download option. + +4) The Studio will generate the export and begin downloading immediately. + +## Delete a Chart + +Delete a chart as follows (starting on the charts page described above): + +1) Identify the chart you would like to delete. + +2) Click the X icon. + +3) Click the **confirm delete chart** button. + +4) The chart will be deleted. + +Deleting a chart that is visible to all Organization users will delete it for all users. \ No newline at end of file diff --git a/docs/harperdb-studio/manage-clustering.md b/docs/harperdb-studio/manage-clustering.md new file mode 100644 index 00000000..a3c70ba9 --- /dev/null +++ b/docs/harperdb-studio/manage-clustering.md @@ -0,0 +1,90 @@ +# Manage Clustering + +HarperDB instance clustering and replication can be configured directly through the HarperDB Studio. It is recommended to read through the clustering documentation first to gain a strong understanding of HarperDB clustering behavior. + + + +All clustering configuration is handled through the **cluster** page of the HarperDB Studio, accessed with the following instructions: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/organizations) page. + +2) Click the appropriate organization that the instance belongs to. + +3) Select your desired instance. + +4) Click **cluster** in the instance control bar. + +Note, the **cluster** page will only be available to super users. + +--- +## Initial Configuration + +HarperDB instances do not have clustering configured by default. The HarperDB Studio will walk you through the initial configuration. Upon entering the **cluster** screen for the first time you will need to complete the following configuration. Configurations are set in the **enable clustering** panel on the left while actions are described in the middle of the screen. + +1) Create a cluster user, read more about this here: Clustering Users and Roles. + * Enter username. + + * Enter password. + + * Click **Create Cluster User**. + +2) Click **Set Cluster Node Name**. +3) Click **Enable Instance Clustering**. + +At this point the Studio will restart your HarperDB Instance, required for the configuration changes to take effect. + +--- + +## Manage Clustering +Once initial clustering configuration is completed you a presented with a clustering management screen with the following properties: + +* **connected instances** + + Displays all instances within the Studio Organization that this instance manages a connection with. + +* **unconnected instances** + + Displays all instances within the Studio Organization that this instance does not manage a connection with. + +* **unregistered instances** + + Displays all instances outside of the Studio Organization that this instance manages a connection with. + +* **manage clustering** + + Once instances are connected, this will display clustering management options for all connected instances and all schemas and tables. +--- + +## Connect an Instance + +HarperDB Instances can be clustered together with the following instructions. + +1) Ensure clustering has been configured on both instances and a cluster user with identical credentials exists on both. + +2) Identify the instance you would like to connect from the **unconnected instances** panel. + +3) Click the plus icon next the appropriate instance. + +4) If configurations are correct, all schemas will sync across the cluster, then appear in the **manage clustering** panel. If there is a configuration issue, a red exclamation icon will appear, click it to learn more about what could be causing the issue. + +--- + +## Disconnect an Instance + +HarperDB Instances can be disconnected with the following instructions. + +1) Identify the instance you would like to disconnect from the **connected instances** panel. + +2) Click the minus icon next the appropriate instance. + +--- + +## Manage Replication + +Subscriptions must be configured in order to move data between connected instances. Read more about subscriptions here: Creating A Subscription. The **manage clustering** panel displays a table with each row representing an channel per instance. Cells are bolded to indicate a change in the column. Publish and subscribe replication can be configured per table with the following instructions: + +1) Identify the instance, schema, and table for replication to be configured. + +2) For publish, click the toggle switch in the **publish** column. + +3) For subscribe, click the toggle switch in the **subscribe** column. \ No newline at end of file diff --git a/docs/harperdb-studio/manage-functions.md b/docs/harperdb-studio/manage-functions.md new file mode 100644 index 00000000..499337f0 --- /dev/null +++ b/docs/harperdb-studio/manage-functions.md @@ -0,0 +1,167 @@ +# Manage Functions + +HarperDB Custom Functions can be configured directly through the HarperDB Studio. It is recommended to read through the Custom Functions documentation first to gain a strong understanding of HarperDB Custom Functions behavior. + + + +All Custom Functions configuration is handled through the **functions** page of the HarperDB Studio, accessed with the following instructions: + +1) Navigate to the HarperDB Studio Organizations page. + +2) Click the appropriate organization that the instance belongs to. + +3) Select your desired instance. + +4) Click **functions** in the instance control bar. + +*Note, the **functions** page will only be available to super users.* + +## Initial Configuration + +HarperDB instances do not have Custom Functions configured by default. The HarperDB Studio will walk you through the initial configuration. Upon entering the **functions** screen for the first time you will need to complete the following configuration. Configurations are set in the **enable custom functions** panel on the left while actions are described in the middle of the screen. + +1) Click **Enable Custom Functions**. + +At this point the Studio will restart your HarperDB Instance, required for the configuration changes to take effect. + +## Manage Projects + +Once initial Custom Functions configuration is completed you a presented with a functions management screen with the following properties: + +* **projects** + + Displays a list of Custom Functions projects residing on this instance. +* **/project_name/routes** + + Only displayed if there is an existing project. Displays the routes files contained within the selected project. +* **/project_name/helpers** + + Only displayed if there is an existing project. Displays the helper files contained within the selected project. +* **/project_name/static** + + Only displayed if there is an existing project. Displays the static file count and a link to the static files contained within the selected project. Note, static files cannot currently be deployed through the Studio and must be deployed via the [HarperDB API](https://harperdb.io/docs/harperdb-api/) or manually to the server (not applicable with HarperDB Cloud). +* **Root File Directory** + + Displays the root file directory where the Custom Functions projects reside on this instance. +* **Custom Functions Server URL** + + Displays the base URL in which all Custom Functions are accessed for this instance. + + +## Create a Project + +HarperDB Custom Functions Projects can be initialized with the following instructions. + +1) If this is your first project, skip this step. Click the plus icon next to the **projects** heading. + +2) Enter the project name in the text box located under the **projects** heading. + +3) Click the check mark icon next the appropriate instance. + +4) The Studio will take a few moments to provision a new project based on the [Custom Functions template](https://github.com/HarperDB/harperdb-custom-functions-template). + +5) The Custom Functions project is now created and ready to modify. + +## Modify a Project + +Custom Functions routes and helper functions can be modified directly through the Studio. From the **functions** page: + +1) Select the appropriate **project**. + +2) Select the appropriate **route** or **helper**. + +3) Modify the code with your desired changes. + +4) Click the save icon at the bottom right of the screen. + + *Note, saving modifications will restart the Custom Functions server on your HarperDB instance and may result in up to 60 seconds of downtime for all Custom Functions.* + +## Create Additional Routes/Helpers + +To create an additional **route** to your Custom Functions project. From the **functions** page: + +1) Select the appropriate Custom Functions **project**. + +2) Click the plus icon to the right of the **routes** header. + +3) Enter the name of the new route in the textbox that appears. + +4) Click the check icon to create the new route. + + *Note, adding a route will restart the Custom Functions server on your HarperDB instance and may result in up to 60 seconds of downtime for all Custom Functions.* + +To create an additional **helper** to your Custom Functions project. From the **functions** page: + +1) Select the appropriate Custom Functions **project**. + +2) Click the plus icon to the right of the **helpers** header. + +3) Enter the name of the new helper in the textbox that appears. + +4) Click the check icon to create the new helper. + + *Note, adding a helper will restart the Custom Functions server on your HarperDB instance and may result in up to 60 seconds of downtime for all Custom Functions.* + +## Delete a Project/Route/Helper + +To delete a Custom Functions project from the **functions** page: + +1) Click the minus icon to the right of the **projects** header. + +2) Click the red minus icon to the right of the Custom Functions project you would like to delete. + +3) Confirm deletion by clicking the red check icon. + + *Note, deleting a project will restart the Custom Functions server on your HarperDB instance and may result in up to 60 seconds of downtime for all Custom Functions.* + +To delete a Custom Functions _project route_ from the **functions** page: + +1) Select the appropriate Custom Functions **project**. + +2) Click the minus icon to the right of the **routes** header. + +3) Click the red minus icon to the right of the Custom Functions route you would like to delete. + +4) Confirm deletion by clicking the red check icon. + + *Note, deleting a route will restart the Custom Functions server on your HarperDB instance and may result in up to 60 seconds of downtime for all Custom Functions.* + +To delete a Custom Functions _project helper_ from the **functions** page: + +1) Select the appropriate Custom Functions **project**. + +2) Click the minus icon to the right of the **helper** header. + +3) Click the red minus icon to the right of the Custom Functions header you would like to delete. + +4) Confirm deletion by clicking the red check icon. + + *Note, deleting a header will restart the Custom Functions server on your HarperDB instance and may result in up to 60 seconds of downtime for all Custom Functions.* + +## Deploy Custom Functions Project to Other Instances + +The HarperDB Studio provides the ability to deploy Custom Functions projects to additional HarperDB instances within the same Studio Organization. To deploy Custom Functions projects to additional instances, starting from the **functions** page: + +1) Select the **project** you would like to deploy. + +2) Click the **deploy** button at the top right. + +3) A list of instances (excluding the current instance) within the organization will be displayed in tabular with the following information: + + * **Instance Name**: The name used to describe the instance. + + * **Instance URL**: The URL used to access the instance. + + * **CF Capable**: Describes if the instance version supports Custom Functions (yes/no). + + * **CF Enabled**: Describes if Custom Functions are configured and enabled on the instance (yes/no). + + * **Has Project**: Describes if the selected Custom Functions project has been previously deployed to the instance (yes/no). + + * **Deploy**: Button used to deploy the project to the instance. + + * **Remote**: Button used to remove the project from the instance. *Note, this will only be visible if the project has been previously deployed to the instance.* + +4) In the appropriate instance row, click the **deploy** button. + + *Note, deploying a project will restart the Custom Functions server on the HarperDB instance receiving the deployment and may result in up to 60 seconds of downtime for all Custom Functions.* \ No newline at end of file diff --git a/docs/harperdb-studio/manage-instance-roles.md b/docs/harperdb-studio/manage-instance-roles.md new file mode 100644 index 00000000..967489b7 --- /dev/null +++ b/docs/harperdb-studio/manage-instance-roles.md @@ -0,0 +1,72 @@ +# Manage Instance Roles + +HarperDB users can be managed directly through the HarperDB Studio. It is recommended to read through the users & roles documentation to gain a strong understanding of how they operate. + + + +Instance role configuration is handled through the roles page of the HarperDB Studio, accessed with the following instructions: + +1) Navigate to the HarperDB Studio Organizations page. + +2) Click the appropriate organization that the instance belongs to. + +3) Select your desired instance. + +4) Click **rules** in the instance control bar. + +*Note, the **roles** page will only be available to super users.* + + + +The *roles management* screen consists of the following panels: + +* **super users** + + Displays all super user roles for this instance. +* **cluster users** + + Displays all cluster user roles for this instance. +* **standard roles** + + Displays all standard roles for this instance. +* **role permission editing** + + Once a role is selected for editing, permissions will be displayed here in JSON format. + +*Note, when new tables are added that are not configured, the Studio will generate configuration values with permissions defaulting to `false`.* + +## Role Management + +#### Create a Role + +1) Click the plus icon at the top right of the appropriate role section. + +2) Enter the role name. + +3) Click the green check mark. + +4) Configure the role permissions in the role permission editing panel. + + *Note, to have the Studio generate attribute permissions JSON, toggle **show all attributes** at the top right of the role permission editing panel.* + +5) Click **Update Role Permissions**. + +#### Modify a Role + +1) Click the appropriate role from the appropriate role section. + +2) Modify the role permissions in the role permission editing panel. + + *Note, to have the Studio generate attribute permissions JSON, toggle **show all attributes** at the top right of the role permission editing panel.* + +3) Click **Update Role Permissions**. + +#### Delete a Role + +Deleting a role is permanent and irreversible. A role cannot be remove if users are associated with it. + +1) Click the minus icon at the top right of the schemas section. + +2) Identify the appropriate role to delete and click the red minus sign in the same row. + +3) Click the red check mark to confirm deletion. \ No newline at end of file diff --git a/docs/harperdb-studio/manage-instance-users.md b/docs/harperdb-studio/manage-instance-users.md new file mode 100644 index 00000000..1b638d69 --- /dev/null +++ b/docs/harperdb-studio/manage-instance-users.md @@ -0,0 +1,59 @@ +# Manage Instance Users + +HarperDB instance clustering and replication can be configured directly through the HarperDB Studio. It is recommended to read through the clustering documentation first to gain a strong understanding of HarperDB clustering behavior. + + + +Instance user configuration is handled through the **users** page of the HarperDB Studio, accessed with the following instructions: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/organizations) page. + +2) Click the appropriate organization that the instance belongs to. + +3) Select your desired instance. + +4) Click **users** in the instance control bar. + +*Note, the **users** page will only be available to super users.* + +## Add a User + +HarperDB instance users can be added with the following instructions. + +1) In the **add user** panel on the left enter: + + * New user username. + + * New user password. + + * Select a role. + + *Learn more about role management here: [Manage Instance Roles](https://harperdb.io/docs/harperdb-studio/manage-instance-roles/).* + +2) Click **Add User**. + +## Edit a User + +HarperDB instance users can be modified with the following instructions. + +1) In the **existing users** panel, click the row of the user you would like to edit. + +2) To change a user’s password: + + 1) In the **Change user password** section, enter the new password. + + 2) Click **Update Password**. + +3) To change a user’s role: + + 1) In the **Change user role** section, select the new role. + + 2) Click **Update Role**. + +4) To delete a user: + + 1) In the **Delete User** section, type the username into the textbox. + + *This is done for confirmation purposes.* + + 2) Click **Delete User**. \ No newline at end of file diff --git a/docs/harperdb-studio/manage-schemas-browse-data.md b/docs/harperdb-studio/manage-schemas-browse-data.md new file mode 100644 index 00000000..0f5f3d9b --- /dev/null +++ b/docs/harperdb-studio/manage-schemas-browse-data.md @@ -0,0 +1,128 @@ +# Manage Schemas / Browse Data + +Manage instance schemas/tables and browse data in tabular format with the following instructions: + +1) Navigate to the HarperDB Studio Organizations page. +2) Click the appropriate organization that the instance belongs to. +3) Select your desired instance. +4) Click **browse** in the instance control bar. + +Once on the instance browse page you can view data, manage schemas and tables, add new data, and more. + +## Manage Schemas and Tables + +#### Create a Schema + +1) Click the plus icon at the top right of the schemas section. +2) Enter the schema name. +3) Click the green check mark. + + +#### Delete a Schema + +Deleting a schema is permanent and irreversible. Deleting a schema removes all tables and data within it. + +1) Click the minus icon at the top right of the schemas section. +2) Identify the appropriate schema to delete and click the red minus sign in the same row. +3) Click the red check mark to confirm deletion. + + +#### Create a Table + +1) Select the desired schema from the schemas section. +2) Click the plus icon at the top right of the tables section. +3) Enter the table name. +4) Enter the primary key. + + *The primary key is also often referred to as the hash attribute in the studio, and it defines the unique identifier for each row in your table.* +5) Click the green check mark. + + +#### Delete a Table +Deleting a table is permanent and irreversible. Deleting a table removes all data within it. + +1) Select the desired schema from the schemas section. +2) Click the minus icon at the top right of the tables section. +3) Identify the appropriate table to delete and click the red minus sign in the same row. +4) Click the red check mark to confirm deletion. + +## Manage Table Data + +The following section assumes you have selected the appropriate table from the schema/table browser. + + + +#### Filter Table Data + +1) Click the magnifying glass icon at the top right of the table browser. +2) This expands the search filters. +3) The results will be filtered appropriately. + + +#### Load CSV Data + +1) Click the data icon at the top right of the table browser. You will be directed to the CSV upload page where you can choose to import a CSV by URL or upload a CSV file. +2) To import a CSV by URL: + 1) Enter the URL in the **CSV file URL** textbox. + 2) Click **Import From URL**. + 3) The CSV will load and you will be redirected back to browse table data. +3) To upload a CSV file: + 1) Click **Click or Drag to select a .csv file** (or drag your CSV file from your file browser). + 2) Navigate to your desired CSV file and select it. + 3) Click **Insert X Records**, where X is the number of records in your CSV. + 4) The CSV will load, and you will be redirected back to browse table data. + + +#### Add a Record + +1) Click the plus icon at the top right of the table browser. +2) The Studio will pre-populate existing table attributes in JSON format. + + *The primary key is not included, but you can add it in and set it to your desired value. Auto-maintained fields are not included and cannot be manually set. You may enter a JSON array to insert multiple records in a single transaction.* +3) Enter values to be added to the record. + + *You may add new attributes to the JSON; they will be reflexively added to the table.* +4) Click the **Add New** button. + + +#### Edit a Record + +1) Click the record/row you would like to edit. +2) Modify the desired values. + + *You may add new attributes to the JSON; they will be reflexively added to the table.* + +3) Click the **save icon**. + + +#### Delete a Record + +Deleting a record is permanent and irreversible. If transaction logging is turned on, the delete transaction will be recorded as well as the data that was deleted. + +1) Click the record/row you would like to delete. +2) Click the **delete icon**. +3) Confirm deletion by clicking the **check icon**. + +## Browse Table Data + +The following section assumes you have selected the appropriate table from the schema/table browser. + +#### Browse Table Data + +The first page of table data is automatically loaded on table selection. Paging controls are at the bottom of the table. Here you can: + +* Page left and right using the arrows. +* Type in the desired page. +* Change the page size (the amount of records displayed in the table). + + +#### Refresh Table Data + +Click the refresh icon at the top right of the table browser. + + + +#### Automatically Refresh Table Data + +Toggle the auto switch at the top right of the table browser. The table data will now automatically refresh every 15 seconds. Filters and pages will remain set for refreshed data. + diff --git a/docs/harperdb-studio/organizations.md b/docs/harperdb-studio/organizations.md new file mode 100644 index 00000000..8206601f --- /dev/null +++ b/docs/harperdb-studio/organizations.md @@ -0,0 +1,101 @@ +# Organizations +HarperDB Studio organizations provide the ability to group HarperDB Cloud Instances. Organization behavior is as follows: + +* Billing occurs at the organization level to a single credit card. +* Organizations retain their own unique HarperDB Cloud subdomain. +* Cloud instances reside within an organization. +* Studio users can be invited to organizations to share instances. + + +An organization is automatically created for you when you sign up for HarperDB Studio. If you only have one organization, the Studio will automatically bring you to your organization’s page. + +--- + +## List Organizations +A summary view of all organizations your user belongs to can be viewed on the [HarperDB Studio Organizations](https://studio.harperdb.io/?redirect=/organizations) page. You can navigate to this page at any time by clicking the **all organizations** link at the top of the HarperDB Studio. + +## Create a New Organization +A new organization can be created as follows: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/?redirect=/organizations) page. +2) Click the **Create a New Organization** card. +3) Fill out new organization details + * Enter Organization Name + *This is used for descriptive purposes only.* + * Enter Organization Subdomain + *Part of the URL that will be used to identify your HarperDB Cloud Instances. For example, with subdomain “demo” and instance name “c1” the instance URL would be: https://c1-demo.harperdbcloud.com.* +4) Click Create Organization. + +## Delete an Organization +An organization cannot be deleted until all instances have been removed. An organization can be deleted as follows: + +1) Navigate to the HarperDB Studio Organizations page. +2) Identify the proper organization card and click the trash can icon. +3) Enter the organization name into the text box. + + *This is done for confirmation purposes to ensure you do not accidentally delete an organization.* +4) Click the **Do It** button. + +## Manage Users +HarperDB Studio organization owners can manage users including inviting new users, removing users, and toggling ownership. + + + +#### Inviting a User +A new user can be invited to an organization as follows: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/?redirect=/organizations) page. +2) Click the appropriate organization card. +3) Click **users** at the top of the screen. +4) In the **add user** box, enter the new user’s email address. +5) Click **Add User**. + +Users may or may not already be HarperDB Studio users when adding them to an organization. If the HarperDB Studio account already exists, the user will receive an email notification alerting them to the organization invitation. If the user does not have a HarperDB Studio account, they will receive an email welcoming them to HarperDB Studio. + +--- + +#### Toggle a User’s Organization Owner Status +Organization owners have full access to the organization including the ability to manage organization users, create, modify, and delete instances, and delete the organization. Users must have accepted their invitation prior to being promoted to an owner. A user’s organization owner status can be toggled owner as follows: + +1) Navigate to the HarperDB Studio Organizations page. +2) Click the appropriate organization card. +3) Click **users** at the top of the screen. +4) Click the appropriate user from the **existing users** section. +5) Toggle the **Is Owner** switch to the desired status. +--- + +#### Remove a User from an Organization +Users may be removed from an organization at any time. Removing a user from an organization will not delete their HarperDB Studio account, it will only remove their access to the specified organization. A user can be removed from an organization as follows: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/?redirect=/organizations) page. +2) Click the appropriate organization card. +3) Click **users** at the top of the screen. +4) Click the appropriate user from the **existing users** section. +5) Type **DELETE** in the text box in the **Delete User** row. + + *This is done for confirmation purposes to ensure you do not accidentally delete a user.* +6) Click **Delete User**. + +## Manage Billing + +Billing is configured per organization and will be billed to the stored credit card at appropriate intervals (monthly or annually depending on the registered instance). Billing settings can be configured as follows: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/?redirect=/organizations) page. +2) Click the appropriate organization card. +3) Click **billing** at the top of the screen. + +Here organization owners can view invoices, manage coupons, and manage the associated credit card. + + + +*HarperDB billing and payments are managed via Stripe.* + + + +### Add a Coupon + +Coupons are applicable towards any paid tier or user-installed instance and you can change your subscription at any time. Coupons can be added to your Organization as follows: + +1) In the coupons panel of the **billing** page, enter your coupon code. +2) Click **Add Coupon**. +3) The coupon will then be available and displayed in the coupons panel. \ No newline at end of file diff --git a/docs/harperdb-studio/query-instance-data.md b/docs/harperdb-studio/query-instance-data.md new file mode 100644 index 00000000..04f178b8 --- /dev/null +++ b/docs/harperdb-studio/query-instance-data.md @@ -0,0 +1,49 @@ +# Query Instance Data + +SQL queries can be executed directly through the HarperDB Studio with the following instructions: + +1) Navigate to the [HarperDB Studio Organizations](https://studio.harperdb.io/organizations) page. +2) Click the appropriate organization that the instance belongs to. +3) Select your desired instance. +4) Click **query** in the instance control bar. +5) Enter your SQL query in the SQL query window. +6) Click **Execute**. + +*Please note, the Studio will execute the query exactly as entered. For example, if you attempt to `SELECT *` from a table with millions of rows, you will most likely crash your browser.* + +## Browse Query Results Set + +#### Browse Results Set Data + +The first page of results set data is automatically loaded on query execution. Paging controls are at the bottom of the table. Here you can: + +* Page left and right using the arrows. +* Type in the desired page. +* Change the page size (the amount of records displayed in the table). + +#### Refresh Results Set + +Click the refresh icon at the top right of the results set table. + +#### Automatically Refresh Results Set + +Toggle the auto switch at the top right of the results set table. The results set will now automatically refresh every 15 seconds. Filters and pages will remain set for refreshed data. + +## Query History + +Query history is stored in your local browser cache. Executed queries are listed with the most recent at the top in the **query history** section. + + +#### Rerun Previous Query + +* Identify the query from the **query history** list. +* Click the appropriate query. It will be loaded into the **sql query** input box. +* Click **Execute**. + +#### Clear Query History + +Click the trash can icon at the top right of the **query history** section. + +## Create Charts + +The HarperDB Studio includes a charting feature where you can build charts based on your specified queries. Visit the Charts documentation for more information. \ No newline at end of file diff --git a/docs/harperdb-studio/resources.md b/docs/harperdb-studio/resources.md new file mode 100644 index 00000000..7269e57f --- /dev/null +++ b/docs/harperdb-studio/resources.md @@ -0,0 +1,39 @@ +# Resources (Marketplace, Drivers, Tutorials, & Example Code) + +HarperDB Studio resources are available regardless of whether or not you are logged in. + +# HarperDB Marketplace + +The [HarperDB Marketplace](https://studio.harperdb.io/resources/marketplace/active) is a collection of SDKs and connectors that enable developers to expand upon HarperDB for quick and easy solution development. Extensions are built and supported by the HarperDB Community. Each extension is hosted on the appropriate package manager or host. + + + +To download a Marketplace extension: + +1) Navigate to the [HarperDB Marketplace](https://studio.harperdb.io/resources/marketplace/active) page. +2) Identity the extension you would like to use. +3) Either click the link to the package. +4) Follow the extension’s instructions to proceed. + +You can submit your rating for each extension by clicking on the stars. + +## HarperDB Drivers + +HarperDB offers standard drivers to connect real-time HarperDB data with BI, analytics, reporting and data visualization technologies. Drivers are built and maintained by [CData Software](https://www.cdata.com/drivers/harperdb/). + + + +To download a driver: + +1) Navigate to the [HarperDB Drivers](https://studio.harperdb.io/resources/marketplace/active) page. +2) Identity the driver you would like to use. +3) Click the download link. +4) For additional instructions, visit the support link on the driver card. + +## Video Tutorials + +HarperDB offers video tutorials available in the Studio on the [HarperDB Tutorials](https://studio.harperdb.io/resources/tutorials/UExsZ1RNVEtzeXBTNUdJbjRZaTNOeEM0aW5YX3RBNU85SS4yODlGNEE0NkRGMEEzMEQy) page as well as our [YouTube channel](https://www.youtube.com/playlist?list=PLlgTMTKsypS5GIn4Yi3NxC4inX_tA5O9I). The HarperDB Studio is changing all the time, as a result these, the videos may not include all of the current Studio features. + +## Example Code + +The [code examples](https://studio.harperdb.io/resources/examples/QuickStart%20Examples/Create%20dev%20Schema) page offers example code for many different programming languages. These samples will include a placeholder for your authorization token. Full code examples with the authorization token prepopulated are available within individual instance pages. \ No newline at end of file diff --git a/images/ave-age-per-owner-ex.png b/images/ave-age-per-owner-ex.png new file mode 100644 index 0000000000000000000000000000000000000000..8e39d22c360f412c803f819545e1f3955fc32d11 GIT binary patch literal 38173 zcmcF~WmuGL6eis%?a&}0sld>kDqV`?P=X9C;Q&LobcvL-gfKKn58WUNLw9#b#}4{! z{NBCx57!I4JoB7B=e`f&n(E4gc#rW=P*4b8s3>Topr8Yh&-*x7$gey7@tG(nekd;# zWOY4FcAIg%)0KVhTk|ld6M?8M{(Z8%E=eW!9HS`qoXw2YwEnf#m+Xm&ta(hht$qn? zgq+H9ei*&7j~?xc9N7=VdvC&64W-tulf8QO z`g9BZ?w+-g`fmMtP~hZN!d1e5X$iy>L?Va76cp>tws}FS0wT0nuvc-a>w*|fQOSvht0N|99v|;{TgNGplg` z80W&**D5o|aoO3~pBHYxBYLs>-p%UJpL_2XRJCl*F&;kDg?P`7;F48cBH7Y=*7I(4 zvCZtF&T?5LDKR)|DCm}Z)lcH=&!1qvM`&Qbh21WAA=RWwLy$1>SygMR=>|6H*1_Ih z)HL6ujLhRWYj5SN=5`Z6THz6aN>;N*lht2wS)b6xnalf1d$ZsWgqzn((|3TH+W*+E zT=LYNckieqCnom(er?JVBXwot>6MLX#3+(~f9814#&FSw zZF#ajr1eql!;-7NXS<0qIXvtBX5-ky_uRu}D?g>bN9yd^$HR7PtAqJw(NqS&V*p@n zh2g&5`!ts!N&0R@dSY@tkm2U*-Td_Q7cw{&6E(qmarF!_J*huM8eOZQ4>su(j)u_? zphgKSqAnzCMQk3#NOab<45-|Fzl}zbp1P((U9dmAqfZDGjID%Kpwe9SDoZiHHu;!u z#33V;%^kirh`Wmo3aZFm0J!+Oh=bbi%ZJl zTQAdE<6b1N+4{Q<2~GB9N_v?`GdP0ZGyGSP}c~0R8JF+91samA*rVuZb;+ zClr3v(DHp}VR|vzrt%u%D}Q)p=`gMH#0nJHx#aq1`}cIkP0alTal_5=z*^eEjkPXT z6-{iW0@Ik3yI0d~%S9Jo$O)c*pa}q=)Ogr{h1N-cR`$Cb`iG0hOzQq}WFVZ3{q$h7 z{c_)=#{Id)9w+nXIM;#1zFo}~v0f?_1qQ-7= zdvDX{qR{t>J@uJShG?vPHAM0Kb^e zEt4E^==IgJk?9M@{f|barnp&a{@C=fzW3+8lXNH~!U!T?Lg?mX%b8HK{n_pm#P>oP zu`gV|QJ2RJjwKZdKkzy5s>vl%CVx*rw4BJPQ_Vuagjz^FobC3LpBOt@FbFM(xK{py z#QCi2Tk^J#H*0*pMTDr>^k`_&jGe^kn$&2noQ(X8HvFsv8$p4QDA<(Fi?ru+9$7bs zq0+z9#e+I)?PpG(fdKN|q2!$o<{H{r1hA&-t9#JRtzMdD?t;wev$_@yGHT91$ZO0)tMEbh4Re!UK2` zIKh0rmjflOx$fAcXIwjK@A0cAW22+Q{?1Gk!5iTDBLJ@%?YAt$*>{U($ z8COVnFQB`KXFM2-FvCzv)NVhrs7#X6H;n2MxIG7+axoT*95a4}mk4ZiUfQnKn}OC} zM69&N>0~l_-q$k=6V7k6{Pu8E@FDVxmMZ~_2c7}G?U+<5dU{Fog)J>68<@?~6G|lJ zPUfc2ODi7&TC+}cSsYUTldtUP>h==XIfYmoe%m#u!&0=j@}Wdq3ry1{K7aJ+7=DJ> z`8O@TWh6R%>U}kyHL;=47pHq9qWwOaZYxAebV#+yl3E0{;IjUvoC+Us!tf*$LYF|>~D~$ z!8b-cRZs1#)PMKqalU=arL2*GcTL}S!2m>rE>oulhjx<)c$yKmUYOqhFUNi1)g{TX36JcY=F5wtOhbM7b%$lW!y%IywpS z*sV+A$04rEWXyeHGrfU!rg>zN+{i!nCq&(*a+b6C%8t{sBEHq}HI&xONgC^rz9M~pBHiJpYJ7vrYqiSVQ>m>`~M1!=K zGRjLG*;3g%g^K=I3;f%F;X}Yb$UUTQe2=phE*TpBv@#^lw46Bo^uG1)`#D-KeIqmN z{`6DO>kCgG%8T!Zjf_r<^6e?ruJ!iRk>6yU#?4+BAlht087qdVVMIV$cY^T>jr1iW zi+ZX|cMEz{2$dPP#F8@Y=LNKOv{7`6424sKnRt-@o}%ml{%|n+ZWzP$N|PsZ^np{R zGn7E5D*C$GcD&jz0O8Mcq(B$VdW>@PYht26x|!-xyI41Mj%9`hSKA?7GpCcPv&3;w zlYD7AYPU1>6&5?iR_M@Z0%i|Q$Kfb`h3`Ey1BhTFr|0*@9Ihu+3Dx_30k{U`P(h%KQub|9n-ua=O#05yJp7KqVd;BsAIT*c zi;M`!leP>$=t*=<4xV~TGm(&x(A%&jGG|#1cwZ>e90ZaWFov_xH(d?jx6N+=vaBq_ zlO|qj_tActT)PzpyBFsg%eaz@8mMgwPb zul6D(K8$6h7_5*gSzQD@qxdWlYP8sHq_^1};WGR(R>LotdADJ4m8BfU(!xUD8J;Pb zPIaklL>&>jOv@~+G@H+#f+{lR_BHz{J)?se>^~#$peS;T;1QKVXyqh3O zkZU_4A^a>Kw8Hq3adok`F`9cBekg^&yf*UW`Y59eU9r24I;hGmA9#!C={W(nGqfE-ybb z4=#d=&Z>;^;1k)?89$~J4lj{b9^p-Y*Z*ZIK6kxpmkB`J)R zK8O$kb4sUpRLh947!yF9X%Cr~U_?mN4?hdIoiPF2MA_k0M4U|Bmh#w3eK&5#d}Mn_ zsmtYJ53{XV?wuyix**(8kTHNAEz=@GEShbxfjno_!U&Pwqxr=e(w}}e@=k^o<}0-u zZZRhX?bo=Y7J&fjBRPfaUC#mL0AVN~7VA0|<+|~IB`vC3`ZUxyN6CxN>#h4$=)|u&GJQbzomT^QXU(hUt$S0__0nNjtp)5SIZ**c<-U1k%;%9X(dD~ zpf2=WEnGN+=73bvGRhX|SA^?(4Dpl@o+EaFbJq7~A!CsDV5gC1)0fRNbBmI7UQGa{ z>F6c5+QL3!2dBEm&Vdn|qmLab&Ydi&o-17&^eG(k3Nt!SOm5~y70-mdmpu+fn>@Fi zS6rRX*&mePe1s)Ff*>xspf$}|MM5lAoM0V ztn8{PfikCxfNm^2+W&yp=axo@S+_~!O6V)bm??E;^WdA;%jUIWBKA16G%e>6n+-eHIZwQJ*V8Cd+^TEyKeVQl*oV z+jwVCph=35-NdHCg6B%qaZS088kTL>={u@TaLL*GoG#(4Fdf67mDRJ_8BP`yiyP+AnY=SoDAyfK z2y2g^ni&W{0hwb)W~2mW(jLSD&8TRcR%(w{#=F!G~dp!KpH!Bk>k0xaK&_5PLV}ulIaK|F@h(c4vo54z=8NSr6Qcg14x%cmx*F-H(q+! zXdlCSbZpBz@yY&u1_mn8{Kq!jUy9T0KlS3^A=wYsxz$fF8av_$!0g_GDauemRom+s zFPp%6aOPLR%0*cY4&p0|suQc*K{6qmb&LP~A4hd8O5OeXsh$wrv?jJhQwq6zQw?uyVM6e5!3_D*kF(-ZhMS%WOm z@X|z_A3dDV$xTnf17<$4V+0cgL8L#apzPz>HjJ30`pSM6yjk^Q0V}LbBh{fD3};hM zf6xnCHVkzA3g?A1Ina5T75S6reVr6Q(hyDu941?~gTFjdyrU-KfqVM-k5-De;s8HT zO_Oe`FcZ{CvZ>CT=Y=@Zq(=N7ODR zv0GHaa>xq@eNsm@CFh9GRIW-vYNc<&%oH?MsKcSs>TKV%HgNIN&NO2P^%bgeLf|2> zxRJj}w~!AHuJN1&KMg%%oi&KCQ{Ez?Ga~MT_V?ju8#zhZ_X+FYvTZ*S&TErF~9e+7FUM-!3D`4jb@u>Yh>?Iq|MBj57-@8kZ8 zsA?IqY!mT8<}c%BNrU%LN7=lYe*U`8`;oD0Hs3gEu8gZGis_ye=ID)ygx z6WoyPn->Iv>~IbaV#0|LiyO2L7HmP@2YI6x(gsvO)4Su8wPEpt#Kwab!)RL3M`R1| zVEjMseywvN#H;Vc0%3NOys6KvUcNyjCMK#N>&b;D?MnAS!2@g7=k}i;v9G|fB%dRQ zH$FYlbjQ0@Bkz|27f5C==1w09 zcmq)|;X}mVA}cb&EHwAKL6y=2JUP|iTIjuFxDY=-KM$*K=NBS)U4;xxq5S;(rQhR! z$x?E(BK)*DCe*RdXcgw2#IMl3l!70WWB;5NTw34WLaiQhKq8 z>}}jq?1a^(&8HYwS-%!Snr(?<;sb9q0iX{%)(2wU1ps!85kGeRt$TSw) zINYPvoAw+aMzgmMS5QrerccB3QQg-rIo1-ijoPZP4Q2UCO+tgV`5hEP$VB#dCv?V^ zYl|@#-i>vBy)|A~Gb%em2<-_#)30GNYe#L|^phl3cqOr99=O-wTsO0#gx2$rs-_gw z^LW9t*`NlchW_!vI=++)4PX~tL_zq9IEfq^mcFv*?X7O#HyA1j4}!Qog2wx;o3(%i zdbDi5??nM2sA_a z3|d9jB5ZG;oyZl(x@cN18VaC0gQL>yit+5H#`NbgZA<6pe=^5l2-$`%+q zF}2Y|bc+7XGprB6x1zrJm;iCm-xFXVt))pbnbq4^<9|0?nL25yi_jE#@WBdI6;KD& zhjonZB4XS@)V+mJUxr<+?JDJ4YTC|X)sQ=7`V9^l9S%dQr8zJ82z{l`+3rq(4vCJo6dRE36h)GzyKM*(JP(; z65nXAvZb~durvh@_bIkr`WzIS&6WKFn)dXj^fsB|A3Xy0)59Vw)Jlg`_W#Sy4_Pbi z2vhxrW@ywYMrLslvbmE-dru`LEN^misG15fc9sojl01)b3ITuX?Fdwh12 zHAH3b1fueVH-_Z-%hkTvE7awXX5dGp;vZUvzxlSCOk~e#eyMY2KD@;r>Y6bTrrOvd zhwG9eKHJzwT(31=6^F@x%yk&>Ig8P@aeeAp12_SxQls&a{ zA9f6_Xd(c`eVcW)p_B|L;Rmc@;Os{$l^8nH?85EW;339tGfrh_+aM>hz9cbMm6aw3 zJ5J^FFU<#Fd`vrQc^daVX~DfT3`kqaxr>(exdB)<_g8(rBFo0|rhE-FkAVA->tF@e z`w3iPA$lhl?o_8y`C3d^jT^!2=^8#zgQgD9hl8w8N<0o6V5OdB2Kc!G+N^~Op>T8C zgyGq>4339%tBtdOSzAM3pzNF%6PJRusR>Wy@Jnv+tX(FzgTxdT-1?r6AX#yNS!wR=NEb9Li^+KpT)j4n z?x+zy2k#eVuhZMN1>`&2m$x;)(0y=ia-)RjXgx8wQVi(tlr+c4(-I{eOK|*3H(~$m zn7Uv5vCX(jqkDKaPYSD-ieV}2nfmKonO|=}7Jgxz+@Qc~XD1XllTv%5!(|mJW97G3 zAwm~(w1J*O9)U%UvZ{2+779P#ALq;jN?C~CM0uy4F;A#NkESuQd_3#$)4iX77f;!@ zE82z1XjmK!`hduR0WZ&9Sk;h%OU&5FD}L4w&ZcM-_$Z#7M9}1e{HKt4!Lx#Pr9g|v z4s}{}^%UPtXD@D+yEm}s!eoA-lz$6<1PsqOxu8BG-~JL#3M3Jdxwlr7^UdUsuOD*< z;6*;hOPggA1&vd?bmF~2AdbHPL1bvx)1eCMvRccYE);3jEMNj9L{M!oeMEi$*BMrz z&kuG|IF|ao4IFE7l7b8BJn#xAJ8u&C2uO|ktFXd8bjG@&{*1NZVeuqtbk1b6{*s-Q zX7u6=MDZn?e#l8Wvpd@!sIfA5;G-4wK>`aL+ClBSauNy1V%U`o!h-Llr|sR*o_c=v z9Fw8(w_JX90M^ugiJ$gOaB23^&q7{29U}{t)>We{G3)Z8vL}cFnCT^H03su1K{+(* z{gsXK$Ew^#voRD~=mJpxM_-C$c*iU+BTRyzV*aA zi(ZN2nLl7-q<;t%vj)x}h=aBvowAi_($*!OCD|2j^UrjZ`_nA+4h^hR9a%6`Oh&M# z$v7)mFmPQ~@!*g~CuSL{ztL9sWLv6_-AM!eW^Y1o`%Vcxjuhmo&Gro@oACmqgM(zm z*~H3ZabF~x{I2vUnkatt@idSr)X?`UYKKl08Pl{9YLwHB= zUr3+8BA(Nr4+~Hacd>zjfc%Ztr_7){vjp|nAKZO6zpJju+qpkJ>?ZTNwGo$7W!&oZWEO_%M7EN)#^o;WDMc@kK?RBb?k zE~!|cKt&vqtE%MPB(wI#Nx8)>!5tuZCNk=^JbkEZ%R2D}w}*;uxoPFaFC zt>~3*$EZ^`npI%vSAz*jeMf!X9h$GO7pQn?a4R@Ynr=w-rx3eQqbHRSFCnk>m@Kf* zLceX{eW&nSdMEbnW$p69#K3OJd#imbF3Yy`v0f61yGwf?)!G2KsDL{>861E z(I;5D$mPr~@+WkCtWFq(^Bo>5jFF}(29b)mu_LQ3mhCw9o}M=COE^oODz+*^+i z5SHCP`>;d-)Z${yj*qJHt*qY~(JPImp?*G`o6)qtuUv^;hMIm(@1us4B0*WV@^{t@ zn^kM09wl}yN9enmutcsx4vK#%Db(QLEpHkWVfmPXfNI?J6F>P-*caqV9mPfqRdnyC zNIPANS`DGzMtQ;$)IW5B#aKXT*%c%DZz+oY#@fykQU(`PX@B?|p?c8}P><$FlpPh{ zgX>x#&2ge1#SAvzi*>f0^TwPyxT(e*V%H9f_ zz_z0BuTj0y0h4cFaLgQ~a=Bo)X?col_ksXwtY78$ocdjDg^jz*Huad2DWfIc1n@g+ z?&qcN3u!TaORN4+bvp)`gVzkd)nEPz-O3It4GP=|4zcQ@a^FnJ)%zAgNam!>ynOMA z@L!Sv?FZXg4Xj+N$rs;{UGq|2N7IxVC0D|p4HRPD@7rh*xGBs9Vy+ zt+i~uq%8a%gzgUxR{OR6620P2jUoMuvqN?;!BM9@FBvH?^f@#56pbsJsgQ7lE;%)Y zd)%E{$4w_#tj@008*S2~p>oOP)}!jjNZRjr3A%S(>ONWWk$KZtTFBvcRX|^${$YOb zVdP-6A%S>#)qImZWW%~f+#cF0sz3D%CLG-ep2RqyU&bep!_${KE=3xlPp$(KQmYtDi7V5uz@n5RV z-fVKKq<~|cXDB03s%WcP^COMn@byW1Z9&^4he773N@J~JEptry7PBBi{hp_<;nriy zz-{)1+uM$xx&7~u40Sx{MrW2Mc_r z_g9l^^Iiu@j?j}dm#fn)Cpi)-DK0LqwM?)5xQBHW>!B|cWLc(0pg`A6CYxf$x%b&g_$mc=Inm?G+{8_JTeq{B}W?FYtFRXQyE(g|)k- zeuHCuj5(bqusxC_*~nB+#^nU_lYjTs{9C@QmvPdBD&TBUYZ?n1WNL`&)a%)rVAI`3 zzlZVhnDq5%o*JUQo{$|)fv|H3omhhL0WoY977}{#`t|Eiu!gWj&F~voQiR%)B_` zzyRswDs=Yl7Q$YTzDDSl98d_`t5+fS3V%yI)RU$T3PBn&An&&(M%}}YNXU3=;qBYE zr|ggXP{soH+}zxbj|ccd$wKJ?r;BE99-k>A7a-^+LKdx^zKx`_i%|ax+CY4Ws8*L| z6DTi=uUweo#nqObk*4peM(e(YN!u(eW3W6>>LKs1wI1 z&V%F))4?$JY*-&*JQ;9IVL`q^j{Umcyc@sYCUWqmL#T2u6njwI;D=%zRqd^l{W2AS z-6WavZy%`4+=dBArzl9KbSTGpr^?78wSVCGtw(|kYmRr~eHUAmr;O=#Nz+?wW2{33 z+cr{QlJ-?&T8A5(Q?T0ir|3l_hMAQ;@pbkIQw&B=wC(zdy|eH99fr(&HB-hF5L;xH z;I9O5M+IZQ#rcJBr&}!~L((0^KkpvobA8tLu)X&%X|MIMXC*%DUcOyq)cKYk+dftG z?iJEFB4x)SrqWbr*0-FPWl64kt`4C}U{}F;AZPO`JF2-iF8*wnU zhPn`nAJM|t|7GhafNVe6cYy#y$OrxQ3ilcX0@m%QTpu9Rb~_{D`~X@xL$tNDw=iw( zGsNzrK1sf|;Oej(bKO#pq`g%OCwkFb|n;jx?GEt)-G^+C?@DEqRn zHCOSj^{!oYBY3B6AmBsYCY=vyab?HCV?k;SPg&j~7D1ypcd-n!^Z;Cp)NdZ~N+n=#aGVY@i@ zZ`#x!89bmX%sacgiH-X|UfoZOkF%_;tsVDsm2b||ieqCp3y=r{%PK3MbMo>M`5tU; zhJ`cqfB?&#qUM>?L^Rms8qlK<^E~X<_Bb)%N#0;fp7tDU29vq%mEKpAmAifOa{%A$ zsX~>pUPNR5MPSX9t-*cjR|3$JBomR?96FWpYm$n>9a=UyVlJE{bnh5m;=rY=%Gl9b z9pLUBO;|;iV>&&+h)lQA&2)~1i#KS+7y-B5I8TEgSdK~xk;L3uFMEwJcA9|mlen*L zMU)lZW=b>Vzy5{$xGI1O2;ulb+PAMMXh(H$yCc>9WD1?gvKZc-XsV-;7x**@%WR+6 zE9Mi0NO`E`@a49YZ)Ml)hQW44(dZc$5)1uNvTG67@WH`rH>G{i56Ul^_gIk8r$foF zj4~B!tS1N3p^!L}{GgsRzxh8zOT!f7beb;{lO$ zJ|_=P5+*+7W<($TyYmWXwM+?v^KF^xPp2Qyjx0>Wz91PBN%o$B3i+PX4W_R4+|bq0 zYRucob0w>!ikOr-m0=PlBtrLwT_-On-g5opDuE5iRqIj6-D63S2HgBrFZnnwbM}1rW|=9 zd$A{>0FAyemOYmn&$MMvJlfy1qS<9p5%`l<^|E<40y~xBYoVWB3SvZV14z20zrfz*V$)gFlYll!z9(m(kU^aeoN_lO|MPI%T5_XQ^}Ls| z9#%w^;ejCWJkYbFa=Z<@iv2{~A+AFKjLJTyoiQXX9KVT*!ZR=Q^6ctmH)})trVwU0{0KVOr?&L$R*t;yZz1S*Z zh_6{F=W_xfmjGiLM)bVqry(2#!zTmIYh<+IF1nXkp+Xt%iTDgmT zx*yI%A~;j$m}2PItk-gqanS40dTq?3L_!bQ%!YZU0`0$;SfX4v9m4V7?k;)?kDqz# zBM|H)o4=1-)~hcie%p%pTqT}nDR~URcD))Dtu>0ne~b<0EyJ^ogfv~ti+?8R_4M(I z&P{L@sy`+5#7m#x8qb$gzUHKn5M!)VTip?07QS*H5_{s4pc0q67rRpDp znQZ2Y!1@T*MhVlLr)STPSE-zUw4hw@)ciaz`;54tiPxfs)Ap|-eg~he z=~zpmUwwDMY8K=UsT^9&m^oO@3E!?OP+m->38!IN%{R(PtA6&qdztF~v1stlso~}- zf;rXe_VMj-DRR_mMct1>6wXmj8hpaKRv4++lB7%=FH{v(#di8MOe^5FK2i9k8diiO zbNGZEJS)bT64o-o#^-rt)0b6FDf(^tq->bl>mq-}GPH%>m|Y`ebF?<`c^nn{5E2TU zJ&x62bZphK|H1u;=SK~X*tFoGo!Io!!l!6EjdOa9YM&f*EemnspD!kYq9}wB*a27z zaZKLJ;&-((OfG(s4jj?(Cz-@q4peq|CBMDROIz5&!k?JyE2d4bxh9TBgtSq*d#ru( z#^z!hYPD`qiFBbChn&&^1V#!>SEdKV@1#f}P&LOGnuL;@ap8GWw#ebGmnPrB)H;^b zDL)vHwu$UktaPWS$KK@^%NjhkNwSKF5dJ*?F3r(qg}mvKA@2WhO8eyg)aJ)^z!l`w z;?XT`)zh~PF7BFa#?N$A;MU{0QEyqzhtpCuEY?FdMS+k^m0igUZb-CRV{crF#!j}4 zQ!Sne;v(H*WtzLzJ6;u4S$YoM{p%7@t3k=d)Or}N$J|PKyI6FEpJq9+mc%>~zW+oq zgNHC;A>xsE$?v0e5Vd~ETl#O4UvHge$oa~us0r7fSNE5?PV}EK2&H0z)3a@gau~wQ zQ#JG$gryerHQ`vFe|H?#aS|u>mqp&2XVhAyxZurQPx4uFO#2aRE!kx1jp>^o`Bfsb z`aW%1%bma%{}z)yaW-=s4v1%w-Gai*oERtvbYM}gkwsMb6wF=!6{q*}c_R-Fwd1?< zE{}81MM=}9lbHa^lw?{Q|4Rb24K2M8gBT0x``Gvb0 zmY^mCj%(udmhbsV2Zc6d*jjHXJFbgA|Mpn|rm@kpB`V>T4>=1oUtj0gYWdcEo-f~d zukcb~gN9E<0H3vUn$0%73;pz^KKoFy%`0DG4OM(Y1lXsvugbG6)+7-%5(guc@T%@v z&TDDifUS!Xv7Uod$0vNL3fAof)wfgQ7Xz-&$gIVc6i#WMD*ty)9>Rj3z+j@ zFQSe%D>ywtu7eW%5tA%-S@GGKHJypd@j^d&z%n1dnxvd0^%pH1j!{ZdS=i)nDhk$0 zk1>{;O1_Sm*F)A&qo*UY{Cd{2LId0#H^nMP4B&BB^TU?JPq+ZLtQnvvof$_Ybmc>u z%8DyHG8RfbUsZA6?tF(N2AMdqsW-gLl${G%N}~Q5TH7QI(W|!OLcWwYjSQGyJXz4X zgwt3(PyzvT#r52yVs73#iIIHPWbB}BlU(%Xst@VIHF!MWX?~2hQZcJ^=U1ge@rw8W zZnBUF&EGXThGWZ3)hg#9}MznVI!wFUC=Swkik>ze4kznt@7gQ4;wNQf0g& zP<-_}Bs_?)ESt+3?*#&>;;T%3rA@l$c}-8hJOYNAzE4Tr_^r}J4-i(n#H7XFz|6=c zP(%m6aSi=|ljdoK2FIE?%&ek$T^3JAT)SnGdd2=WZ#5!G`%f*8HstQ{$3H(r12O?q z>_4v-B(*=B>FY(0j?d==jY)N7YL5Krt>043vxWP(Zvg~2a>zDFPR3EO4DR?FdSgF3kIGsRMMndZO z!lh#r((~JywxE@|p6XZG5)BsZ8#&;d~y4f4VDY3=n zZY)e|kIPTZ9Gv`@B5F`A2#U(**5rG#0ZvM8#6ESZ4q;$OG&nGL8p-#zoQjy_k10_Y zeHn1$$C|b~aF`f0t^i74`Rq>Qs~n^@#SYX)Xz>~9tTHh(1HZMK7B26#noD`^HjIXa1`E66Orr#N;p^! zfwN`xMZiSuYSM3%$}{b%&hOP<{@$2K{XNQGu4Ee}&qt`zL#+Gfwur29caK9u9MiS- zy%3UDi?ZD~6h030{;h+^!$nH}WD)oQ>df*XxW`j~{3@s_sL!3u{s^T>5z_8fQr&@0 zCXUt1+-=XRh|irME7q zvoi%5@~MY7Y3_Jwy2y(dJpduy1i@qV1B{najSrxDZDWaCg1}3wh}T4%c29Q-Kj285 z$TwOo^$}*Np5rBkfVRkx3h(BkR})xo-BR1dWA%$5gjR)Ow5_DvkM0 z!ft@!#qKg{bMxH#x+KrftLboDq*(hLKU8T!frXjZH7Cz@w(;U}c5wj`R&P?M>PRl3sY=HCcF(vaCr8 zY=p~zbn5er5vz~GgNAlr2rU)rS+qABxI`(yyPGqiZBgIZ_rGc*n4hmrKU#oy*SxEB z9w*DF?LTvlA=V2WH51D59 zQYnEe#1OZ)RuiIjR@X6KJ~-xl$eAbDevUVLyoK}>-CjD7g-ITs)_C!K0gS2WVGrl8 z&Gt5uTZ1~ufc;AbN5OOtr{g|E;zN`^TEYV~xtJZP*}0_wXjOkhSJ=Vay%v_eseQRw zOYa=Kp%1};46nC{UCO4|D7dxd>}kDi$4j{>a-bwf8WX5W1ZMJZl2Ra?%@^r$zn>k9 zyB;gdZQBI8OM4~4yQx$igkM z-fwLGu7RUbdB$<;ABcZFZZP!ed1aY9vN8$HjUxh)+3CdNYa&!JK(0P+HZ^&pPhXG& zK@XAxv=G(rlo07#&oo?^z%S9<{zk@QBwCFhtoC?Y19O|`7t@~++5*^7*9BhMOYfE< zlZb^%1M93hC2RG!JXO3VCHK7IzA1?%VuW0n+6a3>+bLxv%G<4h@O!?NHu!Ppxo!O(tQYcZ zZRM+Z;MK)}dg@30r1>7NPo~#VSjg~2E|4Y+N~N+I!r6XQR-5-YL{ds2-hK2m6$d$3 z7TWA;B3R*SF=@4MDxe97{$q?^MrZ1#F$L2690Z`1=KJsUNAu%Y>0B;%rwHOtp)i{5 zj%z-GeJ{FWno0dsWWFK%^KJntu8PqZu?Wk7Ww>NFvY*T0v;+m7Rc|792Y-Eo?8sP^ z>^`;S-)o1)i5?XFLjEtnkzMQ9gMT4c&G_4iKe)Sp>4T!NH)av+D!4s&AbK=HDj(Z)y&dFtm!}10{_1-kj#V;krH&B% zR_{^d6855~v6``K;JUa*{&(rJBPEOaoC^gLaCo)v9mrzY|JJ=Xr)_$)dEuKqcIdCN zg4bj=T7tOA+{WZ>kLEda*rI=xo2~~FvT>ka)0ry;bbuCdY)dT?Pfnw8O!_y>4@UT% z`={Rgvto)d$l^r5Z2IEK_I$aXZ@KoRQNjM6qCcVe^vCKWPu!=;{Pf1NozvW3b=iYb=o2N7S8-E^6T(3sO6av+m>h1|-O+1%QS+uwKa z8ANVT{W)lEt`KHBJBnZw=54h!L43x01n)kDe>E?8Eqog-0`JD55|JShathf6P)Hphv(*%pup&)oDz3sNg^E@C8B727p9gg5$8KXJR@BzWfkzea0#=xkC;;#W zo$!AU^%YQ2eNn$EDkY#&(h3riN(>Dm-AFeM9YdG22uOoS4k_K;Ekh68Lw9%0(7en4 zd+&R!#agV(T<+!GbN1P1|6(Jo%NwRb@4qDxq!jXa6cV($6{_B(x|yLZtl5wgI4-D` zQoX?gRV@;_pFNwg zFIaW=(#D>==;f3kbN;@spiTW%S$}9gD$%9j$RDMz4-$~;z@=HvbxXs zsPp&VuU8T-p-?jp4dV?iAlZEuVTA|KE3_VHOE!Ky{>0|N+vUFI+gn@1h9)yr7XOvM zjhdiMYM4}ep$o|m-V+ZDPVNQc-sG~!IU^zgMu7=2`AIlp9Wut>wW^7})QnacGE~?! z@|W{0gzqjOGJz}rgImyM#z6#8x9u>-qfS&%@lG)h;5;*=X^v}5=OWS>F9XF=s3PKY zax&~!&)R{M)j)CNhLz9 z{H(A}3=rm-^#E~(b|2Q&iObziFE(&=RF!=!>MMK^A$0cmaDw z_e25wm1Zu+;2^u&=n=zTy1d;wuYVBYWzE%XA;^R@WO&I{SFT z_1s}~Q+?Ho=CfcO64G%6q2$&4saT?>GN`+B`HP{im-|XDaJu<^MBKhMDnWzO!M7fz zqLCA5ooBlPiZLu&Avx`S{JZZKx3F zGN?RI_{zv1=fL7ZLYe7g$0Qrz#+K?fiw))aCd`zt{U}PUo?R21xr7()#7=|emVc0EI9IF?I)Jq~@ASR!q1SDz^m0ODl%Z9+{jZgc)Q z=?hGbc9`KNG0hc6&wYwxMVS)&EFKz$Dx#u}EyHiRCZvSERK09FQ(G zm`#qmPP+8YFj3agJ@Uaj%R9jm!CS&``t>8_G>XIipO=>dPjYG7H>XH29~1*-8vF#7 zb4M?;OjQjENBm>4^%j9D1eOJ31aB<@6oOkEC90WpbCpsp>Y@3whV5+Y*HF}@%m+R^ zHZ>bnluISuJU}s*n((OXzU|d%iwr1+G+HvXOsf=G!%K{NsXqGBRA5_f#g;KTxvSQ!)uF*T96#<-<_n8E#rKqV51LsOXA8oE51e?yLcK>eXc)ErSNQ2 zJqPijjw3S7Y^hhwUH=3{GH`9CDBT^64wT+nE+G`YY!~ za9_NR&(*W3N6zT*wUS)CY&8U8@z^2&XXZ$};I=E2)=XcWR=idN>Y&2PzF$Gh*sAm8 z*kg07>-C|)v0cD`@_2XJLQ$WBnW9;k;Ga%Jt84?mJ7?PZI4s2VK5fA!cc$=x}7+g=5^$CD2? zl1$3Uyp6G0#DAST_`cV#0z;Tm$g>IEwxsoN<-it@W{9kesQ7icOqJ+eajWiFO%_Gv zrg`%*s}~)$WjqEg^hVRm0$-X4&8RhT;qtGK(~66W-F?`K2R176*Z-26TSskdpgCk* zmQ@{E#D@#dL)F158RR2`1~WE{&uP^`a;5RBvV7H=P<#7uN-8VlJmYDJvTJk!{wMTr z?KgbW{LNn@n(dTH?{@zZ=qb@8_y?&?grR|4z?nnmE!ho{>%-|ZGZMwc17O3w>{`h; z*4N8%>J|&N8iRW~FSY1SfMN;2l$Mi2m$o;xr{QyG`-NAKSx`re@gD4#Y80&nu9V~&)iJ(1EQ%s7 zNwcD}nPl>HpBHexRqsoD6zAj8r+P(7q+2_4U9)qV3gKm836#6Ev=MwS9~fb&jYkpZ zhmN-sAaR7*WHNk56p1fSpJk?zw)Z{bt7&Rrw&;j#v=G>j{a1x=?&za+8}PU2{_pXY z%o-%!bqCn9*=OX*9+@uRfpTQ7n7pWj|3F#GP;4 zM4%q7!OTrw`Kn%C;W0keu`&0%Gfm*$)=~$}SEk24MZcfjE3;(ZFFHUytf1tHQ*`ud z{L2fg`T%K5*wmdH2S`LdXt*U@23i@9t$JkRl`>dB#`e3U_CpO4lakgue4|HIgx_1W z8l<^^(LVWI6Q*t4&1bH37UZ4-=*-Y_u_HJeOxl1&^A(|Uj@JHoBXFXSdOz#3uQARK!UqaLJ*rAV^o`)%=?3__zj7P)LM|Gc2ZK+VZuwrd6^r( z6AZy0BTN>66bdhYB^41NDv=Qy8hRdL{*rm|4q5A*^A-X?-S3(ysLcUz*Wvya)dx6A z(YFF_S=9)Uri1UM9zs?xW_MR}=v5pc%sEeBi`P!2VI^BJz*sei>Tk`})o(QMZMqsU zggUp8wl$FiCCRMc^fy)X@Ceh3d7qRD|IWtK=&fo^WjQS={(b4IO-Rp3ushzBI`xnd zCZwBV+vJUGicD3O)gBP^zEn=4SBp{`Bw-@>V`_LPZek{!W zt08yU)kZI7no=ut7K4ua6PmQvwYiehEuX{$F*Q)J`IV6p!|5z82|&v`2V=+%@+3KI zvN6pAHIotrm)j~WJX@YqtEefq5_J=&z#!?UT;x;#Dvn8R)hD{U<*Rhs##K|b(aiRi z^lR2by-6o0F~eL%?sqJeuVN*Q5aE@9DxFQ(zolYs?PJO_F1$TCku>Y;XJX90Xqr$y z8}u~Iej@tA%6VRJ^XCNnE(MdxhKJQ!Hd#!q3i!ZricWv-WD8xFNduAa%v@? zD0la6NI(M7&*M?OUEW6h&Vn-J`Mn4plPby$fW3@2vSKYNsSd^nedz{HlmuRKne4d7 zKAr4ciJX86?BlzwXLKr`n~rTHqO`aQy?kGB(=4Zsb?ON`a_;{FlxFr{j+Bp;2FCBa z%MlbAcr-(`W+A>ojQ7SCCJ6fe&lC1S92@Cuaix~@;vU#=CyCxHvqa`k=IR6M|Iz&H zqa|((!WSg%uBtLyKVh~A^x5O!1Hgt)A;2dz&|@KrG*J#aDQK)-(Nw=e&Z(BOE6*?w ztt-^f+|Tl@Y-vd}T=?@L?gk4ajwC*2=Rm(%7}<^`!?R%##cNxrO1MfS5;-6SlzfiF z%tqF&jE}TFH5vyHvk-4zZwOh!3!xe{X88mj&6NNs#=NyRv2N_gTF35LOErX%8_j{~%l<-&D6g zf=7c*_QD$SN0s9Am>!_iIt;ndL55W-W%SSi8tTT(-sJwHUx#plk$uz*x?Nf+;n~L} zA#PbwD&=d+jIv%ZP#j1@utO;y1cqJzGHRdA%U{?uCLfVK4Zo#mT^>XBzYm$37g81l zLb9I>dZ9YWrV8MR7L>9>P|9R*uOn}2nCS4YkqTF)GmGzbjc6Wm>SJ4f=B;st9BajZ zmmG5ail1d^#B_v!;8=pKZGvW;C7C4QuRs7Kihryj?zq1mxQ9V@>Q8pf>NzIiW-|JO zgIJn(`5twdYdq*f+}G#BFRJh3Q{6A*ladN~BS>!T^iXPK0#^Tmm1NiB=auXOOw^}~ z_39t}pCAk6+dbY$82t|j2MU*;()2C+AZQz{CFB&|6RHtoPS2~xA1;v-Aa9E&CNG8f zOB@2D!>Sdy0!lhH6ewA`fbjkDW2~fDTk9CZv&rPf-k3;Bj#|^yg2I79ivHvdbFk5Z z1u-5f89)=DelXLUV3z3Z2djcR8?wjO7MuOw-aQVYxW+8^`>}s<(6!mM8!oIb8Z8Yx zyl&=ctn!L646=0^!+6K z)Ox*Q3#0HQqH>hWS}OGKFI(~n1#7*(T;TOxzQmF;k@n(%fpSj*Kue=(4=>_-20ooR zr1e>W}jVV(C;dD0VLwx?htdcsv(9*R!iaAmhnR;bqk;|C#EzomD-JmmO#f0cb& zp?eRB`Ih7^3e_JOA;i4I2#}}i^#L1c2#Ouz%y427z}?EDWQAUa=FM4#rcCL-!xb->ZH%L?A>2a=z-=#b<7nf$a@Eq*J(t zY1v3W0}>$${aI!)xst=E8`(uuBV^ZB5aSZdRryw=+3f)mSMuU@0cNNUqC+4553Yj* zBFyE+yso?Pp-LHoAO8B+v0N)S_-Pt(X>|{$WMI@p__s5-Qrj4Q9ttq^V&wZ-LI|-hpFWHjTd>h* zlHl>i5tCdIbqi|y>Gk7s#w0E103io!x${o|y6G1I@G+otc~Sr4z&_Y6(zQ3H>{D!t z-gJ*ZXB&G*=%5b0X^N0VA|ztEvNs& zQxb-zz_+Vec-2QSJXO2v-+QdnM(%boLAowu5?Ed^PjdKo~)^%(>=>`z3>DQp|BJ_aZzU`wPtm_$dHx|hukNcGAb_Eqs#{~+h za({+^FjOS=(d4-ay6A{+vCbajpHMZyGAZm=9;J;x0lGmGP!}dYGW&7uQ6mNTZb+X$ zX?WJwIluUTW2)NJ*!$N3Al-Y~mpnU%mx<3mxQ*$zk)CB29DhO6CPkzR9jl_-vUkbI z>%=nre2bvwGCRa?`d6u~W+?ka)5PQq3sH=b2~6TuXR+nA_@|RHBRJKj;=KKOUucVF z`Z@6MdRC+htLi#JmCHLyUU8cT8LoySY>msBhk>~HN_2?EABQOEU*nrF75zuB4gXgl z@d8Me3PNmw1KcvZkn|m9%4xp}OcMa^1~Ah1Kz*AURj*@1z2C|rEP;5_R>AYg-iQ(x z>M{QS2(QZ5_~XE@l@5Qu$VZx&5wH6&{H(b}lug;(yuxF&ZhOo-mU)wSH-30n{@RYo zvbFBj1}XG4=((zc`WzzUr7T}kyh}!URRXQ)Ka^!^2q`v@Q*3@^He1CjR=(Kt z&4U`eI@2L+I&IxpWMigLTC)jfqfXnZ?+Yul5AMLEp9>+a73f^)S`CC?CbhFw_W_cH z7xT8;uRyHoi0{@w;rBX=5u*HA|TmzQpfgxUldJ`87T0EY}^;eSR0QJs_27b*@6G}QsLnLLSpt6i)A z??nbsLw>Mu92`E^sa;BqXd3^%b}gjE0AZH|`ODkZTKdOGE3&GYsMX`9?C4TVgpi)Q zo|io>49qHJ(n#7~%M zl_xEld$ZhO*ekA)SxMU<<_8o;UKJe8d~odD6IaH2m4wRzu^W`bBn!1l6RYdK20%eB z;6{2oUV(@J$~7SnPND!m*mqhPcSb^Xy{$!2Bva#XD`GV(Z~lB;Z;#{GZu=|jZmWW! zDu!2$-GNZzxZa}Lsh+b_OJjJa+R5IvSzkmva>wIX&PPcB*M&~^k3f!G4nT+@e+_yR zYYik2<+8?VoKp+lZ*0M6MCdMCiU=o>Z<&L&jYH-SB^p42!Ux9ppLxV`Ahl7MIx4#)(T4!U=vIefqL<0dlG+vYECGMpSUo=%&`m* znr|UlPS!#jWlvWKOij)?h+$co~D z<*2}{*#XH%{2pe~n%&_cz-8$r-v)eB&P|>oR0dy|FIsgP7%iNGXe~+4JR;*54KkRk zhNE0rk>+AM4E(uFiHHW4$^` z@j)96BxW-&J>;Wlpg9Zg!b8Kw`-Uh(^5Z zERZt2m~a}HX>QRlL}LoSu*UFQeeSe8gH}t|K3ZNWaK=@jE!B}#`@^{+*G>iR4>yot zIjYu*#?_@8j-fBS7G^q893?l*DOCP1m_tV%ub*6V6A8La*) z2bIJBU+Mzg-2?xGg#VHm@|GF?L^Ez4gCxd`hQ~$9;t|^{7aM=7mvk(kXOg+syE1M1 z;c9hJrz&!5eg_J7iYe1FJUB&ofCR&sAzeeC6xsg&19-F%-D@9}?Nci&Gwu~})%8Mh zwzNZDMRV?bsFjb#9CNTAlf=sELHvsmSk6|;OAxx#w3v?XvN-{WgxWPg-t$Ji&PMzV zz^I^p$gq>lVRT6Zz#ah<@D`ZeLtG)^X0{JXy=YOqLaVxNo?<&(yMxZOqA?tX1(R4x zdwk#(i~`aCG8G{dX0pR>0$Kn0VCl6Fz}s}fC)x31_x0TWL(4s3p<`%RV})k`JXKx9 z**VTuL*WJo|Cu1JPmzS4%4tE!?K^NZX->;WLmGGeh|2*RZs+9Mh11Ejk@o(Z9}VOt zRa+@c*?05q4ir5>+v!#Z0c|_*^^00>J8P?DY<+<)3>97R{-bFm!k@L(5Yk)mJh5Q6&uP;DI zGUa@Df`IV>xz;OCcg5+?!DH&Q0GuCrK{wWMO;gPvUwQUTiaz6+YvP_Ep_2@O+jWB^ z_Z&In;K4Y8~O4i ztYNh12}yWyq>*(@5Fh~ZnR*!@pYz{{#(97jAEGdT+8%02*Ou`DQZRr$LkHvvCeDAg zx(gS>?IuAMjq<$OWD{;`9v^07OX}+t^u2b!2`J!&b#wFv(gwLIBeW;fFYfysveo`{%4@@)OC4gOje3 zPMYxXtjr7}J?R&8duQh$k%YlL1Xolg3v-&g;*<0b>d%bKAD(0(q92K3SiF_)Rv_n( zAl9l`_flz^J$`L_oH6l-c>kL9-m%5;TzDSUO1~{AC_UoA$Le!la^QV;UAmd<3ea}w zBF_TkV}2_dgB^E*pKDzVvUn$s>}bhxlTuQYp<-+al))S~a{94=X|yNx5&XX7<%d68 zjUcn+L;>cLIfJ4oLR-G28H!6@rs#fY7RcZ4la$cYwh3=144u859hu*jt*i6|AR2l) zDEk<8Qk{*dyjkR#3-0$T{zgGB3c(T7v`pvpKRCd{5h-Zplyd-QJ`2BVo?FKzGmXV$ z6M07`CUo%E2U}++h9g#d$Blyq1?$?$sy;jhA`Ea6@SO#J@9_o9;Q+>;E~?&^119U0 z>v_c*TkT*1>^byZqwYHKr}}_!A|mEfemo|m-y8;(+GQ9Ws$-5`;QT9C{;=M`rUo|do!xvMpleXLvdcz;#3Ga**oKS{)mjFypC3? zXq=JARh(8E(mH@5RZpc2WFN^Sg=M{Zsd^OFKs;qDhyBvT4F)X3dK}{kyulosuQd=w zp?kVP++OVgIDD4aB(e|h#1NdRGIX&v(bvf;4E0%7hhCQqoSe}Xn%rH3;3wQl!+ZAE z%ZpCNiL3QC56|UG@5U9B05qw|(Mbxo60!Ugt&1pvI)9G)ibyU82qBgIaaph~T5?5$ z8r?YEe^#0_^a6UuoTQ-P>A%FyrGhe?ar|0?_eV2gV9{Aeit2Qpkf1vo+sL*0eYjss z*)q%5W8?k>A0d1~5Piny9qc4;Xc5eGyRoJ|3;Ld&(Hsj1YEEZmkkroEjtH6F#e?Hs zb`dvatRy=6p<)71Vb+#2+x3$XBBtrzC1uk~p6a~;*Cxy8$wTf7dZk@oSz7cwr%Z>c z*DLl`^zNL8!j8lO!WHPr@V2p&+^BSkZ$EDu6NBzI^QQXLO6s&ya>|0^fE7CD3t;}R zpaf@$&NWuB)r9)tH%?xoOzgF!h0Wcr*q+ zKk2>ly?fj$yvsw!`qbk3Yv*S_)p2{x z>pu2~nW}u?4q%%KfBHPMJ!k>lVEc6{z(*3zrVh>Ym*jI`g7B#GQCCUrScYOH?WYMZ zksn4d{B~;&D?6z`AmzJHfZ+D37KY8KGSEu{@Q>7*dNhj*v$2J-+B^)Fwc~j&YOSMSa=}6yx zFsRxLkHK~Iv>I~5tB;D$CjuogK-Bw2r#MDGR^03f>QHf|q-XvC(Gh~0MO(&vS{?sg z3+eP51G-m!{tu938=Jbh%c}IRS`79>_@lj?Dm}&Ob&ikw2-21JX3EEoJ)+tS8Wqi4 z9R6i9+s^E#DpqgV&V7qdM&o@c*@f1PySr_#bruosu(E0L)n;5^D`ZXex`w?1l)|E( z8!OEQ8BCk{`qRe3!XoN{_H)p%%Q^&9op0O$|Dc~Cp`q&!{WKo@zld10W7ILGb%g<{ z;-!YR3U-2%CT*prUm7#yA%Y~o^v-VFI+SUy+@Wwf*-5Y4J3C1Tg5_l9a>#twRXAVE z-o*#p6F5H3aeN}JkyiWc5sIrKmR-i3VhA*n0^K5LM?_Fw9=Lt68_>vi6NAqDt$qxOdk258ZZ3yyWIF+|&b(jjZ*x-9yYP`s zbIbiBq|sghj@g{B8+(FDqKVcgxh$nZ?_;XpJtToA~SPnEB6BRxd zp!R0Dds74Zy!x}tL`tQwr2DN`qH$WvEZPHQGRoYJDYOwCl0?cTaa1Q`ugI`2@-DK= zv~GoK6@hp`EAQ2uid@KRuguL9R!0=YnzZ0EIBIDD!$JB3ME4zsEi+W)5UMmilb(>-Z@!WV`O7Ioa%n|lsb@l zRuk##SMrFDS5+xpat$BwAC-%h3!Z%wz+IWP5`fbHHLR47aPg?oOi`z6E(4DTzydPX z%eyI~t!Ha4UqJ!L5iW`+xQ0q!0($_h9OfMQSiJdn4_mHncpW$7{=BVdzB3PyuV#V} z>Gr!qR@zw4AI}8xrbwlD$Gx!DJ}$TRo)JY|;#%xl*>So_<}`Dz2CU9{P`6LKN&?*I zU_Em~8hX^11xFy7GB@WOAtstONcYYeloE* zK?z`TTKiYb3R{F!!x;vQlsdxJvpM`{B?n$d2!;T8o8WpjNOB65PlZHWlUXRXsbsT$ zT!Yv(4A!J^k(K9+&5?iWb4o91PR5%S)xO)Qz zge6nx!}?F?Xgng@QG6g|!uF%9Plp-u6^F##%`C`9Q={3taIeA1eYR*bH1Eo(%WoFp zSV}1&Ij#Q`Dmn$_HZM3xqr6vgGitTPQQtMy?<42+*~-5bs!R_r%)evtvW_6Ka2=MO zo|q<13aObSRvr`l6&Y{NIHLp>O{y;2wv|6gt4Zi>D%<`;RA7^fDin77M+j;nr%g;H zxvVtr`VkAxv18`Ly@K?7O}&qfH(r*MF*%=T%=pkGDZZiCG^Tf-V0HQ`a!?`UM!yOF zt>gHh`ioD8&7vwJo?x05sT!oUiLo`GPLTprv_3*<{Nu9_3v&X-n*jz~pGAObc~@Co zJwg2W8g&?-o9YffY%d480L5lxWcWWCC8=mmHQ9rio({*?Q}5X&QMf1BK$6u!&lMiu zo7Nuo?o;g9Vai0K(G&G{z?pP-0*OG12I?yw-|N^mZd=!u94qhn)l$dAbC9Xbdw`qV zP|qpVLPA1_ON-yn_iA*|pL;VNSGQ)$;ML^48YTrkvscZXVtz7ww^i1?^uXZW4rw`3 z09S5#H68FKdRN)!mHB#NclEv`$EzDcV^!M!v*Ht$lRWBjO^Z1G4G59s#gk4o`b>@k z_;1ZujEO+nJrJlGZA}6+2=+W9A~f%FsDv2YBv|&lew}e%8MOaZ(ipW48&z|ei137d z(Y~VVbuM>8hsA<;w!>2Hdeq&;A>u`A#2lQ1Y3t?An=lE;j+@)()!{z041If{s70qp zVVX~dU=9&BJYc629X&Y6K>vmjA|)5N5F!DNEv595sUUIU|J zvwRy?X~jcw$g~4pG?o@oAlmi_@?&9{>YI~SbjpC1sK)OluG+{bP`#(Nv&`AtltjGm zgbtY+Y@(UJU$e%y4oefr`-Yk_$OG{al2^uiV;4J>_L$hUF~yn)0>-y}G}iY9x@>zl z@4j2?UDd@TVwNj#y;rHMt(};V_|PE8?P57FdXQ_hdRoGsAkdkCYIZe1UQ)DB@jE=N z=)Bdo++IC^{!;d9rctX@_U4ZPm49@?jO=n}apA^F-!~RYFAxfg{5*&;rp?Bi_hySp z@`qYy>r1Mc{RH>x1L^tZj&}u`am{xohnyK6UqJN6`^*b2F_v%s&g#p8!XC{^x!65= zbGV|_xLzMw6!x03MMLZ>N0;g&rc$2w5yf63C~3RDXsp9WVRjOQKpw&D-cD&ScSI`r ztpHv&_j@kyLcqo;W@Zhr?$84FfHgTS0~GMsfnsB1gqsBS(5OY zfAIRRGSb!z5Nc0ijIH3;wft<8NV3s6d6RH|eH##cdY&ym&CiSWf_Y zHPVOX85K0x=)burti*h3W_>O)Fmo#Q)BBV}zMK3~`zogq66`JqwRHSPO!PIKsJUqg zdas*Yc3P*Pb*Y)s_k7RudfkMzZROd^2?hGX*Pq_w?xu6%u*n(*3yUkF_T|OJ{|Owb+>ivG1L7 z`+Xi5vaosfd)WR-zsF<`z7HzdiS_X0YHVsc4#d-49;`%ui;I@#85Nf?)*9A!3Bz&S zk9MP)TWSq_?Q_uS)6#8p{!i$-e0X)wje~Y-w>=qE8Y3volgw#2w*7~4uWFvRzNVyR z+?WpghLg~2NAgaIrSd+fBXW@SJIdYdm~EhQ5LeBJT#pyOqF|v(uN0tWm@ZDvY5ED|>4h z6wzIywM#0lJzr2^L*ES85wvnyllQzvEknt@KDCcs`~q<3K|VTtBDYE~!tSG`D;Ehk#HZ z9$pZ)teI@);hWIeqS(Vco?8gSCZ6@ReZ18EwS0B|?GdAv{fL4VlDN@iTAYpeX{YTX z3-)5O9#NBPVbf6wu!k{F$GP?Nv+~f84AD1^UiX&(>Ah7|5 zvvjA6dW>v$^qZw@Gt_;ohz+mrAqMc5H@3-U!exw!bc$bBS3X~M;VYjXs*Rq7;zCbL z)GCJ8P1d+%Da7x=@k0Ya?V$@zEbDX7f6c^tb{Oi#B3K`^m&fKPiXMBWebOI%n zd0C1`|D3@Bg3L0sDRMaMv_0(3u^&8(~lX;cs z*@WFb$9dbQ6gAl7nypv!3(#uVDuOS$dW-68B-y$5l9l5OT4{n;c58As3s~6i?@NB? zoS5={wHLEY5wPlGTo*gX(J*R`)<*QIx7S4GQh^&>5JvrETe`+~~xfQJAr_d%42fu}T)mq`FQi@RDu zUkc@fhO~)%>{Mk`UP%dMfK0xS8eMJ*Iayypb_pz{vIZtxI9=QNw$PR}R&B~QzIsaH z3X_pPPIfrp-L0(Kfze<0%qwFPb|Q7W{Ywt;$UuqR^#zRb#bpwol1jODwKRU!BavPw%|Dlf6~4uFpNC zTdk(9S7|+}>9Ox}U%vvFL06JqYD38I=I*bUWCfWs@tA}(7-?&5{+QXMu}FsCU4ptz z{-k(CD(n_gD4%7=9!%GJ*nxE2$Aq+82Ndu?RFeC`xX4n(#YYMT`xc+}b99sA@NDIq zG5Jy51$r}hVs_3+70P3R^-fituA7P}vz*7ZMt@M1TjsV`fBNh~-)z?E=2X|sIlne? zODng_k!-BkHWAm(z^Y_!d$#N%M}EQJwco~KwW>B;C#xm`!Qp;GlETQVr?Q+E$C0bh znA|HEKSPR2Ce8Nn3T19~D=%uyR|VEXS1nW@XLI#s?c&Ugw(#5ZfXHI1F4!a* zJc}6gc|>ISOXhbn(r4IX!?;vJdt_(-(D2?PO*%Fn{OPU_TMa0^&b}^gjQ};kPmj|k zKPd~S@vj?uxn;{H%$dnsSF4gUMdw7zkGj{1K1YV$dp#XW4LWUxJo0Er_Cqz3+tP8zOE69j=zN*lH^rO^k>@ z>ZZM_b!+8vmP~!RPt<75`U=-NdTEpm#@Rco_sLs{!v_YbP(JH%wx?(~%#6~edw!d#Dd)Yj`r|liI z-a}}J-9gm*5`Kq~T5aIMH`?g>uNswHdD?S0p)Qt|?;igyHa9Edq$qb!H6Y$%Y}p+( z_nf*}3~%2{{{2;0S3}%P$9E;4Y&#bEp%M-L?BxrsO}4^lCperze3RYZXecGmv4-P~ zo@3*MzQcXNsmzg@IT@<$}Iij&Jj^ZBGsg3$LpM7jTsL}qz) z?+7(TIK`;S6w~eImC4fH-!rq%hxOZvuJcC@ih8Y1Q>d#N%h}?+QTvq+OkURm-H<(M zUElc^EU8cW|`^NqaP$XIl^q_h-Oj(!`~{nsfk zWtDAJlb;IHN^va8J14b%D5A^8Hk74gAeG2Co>9>Y`Wt!Dwd%%4t#kOhMm-*n69xZv z`|a!&AH_TTaZ5-`Vd!)FS>z&S>pGS=qPIMR2J#%pnv>C48_RYq+S; znx6d*X#UmbFg2}Uh7O%}LqYGFo@yaeoR2rH1&if%dS1Og zzJU;&{#uP(Blj^E9gh&&ybsQ7A4&jKbMuk%H4R?e{yO_mqEQd4MpuxuUe>;&{Iwzx z(n11sGHx0ghjneBR%zJ)TZF$yA|q1nD~{xW_~h{)tHm#PV^XYxSNH0lj4-@u2G{W6 zW*M5qq zjRp4wywaBU*33=wjVeD;ZB*y4on=9Ka^NuGVPF3Z411Y#4!xO_+Iz-xQ-g!pC7@qJ4IFwE0!IF}h}r!{Ad7p+>J95%$5{SlPE6su3M;jHTJ$_d&*nK5rlZ z;Hj{al1l11Nhda0=)4bII{Rk{D4b>tMyDcJeLcVlpioa3UjSDmVVSL_r)IPS4v;?J z{{Q!K?97mhHvOX%!t#;ns5eYYh<%Q5h$Q4mZyq_6HE7ygndq&0aI28OP)cRF%C;%?)Lv+0Ksj$eChtdbnOeLjM@evUKhI9R-1{>V(as zM+69IF%eZOW2KK=`s5k&ls&jka+@LOZX38M#0WmVf@W*q<91ibZ(7G>KW|e!q#zH^ zR{VrnM9kdNE;`B~lE;;png9Kpvl8^xW87=P^}cE2FglxE8gL}ud%y6pU=eT5s4LBn z0XQm;?0y-2Q2)3$B0|R_di%2mxEkdPjn+ex+GiDLWZjd1X~3s%n-|ji!)3E*nSQNn z#mSyKQict6_&wxnk}Fb*CE=SyfCD@UMSTP+B=E-U=B=ogxIF{JA#g4zUQOP~M)hR? zP8!44hV(Y4YxH(XiZ!Vd^C|QSbc!tQFV15&c*YUcyWd*tGdtyeuV09`SZ=m;IikB+ zrdzMpcT$o`;Z;=CE0$dzf0{&D{q#0{elqgJKI1g=Cj(^sl_lPRc?jVP52uw1?2}%K z*j1^kH$5*VI=?mI%X*G8^*DJxolWH#H(2Wm^WP(Y-kInH{Pcehc)!9*;=F$N)&Cs0p(X%X zATj^0*)7R1p?EgKh7Z}6?LeXtv z_m94QgTUCv6a_>A(R{c2G(WK*4m(~O_IIQU6A6%Rp`_TUWKpM3-I=%p#1|AAM@bnJ z>jL~KOfmd771@|UAq6?P+)V*{T0={Aw)pca^w$!7tzVdbnX|QW%JN^X*$#ifpXTD` zrZ?`)EwME>Kk6>m1=_3dDEO#ptz#zpt*VviCMnLxw_d7VR;2D9XK>>~uJx@{akt3# zMY=YJ(onO%pY4x7`bj>mIGf)3s9$&jEadCu#NS;Oep=q|zLs!+e!h3R&R-RUtO#RP zT~r;?R3|B?x*rygi+VR3l>(RaanrKS7+xoqJSV8nOD)$o>32Vm?onEY3&*mR$)l>L z7@UsmuZ-fh{?0JZj!Wk29-&z-?(nOX01ZRX3sQGmu3XdcQT97D<>J+P=i!!>J%To+ z4OHfPU@{-Y?u1WK9jh5|Ip;8EQkn~$b;2o*$;`2<{D$I9H3P5_$vxKBnu1Wv?gEbn zToTK_x3)_SY82Rax4`1$j40o4-!v2O8ZgS!Io5Ee7c#Om}$ z$D@o{Suq5pW(lOW0zF6hn=CQ!(Y1~)qtkB0T25k1ds18%woTMEH0aHSQe)!{yr*p? z_1`c`z!!Tu`M8Z??AQC#m6CDHT7M9ct5r&CwnEN2bC?CI!ySy?;c{jB9>vS4E!Xph zS2Z;8)mBsRho0YTOvW-gTBWEV_f$-2Ja(frpw$^1o2}`$)JHWGCr_n*TIh2ZmJ9@a zM02YrdQuB&!DGG8;I3k79ymKw-_@d39xF;k;N$itp7#-2YuFqY;%z(ht#BWA>!tkV z%v1{<&=aRNH!qI?9LU&Ow>X~l6`M-Pc^23fCOF)EK_`j&{TBU(q2fy#C#Et<*S?yw zDmKx1re!;$j@R>oDdyQ-V$vD6Uvj_k?03wBueCDv8CBxcxVdWbintCs*0WDm$Mh?I z|MjX$c_a1$oU-xhp)E~(_Qs4(F(_*elyj> zQ>U-5Z~YgU%~7TfBYkpO4G9U&R2{iiATu$u=I@w`vZ4UIG)4oZ>74cjgBCyLYK=YM z(wR5Qcs@tFTw9inT=nCM$YuO_pcT^fYr*;+4OA$VLEcHF5_!Rks(~JLNh)9r!-Pov zoyoc@*fzhEAa4S2DPHd|eRvTSOcwzReyQ7xoqYJ8+Z9krZ^V^oEXP%EMYDH$y1SIl z2-6C$IV5N|6CPr6o^N>w0(C@IPBFjdjQH(o;I1A~L@V?T17v6Q66qjqR7Qm(N zCx0q?JGMHLWY!Pi-K^9+0lsH}Q*Y?#rZr*^9>E>Sr$C8zeBH_XN4Ut`Tv9u|2 z$g4{kfP#{cWZR;J+@IY{dFPY)8EAc{1ls}{iLRkq@0%4V4Lpy>**1b@c+6=RgC-0Rwf%Gl?;(7??N7RRE~#LR=7UA(I~ z)T=|zSecyfOe}nM-9s#UjPRl>#NJLIw1ygzY#O8s3JY)DQoWE`t!#p_JQxz8D-m2O z4-w3q!x2Enc)kiIy#%$wAy0$!r{L7D-z@px%xkrH*0VJbv+8cGS2?W9Tu#=u zteae{MJu}N8LNgn3!q8NyBsd0NM(%MGa~P>9-lq5yaRBq+KV6m7JG2LsCfi#&&Glu zbzHG|E@Pi$vw8JKBHyaS`CRrHxC=_$|8Av=<`D6=A6BDi{u0NC_LC<-#>6Che0=<( zb%YDq{c9TiUgVI5FPyS+KDBjN1UIo`INg_ZrH|740!$@)WZe_`^D0pICOw|brQnmr z-OZ&CQrCGx?d1P-b>-nu?R`8_WZ$xlW$ekmgt5$wof1lgx3X_BlJ(6Vu0fU=6itk= zMdD`7CHoRHC1XiOLYJu-dl<5Nk9%*+eb1lgIp@25zQ5-=&*yxXced8jnSt)8Xf@`Z z9ZV5wl>sv#viGTlLfN)bzlrKciyd5<<%4cK_IgZ%@VR>9GSUGsQ3;oNPqpiZ!^#h*rH`~JvwJ2 zX?EX-#tQLX(OfIra)EIh2eVE}Jqi*&!99_onjlV@ZV9XVZziJbjuh3~&}XG=&_<+b zwHMTyw(#yDKm;huy2m*#JY}cLFX6^|y%tiFxLcR*!3-Mc4roxRA5$_me|8Elr6;9I z5)TAhymMs)^Ie{6Rrazx~s@DI6=2|*opwW;l&TefT?72fE~+fso~!HF$h8Z*|7oEw7Q1pdsf z2PEwS<4wqAb~ULj8M&_$N^?`^N;L4i~{-+BU-!p zC6vh*=z5qK`i33KDV=QuHWIGUM73KjAdOuz@{Eb@4&j`}$HhSw?OEXz{ ziLM;EX3$`IaV>Hulx#$T_0^{KCsFix8b&&;D(xnLqDp6D=xVa{T@UfXAvw4JIQ?S0 z^7&*pDd#$(ENt6iI{w0CR7RFY;B!M(sSkBU({wKN^Xjhe#VR${A+vL#F*vJ1v|+fsTcJE$w@iVYDxT>mB&qqO6_}rIP(n4z*I++s0Gx^ z@KY(U5r^t6AL#K+F7Q=o29K>0TBP;iVuk)~pKgY*rFu9q%(S7pW=!$QVE0zNK2|n; zf9B90#pc$I=NGe3q1e-Fw><>t3v0p?`$8+2AqPOmt}`$Ub1uL4ZVNpe(mJ$(5?rdlr1Ay zX@m+k=EB%$4C|H?SP!nG+|CSgCqS6xRJ@q6JXHQQIOA$Uu#PuMlc0UT-Rk-v&roP$ zx;>_S%(}FqC>Jm6l#FwB$}}hUYvw1+KbdcJT`A*)Q>e#^!BR?JGsSd1e#KGGVA1_b z&M@xa8J)g4D&Hx+z~bZa8&t(Lg)3nW^>|Or3YU7yiT32SOQeDv-Zy(`Q$w98Q-$4l z5lD@t*~u_oiUEI!6#aHx{zA2Alqc>k1^kl67n6AnI^U86=cn}|vPf}PXQ_dC+BlM@NAU8;A@!8w#xk_j0KLFjfjHLK zJciuaGYGVnDQeMN6e4Rh%HYqMCuhqb!HVfZ(x*f*-|9g&6vv-s41Ys?@&s>?O*@m> z(bMjoHtV+Ym~Z$$q-r*Z?cK}n^TDk7bQavZ#QJh?>hJSPYEl>(8%fy$o2H%l3Z4XJ z1JHzLNj8IsU}4JMY~;}?lMhBqhL^M=D~;IkOu8mjXyu0V7Rt%mWM!;Yh3%>Ft9_NAXj z%5SxFK41sWFpq>2!qEDvVpvH!S!f-bm;tMy7bIJ*U5~~6HG;zw->B5b|2TTs5qfL7 z47NYl^WdgVFab1brdDwRhG$|QxeG!1;lIv`9X95)r{7!0N)KH4Xpk=42YCTkVDNM> zVRBDA{nP|2_)yivYR~?GZ^=E?;qZv)=BQ^i-?HT0usC|8#BZW3*qQF|RD1D9)TUu< z)>7!C`51^LV(&`Ua`fWMp#ln0^k&<{lbxp|U%8i)%eZBM2z`L=07R%?>Iu1{R^>%> z@+NKi8_-u>hK0g{Z<%co~AUbj^{k zImX%3WkcMSww195=#X06CxaUFn+nvGXdi{9F_^+Sh02y64yljc;n2YW%Z2Zm9{}cZZ$_`NP2}JB6d8PMLc2tnv1VZ;<40iF)MlUX zlS}7J{T2`Snh%4;c{L%fV)d_pwa13x0-Hs10u=hd>lYAQ!Fx5t^=E!YBc>irHd(18G0Vqn3zM1e^dx?AHiiZy+tCCFn;V9VUR1 zb+3SgcB_JAyS?H2naHp5{flbG>PUZ`4!~t=u;5Q{6r1viqBtP0$#MB3y)*=V^I-Ad zD6oJ=ucHk8&N_k7TBNRPK)L!qEEJd%4?-C5{QAIuNpUi~n%`+DIC!P{#Kkj+A3PO& z%7x{z4e1Bt6x-6xr%_n_leh{?RP;7<`GvS*zdk6%3~HA6L0vJdrTt3)&<^m6x_=o5 j)bal!DgR%uQ4WtEZ~PKMIk@xn7;ssbSQ$SzbdLKU-Ptq2 literal 0 HcmV?d00001 From 55ab36ecd81ca5cc805b236012815cc8f15cc9be Mon Sep 17 00:00:00 2001 From: terraHDB Date: Mon, 14 Nov 2022 16:52:46 -0600 Subject: [PATCH 9/9] minor change to example function --- docs/security/basic-auth.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/security/basic-auth.md b/docs/security/basic-auth.md index a3ca2147..9cf0678b 100644 --- a/docs/security/basic-auth.md +++ b/docs/security/basic-auth.md @@ -1,6 +1,6 @@ # Authentication -HarperDB utilizes Basic Auth to secure our HTTP requests. In the context of an HTTP transaction, **basic access authentication** is a method for an HTTP user agent to provide a user name and password when making a request. +HarperDB uses Basic Auth and JSON Web Tokens (JWTs) to secure our HTTP requests. In the context of an HTTP transaction, **basic access authentication** is a method for an HTTP user agent to provide a user name and password when making a request. @@ -28,7 +28,7 @@ function callHarperDB(call_object, operation, callback){ "path": "/", "headers": { "content-type": "application/json", - "authorization": "Basic " + Buffer.from(call_object.username + ':' + call_object.password).toString('base64'), + "authorization": "Basic " + btoa(call_object.username + ':' + call_object.password), "cache-control": "no-cache" }