diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..4a79593f81 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,48 @@ +# Contributing to Unleash + +## Getting started + +Before you begin: + +- This application is powered by Node.js v14. +- Have you read the [code of conduct](CODE_OF_CONDUCT.md)? +- Check out the [existing issues](https://github.com/unleash/Unleash/issues) + +### Don't see your issue? Open one + +If you spot something new, [open an issue](https://github.com/unleash/Unleash/issues/new). We'll use the issue to have a conversation about the problem you want to fix. + +### Ready to make a change? Fork the repo + +Fork using GitHub Desktop: + +- [Getting started with GitHub Desktop](https://docs.github.com/en/desktop/installing-and-configuring-github-desktop/getting-started-with-github-desktop) will guide you through setting up Desktop. +- Once Desktop is set up, you can use it to [fork the repo](https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/cloning-and-forking-repositories-from-github-desktop)! + +Fork using the command line: + +- [Fork the repo](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#fork-an-example-repository) so that you can make your changes without affecting the original project until you're ready to merge them. + +Fork with [GitHub Codespaces](https://github.com/features/codespaces): + +- [Fork, edit, and preview](https://docs.github.com/en/free-pro-team@latest/github/developing-online-with-codespaces/creating-a-codespace) using [GitHub Codespaces](https://github.com/features/codespaces) without having to install and run the project locally. + +### Make your update: + +Make your changes to the file(s) you'd like to update. You'll need **Node.js v14** and PostgreSQL 10 to run Unleash locally. [See more details](https://github.com/Unleash/unleash/tree/master/docs/contributing/developer-guide.md) + +### Open a pull request + +When you're done making changes and you'd like to propose them for review by opening a pull request. + +### Submit your PR & get it reviewed + +- Once you submit your PR, others from the Unleash community will review it with you. The first thing you're going to want to do is a self review. +- After that, we may have questions, check back on your PR to keep up with the conversation. +- Did you have an issue, like a merge conflict? Check out GitHub's [git tutorial](https://lab.github.com/githubtraining/managing-merge-conflicts) on how to resolve merge conflicts and other issues. + +### Your PR is merged! + +Congratulations! The whole Unleash community thanks you. :sparkles: + +Once your PR is merged, you will be proudly listed as a contributor in the [contributor chart](https://github.com/unleash/Unleash/graphs/contributors). diff --git a/docs/addons/addons.md b/docs/addons/addons.md index fc3a5dfff6..ef43bd6d72 100644 --- a/docs/addons/addons.md +++ b/docs/addons/addons.md @@ -11,7 +11,8 @@ Currently Unleash support the following Addons out of the box: - [Webhook](./webhook) - A generic way to post messages from Unleash to third party services. - [Slack](./slack) - Allows Unleash to post updates to Slack. -- [Jira Commenter](./jira-commenter) - Allows Unleash to post comments to jira issues (beta). +- [Microsoft Teams](./teams) - Allows Unleash to post updates to Microsoft Teams. +- [Datadog](./datadog) -allows Unleash to post Updates to Datadog when a feature toggle is updated. In future releases we plan to support community built addons. diff --git a/docs/addons/datadog.md b/docs/addons/datadog.md index 03f4c04eaa..a5dc705d6e 100644 --- a/docs/addons/datadog.md +++ b/docs/addons/datadog.md @@ -5,7 +5,7 @@ title: Datadog > This feature was introduced in \_Unleash v4.0.x. -The Datadog addon allows Unleash to post Updates when a feature toggle is updated. To set up this addon, you need to set up a webhook connector for your channel. You can follow [Submitting events to Datadog](https://docs.datadoghq.com/api/latest/events/#post-an-event) on how to do that. +The Datadog addon allows Unleash to post Updates to Datadog when a feature toggle is updated. To set up this addon, you need to set up a webhook connector for your channel. You can follow [Submitting events to Datadog](https://docs.datadoghq.com/api/latest/events/#post-an-event) on how to do that. The Datadog addon will perform a single retry if the HTTP POST against the Datadog Webhook URL fails (either a 50x or network error). Duplicate events may happen, and you should never assume events always comes in order. diff --git a/docs/addons/jira-commenter.md b/docs/addons/jira-commenter.md deleted file mode 100644 index 6641a1ad10..0000000000 --- a/docs/addons/jira-commenter.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -id: jira-commenter -title: Jira commenter ---- - -> This feature was introduced in _Unleash v3.11.0_. - -Enables commenting on issues from unleash when toggles are updated/revived/archived/created. - -## Configuration - -#### Events - -You can choose to trigger updates for the following events (we might add more event types in the future): - -- feature-created -- feature-updated -- feature-archived -- feature-revived - -#### Parameters - -Unleash Jira Commenter addons takes the following parameters. - -- **JIRA base url** - e.g. https://mycompany.atlassian.net. -- **JIRA username** - the username of the user the plugin should comment as. -- **JIRA api token** - an api token for the user the plugin should comment as. - -An api token can be configured using https://id.atlassian.com/manage-profile/security/api-tokens when logged in as the users you'd like the plugin to make the comment as. - -#### Tags - -- The Jira commenter addon also defined the Tag Type "jira". You may use this tag to tell the plugin which issue Unleash should post updates to. -- The value of the tag should be in normal JIRA issue format (PROJECTKEY-ISSUENUMBER). - -![Jira tags](../assets/jira_addon_tags.png) - -In the picture you can see we have defined one Jira tag for the `demo` toggle. In this example, the issue **UC-1** would receive updates then something happens to this toggle diff --git a/docs/addons/slack.md b/docs/addons/slack.md index ac59028e6f..613114cff4 100644 --- a/docs/addons/slack.md +++ b/docs/addons/slack.md @@ -37,4 +37,4 @@ The Slack addon also defined the Tag type "slack". You may use this tag to overr ![Slack Tags](../assets/slack_addon_tags.png) -In the picture you can see we have defined two slack tags for the "Demo" toggle. In this example Unleash will post updates to the **#notifications** and **#random** channel. +In the picture you can see we have defined two slack tags for the "new-payment-system" toggle. In this example Unleash will post updates to the **#notifications** and **#random** channel. diff --git a/docs/advanced/api_access.md b/docs/advanced/api_access.md index d6100885f6..295ce7f8d6 100644 --- a/docs/advanced/api_access.md +++ b/docs/advanced/api_access.md @@ -3,26 +3,21 @@ id: api_access title: API Access --- -> **Enterprise** -> This guide is only relevant if you are using Unleash-hosted. - It is possible to integrate directly with the Admin API. In this guide we will explain all the steps to set it up. ## Step 1: Create API token -You will need access tokens with admin privileges to be allowed to connect to the Admin Unleash-API. You can create these tokens in the “Instance Admin” as part of the “API secrets” section. +Please refer to [Create token](../user_guide/api-token) on how to create an API token. You'll need a token with `Admin` level access for this to work. Please note that it may take up to 60 seconds for the new key to propagate to all Unleash-hosted instances. > If you need an API token to use in a client SDK you should create a "client token" as these have less access. -![Create token](../assets/create_token.png) - ## Step 2: Use Admin API -Now that you have an access token with admin privileges we can use that to perform changes in our Unleash-hosted instance. +Now that you have an access token with admin privileges we can use that to perform changes in our Unleash-hosted instance. -In the example below we will use the [Unleash Admin API](../api/admin/features) to enable the “Demo” feature toggle sing curl. +In the example below we will use the [Unleash Admin API](../api/admin/features) to enable the “Demo” feature toggle sing curl. ```sh curl -X POST -H "Authorization: admintoken" @@ -30,7 +25,7 @@ https://app.unleash-hosted.com/demo/api/admin/features/Demo/toggle/on ``` **Great success!** We have now enabled the feature toggle. We can also verify that it was actually changed by the API user by navigating to the history (audit log) for this feature toggle. - + ![Create token](../assets/api_access_history.png) ## API overview diff --git a/docs/api/admin/addons.md b/docs/api/admin/addons.md index 1d3ac5cc06..978cca4197 100644 --- a/docs/api/admin/addons.md +++ b/docs/api/admin/addons.md @@ -3,7 +3,7 @@ id: addons title: /api/admin/addons --- -> In order to access the admin API endpoints you need to identify yourself. If you are using the `insecure` authentication method, you may use [basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) to identify yourself. +> In order to access the admin API endpoints you need to identify yourself. Unless you're using the `none` authentication method, you'll need to [create an ADMIN token](../token.md) and add an Authorization header using the token. ### List addons and providers diff --git a/docs/api/admin/context.md b/docs/api/admin/context.md index 52e7370fa2..3e684fb936 100644 --- a/docs/api/admin/context.md +++ b/docs/api/admin/context.md @@ -3,7 +3,7 @@ id: context title: /api/admin/context --- -> The context feature is only available as part of Unleash Enterprise. In order to access the API programmatically you need to make sure you obtain a API token with admin permissions. +> The context feature is only available as part of Unleash Enterprise. In order to access the API programmatically you need to make sure you [obtain a API token](../token.md) with admin permissions. ### List context fields defined in Unleash diff --git a/docs/api/admin/events-api.md b/docs/api/admin/events-api.md index a18dc23fea..9ecf714365 100644 --- a/docs/api/admin/events-api.md +++ b/docs/api/admin/events-api.md @@ -3,7 +3,7 @@ id: events title: /api/admin/events --- -> In order to access the admin api endpoints you need to identify yourself. If you are using the `unsecure` authentication method, you may use [basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) to identify yourself. +> In order to access the admin API endpoints you need to identify yourself. Unless you're using the `none` authentication method, you'll need to [create an ADMIN token](../token.md) and add an Authorization header using the token. # Events API diff --git a/docs/api/admin/feature-toggles-api.md b/docs/api/admin/feature-toggles-api.md index c3a314831f..fb808cee93 100644 --- a/docs/api/admin/feature-toggles-api.md +++ b/docs/api/admin/feature-toggles-api.md @@ -3,7 +3,7 @@ id: features title: /api/admin/features --- -> In order to access the admin api endpoints you need to identify yourself. If you are using the `unsecure` authentication method, you may use [basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) to identify yourself. +>> In order to access the admin API endpoints you need to identify yourself. Unless you're using the `none` authentication method, you'll need to [create an ADMIN token](../token.md) and add an Authorization header using the token. ### Fetching Feature Toggles diff --git a/docs/api/admin/feature-types-api.md b/docs/api/admin/feature-types-api.md index c4b61bc36e..8b6c2798fe 100644 --- a/docs/api/admin/feature-types-api.md +++ b/docs/api/admin/feature-types-api.md @@ -3,7 +3,7 @@ id: feature-types title: /api/admin/feature-types --- -> In order to access the admin api endpoints you need to identify yourself. If you are using the `unsecure` authentication method, you may use [basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) to identify yourself. +> In order to access the admin API endpoints you need to identify yourself. Unless you're using the `none` authentication method, you'll need to [create an ADMIN token](../token.md) and add an Authorization header using the token. # Feature Types API diff --git a/docs/api/admin/metrics-api.md b/docs/api/admin/metrics-api.md index dc43f7e1a7..ad84519f9d 100644 --- a/docs/api/admin/metrics-api.md +++ b/docs/api/admin/metrics-api.md @@ -3,7 +3,7 @@ id: metrics title: /api/admin/metrics --- -> In order to access the admin api endpoints you need to identify yourself. If you are using the `unsecure` authentication method, you may use [basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) to identify yourself. +> In order to access the admin API endpoints you need to identify yourself. Unless you're using the `none` authentication method, you'll need to [create an ADMIN token](../token.md) and add an Authorization header using the token. # This document describes the metrics endpoint for admin ui diff --git a/docs/api/admin/project.md b/docs/api/admin/project.md index b69ccce4f7..b7c6578474 100644 --- a/docs/api/admin/project.md +++ b/docs/api/admin/project.md @@ -3,7 +3,7 @@ id: projects title: /api/admin/projects --- -> The projects feature is only available as part of Unleash Enterprise. In order to access the API programmatically you need to make sure you obtain a API token with admin permissions. +> The context feature is only available as part of Unleash Enterprise. In order to access the API programmatically you need to make sure you [obtain an API token](../token.md) with admin permissions. ### List projects in Unleash diff --git a/docs/api/admin/state-api.md b/docs/api/admin/state-api.md index b8d77985dc..6612ad36c9 100644 --- a/docs/api/admin/state-api.md +++ b/docs/api/admin/state-api.md @@ -3,7 +3,7 @@ id: state title: /api/admin/state --- -> In order to access the admin api endpoints you need to identify yourself. If you are using the `unsecure` authentication method, you may use [basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) to identify yourself. +> In order to access the admin API endpoints you need to identify yourself. Unless you're using the `none` authentication method, you'll need to [create an ADMIN token](../token.md) and add an Authorization header using the token. ### Export Feature Toggles & Strategies diff --git a/docs/api/admin/strategies-api.md b/docs/api/admin/strategies-api.md index 742ff86e0e..96ea4335f6 100644 --- a/docs/api/admin/strategies-api.md +++ b/docs/api/admin/strategies-api.md @@ -3,7 +3,7 @@ id: strategies title: /api/admin/strategies --- -> In order to access the admin api endpoints you need to identify yourself. If you are using the `unsecure` authentication method, you may use [basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) to identify yourself. +> In order to access the admin API endpoints you need to identify yourself. Unless you're using the `none` authentication method, you'll need to [create an ADMIN token](../token.md) and add an Authorization header using the token. ### Fetch Strategies diff --git a/docs/api/admin/tags-api.md b/docs/api/admin/tags-api.md index a9dc2e00e2..7f05004b3d 100644 --- a/docs/api/admin/tags-api.md +++ b/docs/api/admin/tags-api.md @@ -3,7 +3,7 @@ id: tags title: /api/admin/tags --- -> In order to access the admin API endpoints you need to identify yourself. If you are using the `insecure` authentication method, you may use [basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) to identify yourself. +> In order to access the admin API endpoints you need to identify yourself. Unless you're using the `none` authentication method, you'll need to [create an ADMIN token](../token.md) and add an Authorization header using the token. ### Create a new tag diff --git a/docs/api/basic-auth.md b/docs/api/basic-auth.md new file mode 100644 index 0000000000..7b9bec5924 --- /dev/null +++ b/docs/api/basic-auth.md @@ -0,0 +1,17 @@ +--- +id: basic-auth +title: Basic Auth +--- + +# Basic auth + +When using the `insecure` authentication method, identifying using basic auth against the API is enough. +Since the `insecure` method doesn't require a password, it is enough to define the username when making HTTP requests. + +### With curl + +Add the `-u myemail@test.com` flag to your curl command. + +### With wget + +Add the `--user=myemail@test.com` flag to your wget command. diff --git a/docs/api/client/feature-toggles-api.md b/docs/api/client/feature-toggles-api.md index f37f0965ef..f4969f6856 100644 --- a/docs/api/client/feature-toggles-api.md +++ b/docs/api/client/feature-toggles-api.md @@ -3,6 +3,9 @@ id: features title: /api/client/features --- +> In order to access the client API endpoints you need to identify yourself. Unless you're using the `none` authentication method, you'll need to [create a CLIENT token](../token.md) and add an Authorization header using the token. + + ### Fetching Feature Toggles `GET: http://unleash.host.com/api/client/features` diff --git a/docs/api/client/metrics-api.md b/docs/api/client/metrics-api.md index ef43012151..1ae4498993 100644 --- a/docs/api/client/metrics-api.md +++ b/docs/api/client/metrics-api.md @@ -3,6 +3,8 @@ id: metrics title: /api/client/metrics --- +> In order to access the client API endpoints you need to identify yourself. Unless you're using the `none` authentication method, you'll need to [create a CLIENT token](../token.md) and add an Authorization header using the token. + ### Send metrics `POST: http://unleash.host.com/api/client/metrics` diff --git a/docs/api/client/register-api.md b/docs/api/client/register-api.md index ece3f1594c..64b0828720 100644 --- a/docs/api/client/register-api.md +++ b/docs/api/client/register-api.md @@ -3,6 +3,8 @@ id: register title: /api/client/register --- +> In order to access the client API endpoints you need to identify yourself. Unless you're using the `none` authentication method, you'll need to [create a CLIENT token](../token.md) and add an Authorization header using the token. + ### Client registration `POST: http://unleash.host.com/api/client/register` diff --git a/docs/api/index.md b/docs/api/index.md index 38ec8f83b4..abed419da9 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -5,7 +5,11 @@ title: API Documentation ## Client API -This describes the API provided to unleash-clients. +This describes the API provided to unleash-clients. + +Since v4.0.0 all operations require an [API token](token.md) with `Client` level access. + +With versions earlier than v4.0.0 and `insecure` authentication no authentication is required. - [Feature Toggles API](client/feature-toggles-api.md) - [Register API](client/register-api.md) @@ -13,7 +17,9 @@ This describes the API provided to unleash-clients. ## Admin API (internal) -The internal API used by the Admin UI (unleash-frontend): +The internal API used by the Admin UI (unleash-frontend). Since v4.0.0 all operations require an [API token](token.md) with `Admin` level access: + +With versions earlier than v4.0.0 and `insecure` authentication Basic Auth (with curl `-u myemail@test.com:`) is enough - [Feature Toggles API](admin/feature-toggles-api.md) - [Strategies API](admin/strategies-api.md) diff --git a/docs/assets/The-Unleash-Proxy-API.png b/docs/assets/The-Unleash-Proxy-API.png new file mode 100644 index 0000000000..348fd4041a Binary files /dev/null and b/docs/assets/The-Unleash-Proxy-API.png differ diff --git a/docs/assets/add_new_api_key.png b/docs/assets/add_new_api_key.png new file mode 100644 index 0000000000..c798a5b8f7 Binary files /dev/null and b/docs/assets/add_new_api_key.png differ diff --git a/docs/assets/admin_side_menu.png b/docs/assets/admin_side_menu.png new file mode 100644 index 0000000000..d27449563d Binary files /dev/null and b/docs/assets/admin_side_menu.png differ diff --git a/docs/assets/admin_tab_menu.png b/docs/assets/admin_tab_menu.png new file mode 100644 index 0000000000..8803357fbf Binary files /dev/null and b/docs/assets/admin_tab_menu.png differ diff --git a/docs/assets/api_key_list.png b/docs/assets/api_key_list.png new file mode 100644 index 0000000000..b62a09e23d Binary files /dev/null and b/docs/assets/api_key_list.png differ diff --git a/docs/assets/archive-toggle-revive.png b/docs/assets/archive-toggle-revive.png index f9cb57ea06..037b572287 100644 Binary files a/docs/assets/archive-toggle-revive.png and b/docs/assets/archive-toggle-revive.png differ diff --git a/docs/assets/archive-toggle.png b/docs/assets/archive-toggle.png index 4955033cd9..04dfc0527b 100644 Binary files a/docs/assets/archive-toggle.png and b/docs/assets/archive-toggle.png differ diff --git a/docs/assets/constraints_legal_values.png b/docs/assets/constraints_legal_values.png index 75a10f5448..ab0b3b7b3b 100644 Binary files a/docs/assets/constraints_legal_values.png and b/docs/assets/constraints_legal_values.png differ diff --git a/docs/assets/context-fields.png b/docs/assets/context-fields.png index 63cae45b1f..864fc24891 100644 Binary files a/docs/assets/context-fields.png and b/docs/assets/context-fields.png differ diff --git a/docs/assets/control_rollout_multiple_strategies.png b/docs/assets/control_rollout_multiple_strategies.png new file mode 100644 index 0000000000..9995e6f0f1 Binary files /dev/null and b/docs/assets/control_rollout_multiple_strategies.png differ diff --git a/docs/assets/control_rollout_standard_strategy.png b/docs/assets/control_rollout_standard_strategy.png new file mode 100644 index 0000000000..69cfdd42c0 Binary files /dev/null and b/docs/assets/control_rollout_standard_strategy.png differ diff --git a/docs/assets/control_rollout_userid_strategy.png b/docs/assets/control_rollout_userid_strategy.png new file mode 100644 index 0000000000..da3b34da33 Binary files /dev/null and b/docs/assets/control_rollout_userid_strategy.png differ diff --git a/docs/assets/create_feature_toggle_button.png b/docs/assets/create_feature_toggle_button.png new file mode 100644 index 0000000000..aa81496862 Binary files /dev/null and b/docs/assets/create_feature_toggle_button.png differ diff --git a/docs/assets/create_feature_toggle_list.png b/docs/assets/create_feature_toggle_list.png new file mode 100644 index 0000000000..eac6ccea72 Binary files /dev/null and b/docs/assets/create_feature_toggle_list.png differ diff --git a/docs/assets/create_feature_toggle_save.png b/docs/assets/create_feature_toggle_save.png new file mode 100644 index 0000000000..ae91fb79bc Binary files /dev/null and b/docs/assets/create_feature_toggle_save.png differ diff --git a/docs/assets/create_feature_toggle_userIds.png b/docs/assets/create_feature_toggle_userIds.png new file mode 100644 index 0000000000..511bea6227 Binary files /dev/null and b/docs/assets/create_feature_toggle_userIds.png differ diff --git a/docs/assets/create_toggle_1.png b/docs/assets/create_toggle_1.png deleted file mode 100644 index 2e39b5096c..0000000000 Binary files a/docs/assets/create_toggle_1.png and /dev/null differ diff --git a/docs/assets/create_toggle_2.png b/docs/assets/create_toggle_2.png deleted file mode 100644 index b492fb86b0..0000000000 Binary files a/docs/assets/create_toggle_2.png and /dev/null differ diff --git a/docs/assets/create_toggle_3.png b/docs/assets/create_toggle_3.png deleted file mode 100644 index 8d7d14a11e..0000000000 Binary files a/docs/assets/create_toggle_3.png and /dev/null differ diff --git a/docs/assets/custom-constraints.png b/docs/assets/custom-constraints.png index edcac6f34a..86c9ad7695 100644 Binary files a/docs/assets/custom-constraints.png and b/docs/assets/custom-constraints.png differ diff --git a/docs/assets/global_audit_log.png b/docs/assets/global_audit_log.png index 312d6303e2..e659740e2f 100644 Binary files a/docs/assets/global_audit_log.png and b/docs/assets/global_audit_log.png differ diff --git a/docs/assets/multiple_activation_strategies.png b/docs/assets/multiple_activation_strategies.png deleted file mode 100644 index 3663b70df3..0000000000 Binary files a/docs/assets/multiple_activation_strategies.png and /dev/null differ diff --git a/docs/assets/new_context_field.png b/docs/assets/new_context_field.png index 392d4a9ae7..a08a050be2 100644 Binary files a/docs/assets/new_context_field.png and b/docs/assets/new_context_field.png differ diff --git a/docs/assets/project_add.png b/docs/assets/project_add.png deleted file mode 100644 index c8514a8e78..0000000000 Binary files a/docs/assets/project_add.png and /dev/null differ diff --git a/docs/assets/project_configure.png b/docs/assets/project_configure.png deleted file mode 100644 index 8c496d2562..0000000000 Binary files a/docs/assets/project_configure.png and /dev/null differ diff --git a/docs/assets/project_delete.png b/docs/assets/project_delete.png deleted file mode 100644 index d2ab3bd785..0000000000 Binary files a/docs/assets/project_delete.png and /dev/null differ diff --git a/docs/assets/project_select.png b/docs/assets/project_select.png new file mode 100644 index 0000000000..0d1ce731d9 Binary files /dev/null and b/docs/assets/project_select.png differ diff --git a/docs/assets/projects_change_project.png b/docs/assets/projects_change_project.png new file mode 100644 index 0000000000..01d67346e2 Binary files /dev/null and b/docs/assets/projects_change_project.png differ diff --git a/docs/assets/projects_delete_button.png b/docs/assets/projects_delete_button.png new file mode 100644 index 0000000000..16c2e11756 Binary files /dev/null and b/docs/assets/projects_delete_button.png differ diff --git a/docs/assets/projects_existing_toggle_dropdown.png b/docs/assets/projects_existing_toggle_dropdown.png new file mode 100644 index 0000000000..342e0e29a1 Binary files /dev/null and b/docs/assets/projects_existing_toggle_dropdown.png differ diff --git a/docs/assets/projects_menu.png b/docs/assets/projects_menu.png new file mode 100644 index 0000000000..9d50167e58 Binary files /dev/null and b/docs/assets/projects_menu.png differ diff --git a/docs/assets/projects_menu_open.png b/docs/assets/projects_menu_open.png new file mode 100644 index 0000000000..ed90e8f7ce Binary files /dev/null and b/docs/assets/projects_menu_open.png differ diff --git a/docs/assets/projects_new_project.png b/docs/assets/projects_new_project.png new file mode 100644 index 0000000000..4f94a1715c Binary files /dev/null and b/docs/assets/projects_new_project.png differ diff --git a/docs/assets/projects_save_new_project.png b/docs/assets/projects_save_new_project.png new file mode 100644 index 0000000000..8a8e50f389 Binary files /dev/null and b/docs/assets/projects_save_new_project.png differ diff --git a/docs/assets/projects_select_dropdown.png b/docs/assets/projects_select_dropdown.png new file mode 100644 index 0000000000..d1ebd661ab Binary files /dev/null and b/docs/assets/projects_select_dropdown.png differ diff --git a/docs/assets/projects_toggle_project_dropdown.png b/docs/assets/projects_toggle_project_dropdown.png new file mode 100644 index 0000000000..b697698caa Binary files /dev/null and b/docs/assets/projects_toggle_project_dropdown.png differ diff --git a/docs/assets/rbac.png b/docs/assets/rbac.png new file mode 100644 index 0000000000..acb396cde8 Binary files /dev/null and b/docs/assets/rbac.png differ diff --git a/docs/assets/slack_addon_tags.png b/docs/assets/slack_addon_tags.png index 101a095b23..f6b314a542 100644 Binary files a/docs/assets/slack_addon_tags.png and b/docs/assets/slack_addon_tags.png differ diff --git a/docs/assets/strategy-constraints.png b/docs/assets/strategy-constraints.png index 654e498162..659c83ee3b 100644 Binary files a/docs/assets/strategy-constraints.png and b/docs/assets/strategy-constraints.png differ diff --git a/docs/assets/strategy_constraints.png b/docs/assets/strategy_constraints.png new file mode 100644 index 0000000000..659c83ee3b Binary files /dev/null and b/docs/assets/strategy_constraints.png differ diff --git a/docs/assets/timestamp_create_strategy.png b/docs/assets/timestamp_create_strategy.png index 67e5332867..4a16c8e166 100644 Binary files a/docs/assets/timestamp_create_strategy.png and b/docs/assets/timestamp_create_strategy.png differ diff --git a/docs/assets/timestamp_use_strategy.png b/docs/assets/timestamp_use_strategy.png index 3e92644ec4..49f42e325a 100644 Binary files a/docs/assets/timestamp_use_strategy.png and b/docs/assets/timestamp_use_strategy.png differ diff --git a/docs/assets/unleash-toggle-history.png b/docs/assets/unleash-toggle-history.png index 8409100b22..4d61479432 100644 Binary files a/docs/assets/unleash-toggle-history.png and b/docs/assets/unleash-toggle-history.png differ diff --git a/docs/assets/userWithId.png b/docs/assets/userWithId.png deleted file mode 100644 index 33e4ef08d3..0000000000 Binary files a/docs/assets/userWithId.png and /dev/null differ diff --git a/docs/assets/user_admin-add-user.png b/docs/assets/user_admin-add-user.png new file mode 100644 index 0000000000..0de4d94305 Binary files /dev/null and b/docs/assets/user_admin-add-user.png differ diff --git a/docs/assets/user_admin_add_user_modal.png b/docs/assets/user_admin_add_user_modal.png new file mode 100644 index 0000000000..cec1fd8e31 Binary files /dev/null and b/docs/assets/user_admin_add_user_modal.png differ diff --git a/docs/assets/variants.png b/docs/assets/variants.png index e77dc9d3bc..5e88465a34 100644 Binary files a/docs/assets/variants.png and b/docs/assets/variants.png differ diff --git a/docs/client-specification.md b/docs/client-specification.md index 4b6535e945..c611d8ec38 100644 --- a/docs/client-specification.md +++ b/docs/client-specification.md @@ -1,5 +1,5 @@ --- -id: client_specification +id: client-specification title: Client Specification --- @@ -66,7 +66,7 @@ A feature toggle is defined as: } ``` -A simple demo of the `isEnabled` function in JavaScript style (most of the implementation will likely be more functional): +A simple demo of the `isEnabled` function in JavaScript style (most of the implementation will likely be more functional): ```javascript function isEnabled(name, unleashContext = {}, defaultValue = false) { @@ -94,7 +94,7 @@ function isEnabled(name, unleashContext = {}, defaultValue = false) { Activation strategies are defined and configured in the unleash-service. It is up to the client to provide the actual implementation of each activation strategy. -Unleash also ships with a few built-in strategies, and expects client SDK's to implement these. Read more about these [activation strategies](activation-strategies.md). For the built-in strategies to work as expected the client should also allow the user to define an [unleash-context](unleash-context.md). The context should be possible to pass in as part of the `isEnabled` call. +Unleash also ships with a few built-in strategies, and expects client SDK's to implement these. Read more about these [activation strategies](activation-strategies.md). For the built-in strategies to work as expected the client should also allow the user to define an [unleash-context](unleash-context.md). The context should be possible to pass in as part of the `isEnabled` call. ### Extension points diff --git a/docs/developer-guide.md b/docs/contributing/developer-guide.md similarity index 89% rename from docs/developer-guide.md rename to docs/contributing/developer-guide.md index eb52d2503c..df26727080 100644 --- a/docs/developer-guide.md +++ b/docs/contributing/developer-guide.md @@ -1,7 +1,13 @@ ---- -id: developer_guide -title: Developer guide ---- +## Introduction + +Before developing on this project you will need two things: + +- PostgreSQL 10.x or newer +- Node.js 14.x or newer + +```sh +npm run start:dev +``` ## PostgreSQL @@ -36,7 +42,9 @@ export TEST_DATABASE_URL=postgres://unleash_user:passord@localhost:5432/unleash_ If you don't want to install PostgreSQL locally, you can spin up an Docker instance. We have created a script to ease this process: `scripts/docker-postgres.sh` -## Commands +## Start the application + +In order to start the application you will need Node.js v14.x or newer installed locally. ``` // Install dependencies diff --git a/docs/database-schema.md b/docs/database-schema.md deleted file mode 100644 index 60b6b2bc6a..0000000000 --- a/docs/database-schema.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -id: database_schema -title: Database Schema ---- - -This document describes our current database schema used in PostgreSQL. We use db-migrate to migrate (create tables, add columns, etc.) the database. - -## Table: _migrations_ - -Used by db-migrate module to keep track of migrations. - -| NAME | TYPE | SIZE | NULLABLE | COLUMN_DEF | -| --- | --- | --- | --- | --- | -| id | serial | 10 | 0 | nextval('migrations_id_seq'::regclass) | -| name | varchar | 255 | 0 | (null) | -| run_on | timestamp | 29 | 0 | (null) | - -## Table: _events_ - -| NAME | TYPE | SIZE | NULLABLE | COLUMN_DEF | -| --- | --- | --- | --- | --- | -| id | serial | 10 | 0 | nextval('events_id_seq'::regclass) | -| created_at | timestamp | 29 | 1 | now() | -| type | varchar | 255 | 0 | (null) | -| created_by | varchar | 255 | 0 | (null) | -| data | json | 2147483647 | 1 | (null) | - -## Table: _strategies_ - -| NAME | TYPE | SIZE | NULLABLE | COLUMN_DEF | -| ------------------- | --------- | ---------- | -------- | ---------- | -| created_at | timestamp | 29 | 1 | now() | -| name | varchar | 255 | 0 | (null) | -| description | text | 2147483647 | 1 | (null) | -| parameters_template | json | 2147483647 | 1 | (null) | - -## Table: _features_ - -| **NAME** | **TYPE** | **SIZE** | **NULLABLE** | **COLUMN_DEF** | **COMMENT** | -| --- | --- | --- | --- | --- | --- | -| created_at | timestamp | 29 | 1 | now() | | -| name | varchar | 255 | 0 | (null) | | -| enabled | int4 | 10 | 1 | 0 | | -| description | text | 2147483647 | 1 | (null) | | -| archived | int4 | 10 | 1 | 0 | | -| strategies | json | 2147483647 | 1 | (null) | | -| type | varchar | 2147483647 | 1 | release | | -| last_seen_at | timestamp | 29 | 1 | (null) | | - -## Table: _client_strategies_ - -| COLUMN_NAME | TYPE_NAME | COLUMN_SIZE | NULLABLE | COLUMN_DEF | -| ----------- | --------- | ----------- | -------- | ---------- | -| app_name | varchar | 255 | 0 | (null) | -| updated_at | timestamp | 29 | 1 | now() | -| strategies | json | 2147483647 | 1 | (null) | - -## Table: _client_instances_ - -| COLUMN_NAME | TYPE_NAME | COLUMN_SIZE | NULLABLE | COLUMN_DEF | -| ----------- | --------- | ----------- | -------- | ---------- | -| app_name | varchar | 255 | 1 | (null) | -| instance_id | varchar | 255 | 1 | (null) | -| client_ip | varchar | 255 | 1 | (null) | -| last_seen | timestamp | 29 | 1 | now() | -| created_at | timestamp | 29 | 1 | now() | - -## Table: _client_metrics_ - -| COLUMN_NAME | TYPE_NAME | COLUMN_SIZE | NULLABLE | COLUMN_DEF | -| --- | --- | --- | --- | --- | -| id | serial | 10 | 0 | nextval('client_metrics_id_seq'::regclass) | -| created_at | timestamp | 29 | 1 | now() | -| metrics | json | 2147483647 | 1 | (null) | - -## Table: _feature_types_ - -| COLUMN_NAME | TYPE_NAME | COLUMN_SIZE | NULLABLE | COLUMN_DEF | -| ------------- | --------- | ----------- | -------- | ---------- | -| id | varchar | 255 | 0 | (null) | -| name | varchar | | 0 | (null) | -| description | varchar | | 1 | (null) | -| lifetime_days | integer | | 1 | (null) | diff --git a/docs/deploy/configuring-unleash-v3.md b/docs/deploy/configuring-unleash-v3.md new file mode 100644 index 0000000000..9e0a8f63eb --- /dev/null +++ b/docs/deploy/configuring-unleash-v3.md @@ -0,0 +1,114 @@ +--- +id: configuring_unleash_v3 +title: Configuring Unleash +--- + +> This is the guide on how to configure **Unleash v3 self-hosted**. If you are using Unleash v4 you should checkout [configuring Unleash](./configuring_unleash) + +In order to customize "anything" in Unleash you need to use [Unleash from Node.js](./getting_started#option-two---from-nodejs): + +```js +const unleash = require('unleash-server'); + +const unleashOptions = { + db: { + user: 'unleash_user', + password: 'passord', + host: 'localhost', + port: 5432, + database: 'unleash', + ssl: false, + pool: { + min: 0, + max: 4, + idleTimeoutMillis: 30000, + }, + }, + enableRequestLogger: true, +}; + +unleash.start(unleashOptions); +``` + +**Available Unleash options include:** + +- **db** - The database configuration object taking the following properties: + - _user_ - the database username (`DATABASE_USERNAME`) + - _password_ - the database password (`DATABASE_PASSWORD`) + - _host_ - the database hostname (`DATABASE_HOST`) + - _port_ - the database port defaults to 5432 (`DATABASE_PORT`) + - _database_ - the database name to be used (`DATABASE_NAME`) + - _ssl_ - an object describing ssl options, see https://node-postgres.com/features/ssl (`DATABASE_SSL`, as a stringified json object) + - _version_ - the postgres database version. Used to connect a non-standard database. Defaults to `undefined`, which let the underlying adapter to detect the version automatically. (`DATABASE_VERSION`) + - _pool_ - an object describing pool options, see https://knexjs.org/#Installation-pooling. We support the following three fields: + - _min_ - minimum connections in connections pool (defaults to 0) (`DATABASE_POOL_MIN`) + - _max_ - maximum connections in connections pool (defaults to 4) (`DATABASE_POOL_MAX`) + - _idleTimeoutMillis_ - time in milliseconds a connection must be idle before being marked as a candidate for eviction (defaults to 30000) (`DATABASE_POOL_IDLE_TIMEOUT_MS`) +- **databaseUrl** - (_deprecated_) the postgres database url to connect to. Only used if _db_ object is not specified, and overrides the _db_ object and any environment variables that change parts of it (like `DATABASE_SSL`). Should include username/password. This value may also be set via the `DATABASE_URL` environment variable. Alternatively, if you would like to read the database url from a file, you may set the `DATABASE_URL_FILE` environment variable with the full file path. The contents of the file must be the database url exactly. +- **databaseSchema** - the postgres database schema to use. Defaults to 'public'. (`DATABASE_SCHEMA`) +- **port** - which port the unleash-server should bind to. If port is omitted or is 0, the operating system will assign an arbitrary unused port. Will be ignored if pipe is specified. This value may also be set via the `HTTP_PORT` environment variable +- **host** - which host the unleash-server should bind to. If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise. This value may also be set via the `HTTP_HOST` environment variable +- **pipe** - parameter to identify IPC endpoints. See https://nodejs.org/api/net.html#net_identifying_paths_for_ipc_connections for more details +- **enableLegacyRoutes** (boolean) - allows you to turn on/off support for legacy routes to support older clients. Disabled by default. Will be removed in 4.x. +- **serverMetrics** (boolean) - use this option to turn on/off prometheus metrics. +- **preHook** (function) - this is a hook if you need to provide any middlewares to express before `unleash` adds any. Express app instance is injected as first argument. +- **preRouterHook** (function) - use this to register custom express middlewares before the `unleash` specific routers are added. This is typically how you would register custom middlewares to handle authentication. +- **adminAuthentication** (string) - use this when implementing custom admin authentication [securing-unleash](./securing-unleash.md). Possible values are: + - `none` - will disable authentication altogether + - `unsecure` - (default) will use simple cookie based authentication. UI will require the user to specify an email in order to use unleash. + - `custom` - use this when you implement your own custom authentication logic. +- **ui** (object) - Set of UI specific overrides. You may set the following keys: `headerBackground`, `environment`, `slogan`. +- **getLogger** (function) - Used to register a [custom log provider](#how-do-i-configure-the-log-output). +- **eventHook** (`function(event, data)`) - If provided, this function will be invoked whenever a feature is mutated. The possible values for `event` are `'feature-created'`, `'feature-updated'`, `'feature-archived'`, `'feature-revived'`. The `data` argument contains information about the mutation. Its fields are `type` (string) - the event type (same as `event`); `createdBy` (string) - the user who performed the mutation; `data` - the contents of the change. The contents in `data` differs based on the event type; For `'feature-archived'` and `'feature-revived'`, the only field will be `name` - the name of the feature. For `'feature-created'` and `'feature-updated'` the data follows a schema defined in the code [here](https://github.com/Unleash/unleash/blob/master/lib/routes/admin-api/feature-schema.js#L38-L59). See an [api here](/docs/api/admin/events). +- **baseUriPath** (string) - use to register a base path for all routes on the application. For example `/my/unleash/base` (note the starting /). Defaults to `/`. Can also be configured through the environment variable `BASE_URI_PATH`. +- **unleashUrl** (string) - Used to specify the official URL this instance of Unleash can be accessed at for an end user. Can also be configured through the environment variable `UNLEASH_URL`. +- **secureHeaders** (boolean) - use this to enable security headers (HSTS, CSP, etc) when serving Unleash from HTTPS. Can also be configured through the environment variable `SECURE_HEADERS`. +- **checkVersion** - the checkVersion object deciding where to check for latest version + - `url` - The url to check version (Defaults to `https://version.unleash.run`) - Overridable with (`UNLEASH_VERSION_URL`) + - `enable` - Whether version checking is enabled (defaults to true) - Overridable with (`CHECK_VERSION`) (if anything other than `true`, does not check) + +### Disabling Auto-Start + +If you're using Unleash as part of a larger express app, you can disable the automatic server start by calling `server.create`. It takes the same options as `server.start`, but will not begin listening for connections. + +```js +const unleash = require('unleash-server'); +// ... const app = express(); + +unleash + .create({ + databaseUrl: 'postgres://unleash_user:password@localhost:5432/unleash', + port: 4242, + }) + .then(result => { + app.use(result.app); + console.log(`Unleash app generated and attached to parent application`); + }); +``` + +## Securing Unleash + +You can integrate Unleash with your authentication provider (OAuth 2.0). Read more about [securing unleash](./securing-unleash.md). + +## How do I configure the log output? + +By default, `unleash` uses [log4js](https://github.com/nomiddlename/log4js-node) to log important information. It is possible to swap out the logger provider (only when using Unleash programmatically). You do this by providing an implementation of the **getLogger** function as This enables filtering of log levels and easy redirection of output streams. + +```javascript +function getLogger(name) { + // do something with the name + return { + debug: console.log, + info: console.log, + warn: console.log, + error: console.error, + }; +} +``` + +The logger interface with its `debug`, `info`, `warn` and `error` methods expects format string support as seen in `debug` or the JavaScript `console` object. Many commonly used logging implementations cover this API, e.g., bunyan, pino or winston. + +## Database pooling connection timeouts + +- Please be aware of the default values of connection pool about idle session handling. +- If you have a network component which closes idle sessions on tcp layer, please ensure, that the connection pool idleTimeoutMillis setting is lower than the timespan as the network component will close the idle connection. diff --git a/docs/deploy/configuring-unleash.md b/docs/deploy/configuring-unleash.md index d128db1e36..b5c4cbca71 100644 --- a/docs/deploy/configuring-unleash.md +++ b/docs/deploy/configuring-unleash.md @@ -3,7 +3,44 @@ id: configuring_unleash title: Configuring Unleash --- -In order to customize "anything" in Unleash you need to use [Unleash from Node.js](./getting_started#option-two---from-nodejs): +> This is the guide on how to configure **Unleash v4 self-hosted**. If you are still using Unleash v3 you should checkout [configuring Unleash v3](./configuring_unleash_v3) + +# Must configure + +## Database details + +In order for Unleash server to work, you must setup database connection details. + +- If using docker, use environment variables + - `DATABASE_HOST` - the database hostname - defaults to `localhost` + - `DATABASE_PORT` - the port the database is listening on - defaults to `5432` + - `DATABASE_USERNAME` - the user configured for access - defaults to `unleash_user` + - `DATABASE_PASSWORD` - the password for the user - defaults to `passord` + - `DATABASE_NAME` - the name of the database - defaults to `unleash` + - `DATABASE_SSL` - a json object representing SSL configuration or `false` for not using SSL + - `DATABASE_SCHEMA` - Which schema to use - defaults to `public` +- We also support `DATABASE_URL` see [libpq's doc](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING) for full format explanation. In short: `postgres://USER:PASSWORD@HOST:PORT/DATABASE` +- If you're using secret files from kubernetes and would like to load a `DATABASE_URL` format from a file, use `DATABASE_URL_FILE` and point it to a path containing a connection URL. + +# Nice to configure + +### Unleash URL + +- Configured with `UNLEASH_URL` ** Should be set to the public discoverable URL of your instance, so if your instance is accessed by your users at `https://unleash.mysite.com` use that. ** If you're deploying this to a subpath, include the subpath in this. So `https://mysite.com/unleash` will also be correct. +- Used to create + - Reset password URLs + - Welcome link for new users + - Links in events for our Slack, Microsoft Teams and Datadog addons + +### Email server details + +Used to send reset-password mails and welcome mails when onboarding new users.
**NOTE** - If this is not configured, you will not be able to allow your users to reset their own passwords. + +For [more details, see here](./email.md) + +# Further customization + +In order to customize "anything" in Unleash you need to use [Unleash from Node.js](./getting_started#option-two---from-nodejs) or start the [docker image](./getting_started#option-one---use-docker) with environment variables. ```js const unleash = require('unleash-server'); @@ -30,6 +67,7 @@ unleash.start(unleashOptions); **Available Unleash options include:** +- **databaseUrl** - (_deprecated_) the postgres database url to connect to. Only used if _db_ object is not specified, and overrides the _db_ object and any environment variables that change parts of it (like `DATABASE_SSL`). Should include username/password. This value may also be set via the `DATABASE_URL` environment variable. Alternatively, if you would like to read the database url from a file, you may set the `DATABASE_URL_FILE` environment variable with the full file path. The contents of the file must be the database url exactly. - **db** - The database configuration object taking the following properties: - _user_ - the database username (`DATABASE_USERNAME`) - _password_ - the database password (`DATABASE_PASSWORD`) @@ -37,31 +75,36 @@ unleash.start(unleashOptions); - _port_ - the database port defaults to 5432 (`DATABASE_PORT`) - _database_ - the database name to be used (`DATABASE_NAME`) - _ssl_ - an object describing ssl options, see https://node-postgres.com/features/ssl (`DATABASE_SSL`, as a stringified json object) + - _schema_ - the postgres database schema to use. Defaults to 'public'. (`DATABASE_SCHEMA`) - _version_ - the postgres database version. Used to connect a non-standard database. Defaults to `undefined`, which let the underlying adapter to detect the version automatically. (`DATABASE_VERSION`) - _pool_ - an object describing pool options, see https://knexjs.org/#Installation-pooling. We support the following three fields: - _min_ - minimum connections in connections pool (defaults to 0) (`DATABASE_POOL_MIN`) - _max_ - maximum connections in connections pool (defaults to 4) (`DATABASE_POOL_MAX`) - _idleTimeoutMillis_ - time in milliseconds a connection must be idle before being marked as a candidate for eviction (defaults to 30000) (`DATABASE_POOL_IDLE_TIMEOUT_MS`) -- **databaseUrl** - (_deprecated_) the postgres database url to connect to. Only used if _db_ object is not specified, and overrides the _db_ object and any environment variables that change parts of it (like `DATABASE_SSL`). Should include username/password. This value may also be set via the `DATABASE_URL` environment variable. Alternatively, if you would like to read the database url from a file, you may set the `DATABASE_URL_FILE` environment variable with the full file path. The contents of the file must be the database url exactly. -- **databaseSchema** - the postgres database schema to use. Defaults to 'public'. (`DATABASE_SCHEMA`) -- **port** - which port the unleash-server should bind to. If port is omitted or is 0, the operating system will assign an arbitrary unused port. Will be ignored if pipe is specified. This value may also be set via the `HTTP_PORT` environment variable -- **host** - which host the unleash-server should bind to. If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise. This value may also be set via the `HTTP_HOST` environment variable -- **pipe** - parameter to identify IPC endpoints. See https://nodejs.org/api/net.html#net_identifying_paths_for_ipc_connections for more details -- **enableLegacyRoutes** (boolean) - allows you to turn on/off support for legacy routes to support older clients. Disabled by default. Will be removed in 4.x. -- **serverMetrics** (boolean) - use this option to turn on/off prometheus metrics. +- **server** - The server config object taking the following properties + - _port_ - which port the unleash-server should bind to. If port is omitted or is 0, the operating system will assign an arbitrary unused port. Will be ignored if pipe is specified. This value may also be set via the `HTTP_PORT` environment variable + - _host_ - which host the unleash-server should bind to. If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise. This value may also be set via the `HTTP_HOST` environment variable + - _pipe_ - parameter to identify IPC endpoints. See https://nodejs.org/api/net.html#net_identifying_paths_for_ipc_connections for more details + - _serverMetrics_ (boolean) - use this option to turn on/off prometheus metrics. + - _baseUriPath_ (string) - use to register a base path for all routes on the application. For example `/my/unleash/base` (note the starting /). Defaults to `/`. Can also be configured through the environment variable `BASE_URI_PATH`. + - _unleashUrl_ (string) - Used to specify the official URL this instance of Unleash can be accessed at for an end user. Can also be configured through the environment variable `UNLEASH_URL`. - **preHook** (function) - this is a hook if you need to provide any middlewares to express before `unleash` adds any. Express app instance is injected as first argument. -- **preRouterHook** (function) - use this to register custom express middlewares before the `unleash` specific routers are added. This is typically how you would register custom middlewares to handle authentication. -- **adminAuthentication** (string) - use this when implementing custom admin authentication [securing-unleash](./securing-unleash.md). Possible values are: - - `none` - will disable authentication altogether - - `unsecure` - (default) will use simple cookie based authentication. UI will require the user to specify an email in order to use unleash. - - `custom` - use this when you implement your own custom authentication logic. +- **preRouterHook** (function) - use this to register custom express middlewares before the `unleash` specific routers are added. +- **authentication** - (object) - An object for configuring/implementing custom admin authentication + - enableApiToken (boolean) - Should unleash require API tokens for access? Defaults to `true` + - type (string) What kind of authentication to use. Possible values + - `open-source` - + - `custom` - If implementing your own authentication hook, use this + - `none` - Turn off authentication all together + - `demo` - Only requires an email to sign-in (was default in v3) + - customAuthHandler: (function) - custom express middleware handling authentication. Used when type is set to `custom` + - createAdminUser: (boolean) - whether to create an admin user with default password - Defaults to `true` - **ui** (object) - Set of UI specific overrides. You may set the following keys: `headerBackground`, `environment`, `slogan`. - **getLogger** (function) - Used to register a [custom log provider](#how-do-i-configure-the-log-output). +- **logLevel** (`debug` | `info` | `warn` | `error` | `fatal`) - The lowest level to log at, also configurable using environment variable `LOG_LEVEL`. - **eventHook** (`function(event, data)`) - If provided, this function will be invoked whenever a feature is mutated. The possible values for `event` are `'feature-created'`, `'feature-updated'`, `'feature-archived'`, `'feature-revived'`. The `data` argument contains information about the mutation. Its fields are `type` (string) - the event type (same as `event`); `createdBy` (string) - the user who performed the mutation; `data` - the contents of the change. The contents in `data` differs based on the event type; For `'feature-archived'` and `'feature-revived'`, the only field will be `name` - the name of the feature. For `'feature-created'` and `'feature-updated'` the data follows a schema defined in the code [here](https://github.com/Unleash/unleash/blob/master/lib/routes/admin-api/feature-schema.js#L38-L59). See an [api here](/docs/api/admin/events). -- **baseUriPath** (string) - use to register a base path for all routes on the application. For example `/my/unleash/base` (note the starting /). Defaults to `/`. Can also be configured through the environment variable `BASE_URI_PATH`. -- **unleashUrl** (string) - Used to specify the official URL this instance of Unleash can be accessed at for an end user. Can also be configured through the environment variable `UNLEASH_URL`. - **secureHeaders** (boolean) - use this to enable security headers (HSTS, CSP, etc) when serving Unleash from HTTPS. Can also be configured through the environment variable `SECURE_HEADERS`. -- **checkVersion** - the checkVersion object deciding where to check for latest version +- **versionCheck** - the object deciding where to check for latest version - `url` - The url to check version (Defaults to `https://version.unleash.run`) - Overridable with (`UNLEASH_VERSION_URL`) - `enable` - Whether version checking is enabled (defaults to true) - Overridable with (`CHECK_VERSION`) (if anything other than `true`, does not check) - **email** - the email object configuring an SMTP server for sending welcome mails and password reset mails @@ -69,9 +112,8 @@ unleash.start(unleashOptions); - `port` - Which port the SMTP server is running on. Defaults to 465 (Secure SMTP) - `secure` (boolean) - Whether to use SMTPS or not. - `sender` - Which email should be set as sender of mails being sent from Unleash? - - **auth** - For now a user/pass object containing auth details for your SMTP server - - `user` - Username for your SMTP server - - `pass` - Password for your SMTP server + - `smtpuser` - Username for your SMTP server + - `smtppass` - Password for your SMTP server ### Disabling Auto-Start diff --git a/docs/deploy/email.md b/docs/deploy/email.md new file mode 100644 index 0000000000..3c5354fcbd --- /dev/null +++ b/docs/deploy/email.md @@ -0,0 +1,49 @@ +--- +id: email +title: Email +--- + +# Email service + +New since v4.0.0 is an email service allowing us to send reset password and welcome mails to new users. +In order for this to work you'll need to tell unleash what SMTP service you'd like to send mails from. + +If the service is not configured you'll see a log line every time you add a new user saying +```bash +[2021-05-07T12:59:04.572] [WARN] routes/user-controller.ts - email was not sent to the user because email configuration is lacking +``` + +## Configuring + +Depending on your deploy case there are different ways of configuring this service. Full documentation of all configuration possibilities is available [here](./configuring-unleash.md) + +### Docker + +With docker, we configure the mail service via environment variables. + +You'll want to at least include EMAIL_HOST, EMAIL_USER, EMAIL_PASSWORD and EMAIL_SENDER + +Environment variables: +* EMAIL_HOST - Your SMTP server address +* EMAIL_PORT - Your SMTP server port - defaults to 567 +* EMAIL_SECURE - whether to use SMTPS - set to `false` or `true` - defaults to false, +* EMAIL_USER - the username to authenticate against your SMTP server +* EMAIL_PASSWORD - the password for your SMTP user +* EMAIL_SENDER - which address should reset-password mails and welcome mails be sent from - defaults to `noreply@unleash-hosted.com` which is probably not what you want. + +### Node + +With node, we can configure this when calling Unleash's start method. + +```js +const unleash = require('unleash-server'); + +unleash.start({ + email: { + host: 'myhost', + smtpuser: 'username', + smtppass: 'password', + sender: 'noreply@mycompany.com' + } +}); +``` diff --git a/docs/deploy/getting-started.md b/docs/deploy/getting-started.md index ace15330c5..f552311b48 100644 --- a/docs/deploy/getting-started.md +++ b/docs/deploy/getting-started.md @@ -3,19 +3,21 @@ id: getting_started title: Getting Started --- -> This section only applies if you plan to self-host Unleash. If you are looking for our hosted solution you should head over to [Unleash-hosted.com](https://www.unleash-hosted.com) +> This section only applies if you plan to self-host Unleash. If you are looking for our hosted solution you should head over to [www.getunleash.io](https://www.getunleash.io/plans) ## Requirements You will need: -- [Node.js](https://nodejs.org/en/download/) (version 12 or later) +- [Node.js](https://nodejs.org/en/download/) (version 14 or later) - [PostgreSQL](https://www.postgresql.org/download/) (version 10 or later) - [Create an unleash user and database](/docs/developer_guide). ## Start Unleash server -Whichever option you choose to start Unleash, you must specify a database URI (it can be set in the environment variable DATABASE_URL). +Whichever option you choose to start Unleash, you must specify a database URI (it can be set in the environment variable DATABASE_URL). If your database server is not set up to support SSL you'll also need to set the environment variable `DATABASE_SSL` to `false` + +--- Once the server has started, you will see the message: @@ -23,6 +25,13 @@ Once the server has started, you will see the message: Unleash started on http://localhost:4242 ``` +--- + +**Unleash v4:** The first time Unleash starts it will create a default user which you can use to sign-in to you Unleash instance and add more users with: + +- username: `admin` +- password: `unleash4all` + ### Option one - use Docker **Useful links:** @@ -47,6 +56,7 @@ docker run -e POSTGRES_PASSWORD=some_password \ docker run -p 4242:4242 \ -e DATABASE_HOST=postgres -e DATABASE_NAME=unleash \ -e DATABASE_USERNAME=unleash_user -e DATABASE_PASSWORD=some_password \ + -e DATABASE_SSL=false \ --network unleash unleashorg/unleash-server ``` @@ -73,8 +83,17 @@ docker run -p 4242:4242 \ unleash .start({ - databaseUrl: 'postgres://unleash_user:password@localhost:5432/unleash', - port: 4242, + db: { + ssl: false, + host: 'localhost', + port: 5432, + database: 'unleash', + user: 'unleash_user', + password: 'passord', + }, + server: { + port: 4242, + }, }) .then(unleash => { console.log( @@ -88,21 +107,16 @@ docker run -p 4242:4242 \ node server.js ``` -### Option three - from a terminal/bash shell +## Create an api token for your client -_(deprecated)_ - -```sh -npm install unleash-server -g -unleash -d postgres://unleash_user:password@localhost:5432/unleash -p 4242 -``` +- [API Token creation](../user_guide/api-token) ## Test your server and create a sample API call -Once the Unleash server has started, go to [localhost:4242](http://localhost:4242) in your browser. If you see a list of example feature toggles, try modifying one of them with [curl](https://curl.se/) from a terminal/bash shell: +Once the Unleash server has started, go to [localhost:4242](http://localhost:4242) in your browser. If you see an empty list of feature toggles, try creating one with [curl](https://curl.se/) from a terminal/bash shell: ``` -curl --location --request PUT 'http://localhost:4242/api/admin/features/Feature.A' --header 'Content-Type: application/json' --data-raw '{\ +curl --location -H "Authorization: " --request POST 'http://localhost:4242/api/admin/features' --header 'Content-Type: application/json' --data-raw '{\ "name": "Feature.A",\ "description": "Dolor sit amet.",\ "type": "release",\ diff --git a/docs/deploy/google-auth-hook-v3.md b/docs/deploy/google-auth-hook-v3.md new file mode 100644 index 0000000000..0eb94c13f2 --- /dev/null +++ b/docs/deploy/google-auth-hook-v3.md @@ -0,0 +1,255 @@ +--- +id: google_auth_v3 +title: Google Auth Hook +--- + +> You can also find the complete [source code for this guide](https://github.com/Unleash/unleash-examples/tree/main/v3/securing-google-auth) in the unleash-examples project. + +This part of the tutorial shows how to create a sign-in flow for users and integrate with Unleash server project. The implementation assumes that I am working in `localhost` with `4242` port. + +This is a simple `index.js` server file. + +```javascript +const unleash = require('unleash-server'); + +unleash.start(options).then(unleash => { + console.log(`Unleash started on http://localhost:${unleash.app.get('port')}`); +}); +``` + +### Creating a web application client ID + +1. Go to the credentials section in the [Google Cloud Platform Console](https://console.cloud.google.com/apis/credentials?_ga=2.77615956.-1991581217.1542834301). + +2. Click **OAuth consent screen**. Type a product name. Fill in any relevant optional fields. Click **Save**. + +3. Click **Create credentials > OAuth client ID**. + +4. Under **Application type**, select **Web Application**. + +5. Type the **Name**. + +6. Under **Authorized redirect URIs** enter the following URLs, one at a time. + +``` +http://localhost:4242/api/auth/callback +``` + +7. Click **Create**. + +8. Copy the **CLIENT ID** and **CLIENT SECRET** and save them for later use. + +### Add dependencies + +Add two dependencies [`@passport-next/passport`](https://www.npmjs.com/package/@passport-next/passport) and [`@passport-next/passport-google-oauth2`](https://www.npmjs.com/package/@passport-next/passport-google-oauth2) inside `index.js` file + +```js +const unleash = require('unleash-server'); +const passport = require('@passport-next/passport'); +const GoogleOAuth2Strategy = require('@passport-next/passport-google-oauth2') + .Strategy; +``` + +### Configure the Google strategy for use by Passport.js + +OAuth 2-based strategies require a `verify` function which receives the credential (`accessToken`) for accessing the Google API on the user's behalf, along with the user's profile. The function must invoke `cb` with a user object, which will be set at `req.user` in route handlers after authentication. + +```js +const GOOGLE_CLIENT_ID = '...'; +const GOOGLE_CLIENT_SECRET = '...'; +const GOOGLE_CALLBACK_URL = 'http://localhost:4242/api/auth/callback'; + +passport.use( + new GoogleOAuth2Strategy( + { + clientID: GOOGLE_CLIENT_ID, + clientSecret: GOOGLE_CLIENT_SECRET, + callbackURL: GOOGLE_CALLBACK_URL, + }, + function(accessToken, refreshToken, profile, cb) { + // Extract the minimal profile information we need from the profile object + // and connect with Unleash to get name and email. + cb( + null, + new unleash.User({ + name: profile.displayName, + email: profile.emails[0].value, + }), + ); + }, + ), +); +``` + +Add `googleAdminAuth()` function and other options + +```js +function googleAdminAuth(app) {} + +let options = { + adminAuthentication: 'custom', + preRouterHook: googleAdminAuth, +}; + +unleash.start(options).then(instance => { + console.log( + `Unleash started on http://localhost:${instance.app.get('port')}`, + ); +}); +``` + +### In `googleAdminAuth` function + +Configure `passport` package. + +```js +function googleAdminAuth(app) { + app.use(passport.initialize()); + app.use(passport.session()); + passport.serializeUser((user, done) => done(null, user)); + passport.deserializeUser((user, done) => done(null, user)); + // ... +} +``` + +Implement a preRouter hook for `/api/admin/login`. It's necessary for login with Google. + +```js +function googleAdminAuth(app) { + // ... + app.get( + '/api/admin/login', + passport.authenticate('google', { scope: ['email'] }), + ); + // ... +} +``` + +Implement a preRouter hook for `/api/auth/callback`. It's a callback when the login is executed. + +```js +function googleAdminAuth(app) { + // ... + app.get( + '/api/auth/callback', + passport.authenticate('google', { + failureRedirect: '/api/admin/error-login', + }), + (req, res) => { + // Successful authentication, redirect to your app. + res.redirect('/'); + }, + ); + // ... +} +``` + +Implement a preRouter hook for `/api/admin`. + +```js +function googleAdminAuth(app) { + // ... + app.use('/api/admin/', (req, res, next) => { + if (req.user) { + next(); + } else { + // Instruct unleash-frontend to pop-up auth dialog + return res + .status('401') + .json( + new unleash.AuthenticationRequired({ + path: '/api/admin/login', + type: 'custom', + message: `You have to identify yourself in order to use Unleash. Click the button and follow the instructions.`, + }), + ) + .end(); + } + }); + // ... +} +``` + +### The complete code + +The `index.js` server file. + +```js +'use strict'; + +const unleash = require('unleash-server'); +const passport = require('@passport-next/passport'); +const GoogleOAuth2Strategy = require('@passport-next/passport-google-oauth2'); + +const GOOGLE_CLIENT_ID = '...'; +const GOOGLE_CLIENT_SECRET = '...'; +const GOOGLE_CALLBACK_URL = 'http://localhost:4242/api/auth/callback'; + +passport.use( + new GoogleOAuth2Strategy( + { + clientID: GOOGLE_CLIENT_ID, + clientSecret: GOOGLE_CLIENT_SECRET, + callbackURL: GOOGLE_CALLBACK_URL, + }, + (accessToken, refreshToken, profile, cb) => { + cb( + null, + new unleash.User({ + name: profile.displayName, + email: profile.emails[0].value, + }), + ); + }, + ), +); + +function googleAdminAuth(app) { + app.use(passport.initialize()); + app.use(passport.session()); + passport.serializeUser((user, done) => done(null, user)); + passport.deserializeUser((user, done) => done(null, user)); + + app.get( + '/api/admin/login', + passport.authenticate('google', { scope: ['email'] }), + ); + app.get( + '/api/auth/callback', + passport.authenticate('google', { + failureRedirect: '/api/admin/error-login', + }), + (req, res) => { + res.redirect('/'); + }, + ); + + app.use('/api/admin/', (req, res, next) => { + if (req.user) { + next(); + } else { + return res + .status('401') + .json( + new unleash.AuthenticationRequired({ + path: '/api/admin/login', + type: 'custom', + message: `You have to identify yourself in order to use Unleash. Click the button and follow the instructions.`, + }), + ) + .end(); + } + }); +} + +const options = { + adminAuthentication: 'custom', + preRouterHook: googleAdminAuth, +}; + +unleash.start(options).then(instance => { + console.log( + `Unleash started on http://localhost:${instance.app.get('port')}`, + ); +}); +``` diff --git a/docs/deploy/google-auth-hook.md b/docs/deploy/google-auth-hook.md index 7a98da6071..4432400eb1 100644 --- a/docs/deploy/google-auth-hook.md +++ b/docs/deploy/google-auth-hook.md @@ -3,8 +3,12 @@ id: google_auth title: Google Auth Hook --- +> You can also find the complete [source code for this guide](https://github.com/Unleash/unleash-examples/tree/main/v4/securing-google-auth) in the unleash-examples project. + This part of the tutorial shows how to create a sign-in flow for users and integrate with Unleash server project. The implementation assumes that I am working in `localhost` with `4242` port. +**If you are still using Unleash v3 you need to follow the [google-auth-hook-v3](./google-auth-hook-v3)** + This is a simple `index.js` server file. ```javascript @@ -48,45 +52,19 @@ const GoogleOAuth2Strategy = require('@passport-next/passport-google-oauth2') .Strategy; ``` -### Configure the Google strategy for use by Passport.js - -OAuth 2-based strategies require a `verify` function which receives the credential (`accessToken`) for accessing the Google API on the user's behalf, along with the user's profile. The function must invoke `cb` with a user object, which will be set at `req.user` in route handlers after authentication. +Add `googleAdminAuth()` function and other options. Make sure to also accept the services argument to get access to the `userService`. ```js -const GOOGLE_CLIENT_ID = '...'; -const GOOGLE_CLIENT_SECRET = '...'; -const GOOGLE_CALLBACK_URL = 'http://localhost:4242/api/auth/callback'; - -passport.use( - new GoogleOAuth2Strategy( - { - clientID: GOOGLE_CLIENT_ID, - clientSecret: GOOGLE_CLIENT_SECRET, - callbackURL: GOOGLE_CALLBACK_URL, - }, - function(accessToken, refreshToken, profile, cb) { - // Extract the minimal profile information we need from the profile object - // and connect with Unleash to get name and email. - cb( - null, - new unleash.User({ - name: profile.displayName, - email: profile.emails[0].value, - }), - ); - }, - ), -); -``` - -Add `googleAdminAuth()` function and other options - -```js -function googleAdminAuth(app) {} +function googleAdminAuth(app, config, services) { + const { baseUriPath } = config.server; + const { userService } = services; +} let options = { - adminAuthentication: 'custom', - preRouterHook: googleAdminAuth, + authentication: { + type: 'custom', + customAuthHandler: googleAdminAuth, + }, }; unleash.start(options).then(instance => { @@ -96,12 +74,45 @@ unleash.start(options).then(instance => { }); ``` +### In `googleAdminAuth` function: Configure the Google strategy for use by Passport.js + +OAuth 2-based strategies require a `verify` function which receives the credential (`accessToken`) for accessing the Google API on the user's behalf, along with the user's profile. The function must invoke `cb` with a user object, which will be set at `req.user` in route handlers after authentication. + +```js +const GOOGLE_CLIENT_ID = '...'; +const GOOGLE_CLIENT_SECRET = '...'; +const GOOGLE_CALLBACK_URL = 'http://localhost:4242/api/auth/callback'; + +function googleAdminAuth(app, config, services) { + const { baseUriPath } = config.server; + const { userService } = services; + + passport.use( + new GoogleOAuth2Strategy( + { + clientID: GOOGLE_CLIENT_ID, + clientSecret: GOOGLE_CLIENT_SECRET, + callbackURL: GOOGLE_CALLBACK_URL, + }, + async function(accessToken, refreshToken, profile, cb) { + // Extract the minimal profile information we need from the profile object + // and connect with Unleash + const email = profile.emails[0].value; + const user = await userService.loginUserWithoutPassword(email, true); + cb(null, user); + }, + ), + ); +} +``` + ### In `googleAdminAuth` function Configure `passport` package. ```js -function googleAdminAuth(app) { +function googleAdminAuth(app, config, services) { + // ... app.use(passport.initialize()); app.use(passport.session()); passport.serializeUser((user, done) => done(null, user)); @@ -113,7 +124,7 @@ function googleAdminAuth(app) { Implement a preRouter hook for `/api/admin/login`. It's necessary for login with Google. ```js -function googleAdminAuth(app) { +function googleAdminAuth(app, config, services) { // ... app.get( '/api/admin/login', @@ -126,7 +137,7 @@ function googleAdminAuth(app) { Implement a preRouter hook for `/api/auth/callback`. It's a callback when the login is executed. ```js -function googleAdminAuth(app) { +function googleAdminAuth(app, config, services) { // ... app.get( '/api/auth/callback', @@ -145,7 +156,7 @@ function googleAdminAuth(app) { Implement a preRouter hook for `/api/admin`. ```js -function googleAdminAuth(app) { +function googleAdminAuth(app, config, services) { // ... app.use('/api/admin/', (req, res, next) => { if (req.user) { @@ -170,6 +181,8 @@ function googleAdminAuth(app) { ### The complete code +> You can also find the complete [source code for this guide](https://github.com/Unleash/unleash-examples/tree/main/v4/securing-google-auth) in the unleash-examples project. + The `index.js` server file. ```js @@ -183,26 +196,25 @@ const GOOGLE_CLIENT_ID = '...'; const GOOGLE_CLIENT_SECRET = '...'; const GOOGLE_CALLBACK_URL = 'http://localhost:4242/api/auth/callback'; -passport.use( - new GoogleOAuth2Strategy( - { - clientID: GOOGLE_CLIENT_ID, - clientSecret: GOOGLE_CLIENT_SECRET, - callbackURL: GOOGLE_CALLBACK_URL, - }, - (accessToken, refreshToken, profile, cb) => { - cb( - null, - new unleash.User({ - name: profile.displayName, - email: profile.emails[0].value, - }), - ); - }, - ), -); +function googleAdminAuth(app, config, services) { + const { baseUriPath } = config.server; + const { userService } = services; + + passport.use( + new GoogleOAuth2Strategy( + { + clientID: GOOGLE_CLIENT_ID, + clientSecret: GOOGLE_CLIENT_SECRET, + callbackURL: GOOGLE_CALLBACK_URL, + }, + async (accessToken, refreshToken, profile, cb) => { + const email = profile.emails[0].value; + const user = await userService.loginUserWithoutPassword(email, true); + cb(null, user); + }, + ), + ); -function googleAdminAuth(app) { app.use(passport.initialize()); app.use(passport.session()); passport.serializeUser((user, done) => done(null, user)); @@ -241,13 +253,11 @@ function googleAdminAuth(app) { } const options = { - adminAuthentication: 'custom', - preRouterHook: googleAdminAuth, + authentication: { + type: 'custom', + customAuthHandler: googleAdminAuth, + }, }; -unleash.start(options).then(instance => { - console.log( - `Unleash started on http://localhost:${instance.app.get('port')}`, - ); -}); +unleash.start(options); ``` diff --git a/docs/deploy/migration-guide.md b/docs/deploy/migration-guide.md index f0375d21f0..204e767f99 100644 --- a/docs/deploy/migration-guide.md +++ b/docs/deploy/migration-guide.md @@ -7,11 +7,21 @@ Generally, the intention is that `unleash-server` should always provide support ## Upgrading from v3.x to v4.x -(**Work In Progress**: Will be finalized when we release the official v4 version). +Before you upgrade we strongly recommend that you take a full [database backup](/database_backup), to make sure you can downgrade to version 3. -Before you upgrade we strongly recommends that you take a full [database backup](/database_backup), to make sure you can downgrade to version 3. +You can also read the highlights of **[what's new in v4](../user_guide/v4-whats-new)**. -### 1. Role-based Access Control (RBAC) +### 1. All API calls now requires token. + +If you are upgrading from Unleash Open-Source v3 client SDKs did not need to use an API token in order to connect to Unleash-server. Starting from v4 we have back-ported the API token handling for Enterprise in to the Open-Source version. This means that all client SDKs now need to use a client token in order to connect to Unleash. + +Read more in the [API token documentation](../user_guide/api-token). + +### 2. Configuring Unleash + +We have done a lot of changes to the options you can pass in to Unleash. If you are manually configuring Unleash you should have a look on the updated [configuring Unleash documentation](./configuring_unleash) + +### 3. Role-based Access Control (RBAC) We have implemented RBAC in Unleash v4. This has totally changed the permission system in Unleash. @@ -32,12 +42,20 @@ const user = userService.loginUserWithoutPassword( req.session.user = user; ``` -### 3. Legacy v2 routes removed +[Read more about RBAC](../user_guide/rbac) + +### 4. Legacy v2 routes removed Only relevant if you use the `enableLegacyRoutes` option. In v2 you could query feature toggles on `/api/features`. This was deprecated in v4 and we introduced two different endpoints (`/api/admin/features` and `/api/client/features`) to be able to optimize performance and security. In v3 you could still enable the legacy routes via the `enableLegacyRoutes` option. This was removed in v4. +### 5. Unleash CLI has been removed + +Unleash no longer ships with a binary that allows you to start Unleash directly from the command line. From v4 you need to either use Unleash via docker or programmatically. + +Read more in our [getting started documentation](./getting_started) + ## Upgrading from v2.x to v3.x The notable change introduced in Unleash v3.x is a strict separation of API paths for client requests and admin requests. This makes it easier to implement different authentication mechanisms for the admin UI and all unleash-clients. You can read more about [securing unleash](https://github.com/Unleash/unleash/blob/master/docs/securing-unleash.md). diff --git a/docs/deploy/securing-unleash-v3.md b/docs/deploy/securing-unleash-v3.md new file mode 100644 index 0000000000..e84fb0ab0c --- /dev/null +++ b/docs/deploy/securing-unleash-v3.md @@ -0,0 +1,93 @@ +--- +id: securing-unleash-v3 +title: Securing Unleash v3 +--- + +> This guide is only relevant if you are using Unleash Open-Source. The Enterprise edition does already ship with a secure setup and multiple SSO options. + +The Unleash API is split into two different paths: `/api/client` and `/api/admin`. This makes it easy to have different authentication strategy for the admin interface and the client-api used by the applications integrating with Unleash. + +## General settings + +Unleash uses an encrypted cookie to maintain a user session. This allows users to be logged in across multiple instances of Unleash. To protect this cookie, Unleash will automatically generate a secure token the first time you start Unleash. + +## Securing the Admin API + +To secure the Admin API, you have to tell Unleash that you are using a custom admin authentication and implement your authentication logic as a preHook. + +```javascript +const unleash = require('unleash-server'); +const myCustomAdminAuth = require('./auth-hook'); + +unleash + .start({ + databaseUrl: 'postgres://unleash_user:passord@localhost:5432/unleash', + adminAuthentication: 'custom', + preRouterHook: myCustomAdminAuth, + }) + .then(unleash => { + console.log( + `Unleash started on http://localhost:${unleash.app.get('port')}`, + ); + }); +``` + +Additionally, you can trigger the admin interface to prompt the user to sign in by configuring your middleware to return a `401` status on protected routes. The response body must contain a `message` and a `path` used to redirect the user to the proper login route. + +```json +{ + "message": "You must be logged in to use Unleash", + "path": "/custom/login" +} +``` + +Examples of custom authentication hooks: + +- [google-auth-hook.js](https://github.com/Unleash/unleash/blob/master/examples/google-auth-hook.js) +- [basic-auth-hook.js](https://github.com/Unleash/unleash/blob/master/examples/basic-auth-hook.js) +- [keycloak-auth-hook.js](https://github.com/Unleash/unleash/blob/master/examples/keycloak-auth-hook.js) + +We also have a version of Unleash deployed on Heroku which uses Google OAuth 2.0: https://secure-unleash.herokuapp.com + +## Securing the Client API + +A common way to support client access is to use pre-shared secrets. This can be solved by having clients send a shared key in an HTTP header with every client request to the Unleash API. All official Unleash clients should support this. + +In the [Java client](https://github.com/Unleash/unleash-client-java#custom-http-headers) this would look like this: + +```java +UnleashConfig unleashConfig = UnleashConfig.builder() + .appName("my-app") + .instanceId("my-instance-1") + .unleashAPI(unleashAPI) + .customHttpHeader("Authorization", "12312Random") + .build(); +``` + +On the Unleash server side, you need to implement a preRouter hook which verifies that all calls to `/api/client` include this pre-shared key in the defined header. This could look something like this. + +```javascript +const unleash = require('unleash-server'); +const sharedSecret = '12312Random'; + +unleash + .start({ + databaseUrl: 'postgres://unleash_user:passord@localhost:5432/unleash', + preRouterHook: app => { + app.use('/api/client', (req, res, next) => { + if (req.header('authorization') !== sharedSecret) { + res.sendStatus(401); + } else { + next(); + } + }); + }, + }) + .then(unleash => { + console.log( + `Unleash started on http://localhost:${unleash.app.get('port')}`, + ); + }); +``` + +[client-auth-unleash.js](https://github.com/Unleash/unleash/blob/master/examples/client-auth-unleash.js) diff --git a/docs/deploy/securing-unleash.md b/docs/deploy/securing-unleash.md index daf1800ec8..61e25b4d5b 100644 --- a/docs/deploy/securing-unleash.md +++ b/docs/deploy/securing-unleash.md @@ -3,15 +3,15 @@ id: securing_unleash title: Securing Unleash --- -> This guide is only relevant if you are using Unleash Open-Source. The Enterprise edition does already ship with a secure setup and multiple SSO options. +**If you are still using Unleash v3 you need to follow the [securing-unleash-v3](./securing-unleash-v3)** -The Unleash API is split into two different paths: `/api/client` and `/api/admin`. This makes it easy to have different authentication strategy for the admin interface and the client-api used by the applications integrating with Unleash. +> This guide is only relevant if you are using Unleash Open-Source. The Enterprise edition does already ship with multiple SSO options, such as SAML 2.0, OpenId Connect. -## General settings +Unleash Open-Source v4 comes with username/password authentication out of the box. In addition Unleash v4 also comes with API token support, to make it easy to handle access tokens for Client SDKs and programmatic asses to the Unleash APIs. -Unleash uses an encrypted cookie to maintain a user session. This allows users to be logged in across multiple instances of Unleash. To protect this cookie, Unleash will automatically generate a secure token the first time you start Unleash. +### Implementing Custom Authentication -## Securing the Admin API +If you do not wish to use the built-in To secure the Admin API, you have to tell Unleash that you are using a custom admin authentication and implement your authentication logic as a preHook. @@ -22,8 +22,10 @@ const myCustomAdminAuth = require('./auth-hook'); unleash .start({ databaseUrl: 'postgres://unleash_user:passord@localhost:5432/unleash', - adminAuthentication: 'custom', - preRouterHook: myCustomAdminAuth, + authentication: { + type: 'custom', + customAuthHandler: myCustomAdminAuth, + }, }) .then(unleash => { console.log( @@ -43,51 +45,6 @@ Additionally, you can trigger the admin interface to prompt the user to sign in Examples of custom authentication hooks: -- [google-auth-hook.js](https://github.com/Unleash/unleash/blob/master/examples/google-auth-hook.js) -- [basic-auth-hook.js](https://github.com/Unleash/unleash/blob/master/examples/basic-auth-hook.js) -- [keycloak-auth-hook.js](https://github.com/Unleash/unleash/blob/master/examples/keycloak-auth-hook.js) - -We also have a version of Unleash deployed on Heroku which uses Google OAuth 2.0: https://secure-unleash.herokuapp.com - -## Securing the Client API - -A common way to support client access is to use pre-shared secrets. This can be solved by having clients send a shared key in an HTTP header with every client request to the Unleash API. All official Unleash clients should support this. - -In the [Java client](https://github.com/Unleash/unleash-client-java#custom-http-headers) this would look like this: - -```java -UnleashConfig unleashConfig = UnleashConfig.builder() - .appName("my-app") - .instanceId("my-instance-1") - .unleashAPI(unleashAPI) - .customHttpHeader("Authorization", "12312Random") - .build(); -``` - -On the Unleash server side, you need to implement a preRouter hook which verifies that all calls to `/api/client` include this pre-shared key in the defined header. This could look something like this. - -```javascript -const unleash = require('unleash-server'); -const sharedSecret = '12312Random'; - -unleash - .start({ - databaseUrl: 'postgres://unleash_user:passord@localhost:5432/unleash', - preRouterHook: app => { - app.use('/api/client', (req, res, next) => { - if (req.header('authorization') !== sharedSecret) { - res.sendStatus(401); - } else { - next(); - } - }); - }, - }) - .then(unleash => { - console.log( - `Unleash started on http://localhost:${unleash.app.get('port')}`, - ); - }); -``` - -[client-auth-unleash.js](https://github.com/Unleash/unleash/blob/master/examples/client-auth-unleash.js) +- [securing-google-auth](https://github.com/Unleash/unleash-examples/tree/main/v4/securing-google-auth) +- [securing-basic-auth](https://github.com/Unleash/unleash-examples/tree/main/v4/securing-basic-auth) +- [securing-keycloak-auth](https://github.com/Unleash/unleash-examples/tree/main/v4/securing-keycloak-auth) diff --git a/docs/getting-started.md b/docs/getting-started.md deleted file mode 100644 index 7f85d251db..0000000000 --- a/docs/getting-started.md +++ /dev/null @@ -1,110 +0,0 @@ ---- -id: getting_started -title: Getting Started ---- - -## Requirements - -You will need: - -- [**Node.js**](https://nodejs.org/en/download/) (version 12 or later) -- [**PostgreSQL**](https://www.postgresql.org/download/) (version 10 or later) -- [Create an unleash user and database](./developer-guide.md). - -## Start Unleash server - -Whichever option you choose to start Unleash, you must specify a database URI (it can be set in the environment variable DATABASE_URL). - -Once the server has started, you will see the message: - -```sh -Unleash started on http://localhost:4242 -``` - -### Option one - from a terminal/bash shell - -```sh -npm install unleash-server -g -unleash -d postgres://unleash_user:password@localhost:5432/unleash -p 4242 -``` - -### Option two - from Node.js - -1. Create a new folder/directory on your development computer. -2. From a terminal/bash shell, install the dependencies: - - ```sh - npm init - npm install unleash-server --save - ``` - -3. Create a file called _server.js_, paste the following into it and save. - - ```js - const unleash = require('unleash-server'); - - unleash - .start({ - databaseUrl: 'postgres://unleash_user:password@localhost:5432/unleash', - port: 4242, - }) - .then(unleash => { - console.log( - `Unleash started on http://localhost:${unleash.app.get('port')}`, - ); - }); - ``` - -4. Run _server.js_: - ```sh - node server.js - ``` - -### Option three - use Docker - -[View the image on dockerhub](https://hub.docker.com/r/unleashorg/unleash-server/) - -#### Docker-compose - -1. Clone the [unleash-docker](https://github.com/Unleash/unleash-docker) repository. -2. Run `docker-compose build` in repository root folder. -3. Run `docker-compose up` in repository root folder. - -#### Manually - -1. Create a network by running `docker create network unleash` -2. Run - -```sh -docker run -e POSTGRES_PASSWORD={INSERT_PASSWORD} -e POSTGRES_USER={INSERT_USER} -e POSTGRES_DB=unleash --network unleash postgres - -docker run -p 4242:4242 --network unleash -e DATABASE_URL=postgres://{INSERT_USER}:{INSERT_PASSWORD}@postgres:5432/unleash unleashorg/unleash-server -``` - -## Test your server and create a sample API call - -Once the Unleash server has started, go to [localhost:4242](http://localhost:4242) in your browser. If you see a list of example feature toggles, try modifying one of them with [curl](https://curl.se/) from a terminal/bash shell: - -``` -curl --location --request PUT 'http://localhost:4242/api/admin/features/Feature.A' --header 'Content-Type: application/json' --data-raw '{\ - "name": "Feature.A",\ - "description": "Dolor sit amet.",\ - "type": "release",\ - "enabled": false,\ - "stale": false,\ - "strategies": [\ - {\ - "name": "default",\ - "parameters": {}\ - }\ - ]\ -}'\ -``` - -## Version check - -- Unleash checks that it uses the latest version by making a call to https://version.unleash.run. - - This is a cloud function storing instance id to our database for statistics. -- This request includes a unique instance id for your server. -- If you do not wish to check for upgrades define the environment variable `CHECK_VERSION` to anything else other than `true` before starting, and Unleash won't make any calls - - `export CHECK_VERSION=false` diff --git a/docs/sdks/community.md b/docs/sdks/community.md new file mode 100644 index 0000000000..480267ebb0 --- /dev/null +++ b/docs/sdks/community.md @@ -0,0 +1,28 @@ +--- +id: community +title: Community SDKs... +--- + +We from the Unleash teams take care of a handful of official SDKs for all the major programming languages. + +We ♥ love ♥ our contributors, and your effort make it easier to use Unleash everywhere. + +Community developed Client SDKs we already now about: + +- [cognitedata/unleash-client-rust](https://github.com/cognitedata/unleash-client-rust) (Rust) +- [silvercar/unleash-client-kotlin](https://github.com/silvercar/unleash-client-kotlin) (Kotlin) +- [uekoetter.dev/unleash-client-dart](https://pub.dev/packages/unleash) (Dart) +- [minds/unleash-client-php](https://gitlab.com/minds/unleash-client-php) (PHP) +- [Stogon/unleash-bundle](https://git.stogon.io/Stogon/unleash-bundle/) (PHP - Symfony) +- [afontaine/unleash_ex](https://gitlab.com/afontaine/unleash_ex) (Elixir) +- [mikefrancis/laravel-unleash](https://github.com/mikefrancis/laravel-unleash) (Laravel - PHP) +- [AppsFlyer/clojure-unleash](https://github.com/AppsFlyer/unleash-client-clojure) (Clojure) +- [pmb0/nestjs-unleash](https://github.com/pmb0/nestjs-unleash) (NestJS - Node.js) +- _...your implementation for your favorite language._ + +### Implement your own SDK? + +If none of the above SDKs fits your need there is always the option of developing your own SDK. To guide the implementation we have a few resources available: + +- [Unleash Client Specifications](https://github.com/Unleash/client-specification) - Used by all official SDKs to make sure they all behave correctly across different language implementations. This helps us verify that 10% of the users in the Java SDK means the exactly same 10% of the users in Python. +- [Client SDK overview](../client-specification) - Overall guide of the Unleash Architecture and important aspects of the SDK role in it all. diff --git a/docs/sdks/dot_net.md b/docs/sdks/dot_net.md index 3d9d493cc8..15b350ff5a 100644 --- a/docs/sdks/dot_net.md +++ b/docs/sdks/dot_net.md @@ -8,7 +8,7 @@ In this guide we explain how to use feature toggles in a .NET application using > **Required details** > > - **API URL** – Where you should connect your client SDK -> - **API Secret** – Your API secret required to connect to your instance. +> - **API Secret** – [Your API secret required to connect to your instance](../user_guide/api-token). > > You can find this information in the “Admin” section Unleash management UI. diff --git a/docs/sdks/go.md b/docs/sdks/go.md index cba4f1126c..abb45260bd 100644 --- a/docs/sdks/go.md +++ b/docs/sdks/go.md @@ -6,7 +6,7 @@ title: GO SDK > **Required details** > > - **API URL** – Where you should connect your client SDK -> - **API Secret** – Your API secret required to connect to your instance. +> - **API Secret** – [Your API secret required to connect to your instance](../user_guide/api-token). > > You can find this information in the “Admin” section Unleash management UI. diff --git a/docs/sdks/index.md b/docs/sdks/index.md index 4ebbc451cb..aadc610e69 100644 --- a/docs/sdks/index.md +++ b/docs/sdks/index.md @@ -32,4 +32,4 @@ We have examples for all official client SDKs: - [pmb0/nestjs-unleash](https://github.com/pmb0/nestjs-unleash) (NestJS - Node.js) - _...your implementation for your favorite language._ -When you get access to your instance – we will provide you with your Client secret and your API url for your instance. +When you get access to your instance – [create a client secret](../user_guide/api-token), and we will provide you with your API url for your instance. diff --git a/docs/sdks/java.md b/docs/sdks/java.md index b324ba6f4f..5abee3135b 100644 --- a/docs/sdks/java.md +++ b/docs/sdks/java.md @@ -8,7 +8,7 @@ In this guide we explain how to use feature toggles in a Java application using > **Required details** > > - **API URL** – Where you should connect your client SDK -> - **API Secret** – Your API secret required to connect to your instance. +> - **API Secret** – [Your API secret required to connect to your instance](../user_guide/api-token). > > You can find this information in the “Admin” section Unleash management UI. diff --git a/docs/sdks/node.md b/docs/sdks/node.md index 7a93926172..a145f61dac 100644 --- a/docs/sdks/node.md +++ b/docs/sdks/node.md @@ -8,7 +8,7 @@ In this guide we explain how to use feature toggles in a Node application using > **Required details** > > - **API URL** – Where you should connect your client SDK -> - **API Secret** – Your API secret required to connect to your instance. +> - **API Secret** – [Your API secret required to connect to your instance](../user_guide/api-token). > > You can find this information in the “Admin” section Unleash management UI. diff --git a/docs/sdks/proxy-javascript.md b/docs/sdks/proxy-javascript.md new file mode 100644 index 0000000000..cec2f02c57 --- /dev/null +++ b/docs/sdks/proxy-javascript.md @@ -0,0 +1,66 @@ +--- +id: proxy-javascript +title: JavaScript Proxy SDK +--- + +In this guide we explain how to use feature toggles in a Single Page App via [The Unleash Proxy](./unleash-proxy). You can also checkout the source code for the [JavaScript Proxy SDK](https://github.com/unleash-hosted/unleash-proxy-client-js). + + +### Introduction +For single-page apps we have a tiny proxy-client in JavaScript, without any external dependencies, except from browser APIs. This client will store toggles relevant for the current user in local-storage and synchronize with the Unleash Proxy in the background. This means we can bootstrap the toggles for a specific use the next time the user visits the web-page. + +> We are looking in to also [supporting react-native](https://github.com/Unleash/unleash/issues/785) with this SDK. Reach out if you want to help us validate the implementation. + + +**Step 1: Install** + +``` +npm install unleash-proxy-client --save +``` + +**Step 2: Initialize the SDK** + +You need to have a Unleash-hosted instance, and the proxy need to be enabled. In addition you will need a proxy-specific clientKey in order to connect to the Unleash-hosted Proxy. + + +```js +import { UnleashClient } from 'unleash-proxy-client'; + +const unleash = new UnleashClient({ + url: 'https://eu.unleash-hosted.com/hosted/proxy', + clientKey: 'your-proxy-key', + appName: 'my-webapp' +}); + +// Used to set the context fields, shared with the Unleash Proxy +unleash.updateContext({userId: '1233'}); + +// Start the background polling +unleash.start(); +``` + +**Step 3: Check if feature toggle is enabled** + +```js +unleash.isEnabled('proxy.demo'); +``` + +...or get toggle variant: + +```js +const variant = unleash.getVariant('proxy.demo'); +if(variant.name === 'blue') { + // something with variant blue... +} +``` + +**Listen for updates via the EventEmitter** + +The client is also an event emitter. This means that your code can subscribe to updates from the client. This is a neat way to update a single page app when toggle state updates. + +```js +unleash.on('update', () => { + const myToggle = unleash.isEnabled('proxy.demo'); + //do something useful +}); +``` \ No newline at end of file diff --git a/docs/sdks/python.md b/docs/sdks/python.md index 5096ae48c6..6f4d669177 100644 --- a/docs/sdks/python.md +++ b/docs/sdks/python.md @@ -6,7 +6,7 @@ title: Python SDK > **Required details** > > - **API URL** – Where you should connect your client SDK -> - **API Secret** – Your API secret required to connect to your instance. +> - **API Secret** – [Your API secret required to connect to your instance](../user_guide/api-token). > > You can find this information in the “Admin” section Unleash management UI. diff --git a/docs/sdks/ruby.md b/docs/sdks/ruby.md index 1f2bb9ae6d..86e4ae3d71 100644 --- a/docs/sdks/ruby.md +++ b/docs/sdks/ruby.md @@ -6,7 +6,7 @@ title: Ruby SDK > **Required details** > > - **API URL** – Where you should connect your client SDK -> - **API Secret** – Your API secret required to connect to your instance. +> - **API Secret** – [Your API secret required to connect to your instance](../user_guide/api-token). > > You can find this information in the “Admin” section Unleash management UI. diff --git a/docs/sdks/unleash-proxy.md b/docs/sdks/unleash-proxy.md new file mode 100644 index 0000000000..6506a22abe --- /dev/null +++ b/docs/sdks/unleash-proxy.md @@ -0,0 +1,44 @@ +--- +id: unleash-proxy +title: The Unleash Proxy +--- + +A lot of our users wanted to use feature toggles in their single-page and native applications. To solve this in a performant and privacy concerned way we built The Unleash Proxy + +The Unleash Proxy sits between the Unleash API and the application. It provides a simple and super-fast API, as it has all the data it needs available in memory. + +The proxy solves three important aspects: + +- **Performance** – The proxy will cache all toggles in memory, and will be running on the edge, close to your end-users. A single instance will be able to handle thousands of request/sec, and you can scale it easily by adding additional instances. +- **Security** – The proxy evaluates the feature flags for the user on the server-side, and only exposes the results of enabled feature flags for a specific user. +- **Privacy** – If you run the proxy yourself (we can host it as well though) we will not see your end users. This means that you still have full control of your end-users, the way it should be! + + +![The Unleash Proxy](../assets/The-unleash-proxy.png) + +*The Unleash-Proxy uses the Unleash SDK and exposes a simple API*. The Proxy will synchronize with the Unleash API in the background and provide a simple HTTP API for clients. + +### Unleash Proxy API + +The Unleash-Proxy has a very simple API. It takes the [Unleash Context](../user_guide/unleash_context) as input and will return the feature toggles relevant for that specific context. + +![The Unleash Proxy](../assets/The-Unleash-Proxy-API.png). + +### We care about Privacy! +The Unleash Proxy is important because you should not expose your entire toggle configurations to your end users! Single page apps works in context of a specific user. The proxy will only return the evaluated toggles (with variants) that should be enabled for those specific users in that specific context. + +Most of our customers prefer to run The Unleash Proxy themselves. PS! We actually prefer this as we don’t want to see your users. Running it is pretty simple, it is either a small Node.js process you start or a docker image you use. (We can of course host the proxy for you also.) + +### How to connect to the Proxy? + +The Unleash Proxy takes the heavy lifting of evaluating toggles and only returns enabled toggles and their values to the client. This means that you would get away with a simple http-client in many common use-cases. + +However in some settings you would like a bit more logic around it to make it as fast as possible, and keep up to date with changes. + +- [JavaScript Proxy SDK](./proxy-javascript) +- Android Proxy SDK (coming soon) +- iOS Proxy SDK (coming soon) + +The proxy is also ideal fit for serverless functions such as AWS Lambda. In that scenario the proxy can run on a small container near the serverless function, preferably in the same VPC, giving the lambda extremely fast access to feature flags, at a predictable cost. + +The unleash-proxy is compatible with unleash-enterprise and is offered as a free add-on to all our customers. We can also host it for you, for a small fee covering our cost. diff --git a/docs/user_guide/activation-strategies.md b/docs/user_guide/activation-strategies.md index dba7cbe6a1..0fd58082c2 100644 --- a/docs/user_guide/activation-strategies.md +++ b/docs/user_guide/activation-strategies.md @@ -3,27 +3,35 @@ id: activation_strategy title: Activation Strategies --- -It is powerful to be able to turn a feature on and off instantaneously, without redeploying the application. The next level of control comes when you are able to enable a feature for specific users or enable it for a small subset of users. We achieve this level of control with the help of activation strategies. The most straightforward strategy is the “default” strategy, which basically means that the feature should be enabled to everyone. +It is powerful to be able to turn a feature on and off instantaneously, without redeploying the application. The next level of control comes when you are able to enable a feature for specific users or enable it for a small subset of users. We achieve this level of control with the help of activation strategies. The most straightforward strategy is the standard strategy, which basically means that the feature should be enabled to everyone. The definition of an activation strategy lives in the Unleash API and can be created via the Unleash UI. The implementation of activation strategies lives in various client implementations. Unleash comes with a few common activation strategies. Some of them require the client to provide the [unleash-context](./unleash-context.md), which gives the necessary context for Unleash. -## default +## Standard -It is the simplest activation strategy and basically means "active for everyone". +A basic strategy that means "active for everyone". -## userWithId +This strategy has the following modelling name in the code: -Active for users with a `userId` defined in the `userIds` list. Typically, I want to enable a new feature only for myself in production before I enable it for everyone else. To achieve this, we can use the “UserWithIdStrategy”. This strategy allows you to specify a list of user IDs that you want to expose the new feature for. (A user id may, of course, be an email if that is more appropriate in your system.) +- **default** + +## UserIDs + +Active for users with a `userId` defined in the `userIds` list. A typical use case is to enable a feature for a few specific devs or key persons before enabling the feature for everyone else. This strategy allows you to specify a list of user IDs that you want to expose the new feature for. (A user id may, of course, be an email if that is more appropriate in your system.) **Parameters** - userIds - _List of user IDs you want the feature toggle to be enabled for_ -## flexibleRollout +This strategy has the following modelling name in the code: -A flexible rollout strategy which combines all gradual rollout strategies in to a single strategy (and will in time replace them). This strategy has different options for how you want to handle the stickiness, and have sane default mode. +- **userWithId** + +## Gradual Rollout + +A flexible rollout strategy which combines all gradual rollout strategies in to a single strategy. This strategy allows you to customize what parameter should be sticky, and defaults to userId or sessionId. **Parameters** @@ -35,14 +43,41 @@ A flexible rollout strategy which combines all gradual rollout strategies in to - **groupId** is used to ensure that different toggles will **hash differently** for the same user. The groupId defaults to _feature toggle name_, but the use can override it to _correlate rollout_ of multiple feature toggles. - **rollout** The percentage (0-100) you want to enable the feature toggle for. +This strategy has the following modelling name in the code: + +- **flexibleRollout** + ### Customize stickiness (beta) -By enabling the stickiness option on a custom context field you can use it together with the flexible rollout strategy. This will guarantee a consistent behavior for specific values of this context field. -NB! this feature is currently only supported by the following SDKs: +By enabling the stickiness option on a custom context field you can use it together with the gradual rollout strategy. This will guarantee a consistent behavior for specific values of this context field. NB! this feature is currently only supported by the following SDKs: - [unleash-client-node](https://github.com/Unleash/unleash-client-node) (from v3.6.0) -## gradualRolloutUserId +## IPs + +The remote address strategy activates a feature toggle for remote addresses defined in the IP list. We occasionally use this strategy to enable a feature only for IPs in our office network. + +**Parameters** + +- IPs - _List of IPs to enable the feature for._ + +This strategy has the following modelling name in the code: + +- **remoteAddress** + +## Hostnames + +The application hostname strategy activates a feature toggle for client instances with a hostName in the `hostNames` list. + +**Parameters** + +- hostNames - _List of hostnames to enable the feature toggle for._ + +This strategy has the following modelling name in the code: + +- **applicationHostname** + +## gradualRolloutUserId (DEPRECATED from v4) - Use Gradual rollout instead The `gradualRolloutUserId` strategy gradually activates a feature toggle for logged-in users. Stickiness is based on the user ID. The strategy guarantees that the same user gets the same experience every time across devices. It also assures that a user which is among the first 10% will also be among the first 20% of the users. That way, we ensure the users get the same experience, even if we gradually increase the number of users exposed to a particular feature. To achieve this, we hash the user ID and normalize the hash value to a number between 1 and 100 with a simple modulo operator. @@ -55,7 +90,7 @@ Starting from v3.x all clients should use the 32-bit [MurmurHash3](https://en.wi - percentage - _The percentage (0-100) you want to enable the feature toggle for._ - groupId - _Used to define an activation group, which allows you to correlate rollout across feature toggles._ -## gradualRolloutSessionId +## gradualRolloutSessionId (DEPRECATED from v4) - Use Gradual rollout instead Similar to `gradualRolloutUserId` strategy, this strategy gradually activates a feature toggle, with the exception being that the stickiness is based on the session IDs. This makes it possible to target all users (not just logged-in users), guaranteeing that a user will get the same experience within a session. @@ -64,26 +99,10 @@ Similar to `gradualRolloutUserId` strategy, this strategy gradually activates a - percentage - _The percentage (0-100) you want to enable the feature toggle for._ - groupId - _Used to define an activation group, which allows you to correlate rollout across feature toggles._ -## gradualRolloutRandom +## gradualRolloutRandom (DEPRECATED from v4) - Use Gradual rollout instead The `gradualRolloutRandom` strategy randomly activates a feature toggle and has no stickiness. We have found this rollout strategy very useful in some scenarios, especially when we enable a feature which is not visible to the user. It is also the strategy we use to sample metrics and error reports. **Parameters** - percentage - _The percentage (0-100) you want to enable the feature toggle for._ - -## remoteAddress - -The remote address strategy activates a feature toggle for remote addresses defined in the IP list. We occasionally use this strategy to enable a feature only for IPs in our office network. - -**Parameters** - -- IPs - _List of IPs to enable the feature for._ - -## applicationHostname - -The application hostname strategy activates a feature toggle for client instances with a hostName in the `hostNames` list. - -**Parameters** - -- hostNames - _List of hostnames to enable the feature toggle for._ diff --git a/docs/user_guide/control-rollout.md b/docs/user_guide/control-rollout.md index 59fc27485d..e72cb3db08 100644 --- a/docs/user_guide/control-rollout.md +++ b/docs/user_guide/control-rollout.md @@ -3,7 +3,7 @@ id: control_rollout title: Control rollout --- -It is powerful to be able to turn a feature on and off instantaneously, without redeploying the application. The next level of control comes when you are able to enable a feature for specific users or enable it for a small subset of users. +It is powerful to be able to turn a feature on and off instantaneously, without redeploying the application. The next level of control comes when you are able to enable a feature for specific users or enable it for a small subset of users. In this guide you will learn how to control the roll-out (expose it to real users) of a new feature with the help of activation strategies. @@ -13,37 +13,36 @@ Unleash comes with a few common activation strategies. Some of them require the The built-in activation strategies: -- default -- userWithId -- flexibleRollout -- remoteAddress -- applicationHostname +- Standard +- UserIDs +- Gradual Rollout +- IPs +- Hostnames -## The default activation strategy +## The standard activation strategy -When you create a new feature toggle you will get the default activation strategy, if you don’t configure any specific strategies. The default activation strategy will always evaluate to true, given that the feature toggle is enabled. +When you create a new feature toggle you will get the standard activation strategy, if you don’t configure any specific strategies. The standard activation strategy will always evaluate to true, given that the feature toggle is enabled. -![Default activation strategy](../assets/default_activation_strategy.png) +![Default activation strategy](../assets/control_rollout_standard_strategy.png) -## The userWithId strategy +## The UserIDs strategy -When we have deployed some new code to production it would be nice to enable the new feature for ourselves before we enable it to everyone else. To achieve this with Unleash, you can use the **userWithId** activation strategy. This strategy allows you to specify a list of user IDs that you want to expose the new feature for. +When we have deployed some new code to production it would be nice to enable the new feature for ourselves before we enable it to everyone else. To achieve this with Unleash, you can use the **UserIDs** activation strategy. This strategy allows you to specify a list of user IDs that you want to expose the new feature for. A userId is how you identify users in your system (email, UUID, etc) and is provided as part of the Unleash Context to the client SDK. -![UserWithId activation strategy](../assets/userWithId_activation_strategy.png) +![UserWithId activation strategy](../assets/control_rollout_userid_strategy.png) ## Multiple activation strategies In order to increase the exposure of the feature which is protected with the feature toggle you can configure multiple activation strategies on the same feature toggle. -![Multiple activation strategy](../assets/multiple_activation_strategies.png) +![Multiple activation strategy](../assets/control_rollout_multiple_strategies.png) -In the example above we have to configure two activation strategies, **userWithId** and **flexibleRollout**. If one of them evaluates to true the feature toggle is considered enabled. In the example we have enabled the feature toggle for usersWithId (*me@mail.com and another@mail.com*) in addition to 10% of the traffic. +In the example above we have to configure two activation strategies, **userWithId** and **flexibleRollout**. If one of them evaluates to true the feature toggle is considered enabled. In the example we have enabled the feature toggle for usersWithId (_productlead@mycompany.com and me@mycompany.com_) in addition to 75% of the traffic. ## Summary -You use activation strategies to control who the feature toggle will be enabled for. You can configure multiple strategies for a feature toggle, and they are considered in an OR fashion, meaning if one of them evaluates to true the toggle will be enabled. +You use activation strategies to control who the feature toggle will be enabled for. You can configure multiple strategies for a feature toggle, and they are considered in an OR fashion, meaning if one of them evaluates to true the toggle will be enabled. If you need to limit the exposure (AND) you should look in to [strategy constraints](strategy_constraints), which is the building block for that. - diff --git a/docs/user_guide/create-feature-toggle.md b/docs/user_guide/create-feature-toggle.md index b0658b0a2b..0b1530e8f6 100644 --- a/docs/user_guide/create-feature-toggle.md +++ b/docs/user_guide/create-feature-toggle.md @@ -9,33 +9,33 @@ In this guide you will learn how to create your first feature toggle using Unlea The first time you log-in to your Unleash instance you will see an empty list of feature toggles. In order to create a new feature toggle you have to click the “create feature toggle” button -![Create a feature toggle](../assets/create_toggle_1.png) +![Create a feature toggle](../assets/create_feature_toggle_button.png) ## Step 2: Create Feature toggle -After clicking the “create feature toggle button you will be presented with a form for creating a new feature toggle. You will need to define a few fields before you can actually complete the new feature toggle. +After clicking the “create feature toggle button you will be presented with a form for creating a new feature toggle. You will need to define a few fields before you can actually complete the new feature toggle. -- **Name** – Must be unique across all your feature toggle. The name must also follow a URL friendly format. Can not be changed. -- **Description** – A good description makes it easier for other members on your team to understand why this feature toggle exists. -- **Enabled** – Whether the feature toggle should be enabled or disabled. If the feature toggle is disabled, activation strategy configurations will not be evaluated. -- **Activation** strategies – A list of one or more activation strategies. An activation strategy is used to enable the feature toggle to a subset, or all, of your users. +- **Name** – Must be unique across all your feature toggle. The name must also follow a URL friendly format. Can not be changed. +- **Description** – A good description makes it easier for other members on your team to understand why this feature toggle exists. +- **Enabled** – Whether the feature toggle should be enabled or disabled. If the feature toggle is disabled, activation strategy configurations will not be evaluated. +- **Activation** strategies – A list of one or more activation strategies. An activation strategy is used to enable the feature toggle to a subset, or all, of your users. -In the example below we have chosen to configure the default activation strategy, which means that the feature will be enabled for everyone. +In the example below we have chosen to not set up a strategy, which means that the standard strategy will be applied. -![Create a feature toggle](../assets/create_toggle_2.png) +![Create a feature toggle](../assets/create_feature_toggle_save.png) ## Step 3: Congratulations, you have now created your first feature toggle! The toggle is now created and ready to be used. The toggle does not have any metrics because it is not used by any applications, yet! -![Create a feature toggle](../assets/create_toggle_3.png) +![Create a feature toggle](../assets/create_feature_toggle_list.png) ## Step 4: Enable the feature toggle only for your boss! The next step is to change the activation strategy to only target your boss. You can use the “userWithId”-strategy for that. Using the configuration shown below will only enable the feature toggle for “boss@company.com” and “me@company.com”. Thus, you can safely test your feature in production, without exposing it to your users. In [control roll-out](./control_rollout) with strategies we will go in to greater details on how to use activation strategies to gradually expose new features to your users. -![Create a feature toggle](../assets/userWithId.png) +![Create a feature toggle](../assets/create_feature_toggle_userIds.png) ## Summary -In this guide you created your first feature toggle and enabled it for everyone. In later guides we will learn how we can reduce the risk by enabling the toggle for a controlled set of users first. +In this guide you created your first feature toggle and enabled it for everyone. In later guides we will learn how we can reduce the risk by enabling the toggle for a controlled set of users first. diff --git a/docs/user_guide/discover-unknown-toggles.md b/docs/user_guide/discover-unknown-toggles.md deleted file mode 100644 index 8a24817901..0000000000 --- a/docs/user_guide/discover-unknown-toggles.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -id: discover_unknown_toggles -title: Discover unknown toggles ---- - -Discover unknown toggles diff --git a/docs/user_guide/index.md b/docs/user_guide/index.md index 06e6b28d1a..f6438725ed 100644 --- a/docs/user_guide/index.md +++ b/docs/user_guide/index.md @@ -3,30 +3,28 @@ id: index title: Introduction --- -Welcome to the Unleash getting started guides. We know that getting to know a new solution might be tedious. Our goal with this documentation is to guide you through the most essential concepts of Unleash. +Welcome to the Unleash getting started guides. We know that getting to know a new solution might be tedious. Our goal with this documentation is to guide you through the most essential concepts of Unleash. -One of the most important aspects of the architecture to understand is that feature toggles are evaluated in a client SDKs which runs as part of your application. This makes toggle evaluations super-fast, but of course it compromises a small update-delay when you change your toggle configurations (in terms of seconds and is configurable). +One of the most important aspects of the architecture to understand is that feature toggles are evaluated in a client SDKs which runs as part of your application. This makes toggle evaluations super-fast, but of course it compromises a small update-delay when you change your toggle configurations (in terms of seconds and is configurable). We recommend that you read about [our unique architecture](https://www.unleash-hosted.com/articles/our-unique-architecture) to understand some of the basics of the architecture. ## Unleash Server -Before you can connect your application to Unleash you need a Unleash server. You have a few options available. +Before you can connect your application to Unleash you need a Unleash server. You have a few options available. 1. [Unleash Open-source - Self-managed](/docs/deploy/getting_started) 2. [Unleash Enterprise - Cloud-hosted](https://www.unleash-hosted.com) 3. [Unleash Enterprise - Self-hosted](https://www.unleash-hosted.com) - - ## System Overview Unleash is composed of three parts: - **Unleash API** - The service holding all feature toggles and their configurations. Configurations declare which activation strategies to use and which parameters they should get. - **Unleash UI** - The dashboard used to manage feature toggles, define new strategies, look at metrics, etc. -- **Unleash SDK** - Used by clients to check if a feature is enabled or disabled. The SDK also collects metrics and sends them to the Unleash API. Activation Strategies are also implemented in the SDK. +- **Unleash SDK** - Used by clients to check if a feature is enabled or disabled. The SDK also collects metrics and sends them to the Unleash API. Activation Strategies are also implemented in the SDK. ![system_overview](https://raw.githubusercontent.com/Unleash/unleash/master/docs/assets/unleash-diagram.png 'System Overview') -To be super fast (*we talk nano-seconds*), the client SDK caches all feature toggles and their current configuration in memory. The activation strategies are also implemented in the SDK. This makes it really fast to check if a toggle is on or off because it is just a simple function operating on local state, without the need to poll data from the database. +To be super fast (_we talk nano-seconds_), the [client SDK](../sdks) caches all feature toggles and their current configuration in memory. The activation strategies are also implemented in the SDK. This makes it really fast to check if a toggle is on or off because it is just a simple function operating on local state, without the need to poll data from the database. diff --git a/docs/user_guide/native-apps.md b/docs/user_guide/native-apps.md index d9a71e941a..1c1ba9c362 100644 --- a/docs/user_guide/native-apps.md +++ b/docs/user_guide/native-apps.md @@ -15,8 +15,7 @@ The Unleash-proxy consist of the proxy that exposes a simple API for the client. ## The Proxy-client -There is a js implementation of the proxy-client available. -Find it here: [https://github.com/unleash-hosted/unleash-proxy-client-js](https://github.com/unleash-hosted/unleash-proxy-client-js) +There is a js implementation of the proxy-client available. Find it here: [https://github.com/unleash-hosted/unleash-proxy-client-js](https://github.com/unleash-hosted/unleash-proxy-client-js) ## The Proxy-API @@ -25,7 +24,9 @@ When accessing the Proxy, [Unleash context fields](unleash_context) from the ses ```sh https://hostname.com/api/proxy?appName=webapp&userId=123&country=NO ``` + The Proxy will return a list of all the active feature toggles, including variants, available. See an example below + ```sh { "toggles": [ diff --git a/docs/user_guide/projects.md b/docs/user_guide/projects.md index 82baaac40e..69efd0de1a 100644 --- a/docs/user_guide/projects.md +++ b/docs/user_guide/projects.md @@ -3,57 +3,59 @@ id: projects title: Projects --- -> **Enterprise** -> -> Project support is only available in Unleash Enterprise +> Project support is available as part of Unleash Enterprise. ## Overview + This topic explains how projects are supported in Unleash, how to create and maintain them. ## Understanding purpose of projects -Projects are a way to organize your feature toggles within Unleash. Within a large organization, having multiple feature toggles, staying on top of the feature toggles might become a challenge. Every feature toggle will be part of a project. Projects can be linked to a development team or to functional modules within the software. + +Projects are a way to organize your feature toggles within Unleash. Within a large organization, having multiple feature toggles, staying on top of the feature toggles might become a challenge. Every feature toggle will be part of a project. Projects can be linked to a development team or to functional modules within the software. A common pattern is to organize the feature toggles according to key areas of the application, e.g. “Basic user process” and “Advanced user process”. This is illustrated below. ![Project concept](../assets/project_concept.png) ## Creating a new project + When you log into Unleash for the first time, there is a Default project already created. All feature toggles are included in the Default project, unless explicitly set to a different one. From the top-line menu – click on the hamburger icon. -![Project concept](../assets/project_main_menu.png) +![Project concept](../assets/projects_menu.png) From the menu – choose “Projects” -![Project concept](../assets/project_menu_item.png) +menu open -The available projects will now be listed. -To create a new Project – choose the “+” +The available projects will now be listed. To create a new Project – choose the “+” -![Project concept](../assets/project_add.png) +![Project concept](../assets/projects_new_project.png) The configuration of a new Project is now available. the following input is available to create the new Project. -![Project concept](../assets/project_configure.png) +![Project concept](../assets/projects_save_new_project.png) -| Item | Description | -| ----------- | ----------- | -| Project Id | Id for this Project | -| Project name | The name of the Project. | -| Description | A short description of the project | +| Item | Description | +| ------------ | ---------------------------------- | +| Project Id | Id for this Project | +| Project name | The name of the Project. | +| Description | A short description of the project | ## Deleting an existing project + To keep your feature toggles clean, removing deprecated projects is important. From the overview of Projects – choose the Delete button for the project you want to delete. -![Project concept](../assets/project_delete.png) +![Project concept](../assets/projects_delete_button.png) ## Filter feature toggles on projects + When browsing the feature toggles in Unleash, you might want to filter the view by looking only at the ones included in the project of interest. This is possible from the Feature toggle overview. From the top-line menu – choose the hamburger icon -![Project concept](../assets/feature_toggles_menu.png) +![Project concept](../assets/projects_menu.png) From the menu – choose “Feature toggles” @@ -61,26 +63,28 @@ From the menu – choose “Feature toggles” The list of features toggles can be filtered on the project of your choice. By default, all feature toggles are listed in the view. -![Project concept](../assets/ft_project_filter.png) +![Project concept](../assets/project_select.png) From the drop-down, chose the project to filter on. -![Project concept](../assets/ft_filter_on_project.png) +![Project concept](../assets/projects_select_dropdown.png) The view will now be updated with the filtered feature toggles. ## Assigning project to a new feature toggle + When creating a new feature toggle, the project where the feature toggle will be created may be chosen. The default project is “Default” -![Project concept](../assets/ft_create_chose_project.png) +![Project concept](../assets/projects_change_project.png) -All available projects are available from the drop-down menu. +All available projects are available from the drop-down menu. -![Project concept](../assets/ft_create_assign_project.png) +![Project concept](../assets/projects_toggle_project_dropdown.png) ## Change project for an existing feature toggle + There might be a need to change the project a feature toggle belongs to. Changing the project is possible from the feature toggle configuration page. -![Project concept](../assets/ft_create_assign_project.png) +![Project concept](../assets/projects_existing_toggle_dropdown.png) To change the project, simply change the project from the drop-down menu. diff --git a/docs/user_guide/rbac.md b/docs/user_guide/rbac.md new file mode 100644 index 0000000000..3c71b0be20 --- /dev/null +++ b/docs/user_guide/rbac.md @@ -0,0 +1,50 @@ +--- +id: rbac +title: Role-based Access control +--- + +This document forms the specifications for [Role-Based Access Control](https://en.wikipedia.org/wiki/Role-based_access_control) which was introduced as part of the **Unleash v4 release**. + +### Core principles + +Unleash has two levels in it’s hierarchy of resources: + +1. **Root resources** - Everything that lives across the entire Unleash instance. Examples of this includes: + - activation strategies + - context field definitions + - addon configurations + - applications + - users +2. **Project resources** - Resources which are only available under a project. Today this is only “feature toggles” (but we expect more resources to live under a project in the future). A feature toggle will belong to only one single project. In Unleash-Open source there exists only a single project, the “default” project, while Unleash Enterprise supports multiple projects. + +![RBAC overview](../assets/rbac.png) + +Unleash v4 allows you control access to both "root resources" and individual project resources. + +### Root Roles + +> Available for Unleash Open-Source and Unleash Enterprise. + +Unleash will come with three "root" role out of the box: + +- **Admin** - Used to administer the Unleash instance. Is allowed to add/remove users, add them to roles and update role permissions. +- **Editor** - Represent users with typical read and write access to Unleash. They will typically be allowed to create new projects (for enterprise), create feature toggles on the "default" project, configure context fields etc. They will not be able to add/remove users or roles. -- **Viewer** - Users with this role are only allowed to read resources in Unleash. They might be added as collaborators to specific projects. + +### Project + +> Project roles are part of Unleash Enterprise. + +Per project two roles are now available: + +- **Owner** - Allowed to update the project. This includes adding and removing project members and their role. +- **Member** - Allowed to create and update feature toggles within the project. They can not update the project itself + +It is important to highlight that we have not introduced a Viewer role on the project level. We believe that all users in Unleash should be able to to View all feature toggles and configuration within an organization. (If we learn this not to be the case we can add a separate role for READ access later). + +### Custom Roles + +> Will only be introduced for Unleash Enterprise. + +In a later iteration we will introduce the concept of "custom roles". This will allow full customization to meet internal needs of larger organisations. We believe these should be able to define access across both “root resources” and specific projects resources. We need further investigation with customers before we land custom roles. + +Please let us know if you have feedback or ideas on how custom roles should work in order to solve your company needs. diff --git a/docs/user_guide/single-page-apps.md b/docs/user_guide/single-page-apps.md deleted file mode 100644 index 42b2aea22e..0000000000 --- a/docs/user_guide/single-page-apps.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -id: single_page_apps -title: Working with single page apps ---- - -> This is an Enterprise feature. - -Working with single page apps \ No newline at end of file diff --git a/docs/user_guide/token.md b/docs/user_guide/token.md new file mode 100644 index 0000000000..cc2e3cbe4d --- /dev/null +++ b/docs/user_guide/token.md @@ -0,0 +1,46 @@ +--- +id: api-token +title: API Tokens +--- + +In order to connect to Unleash clients will need an API token to grant access. A client SDK will need to token with "client privileges", which allows them to fetch feature toggle configuration and post usage metics back. + +## Create API Token + +### Permissions + +To create an API token you'll need the CREATE_API_TOKEN permission level, at the time of writing (v4.0.0), this level is only set for instance admins. + +Eventually one should be able to customize which users has access to create tokens. + +All users are able to see tokens with CLIENT level access, but only instance admins can see tokens with ADMIN level access. + +### Step-by-step + +**1. Select `Admin` from the sidebar** + +![Admin menu](../assets/admin_side_menu.png) + +**2. Select `Api Access` from the tab menu** + +![Tab Menu](../assets/admin_tab_menu.png) + +**3. Click `Add new API key` at the bottom of the page** + +**Client keys** + +4a. If you're configuring an SDK select `Client` in the pop-up. And give the key an identifying name allowing you to recognize it later + +![Api key client](../assets/add_new_api_key.png) + +5a. Copy the `Secret` column and add this to your client + +![Api key list](../assets/api_key_list.png) + +**Admin operations** + +4a. If you're going to be using the admin interface via CURL you'll need a key with `Admin` rights. Select `Admin` in the `Add new API key` popup. + +Remember to give the key a username allowing you to recognize the key in audit logs later + +5a. Copy the key in the `Secret` column and use it in your authorization header. For curl, that would be `-H "Authorization: "` diff --git a/docs/user_guide/user-management.md b/docs/user_guide/user-management.md new file mode 100644 index 0000000000..d8a05b5451 --- /dev/null +++ b/docs/user_guide/user-management.md @@ -0,0 +1,16 @@ +--- +id: user-management +title: User Management +--- + +> This feature was introduced in Unleash v4 for Unleash Open-Source. + +From the `Admin > Users` you can manage users with access to Unleash. To add a new user to your Unleash instance simply click the "Add user" button: + +![Add user button](../assets/user_admin-add-user.png) + +Fill out the required fields about the user in the modal. You need to choose which role the new user should have on the "root level". E.g. giving the user an Editor role will allow the user to create a new project. + +![Add user button](../assets/user_admin_add_user_modal.png). + +If you have configured an email server the user will receive the invite link in her inbox, otherwise you should share the magic invite link to Unleash presented in the confirmation dialogue. diff --git a/docs/user_guide/whats-new-v4.md b/docs/user_guide/whats-new-v4.md new file mode 100644 index 0000000000..0b16f44567 --- /dev/null +++ b/docs/user_guide/whats-new-v4.md @@ -0,0 +1,54 @@ +--- +id: v4-whats-new +title: What's new in v4? +--- + +Version 4 of Unleash brings a lot of improvements to Unleash. In this document we will highlight some of the things that has been added. + +### Upgrade with ease + +Unleash can either be hosted by us or self-hosted. If you have a managed Unleash Enterprise instance you are automatically upgraded to version 4. If you manage Unleash yourself (either Open-Source or Enterprise Self-hosted) we recommend reading the [migration guide](../deploy/migration_guide). + +**PS! The first time you access Unleash v4 from a self-hosted instance you will need to login with the default admin user:** + +- username: `admin` +- password: `unleash4all` + +_(We recommend changing the password of the default user from the admin section.)_ + +### Role-Based Access Control + +With Role-Based Access Control you can now assign groups to users in order to control access. You can control access to root resources in addition to controlling access to [projects](./projects). _Please be aware that all existing users will become "Owner" of all existing projects as part of the migration from v3 to v4._ + +[Read more](./rbac) + +### New Addons + +Addons make it easy to integrate Unleash with other systems. In version 4 we bring two new integrations to Unleash: + +- [Microsoft Teams](../addons/teams) +- [Datadog](../datadog) + +### Improved UX + +Unleash v4 includes a new implementation of the frontend based on [Material-ui](https://material-ui.com). This will make it much easier for us to improve the Unleash Admin UI and our ambition is to make it intuitive to use even for non-developers. The improved UX is made available in Unleash Open-Source and Unleash Enterprise. + +### New SSO Option + +In version 4 we added support for [OpenID Connect](https://openid.net/connect/) as part of the Unleash Enterprise offering. OpenID Connect is a simple identity layer on top of the OAuth 2.0 protocol. It allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner. + +### User Management + +In version 4 we improved the User Management and made it available for Unleash Open-Source and Unleash Enterprise. Starting in v4 all users accessing Unleash needs to exist in the Unleash in order to gain access (because they need to have the proper permission from RBAC.) + +[Read more](./user-management) + +### API access + +In version 4 we improved the API Access and made it available for Unleash Open-Source and Unleash Enterprise. Starting from Unleash v4 we require all SDKs to use an access token in order to connect to Unleash. + +[Read more](../advanced/api_access) + +### Custom stickiness + +In Unleash Enterprise v4 you can configure stickiness when your are doing a gradual rollout with the "flexible rollout" strategy or together with feature toggle variants. This means that you can now have a consistent behavior bases on any field available on the [Unleash context](./unleash_context). diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000000..861daea3fe --- /dev/null +++ b/examples/README.md @@ -0,0 +1 @@ +This section has been moved to a separate repository: https://github.com/Unleash/unleash-examples diff --git a/examples/basic-auth-hook.js b/examples/basic-auth-hook.js deleted file mode 100644 index 269b4164fe..0000000000 --- a/examples/basic-auth-hook.js +++ /dev/null @@ -1,26 +0,0 @@ -/* eslint-disable import/no-unresolved */ - -'use strict'; - -const auth = require('basic-auth'); -const { User } = require('../dist/lib/server-impl.js'); - -function basicAuthentication(app) { - app.use('/api/admin/', (req, res, next) => { - const credentials = auth(req); - - if (credentials) { - // you will need to do some verification of credentials here. - const user = new User({ email: `${credentials.name}@domain.com` }); - req.user = user; - return next(); - } - - return res - .status('401') - .set({ 'WWW-Authenticate': 'Basic realm="example"' }) - .end('access denied'); - }); -} - -module.exports = basicAuthentication; diff --git a/examples/basic-auth-unleash.js b/examples/basic-auth-unleash.js deleted file mode 100644 index 49536a503f..0000000000 --- a/examples/basic-auth-unleash.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -// const unleash = require('unleash-server'); -const unleash = require('../dist/lib/server-impl.js'); - -const basicAuth = require('./basic-auth-hook'); - -unleash - .start({ - databaseUrl: 'postgres://unleash_user:passord@localhost:5432/unleash', - adminAuthentication: 'custom', - preRouterHook: basicAuth, - }) - .then(server => { - // eslint-disable-next-line no-console - console.log( - `Unleash started on http://localhost:${server.app.get('port')}`, - ); - }); diff --git a/examples/client-auth-unleash.js b/examples/client-auth-unleash.js deleted file mode 100644 index d7f57959af..0000000000 --- a/examples/client-auth-unleash.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -// const unleash = require('unleash-server'); -const unleash = require('../dist/lib/server-impl.js'); - -// You typically will not hard-code this value in your code! -const sharedSecret = '12312Random'; - -unleash - .start({ - databaseUrl: 'postgres://unleash_user:passord@localhost:5432/unleash', - preRouterHook: app => { - app.use('/api/client', (req, res, next) => { - if (req.header('authorization') === sharedSecret) { - next(); - } else { - res.sendStatus(401); - } - }); - }, - }) - .then(server => { - // eslint-disable-next-line no-console - console.log( - `Unleash started on http://localhost:${server.app.get('port')}`, - ); - }); diff --git a/examples/express-parent-app.js b/examples/express-parent-app.js deleted file mode 100644 index d41ab056ca..0000000000 --- a/examples/express-parent-app.js +++ /dev/null @@ -1,17 +0,0 @@ -/* eslint-disable import/no-unresolved */ - -'use strict'; - -const express = require('express'); -const unleash = require('../dist/lib/server-impl.js'); - -const app = express(); -unleash - .create({ - databaseUrl: 'postgres://unleash_user:passord@localhost:5432/unleash', - baseUriPath: '/unleash', - }) - .then(unl => { - app.use(unl.app); // automatically mounts to baseUriPath - app.listen(process.env.PORT); - }); diff --git a/examples/google-auth-hook.js b/examples/google-auth-hook.js deleted file mode 100644 index 120772a9b2..0000000000 --- a/examples/google-auth-hook.js +++ /dev/null @@ -1,90 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ - -'use strict'; - -/** - * Google OAuth 2.0 - * - * You should read Using OAuth 2.0 to Access Google APIs: - * https://developers.google.com/identity/protocols/OAuth2 - * - * This example assumes that all users authenticating via - * google should have access. You would probably limit access - * to users you trust. - * - * The implementation assumes the following environment variables: - * - * - GOOGLE_CLIENT_ID - * - GOOGLE_CLIENT_SECRET - * - GOOGLE_CALLBACK_URL - */ - -const passport = require('@passport-next/passport'); -const GoogleOAuth2Strategy = require('@passport-next/passport-google-oauth2') - .Strategy; - -// const { User, AuthenticationRequired } = require('unleash-server'); -const { User, AuthenticationRequired } = require('../dist/lib/server-impl.js'); - -passport.use( - new GoogleOAuth2Strategy( - { - clientID: process.env.GOOGLE_CLIENT_ID, - clientSecret: process.env.GOOGLE_CLIENT_SECRET, - callbackURL: process.env.GOOGLE_CALLBACK_URL, - }, - - (accessToken, refreshToken, profile, done) => { - done( - null, - new User({ - name: profile.displayName, - email: profile.emails[0].value, - }), - ); - }, - ), -); - -function enableGoogleOauth(app) { - app.use(passport.initialize()); - app.use(passport.session()); - - passport.serializeUser((user, done) => done(null, user)); - passport.deserializeUser((user, done) => done(null, user)); - app.get( - '/api/admin/login', - passport.authenticate('google', { scope: ['email'] }), - ); - - app.get( - '/api/auth/callback', - passport.authenticate('google', { - failureRedirect: '/api/admin/error-login', - }), - (req, res) => { - // Successful authentication, redirect to your app. - res.redirect('/'); - }, - ); - - app.use('/api/admin/', (req, res, next) => { - if (req.user) { - return next(); - } - // Instruct unleash-frontend to pop-up auth dialog - return res - .status('401') - .json( - new AuthenticationRequired({ - path: '/api/admin/login', - type: 'custom', - message: `You have to identify yourself in order to use Unleash. - Click the button and follow the instructions.`, - }), - ) - .end(); - }); -} - -module.exports = enableGoogleOauth; diff --git a/examples/google-auth-unleash.js b/examples/google-auth-unleash.js deleted file mode 100644 index 0e1ba899f7..0000000000 --- a/examples/google-auth-unleash.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -// const unleash = require('unleash-server'); -const unleash = require('../dist/lib/server-impl.js'); - -const enableGoogleOauth = require('./google-auth-hook'); - -unleash - .start({ - databaseUrl: 'postgres://unleash_user:passord@localhost:5432/unleash', - adminAuthentication: 'custom', - preRouterHook: enableGoogleOauth, - }) - .then(server => { - // eslint-disable-next-line no-console - console.log( - `Unleash started on http://localhost:${server.app.get('port')}`, - ); - }); diff --git a/examples/keycloak-auth-hook.js b/examples/keycloak-auth-hook.js deleted file mode 100644 index 7a379a9c45..0000000000 --- a/examples/keycloak-auth-hook.js +++ /dev/null @@ -1,93 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -/* eslint-disable import/no-unresolved */ - -'use strict'; - -/** - * Keycloak hook for securing an Unleash server - * - * This example assumes that all users authenticating via - * keycloak should have access. You would probably limit access - * to users you trust. - * - * The implementation assumes the following environment variables: - * - * - AUTH_HOST - * - AUTH_REALM - * - AUTH_CLIENT_ID - */ - -const KeycloakStrategy = require('@exlinc/keycloak-passport'); -const passport = require('passport'); - -// const { User, AuthenticationRequired } = require('unleash-server'); -const { User, AuthenticationRequired } = require('../dist/lib/server-impl.js'); - -const host = process.env.AUTH_HOST; -const realm = process.env.AUTH_REALM; -const clientID = process.env.AUTH_CLIENT_ID; -const contextPath = process.env.CONTEXT_PATH; - -passport.use( - 'keycloak', - new KeycloakStrategy( - { - host, - realm, - clientID, - clientSecret: "We don't need that, but is required", - callbackURL: `${contextPath}/api/auth/callback`, - authorizationURL: `${host}/auth/realms/${realm}/protocol/openid-connect/auth`, - tokenURL: `${host}/auth/realms/${realm}/protocol/openid-connect/token`, - userInfoURL: `${host}/auth/realms/${realm}/protocol/openid-connect/userinfo`, - }, - - (accessToken, refreshToken, profile, done) => { - done( - null, - new User({ - name: profile.fullName, - email: profile.email, - }), - ); - }, - ), -); - -function enableKeycloakOauth(app) { - app.use(passport.initialize()); - app.use(passport.session()); - - passport.serializeUser((user, done) => done(null, user)); - passport.deserializeUser((user, done) => done(null, user)); - - app.get('/api/admin/login', passport.authenticate('keycloak')); - - app.get( - '/api/auth/callback', - passport.authenticate('keycloak'), - (req, res) => { - res.redirect(`${contextPath}/`); - }, - ); - - app.use('/api/admin/', (req, res, next) => { - if (req.user) { - return next(); - } - // Instruct unleash-frontend to pop-up auth dialog - return res - .status('401') - .json( - new AuthenticationRequired({ - path: `${contextPath}/api/admin/login`, - type: 'custom', - message: `You have to identify yourself in order to use Unleash. - Click the button and follow the instructions.`, - }), - ) - .end(); - }); -} - -module.exports = enableKeycloakOauth; diff --git a/examples/keycloak-auth-unleash.js b/examples/keycloak-auth-unleash.js deleted file mode 100644 index 9efc7ffab3..0000000000 --- a/examples/keycloak-auth-unleash.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -// const unleash = require('unleash-server'); -const unleash = require('../dist/lib/server-impl.js'); - -const enableKeycloak = require('./keycloak-auth-hook'); - -unleash - .start({ - databaseUrl: 'postgres://unleash_user:passord@localhost:5432/unleash', - adminAuthentication: 'custom', - preRouterHook: enableKeycloak, - }) - .then(server => { - // eslint-disable-next-line no-console - console.log( - `Unleash started on http://localhost:${server.app.get('port')}`, - ); - }); diff --git a/package.json b/package.json index d7a74648d0..80bb84dd4c 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,6 @@ }, "dependencies": { "async": "^3.1.0", - "basic-auth": "^2.0.1", "bcrypt": "^5.0.1", "compression": "^1.7.3", "connect-session-knex": "^2.0.0", @@ -104,8 +103,6 @@ }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@passport-next/passport": "^3.1.0", - "@passport-next/passport-google-oauth2": "^1.0.0", "@types/bcrypt": "^3.0.0", "@types/express": "^4.17.11", "@types/node": "^14.14.37", @@ -129,8 +126,6 @@ "lint-staged": "^10.0.7", "lolex": "^6.0.0", "nyc": "^15.1.0", - "passport": "^0.4.1", - "passport-google-auth": "^1.0.2", "prettier": "^1.19.1", "proxyquire": "^2.1.3", "rimraf": "^3.0.2", diff --git a/website/core/Footer.js b/website/core/Footer.js index 3c5a5ff98a..ffdce7d025 100644 --- a/website/core/Footer.js +++ b/website/core/Footer.js @@ -34,12 +34,9 @@ class Footer extends React.Component {
Docs
- + Getting Started - - Securing Unleash - API Reference @@ -56,8 +53,8 @@ class Footer extends React.Component { Slack community - Unleash-hosted.com + href="https://www.getunleash.io"> + getunleash.io ( const ProjectTitle = () => (

- Unleash + Unleash {siteConfig.tagline}

); diff --git a/website/sidebars.json b/website/sidebars.json index e940c93d11..6ea4398c86 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -2,29 +2,35 @@ "documentation": { "Getting started": [ "user_guide/index", + "user_guide/v4-whats-new", "user_guide/create_feature_toggle", "user_guide/activation_strategy", "user_guide/control_rollout", "user_guide/projects", "user_guide/unleash_context", - "user_guide/technical_debt", - "user_guide/native_apps", - "user_guide/single_page_apps" + "user_guide/user-management", + "user_guide/rbac", + "user_guide/api-token", + "user_guide/technical_debt" ], - "Working with the SDK": [ + "Unleash SDKs": [ "sdks/index", "sdks/java_sdk", "sdks/node_sdk", "sdks/dot_net_sdk", "sdks/go_sdk", "sdks/python_sdk", - "sdks/ruby_sdk" + "sdks/ruby_sdk", + "sdks/unleash-proxy", + "sdks/proxy-javascript", + "sdks/community" ], "Addons framework": [ "addons/index", "addons/webhook", "addons/slack", - "addons/jira-commenter" + "addons/teams", + "addons/datadog" ], "Advanced": [ "advanced/strategy_constraints", @@ -54,7 +60,7 @@ "api/admin/context", "api/admin/projects" ], - "Internal": ["api/internal/internal", "api/internal/health"], + "Status": ["api/internal/internal", "api/internal/health"], "Specification": ["api/open_api"] }, "Deploy and manage": { @@ -62,6 +68,7 @@ "deploy/getting_started", "deploy/configuring_unleash", "deploy/securing_unleash", + "deploy/email", "deploy/google_auth", "deploy/database_backup", "deploy/migration_guide", @@ -70,12 +77,5 @@ }, "Integrations": { "Integrations": ["integrations/integrations"] - }, - "Contribute": { - "Contribute to Unleash": [ - "developer_guide", - "client_specification", - "database_schema" - ] } } diff --git a/website/siteConfig.js b/website/siteConfig.js index bfddedc98a..08411a4f87 100644 --- a/website/siteConfig.js +++ b/website/siteConfig.js @@ -55,7 +55,7 @@ const users = [ ]; const siteConfig = { - title: 'Unleash', // Title for your website. + title: '', // Title for your website. tagline: 'The enterprise ready feature toggle service', url: 'https://docs.getunleash.io', // Your website URL baseUrl: '/', // Base URL for your project */ @@ -75,7 +75,6 @@ const siteConfig = { { doc: 'user_guide/index', label: 'Documentation' }, { doc: 'deploy/getting_started', label: 'Deploy and manage' }, { doc: 'integrations/integrations', label: 'Integrations' }, - { doc: 'developer_guide', label: 'Contribute' }, { doc: 'api/client/features', label: 'API' }, { href: 'https://www.unleash-hosted.com/pricing', label: 'Enterprise' }, { page: 'help', label: 'Help' }, @@ -86,13 +85,13 @@ const siteConfig = { users, /* path to images for header/footer */ - headerIcon: 'img/logo-inverted.png', - footerIcon: 'img/logo-inverted.png', + headerIcon: 'img/Logo_White_Transparent_Horizontal.png', + footerIcon: 'img/Logo_White_Transparent_Horizontal.png', favicon: 'img/favicon/favicon.ico', /* Colors for website */ colors: { - primaryColor: '#3f51b5', + primaryColor: '#39535b', secondaryColor: '#697ce5', }, diff --git a/website/static/img/Logo_DarkBlue_Transparent_Horizontal.png b/website/static/img/Logo_DarkBlue_Transparent_Horizontal.png new file mode 100644 index 0000000000..b3f8ab9d6a Binary files /dev/null and b/website/static/img/Logo_DarkBlue_Transparent_Horizontal.png differ diff --git a/website/static/img/Logo_White_Transparent_Horizontal.png b/website/static/img/Logo_White_Transparent_Horizontal.png new file mode 100644 index 0000000000..d19d74bae1 Binary files /dev/null and b/website/static/img/Logo_White_Transparent_Horizontal.png differ diff --git a/website/static/img/favicon/favicon.ico b/website/static/img/favicon/favicon.ico index fed2443a05..2bfe97c212 100644 Binary files a/website/static/img/favicon/favicon.ico and b/website/static/img/favicon/favicon.ico differ diff --git a/website/static/img/unleash-logo-scaled.png b/website/static/img/unleash-logo-scaled.png new file mode 100644 index 0000000000..20c89016fe Binary files /dev/null and b/website/static/img/unleash-logo-scaled.png differ