diff --git a/cli/notifications_test.go b/cli/notifications_test.go index 9d7ff8a37abc3..9d775c6f5842b 100644 --- a/cli/notifications_test.go +++ b/cli/notifications_test.go @@ -20,7 +20,6 @@ func createOpts(t *testing.T) *coderdtest.Options { t.Helper() dt := coderdtest.DeploymentValues(t) - dt.Experiments = []string{string(codersdk.ExperimentNotifications)} return &coderdtest.Options{ DeploymentValues: dt, } diff --git a/cli/server.go b/cli/server.go index 561c1bac16375..612548372c00a 100644 --- a/cli/server.go +++ b/cli/server.go @@ -56,15 +56,16 @@ import ( "cdr.dev/slog" "cdr.dev/slog/sloggers/sloghuman" - "github.com/coder/coder/v2/coderd/entitlements" - "github.com/coder/coder/v2/coderd/notifications/reports" - "github.com/coder/coder/v2/coderd/runtimeconfig" "github.com/coder/pretty" "github.com/coder/quartz" "github.com/coder/retry" "github.com/coder/serpent" "github.com/coder/wgtunnel/tunnelsdk" + "github.com/coder/coder/v2/coderd/entitlements" + "github.com/coder/coder/v2/coderd/notifications/reports" + "github.com/coder/coder/v2/coderd/runtimeconfig" + "github.com/coder/coder/v2/buildinfo" "github.com/coder/coder/v2/cli/clilog" "github.com/coder/coder/v2/cli/cliui" @@ -679,10 +680,6 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. options.OIDCConfig = oc } - experiments := coderd.ReadExperiments( - options.Logger, options.DeploymentValues.Experiments.Value(), - ) - // We'll read from this channel in the select below that tracks shutdown. If it remains // nil, that case of the select will just never fire, but it's important not to have a // "bare" read on this channel. @@ -946,6 +943,33 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. return xerrors.Errorf("write config url: %w", err) } + // Manage notifications. + cfg := options.DeploymentValues.Notifications + metrics := notifications.NewMetrics(options.PrometheusRegistry) + helpers := templateHelpers(options) + + // The enqueuer is responsible for enqueueing notifications to the given store. + enqueuer, err := notifications.NewStoreEnqueuer(cfg, options.Database, helpers, logger.Named("notifications.enqueuer"), quartz.NewReal()) + if err != nil { + return xerrors.Errorf("failed to instantiate notification store enqueuer: %w", err) + } + options.NotificationsEnqueuer = enqueuer + + // The notification manager is responsible for: + // - creating notifiers and managing their lifecycles (notifiers are responsible for dequeueing/sending notifications) + // - keeping the store updated with status updates + notificationsManager, err := notifications.NewManager(cfg, options.Database, helpers, metrics, logger.Named("notifications.manager")) + if err != nil { + return xerrors.Errorf("failed to instantiate notification manager: %w", err) + } + + // nolint:gocritic // TODO: create own role. + notificationsManager.Run(dbauthz.AsSystemRestricted(ctx)) + + // Run report generator to distribute periodic reports. + notificationReportGenerator := reports.NewReportGenerator(ctx, logger.Named("notifications.report_generator"), options.Database, options.NotificationsEnqueuer, quartz.NewReal()) + defer notificationReportGenerator.Close() + // Since errCh only has one buffered slot, all routines // sending on it must be wrapped in a select/default to // avoid leaving dangling goroutines waiting for the @@ -1002,38 +1026,6 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. options.WorkspaceUsageTracker = tracker defer tracker.Close() - // Manage notifications. - var ( - notificationsManager *notifications.Manager - ) - if experiments.Enabled(codersdk.ExperimentNotifications) { - cfg := options.DeploymentValues.Notifications - metrics := notifications.NewMetrics(options.PrometheusRegistry) - helpers := templateHelpers(options) - - // The enqueuer is responsible for enqueueing notifications to the given store. - enqueuer, err := notifications.NewStoreEnqueuer(cfg, options.Database, helpers, logger.Named("notifications.enqueuer"), quartz.NewReal()) - if err != nil { - return xerrors.Errorf("failed to instantiate notification store enqueuer: %w", err) - } - options.NotificationsEnqueuer = enqueuer - - // The notification manager is responsible for: - // - creating notifiers and managing their lifecycles (notifiers are responsible for dequeueing/sending notifications) - // - keeping the store updated with status updates - notificationsManager, err = notifications.NewManager(cfg, options.Database, helpers, metrics, logger.Named("notifications.manager")) - if err != nil { - return xerrors.Errorf("failed to instantiate notification manager: %w", err) - } - - // nolint:gocritic // TODO: create own role. - notificationsManager.Run(dbauthz.AsSystemRestricted(ctx)) - - // Run report generator to distribute periodic reports. - notificationReportGenerator := reports.NewReportGenerator(ctx, logger.Named("notifications.report_generator"), options.Database, options.NotificationsEnqueuer, quartz.NewReal()) - defer notificationReportGenerator.Close() - } - // Wrap the server in middleware that redirects to the access URL if // the request is not to a local IP. var handler http.Handler = coderAPI.RootHandler @@ -1153,19 +1145,17 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. // Cancel any remaining in-flight requests. shutdownConns() - if notificationsManager != nil { - // Stop the notification manager, which will cause any buffered updates to the store to be flushed. - // If the Stop() call times out, messages that were sent but not reflected as such in the store will have - // their leases expire after a period of time and will be re-queued for sending. - // See CODER_NOTIFICATIONS_LEASE_PERIOD. - cliui.Info(inv.Stdout, "Shutting down notifications manager..."+"\n") - err = shutdownWithTimeout(notificationsManager.Stop, 5*time.Second) - if err != nil { - cliui.Warnf(inv.Stderr, "Notifications manager shutdown took longer than 5s, "+ - "this may result in duplicate notifications being sent: %s\n", err) - } else { - cliui.Info(inv.Stdout, "Gracefully shut down notifications manager\n") - } + // Stop the notification manager, which will cause any buffered updates to the store to be flushed. + // If the Stop() call times out, messages that were sent but not reflected as such in the store will have + // their leases expire after a period of time and will be re-queued for sending. + // See CODER_NOTIFICATIONS_LEASE_PERIOD. + cliui.Info(inv.Stdout, "Shutting down notifications manager..."+"\n") + err = shutdownWithTimeout(notificationsManager.Stop, 5*time.Second) + if err != nil { + cliui.Warnf(inv.Stderr, "Notifications manager shutdown took longer than 5s, "+ + "this may result in duplicate notifications being sent: %s\n", err) + } else { + cliui.Info(inv.Stdout, "Gracefully shut down notifications manager\n") } // Shut down provisioners before waiting for WebSockets diff --git a/coderd/coderd.go b/coderd/coderd.go index 83a780474825b..cbe008a726636 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -37,11 +37,12 @@ import ( "tailscale.com/util/singleflight" "cdr.dev/slog" + "github.com/coder/quartz" + "github.com/coder/serpent" + "github.com/coder/coder/v2/coderd/entitlements" "github.com/coder/coder/v2/coderd/idpsync" "github.com/coder/coder/v2/coderd/runtimeconfig" - "github.com/coder/quartz" - "github.com/coder/serpent" agentproto "github.com/coder/coder/v2/agent/proto" "github.com/coder/coder/v2/buildinfo" @@ -1257,10 +1258,7 @@ func New(options *Options) *API { }) }) r.Route("/notifications", func(r chi.Router) { - r.Use( - apiKeyMiddleware, - httpmw.RequireExperiment(api.Experiments, codersdk.ExperimentNotifications), - ) + r.Use(apiKeyMiddleware) r.Get("/settings", api.notificationsSettings) r.Put("/settings", api.putNotificationsSettings) r.Route("/templates", func(r chi.Router) { diff --git a/coderd/notifications/manager.go b/coderd/notifications/manager.go index 3c983b2b3ee3d..8b765bbe88c33 100644 --- a/coderd/notifications/manager.go +++ b/coderd/notifications/manager.go @@ -54,6 +54,7 @@ type Manager struct { runOnce sync.Once stopOnce sync.Once + doneOnce sync.Once stop chan any done chan any @@ -153,7 +154,9 @@ func (m *Manager) Run(ctx context.Context) { // events, creating a notifier, and publishing bulk dispatch result updates to the store. func (m *Manager) loop(ctx context.Context) error { defer func() { - close(m.done) + m.doneOnce.Do(func() { + close(m.done) + }) m.log.Info(context.Background(), "notification manager stopped") }() @@ -364,7 +367,9 @@ func (m *Manager) Stop(ctx context.Context) error { // If the notifier hasn't been started, we don't need to wait for anything. // This is only really during testing when we want to enqueue messages only but not deliver them. if m.notifier == nil { - close(m.done) + m.doneOnce.Do(func() { + close(m.done) + }) } else { m.notifier.stop() } diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index 6cc9c9467e9fd..ca1f4f78aad72 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -1187,7 +1187,6 @@ func createOpts(t *testing.T) *coderdtest.Options { t.Helper() dt := coderdtest.DeploymentValues(t) - dt.Experiments = []string{string(codersdk.ExperimentNotifications)} return &coderdtest.Options{ DeploymentValues: dt, } diff --git a/coderd/notifications/reports/generator.go b/coderd/notifications/reports/generator.go index 0e5372aa8a894..2424498146c60 100644 --- a/coderd/notifications/reports/generator.go +++ b/coderd/notifications/reports/generator.go @@ -49,7 +49,7 @@ func NewReportGenerator(ctx context.Context, logger slog.Logger, db database.Sto return nil } - err = reportFailedWorkspaceBuilds(ctx, logger, db, enqueuer, clk) + err = reportFailedWorkspaceBuilds(ctx, logger, tx, enqueuer, clk) if err != nil { return xerrors.Errorf("unable to generate reports with failed workspace builds: %w", err) } diff --git a/coderd/notifications_test.go b/coderd/notifications_test.go index 17598cd812f7f..c4f0a551d4914 100644 --- a/coderd/notifications_test.go +++ b/coderd/notifications_test.go @@ -20,7 +20,6 @@ func createOpts(t *testing.T) *coderdtest.Options { t.Helper() dt := coderdtest.DeploymentValues(t) - dt.Experiments = []string{string(codersdk.ExperimentNotifications)} return &coderdtest.Options{ DeploymentValues: dt, } diff --git a/codersdk/deployment.go b/codersdk/deployment.go index da4f3daabea06..ed4d66001d8d6 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -2901,7 +2901,7 @@ const ( // users to opt-in to via --experimental='*'. // Experiments that are not ready for consumption by all users should // not be included here and will be essentially hidden. -var ExperimentsAll = Experiments{ExperimentNotifications} +var ExperimentsAll = Experiments{} // Experiments is a list of experiments. // Multiple experiments may be enabled at the same time. diff --git a/codersdk/provisionerdaemons.go b/codersdk/provisionerdaemons.go index 8fa314fd7849d..7ba10539b671c 100644 --- a/codersdk/provisionerdaemons.go +++ b/codersdk/provisionerdaemons.go @@ -12,6 +12,8 @@ import ( "github.com/google/uuid" "github.com/hashicorp/yamux" + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" "golang.org/x/xerrors" "nhooyr.io/websocket" @@ -278,9 +280,11 @@ func (c *Client) ServeProvisionerDaemon(ctx context.Context, req ServeProvisione type ProvisionerKeyTags map[string]string func (p ProvisionerKeyTags) String() string { + keys := maps.Keys(p) + slices.Sort(keys) tags := []string{} - for key, value := range p { - tags = append(tags, fmt.Sprintf("%s=%s", key, value)) + for _, key := range keys { + tags = append(tags, fmt.Sprintf("%s=%s", key, p[key])) } return strings.Join(tags, " ") } diff --git a/docs/admin/appearance.md b/docs/admin/appearance.md index edfd144834254..945d56a802fe8 100644 --- a/docs/admin/appearance.md +++ b/docs/admin/appearance.md @@ -1,4 +1,4 @@ -# Appearance (enterprise) +# Appearance (enterprise) (premium) Customize the look of your Coder deployment to meet your enterprise requirements. @@ -93,7 +93,3 @@ For CLI, use, export CODER_SUPPORT_LINKS='[{"name": "Hello GitHub", "target": "https://github.com/coder/coder", "icon": "bug"}, {"name": "Hello Slack", "target": "https://codercom.slack.com/archives/C014JH42DBJ", "icon": "https://raw.githubusercontent.com/coder/coder/main/site/static/icon/slack.svg"}, {"name": "Hello Discord", "target": "https://discord.gg/coder", "icon": "https://raw.githubusercontent.com/coder/coder/main/site/static/icon/discord.svg"}, {"name": "Hello Foobar", "target": "https://discord.gg/coder", "icon": "/emojis/1f3e1.png"}]' coder-server ``` - -## Up next - -- [Enterprise](../enterprise.md) diff --git a/docs/admin/audit-logs.md b/docs/admin/audit-logs.md index 8872d382fe79b..e0e17f12e1675 100644 --- a/docs/admin/audit-logs.md +++ b/docs/admin/audit-logs.md @@ -122,5 +122,5 @@ entry: ## Enabling this feature -This feature is only available with an enterprise license. -[Learn more](../enterprise.md) +This feature is only available with a +[Premium or Enterprise license](https://coder.com/pricing). diff --git a/docs/admin/auth.md b/docs/admin/auth.md index 7036c0e339757..58fa2045577e3 100644 --- a/docs/admin/auth.md +++ b/docs/admin/auth.md @@ -1,7 +1,5 @@ # Authentication -![OIDC with Coder Sequence Diagram](../images/oidc-sequence-diagram.svg). - By default, Coder is accessible via password authentication. Coder does not recommend using password authentication in production, and recommends using an authentication provider with properly configured multi-factor authentication @@ -227,7 +225,7 @@ your Coder deployment: CODER_DISABLE_PASSWORD_AUTH=true ``` -## SCIM (enterprise) +## SCIM (enterprise) (premium) Coder supports user provisioning and deprovisioning via SCIM 2.0 with header authentication. Upon deactivation, users are @@ -249,36 +247,50 @@ CODER_TLS_CLIENT_CERT_FILE=/path/to/cert.pem CODER_TLS_CLIENT_KEY_FILE=/path/to/key.pem ``` -## Group Sync (enterprise) +## Group Sync (enterprise) (premium) If your OpenID Connect provider supports group claims, you can configure Coder -to synchronize groups in your auth provider to groups within Coder. +to synchronize groups in your auth provider to groups within Coder. To enable +group sync, ensure that the `groups` claim is being sent by your OpenID +provider. You might need to request an additional +[scope](../reference/cli/server.md#--oidc-scopes) or additional configuration on +the OpenID provider side. -To enable group sync, ensure that the `groups` claim is set by adding the -correct scope to request. If group sync is enabled, the user's groups will be -controlled by the OIDC provider. This means manual group additions/removals will -be overwritten on the next login. +If group sync is enabled, the user's groups will be controlled by the OIDC +provider. This means manual group additions/removals will be overwritten on the +next user login. -```env -# as an environment variable -CODER_OIDC_SCOPES=openid,profile,email,groups -``` +There are two ways you can configure group sync: -```shell -# as a flag ---oidc-scopes openid,profile,email,groups +
+ +## Server Flags + +First, confirm that your OIDC provider is sending claims by logging in with OIDC +and visiting the following URL with an `Owner` account: + +```text +https://[coder.example.com]/api/v2/debug/[your-username]/debug-link ``` -With the `groups` scope requested, we also need to map the `groups` claim name. -Coder recommends using `groups` for the claim name. This step is necessary if -your **scope's name** is something other than `groups`. +You should see a field in either `id_token_claims`, `user_info_claims` or both +followed by a list of the user's OIDC groups in the response. This is the +[claim](https://openid.net/specs/openid-connect-core-1_0.html#Claims) sent by +the OIDC provider. See +[Troubleshooting](#troubleshooting-grouproleorganization-sync) to debug this. -```env +> Depending on the OIDC provider, this claim may be named differently. Common +> ones include `groups`, `memberOf`, and `roles`. + +Next configure the Coder server to read groups from the claim name with the +[OIDC group field](../reference/cli/server.md#--oidc-group-field) server flag: + +```sh # as an environment variable CODER_OIDC_GROUP_FIELD=groups ``` -```shell +```sh # as a flag --oidc-group-field groups ``` @@ -288,14 +300,16 @@ names in Coder and removed from groups that the user no longer belongs to. For cases when an OIDC provider only returns group IDs ([Azure AD][azure-gids]) or you want to have different group names in Coder than in your OIDC provider, -you can configure mapping between the two. +you can configure mapping between the two with the +[OIDC group mapping](../reference/cli/server.md#--oidc-group-mapping) server +flag. -```env +```sh # as an environment variable CODER_OIDC_GROUP_MAPPING='{"myOIDCGroupID": "myCoderGroupName"}' ``` -```shell +```sh # as a flag --oidc-group-mapping '{"myOIDCGroupID": "myCoderGroupName"}' ``` @@ -313,11 +327,103 @@ coder: From the example above, users that belong to the `myOIDCGroupID` group in your OIDC provider will be added to the `myCoderGroupName` group in Coder. -> **Note:** Groups are only updated on login. - [azure-gids]: https://github.com/MicrosoftDocs/azure-docs/issues/59766#issuecomment-664387195 +## Runtime (Organizations) + +> Note: You must have a Premium license with Organizations enabled to use this. +> [Contact your account team](https://coder.com/contact) for more details + +For deployments with multiple [organizations](./organizations.md), you must +configure group sync at the organization level. In future Coder versions, you +will be able to configure this in the UI. For now, you must use CLI commands. + +First confirm you have the [Coder CLI](../install/index.md) installed and are +logged in with a user who is an Owner or Organization Admin role. Next, confirm +that your OIDC provider is sending a groups claim by logging in with OIDC and +visiting the following URL: + +```text +https://[coder.example.com]/api/v2/debug/[your-username]/debug-link +``` + +You should see a field in either `id_token_claims`, `user_info_claims` or both +followed by a list of the user's OIDC groups in the response. This is the +[claim](https://openid.net/specs/openid-connect-core-1_0.html#Claims) sent by +the OIDC provider. See +[Troubleshooting](#troubleshooting-grouproleorganization-sync) to debug this. + +> Depending on the OIDC provider, this claim may be named differently. Common +> ones include `groups`, `memberOf`, and `roles`. + +To fetch the current group sync settings for an organization, run the following: + +```sh +coder organizations settings show group-sync \ + --org \ + > group-sync.json +``` + +The default for an organization looks like this: + +```json +{ + "field": "", + "mapping": null, + "regex_filter": null, + "auto_create_missing_groups": false +} +``` + +Below is an example that uses the `groups` claim and maps all groups prefixed by +`coder-` into Coder: + +```json +{ + "field": "groups", + "mapping": null, + "regex_filter": "^coder-.*$", + "auto_create_missing_groups": true +} +``` + +> Note: You much specify Coder group IDs instead of group names. The fastest way +> to find the ID for a corresponding group is by visiting +> `https://coder.example.com/api/v2/groups`. + +Here is another example which maps `coder-admins` from the identity provider to +2 groups in Coder and `coder-users` from the identity provider to another group: + +```json +{ + "field": "groups", + "mapping": { + "coder-admins": [ + "2ba2a4ff-ddfb-4493-b7cd-1aec2fa4c830", + "93371154-150f-4b12-b5f0-261bb1326bb4" + ], + "coder-users": ["2f4bde93-0179-4815-ba50-b757fb3d43dd"] + }, + "regex_filter": null, + "auto_create_missing_groups": false +} +``` + +To set these group sync settings, use the following command: + +```sh +coder organizations settings set group-sync \ + --org \ + < group-sync.json +``` + +Visit the Coder UI to confirm these changes: + +![IDP Sync](../images/admin/organizations/group-sync.png) + +
+ ### Group allowlist You can limit which groups from your identity provider can log in to Coder with @@ -326,11 +432,36 @@ Users who are not in a matching group will see the following error: ![Unauthorized group error](../images/admin/group-allowlist.png) -## Role sync (enterprise) +## Role sync (enterprise) (premium) If your OpenID Connect provider supports roles claims, you can configure Coder -to synchronize roles in your auth provider to deployment-wide roles within -Coder. +to synchronize roles in your auth provider to roles within Coder. + +There are 2 ways to do role sync. Server Flags assign site wide roles, and +runtime org role sync assigns organization roles + +
+ +## Server Flags + +First, confirm that your OIDC provider is sending a roles claim by logging in +with OIDC and visiting the following URL with an `Owner` account: + +```text +https://[coder.example.com]/api/v2/debug/[your-username]/debug-link +``` + +You should see a field in either `id_token_claims`, `user_info_claims` or both +followed by a list of the user's OIDC roles in the response. This is the +[claim](https://openid.net/specs/openid-connect-core-1_0.html#Claims) sent by +the OIDC provider. See +[Troubleshooting](#troubleshooting-grouproleorganization-sync) to debug this. + +> Depending on the OIDC provider, this claim may be named differently. + +Next configure the Coder server to read groups from the claim name with the +[OIDC role field](../reference/cli/server.md#--oidc-user-role-field) server +flag: Set the following in your Coder server [configuration](./configure.md). @@ -346,7 +477,136 @@ CODER_OIDC_USER_ROLE_MAPPING='{"TemplateAuthor":["template-admin","user-admin"]} > One role from your identity provider can be mapped to many roles in Coder > (e.g. the example above maps to 2 roles in Coder.) -## Troubleshooting group/role sync +## Runtime (Organizations) + +> Note: You must have a Premium license with Organizations enabled to use this. +> [Contact your account team](https://coder.com/contact) for more details + +For deployments with multiple [organizations](./organizations.md), you can +configure role sync at the organization level. In future Coder versions, you +will be able to configure this in the UI. For now, you must use CLI commands. + +First, confirm that your OIDC provider is sending a roles claim by logging in +with OIDC and visiting the following URL with an `Owner` account: + +```text +https://[coder.example.com]/api/v2/debug/[your-username]/debug-link +``` + +You should see a field in either `id_token_claims`, `user_info_claims` or both +followed by a list of the user's OIDC roles in the response. This is the +[claim](https://openid.net/specs/openid-connect-core-1_0.html#Claims) sent by +the OIDC provider. See +[Troubleshooting](#troubleshooting-grouproleorganization-sync) to debug this. + +> Depending on the OIDC provider, this claim may be named differently. + +To fetch the current group sync settings for an organization, run the following: + +```sh +coder organizations settings show role-sync \ + --org \ + > role-sync.json +``` + +The default for an organization looks like this: + +```json +{ + "field": "", + "mapping": null +} +``` + +Below is an example that uses the `roles` claim and maps `coder-admins` from the +IDP as an `Organization Admin` and also maps to a custom `provisioner-admin` +role. + +```json +{ + "field": "roles", + "mapping": { + "coder-admins": ["organization-admin"], + "infra-admins": ["provisioner-admin"] + } +} +``` + +> Note: Be sure to use the `name` field for each role, not the display name. Use +> `coder organization roles show --org=` to see roles for your +> organization. + +To set these role sync settings, use the following command: + +```sh +coder organizations settings set role-sync \ + --org \ + < role-sync.json +``` + +Visit the Coder UI to confirm these changes: + +![IDP Sync](../images/admin/organizations/role-sync.png) + +
+ +## Organization Sync (Premium) + +> Note: In a future Coder release, this can be managed via the Coder UI instead +> of server flags. + +If your OpenID Connect provider supports groups/role claims, you can configure +Coder to synchronize claims in your auth provider to organizations within Coder. + +First, confirm that your OIDC provider is sending clainms by logging in with +OIDC and visiting the following URL with an `Owner` account: + +```text +https://[coder.example.com]/api/v2/debug/[your-username]/debug-link +``` + +You should see a field in either `id_token_claims`, `user_info_claims` or both +followed by a list of the user's OIDC groups in the response. This is the +[claim](https://openid.net/specs/openid-connect-core-1_0.html#Claims) sent by +the OIDC provider. See +[Troubleshooting](#troubleshooting-grouproleorganization-sync) to debug this. + +> Depending on the OIDC provider, this claim may be named differently. Common +> ones include `groups`, `memberOf`, and `roles`. + +Next configure the Coder server to read groups from the claim name with the +[OIDC organization field](../reference/cli/server.md#--oidc-organization-field) +server flag: + +```sh +# as an environment variable +CODER_OIDC_ORGANIZATION_FIELD=groups +``` + +Next, fetch the corresponding organization IDs using the following endpoint: + +```text +https://[coder.example.com]/api/v2/organizations +``` + +Set the following in your Coder server [configuration](./configure.md). + +```env +CODER_OIDC_ORGANIZATION_MAPPING='{"data-scientists":["d8d9daef-e273-49ff-a832-11fe2b2d4ab1", "70be0908-61b5-4fb5-aba4-4dfb3a6c5787"]}' +``` + +> One claim value from your identity provider can be mapped to many +> organizations in Coder (e.g. the example above maps to 2 organizations in +> Coder.) + +By default, all users are assigned to the default (first) organization. You can +disable that with: + +```env +CODER_OIDC_ORGANIZATION_ASSIGN_DEFAULT=false +``` + +## Troubleshooting group/role/organization sync Some common issues when enabling group/role sync. diff --git a/docs/admin/external-auth.md b/docs/admin/external-auth.md index 4ff0cafc403a0..049b7a80d64d5 100644 --- a/docs/admin/external-auth.md +++ b/docs/admin/external-auth.md @@ -195,10 +195,10 @@ Optionally, you can request custom scopes: CODER_EXTERNAL_AUTH_0_SCOPES="repo:read repo:write write:gpg_key" ``` -### Multiple External Providers (enterprise) +### Multiple External Providers (enterprise) (premium) -Multiple providers are an Enterprise feature. [Learn more](../enterprise.md). -Below is an example configuration with multiple providers. +Multiple providers are an [Enterprise feature](https://coder.com/pricing). Below +is an example configuration with multiple providers. ```env # Provider 1) github.com diff --git a/docs/admin/groups.md b/docs/admin/groups.md index b982e7d311448..15a5e1b492e42 100644 --- a/docs/admin/groups.md +++ b/docs/admin/groups.md @@ -9,5 +9,5 @@ access to specific templates. They can be defined via the Coder web UI, ## Enabling this feature -This feature is only available with an enterprise license. -[Learn more](../enterprise.md) +This feature is only available with a +[Premium or Enterprise license](https://coder.com/pricing). diff --git a/docs/admin/high-availability.md b/docs/admin/high-availability.md index 8393b1ac186de..8534357d28801 100644 --- a/docs/admin/high-availability.md +++ b/docs/admin/high-availability.md @@ -73,4 +73,3 @@ Then, increase the number of pods. - [Networking](../networking/index.md) - [Kubernetes](../install/kubernetes.md) -- [Enterprise](../enterprise.md) diff --git a/docs/admin/notifications.md b/docs/admin/notifications.md index d4297fac455d8..91b0b43e42358 100644 --- a/docs/admin/notifications.md +++ b/docs/admin/notifications.md @@ -231,7 +231,7 @@ notification is indicated on the right hand side of this table. ![User Notification Preferences](../images/user-notification-preferences.png) -## Delivery Preferences (enterprise) +## Delivery Preferences (enterprise) (premium) Administrators can configure which delivery methods are used for each different [event type](#event-types). diff --git a/docs/admin/organizations.md b/docs/admin/organizations.md new file mode 100644 index 0000000000000..9f19faab615e8 --- /dev/null +++ b/docs/admin/organizations.md @@ -0,0 +1,110 @@ +# Organizations (Premium) + +> Note: Organizations requires a [Premium license](../licensing.md). For more +> details, [contact your account team](https://coder.com/contact). + +Organizations can be used to segment and isolate resources inside a Coder +deployment for different user groups or projects. + +## Example + +Here is an example of how one could use organizations to run a Coder deployment +with multiple platform teams, all with unique resources: + +![Organizations Example](../images/admin/organizations/diagram.png) + +## The default organization + +All Coder deployments start with one organization called `Coder`. + +To edit the organization details, navigate to `Deployment -> Organizations` in +the top bar: + +![Organizations Menu](../images/admin/organizations/deployment-organizations.png) + +From there, you can manage the name, icon, description, users, and groups: + +![Organization Settings](../images/admin/organizations/default-organization.png) + +## Additional organizations + +Any additional organizations have unique admins, users, templates, provisioners, +groups, and workspaces. Each organization must have at least one +[provisioner](./provisioners.md) as the built-in provisioner only applies to the +default organization. + +You can configure [organization/role/group sync](./auth.md) from your identity +provider to avoid manually assigning users to organizations. + +## Creating an organization + +### Prerequisites + +- Coder v2.16+ deployment with Premium license with Organizations enabled + ([contact your account team](https://coder.com/contact)) for more details. +- User with `Owner` role + +### 1. Create the organization + +Within the sidebar, click `New organization` to create an organization. In this +example, we'll create the `data-platform` org. + +![New Organization](../images/admin/organizations/new-organization.png) + +From there, let's deploy a provisioner and template for this organization. + +### 2. Deploy a provisioner + +[Provisioners](../admin/provisioners.md) are organization-scoped and are +responsible for executing Terraform/OpenTofu to provision the infrastructure for +workspaces and testing templates. Before creating templates, we must deploy at +least one provisioner as the built-in provisioners are scoped to the default +organization. + +Using Coder CLI, run the following command to create a key that will be used to +authenticate the provisioner: + +```sh +coder provisioner keys create data-cluster-key --org data-platform +Successfully created provisioner key data-cluster! Save this authentication token, it will not be shown again. + +< key omitted > +``` + +Next, start the provisioner with the key on your desired platform. In this +example, we'll start it using the Coder CLI on a host with Docker. For +instructions on using other platforms like Kubernetes, see our +[provisioner documentation](../admin/provisioners.md). + +```sh +export CODER_URL=https:// +export CODER_PROVISIONER_DAEMON_KEY= +coder provisionerd start --org +``` + +### 3. Create a template + +Once you've started a provisioner, you can create a template. You'll notice the +"Create Template" screen now has an organization dropdown: + +![Template Org Picker](../images/admin/organizations/template-org-picker.png) + +### 5. Add members + +Navigate to `Deployment->Organizations` to add members to your organization. +Once added, they will be able to see the organization-specific templates. + +![Add members](../images/admin/organizations/organization-members.png) + +### 6. Create a workspace + +Now, users in the data platform organization will see the templates related to +their organization. Users can be in multiple organizations. + +![Workspace List](../images/admin/organizations/workspace-list.png) + +## Beta + +Organizations is in beta. If you encounter any issues, please +[file an issue](https://github.com/coder/coder/issues/new) or contact your +account team. diff --git a/docs/admin/provisioners.md b/docs/admin/provisioners.md index acbf12f55f54e..31e7001843e61 100644 --- a/docs/admin/provisioners.md +++ b/docs/admin/provisioners.md @@ -3,10 +3,10 @@ By default, the Coder server runs [built-in provisioner daemons](../reference/cli/server.md#provisioner-daemons), which execute `terraform` during workspace and template builds. However, there -are sometimes benefits to running external provisioner daemons: +are often benefits to running external provisioner daemons: - **Secure build environments:** Run build jobs in isolated containers, - preventing malicious templates from gaining shell access to the Coder host. + preventing malicious templates from gaining sh access to the Coder host. - **Isolate APIs:** Deploy provisioners in isolated environments (on-prem, AWS, Azure) instead of exposing APIs (Docker, Kubernetes, VMware) to the Coder @@ -20,82 +20,101 @@ are sometimes benefits to running external provisioner daemons: times from the Coder server. See [Scaling Coder](scaling/scale-utility.md#recent-scale-tests) for more details. -Each provisioner can run a single -[concurrent workspace build](scaling/scale-testing.md#control-plane-provisionerd). +Each provisioner runs a single +[concurrent workspace build](scaling/scale-testing.md#control-plane-provisioner). For example, running 30 provisioner containers will allow 30 users to start workspaces at the same time. Provisioners are started with the -[coder provisionerd start](../reference/cli/provisioner_start.md) command. +[`coder provisioner start`](../reference/cli/provisioner_start.md) command in +the [full Coder binary](https://github.com/coder/coder/releases). Keep reading +to learn how to start provisioners via Docker, Kubernetes, Systemd, etc. ## Authentication -The provisioner daemon must authenticate with your Coder deployment. +The provisioner daemon must authenticate with your Coder deployment. If you have +multiple [organizations](./organizations.md), you'll need at least 1 provisioner +running for each organization. -Set a -[provisioner daemon pre-shared key (PSK)](../reference/cli/server.md#--provisioner-daemon-psk) -on the Coder server and start the provisioner with -`coder provisionerd start --psk `. If you are -[installing with Helm](../install/kubernetes.md#install-coder-with-helm), see -the [Helm example](#example-running-an-external-provisioner-with-helm) below. +
-> Coder still supports authenticating the provisioner daemon with a -> [token](../reference/cli/README.md#--token) from a user with the Template -> Admin or Owner role. This method is deprecated in favor of the PSK, which only -> has permission to access provisioner daemon APIs. We recommend migrating to -> the PSK as soon as practical. +## Scoped Key (Recommended) -## Types of provisioners +We recommend creating finely-scoped keys for provisioners. Keys are scoped to an +organization. -Provisioners can broadly be categorized by scope: `organization` or `user`. The -scope of a provisioner can be specified with -[`-tag=scope=`](../reference/cli/provisioner_start.md#t---tag) when -starting the provisioner daemon. Only users with at least the -[Template Admin](../admin/users.md#roles) role or higher may create -organization-scoped provisioner daemons. +```sh +coder provisioner keys create my-key \ + --org default -There are two exceptions: +Successfully created provisioner key my-key! Save this authentication token, it will not be shown again. -- [Built-in provisioners](../reference/cli/server.md#provisioner-daemons) are - always organization-scoped. -- External provisioners started using a - [pre-shared key (PSK)](../reference/cli/provisioner_start.md#psk) are always - organization-scoped. + +``` -### Organization-Scoped Provisioners +Or, restrict the provisioner to jobs with specific tags -**Organization-scoped Provisioners** can pick up build jobs created by any user. -These provisioners always have the implicit tags `scope=organization owner=""`. +```sh +coder provisioner keys create kubernetes-key \ + --org default \ + --tag environment=kubernetes + +Successfully created provisioner key kubernetes-key! Save this authentication token, it will not be shown again. -```shell -coder provisionerd start --org + ``` -If you omit the `--org` argument, the provisioner will be assigned to the -default organization. +To start the provisioner: -```shell -coder provisionerd start +```sh +export CODER_URL=https:// +export CODER_PROVISIONER_DAEMON_KEY= +coder provisioner start ``` -### User-scoped Provisioners +Keep reading to see instructions for running provisioners on +Kubernetes/Docker/etc. -**User-scoped Provisioners** can only pick up build jobs created from -user-tagged templates. Unlike the other provisioner types, any Coder user can -run user provisioners, but they have no impact unless there exists at least one -template with the `scope=user` provisioner tag. +## User Tokens -```shell -coder provisionerd start \ - --tag scope=user +A user account with the role `Template Admin` or `Owner` can start provisioners +using their user account. This may be beneficial if you are running provisioners +via [automation](./automation.md). -# In another terminal, create/push -# a template that requires user provisioners -coder templates push on-prem \ - --provisioner-tag scope=user +```sh +coder login https:// +coder provisioner start +``` + +To start a provisioner with specific tags: + +```sh +coder login https:// +coder provisioner start \ + --tag environment=kubernetes ``` -### Provisioner Tags +Note: Any user can start [user-scoped provisioners](#User-scoped-Provisioners), +but this will also require a template on your deployment with the corresponding +tags. + +## Global PSK + +A deployment-wide PSK can be used to authenticate any provisioner. We do not +recommend this approach anymore, as it makes key rotation or isolating +provisioners far more difficult. To use a global PSK, set a +[provisioner daemon pre-shared key (PSK)](../reference/cli/server.md#--provisioner-daemon-psk) +on the Coder server. + +Next, start the provisioner: + +```sh +coder provisioner start --psk +``` + +
+ +## Provisioner Tags You can use **provisioner tags** to control which provisioners can pick up build jobs from templates (and corresponding workspaces) with matching explicit tags. @@ -110,10 +129,10 @@ automatically. For example: -```shell +```sh # Start a provisioner with the explicit tags # environment=on_prem and datacenter=chicago -coder provisionerd start \ +coder provisioner start \ --tag environment=on_prem \ --tag datacenter=chicago @@ -129,6 +148,10 @@ coder templates push on-prem-chicago \ --provisioner-tag datacenter=chicago ``` +Alternatively, a template can target a provisioner via +[workspace tags](https://github.com/coder/coder/tree/main/examples/workspace-tags) +inside the Terraform. + A provisioner can run a given build job if one of the below is true: 1. A job with no explicit tags can only be run on a provisioner with no explicit @@ -176,9 +199,59 @@ This is illustrated in the below table: > copy the output: > > ``` -> go test -v -count=1 ./coderd/provisionerdserver/ -test.run='^TestAcquirer_MatchTags/GenTable$' +> go test -v -count=1 ./coderd/provisionerserver/ -test.run='^TestAcquirer_MatchTags/GenTable$' > ``` +## Types of provisioners + +Provisioners can broadly be categorized by scope: `organization` or `user`. The +scope of a provisioner can be specified with +[`-tag=scope=`](../reference/cli/provisioner_start.md#t---tag) when +starting the provisioner daemon. Only users with at least the +[Template Admin](../admin/users.md#roles) role or higher may create +organization-scoped provisioner daemons. + +There are two exceptions: + +- [Built-in provisioners](../reference/cli/server.md#provisioner-daemons) are + always organization-scoped. +- External provisioners started using a + [pre-shared key (PSK)](../reference/cli/provisioner_start.md#psk) are always + organization-scoped. + +### Organization-Scoped Provisioners + +**Organization-scoped Provisioners** can pick up build jobs created by any user. +These provisioners always have the implicit tags `scope=organization owner=""`. + +```sh +coder provisioner start --org +``` + +If you omit the `--org` argument, the provisioner will be assigned to the +default organization. + +```sh +coder provisioner start +``` + +### User-scoped Provisioners + +**User-scoped Provisioners** can only pick up build jobs created from +user-tagged templates. Unlike the other provisioner types, any Coder user can +run user provisioners, but they have no impact unless there exists at least one +template with the `scope=user` provisioner tag. + +```sh +coder provisioner start \ + --tag scope=user + +# In another terminal, create/push +# a template that requires user provisioners +coder templates push on-prem \ + --provisioner-tag scope=user +``` + ## Example: Running an external provisioner with Helm Coder provides a Helm chart for running external provisioner daemons, which you @@ -187,21 +260,21 @@ will use in concert with the Helm chart for deploying the Coder server. 1. Create a long, random pre-shared key (PSK) and store it in a Kubernetes secret - ```shell + ```sh kubectl create secret generic coder-provisioner-psk --from-literal=psk=`head /dev/urandom | base64 | tr -dc A-Za-z0-9 | head -c 26` ``` 1. Modify your Coder `values.yaml` to include ```yaml - provisionerDaemon: + provisioneraemon: pskSecretName: "coder-provisioner-psk" ``` 1. Redeploy Coder with the new `values.yaml` to roll out the PSK. You can omit `--version ` to also upgrade Coder to the latest version. - ```shell + ```sh helm upgrade coder coder-v2/coder \ --namespace coder \ --version \ @@ -217,7 +290,7 @@ will use in concert with the Helm chart for deploying the Coder server. - name: CODER_URL value: "https://coder.example.com" replicaCount: 10 - provisionerDaemon: + provisioneraemon: pskSecretName: "coder-provisioner-psk" tags: location: auh @@ -235,7 +308,7 @@ will use in concert with the Helm chart for deploying the Coder server. 1. Install the provisioner daemon chart - ```shell + ```sh helm install coder-provisioner coder-v2/coder-provisioner \ --namespace coder \ --version \ @@ -244,26 +317,26 @@ will use in concert with the Helm chart for deploying the Coder server. You can verify that your provisioner daemons have successfully connected to Coderd by looking for a debug log message that says - `provisionerd: successfully connected to coderd` from each Pod. + `provisioner: successfully connected to coderd` from each Pod. ## Example: Running an external provisioner on a VM -```shell +```sh curl -L https://coder.com/install.sh | sh export CODER_URL=https://coder.example.com export CODER_SESSION_TOKEN=your_token -coder provisionerd start +coder provisioner start ``` ## Example: Running an external provisioner via Docker -```shell +```sh docker run --rm -it \ -e CODER_URL=https://coder.example.com/ \ -e CODER_SESSION_TOKEN=your_token \ --entrypoint /opt/coder \ ghcr.io/coder/coder:latest \ - provisionerd start + provisioner start ``` ## Disable built-in provisioners @@ -272,7 +345,7 @@ As mentioned above, the Coder server will run built-in provisioners by default. This can be disabled with a server-wide [flag or environment variable](../reference/cli/server.md#provisioner-daemons). -```shell +```sh coder server --provisioner-daemons=0 ``` diff --git a/docs/admin/quotas.md b/docs/admin/quotas.md index 88ca4b27860dc..79cd8f43b2162 100644 --- a/docs/admin/quotas.md +++ b/docs/admin/quotas.md @@ -102,5 +102,4 @@ Form will never get held up by quota enforcement. ## Up next -- [Enterprise](../enterprise.md) - [Configuring](./configure.md) diff --git a/docs/admin/rbac.md b/docs/admin/rbac.md index 86fd46a2bf723..7ca9e3c29131a 100644 --- a/docs/admin/rbac.md +++ b/docs/admin/rbac.md @@ -19,5 +19,5 @@ You can set the following permissions: ## Enabling this feature -This feature is only available with an enterprise license. -[Learn more](../enterprise.md) +This feature is only available with an +[Enterprise or Premium license](https://coder.com/pricing). diff --git a/docs/admin/upgrade.md b/docs/admin/upgrade.md index eb24e0f5d5e4f..d9b72f9295dc2 100644 --- a/docs/admin/upgrade.md +++ b/docs/admin/upgrade.md @@ -53,7 +53,3 @@ from Winget. ```pwsh winget install Coder.Coder ``` - -## Up Next - -- [Learn how to enable Enterprise features](../enterprise.md). diff --git a/docs/admin/users.md b/docs/admin/users.md index 02832a7e22320..20cca2711af9c 100644 --- a/docs/admin/users.md +++ b/docs/admin/users.md @@ -10,7 +10,7 @@ Coder offers these user roles in the community edition: | | Auditor | User Admin | Template Admin | Owner | | ----------------------------------------------------- | ------- | ---------- | -------------- | ----- | | Add and remove Users | | ✅ | | ✅ | -| Manage groups (enterprise) | | ✅ | | ✅ | +| Manage groups (premium) | | ✅ | | ✅ | | Change User roles | | | | ✅ | | Manage **ALL** Templates | | | ✅ | ✅ | | View **ALL** Workspaces | | | ✅ | ✅ | @@ -22,6 +22,16 @@ Coder offers these user roles in the community edition: A user may have one or more roles. All users have an implicit Member role that may use personal workspaces. +## Custom Roles (Premium) (Beta) + +Coder v2.16+ deployments can configure custom roles on the +[Organization](./organizations.md) level. + +![Custom roles](../images/admin/organizations/custom-roles.png) + +> Note: This requires a Premium license. +> [Contact your account team](https://coder.com/contact) for more details. + ## Security notes A malicious Template Admin could write a template that executes commands on the diff --git a/docs/changelogs/v2.0.0.md b/docs/changelogs/v2.0.0.md index cfa653900b27b..d245e70819056 100644 --- a/docs/changelogs/v2.0.0.md +++ b/docs/changelogs/v2.0.0.md @@ -64,7 +64,7 @@ ben@coder.com! Stream Kubernetes event logs to the Coder agent logs to reveal Kuernetes-level issues such as ResourceQuota limitations, invalid images, etc. ![Kubernetes quota](https://raw.githubusercontent.com/coder/coder/main/docs/platforms/kubernetes/coder-logstream-kube-logs-quota-exceeded.png) -- [OIDC Role Sync](https://coder.com/docs/admin/auth#group-sync-enterprise) +- [OIDC Role Sync](https://coder.com/docs/admin/auth#group-sync-enterprise-premium) (Enterprise): Sync roles from your OIDC provider to Coder roles (e.g. `Template Admin`) (#8595) (@Emyrk) - Users can convert their accounts from username/password authentication to SSO diff --git a/docs/contributing/feature-stages.md b/docs/contributing/feature-stages.md index 26eaf3272b47b..4489c212e25e0 100644 --- a/docs/contributing/feature-stages.md +++ b/docs/contributing/feature-stages.md @@ -1,21 +1,34 @@ # Feature stages -Some Coder features are released as Alpha or Experimental. +Some Coder features are released in feature stages before they are generally +available. -## Alpha features +If you encounter an issue with any Coder feature, please submit a +[GitHub issues](https://github.com/coder/coder/issues) or join the +[Coder Discord](https://discord.gg/coder). -Alpha features are enabled in all Coder deployments but the feature is subject -to change, or even be removed. Breaking changes may not be documented in the -changelog. In most cases, features will only stay in alpha for 1 month. +## Early access features -We recommend using [GitHub issues](https://github.com/coder/coder/issues) to -leave feedback and get support for alpha features. +Early access features are neither feature-complete nor stable. We do not +recommend using early access features in production deployments. + +Coder releases early access features behind an “unsafe” experiment, where +they’re accessible but not easy to find. ## Experimental features These features are disabled by default, and not recommended for use in production as they may cause performance or stability issues. In most cases, -features will only stay in experimental for 1-2 weeks of internal testing. +experimental features are complete, but require further internal testing and +will stay in the experimental stage for one month. + +Coder may make significant changes to experiments or revert features to a +feature flag at any time. + +If you plan to activate an experimental feature, we suggest that you use a +staging deployment. + +You can opt-out of an experiment after you've enabled it. ```yaml # Enable all experimental features @@ -27,7 +40,7 @@ coder server --experiments=feature1,feature2 # Alternatively, use the `CODER_EXPERIMENTS` environment variable. ``` -## Available experimental features +### Available experimental features @@ -37,3 +50,14 @@ coder server --experiments=feature1,feature2 | `notifications` | Sends notifications via SMTP and webhooks following certain events. | mainline, stable | + +## Beta + +Beta features are open to the public, but are tagged with a `Beta` label. + +They’re subject to minor changes and may contain bugs, but are generally ready +for use. + +## General Availability (GA) + +All other features have been tested, are stable, and are enabled by default. diff --git a/docs/enterprise.md b/docs/enterprise.md deleted file mode 100644 index 275fcddf60be3..0000000000000 --- a/docs/enterprise.md +++ /dev/null @@ -1,56 +0,0 @@ -# Enterprise Features - -Coder is free to use and includes some features that are only accessible with a -paid license. [Contact Sales](https://coder.com/contact) for pricing or -[get a free trial](https://coder.com/trial). - -| Category | Feature | Open Source | Enterprise | -| --------------- | --------------------------------------------------------------------------------------------------- | :---------: | :--------: | -| Support | Email, Prioritization | ❌ | ✅ | -| Scale | [High Availability](./admin/high-availability.md) | ❌ | ✅ | -| Scale | [Multiple External Auth Providers](./admin/external-auth.md#multiple-external-providers-enterprise) | ❌ | ✅ | -| Scale | [Isolated Terraform Runners](./admin/provisioners.md) | ❌ | ✅ | -| Scale | [Workspace Proxies](./admin/workspace-proxies.md) | ❌ | ✅ | -| Governance | [Audit Logging](./admin/audit-logs.md) | ❌ | ✅ | -| Governance | [Browser Only Connections](./networking/#browser-only-connections-enterprise) | ❌ | ✅ | -| Governance | [Groups & Template RBAC](./admin/rbac.md) | ❌ | ✅ | -| Cost Control | [Quotas](./admin/quotas.md) | ❌ | ✅ | -| Cost Control | [Max Workspace Lifetime](./workspaces.md#max-lifetime) | ❌ | ✅ | -| User Management | [Groups](./admin/groups.md) | ❌ | ✅ | -| User Management | [Group & role sync](./admin/auth.md#group-sync-enterprise) | ❌ | ✅ | -| User Management | [SCIM](./admin/auth.md#scim) | ❌ | ✅ | - -## Adding your license key - -There are two ways to add an enterprise license to a Coder deployment: In the -Coder UI or with the Coder CLI. - -### Coder UI - -Click Deployment, Licenses, Add a license then drag or select the license file -with the `jwt` extension. - -![Add License UI](./images/add-license-ui.png) - -### Coder CLI - -### Requirements - -- Your license key -- Coder CLI installed - -### Instructions - -1. Save your license key to disk and make note of the path -2. Open a terminal -3. Ensure you are logged into your Coder deployment - - `coder login ` - -4. Run - - `coder licenses add -f ` - -## Up Next - -- [Learn how to contribute to Coder](./CONTRIBUTING.md). diff --git a/docs/faqs.md b/docs/faqs.md index dceb8ac15b04e..7affd790380ff 100644 --- a/docs/faqs.md +++ b/docs/faqs.md @@ -1,14 +1,13 @@ # FAQs -Frequently asked questions on Coder OSS and Enterprise deployments. These FAQs -come from our community and enterprise customers, feel free to +Frequently asked questions on Coder OSS and Premium deployments. These FAQs come +from our community and enterprise customers, feel free to [contribute to this page](https://github.com/coder/coder/edit/main/docs/faqs.md). -### How do I add an enterprise license? +### How do I add a Premium trial license? Visit https://coder.com/trial or contact -[sales@coder.com](mailto:sales@coder.com?subject=License) to get a v2 enterprise -trial key. +[sales@coder.com](mailto:sales@coder.com?subject=License) to get a trial key. You can add a license through the UI or CLI. diff --git a/docs/guides/configuring-okta.md b/docs/guides/configuring-okta.md index d52c99a5a7974..d26c09feb7f43 100644 --- a/docs/guides/configuring-okta.md +++ b/docs/guides/configuring-okta.md @@ -46,7 +46,7 @@ be sent. Configure Coder to use these claims for group sync. These claims are present in the `id_token`. See all configuration options for group sync in the -[docs](https://coder.com/docs/admin/auth#group-sync-enterprise). +[docs](https://coder.com/docs/admin/auth#group-sync-enterprise-premium). ```bash # Add the 'groups' scope. diff --git a/docs/guides/index.md b/docs/guides/index.md index 40d842685df44..c1768210d0d91 100644 --- a/docs/guides/index.md +++ b/docs/guides/index.md @@ -1,7 +1,7 @@ # Guides and Tutorials Here you can find a list of employee-written guides on Coder for OSS and -Enterprise. These tutorials are hosted on our +Premium. These tutorials are hosted on our [Github](https://github.com/coder/coder/) where you can leave feedback or request new topics to be covered. diff --git a/docs/guides/using-organizations.md b/docs/guides/using-organizations.md deleted file mode 100644 index 88b52313db71a..0000000000000 --- a/docs/guides/using-organizations.md +++ /dev/null @@ -1,135 +0,0 @@ -# Using Organizations - -> Note: Organizations is still under active development and requires a -> non-standard enterprise license to use. Do not use organizations on your -> production instance! -> -> For more details, [contact your account team](https://coder.com/contact). - -Organizations allow you to run a Coder deployment with multiple platform teams, -all with uniquely scoped templates, provisioners, users, groups, and workspaces. - -## Prerequisites - -- Coder deployment with non-standard license with Organizations enabled - ([contact your account team](https://coder.com/contact)) -- User with `Owner` role -- Coder CLI installed on local machine - -## Switch to the preview image and enable the experiment - -To try the latest organizations features, switch to a preview image in your Helm -chart and enable the -[experimental flag](../reference/cli/server.md#--experiments). - -For example, with Kubernetes, set the following in your `values.yaml`: - -```yaml -coderd: - image: - repo: ghcr.io/coder/coder-preview - tag: orgs-preview-aug-16 - env: - - name: CODER_EXPERIMENTS - value: multi-organization -``` - -> See all -> [preview images](https://github.com/coder/coder/pkgs/container/coder-preview) -> in GitHub. Preview images prefixed with `main-` expire after a week. - -Then, upgrade your deployment: - -```sh -helm upgrade coder coder-v2/coder -f values.yaml -``` - -## The default organization - -All Coder deployments start with one organization called `Default`. - -To edit the organization details, navigate to `Deployment -> Organizations` in -the top bar: - -![Organizations Menu](../images/guides/using-organizations/deployment-organizations.png) - -From there, you can manage the name, icon, description, users, and groups: - -![Organization Settings](../images/guides/using-organizations/default-organization.png) - -## Guide: Your first organization - -### 1. Create the organization - -Within the sidebar, click `New organization` to create an organization. In this -example, we'll create the `data-platform` org. - -![New Organization](../images/guides/using-organizations/new-organization.png) - -From there, let's deploy a provisioner and template for this organization. - -### 2. Deploy a provisioner - -[Provisioners](../admin/provisioners.md) are organization-scoped and are -responsible for executing Terraform/OpenTofu to provision the infrastructure for -workspaces and testing templates. Before creating templates, we must deploy at -least one provisioner as the built-in provisioners are scoped to the default -organization. - -using Coder CLI, run the following command to create a key that will be used to -authenticate the provisioner: - -```sh -coder provisioner keys create data-cluster-key --org data-platform -Successfully created provisioner key data-cluster! Save this authentication token, it will not be shown again. - -< key omitted > -``` - -Next, start the provisioner with the key on your desired platform. In this -example, we'll start it using the Coder CLI on a host with Docker. For -instructions on using other platforms like Kubernetes, see our -[provisioner documentation](../admin/provisioners.md). - -```sh -export CODER_URL=https:// -export CODER_PROVISIONER_DAEMON_KEY= -coder provisionerd start --org -``` - -### 3. Create a template - -Once you've started a provisioner, you can create a template. You'll notice the -"Create Template" screen now has an organization dropdown: - -![Template Org Picker](../images/guides/using-organizations/template-org-picker.png) - -### 5. Add members - -Navigate to `Deployment->Organizations` to add members to your organization. -Once added, they will be able to see the organization-specific templates. - -![Add members](../images/guides/using-organizations/organization-members.png) - -### 6. Create a workspace - -Now, users in the data platform organization will see the templates related to -their organization. Users can be in multiple organizations. - -![Workspace List](../images/guides/using-organizations/workspace-list.png) - -## Planned work - -Organizations is under active development. The work is planned before -organizations is generally available: - -- View provisioner health via the Coder UI -- Custom Role support in Coder UI -- Per-organization quotas -- Improved visibility of organization-specific resources throughout the UI -- Sync OIDC claims to auto-assign users to organizations / roles + SCIM support - -## Support & Feedback - -[Contact your account team](https://coder.com/contact) if you have any questions -or feedback. diff --git a/docs/images/add-license-ui.png b/docs/images/add-license-ui.png index 03ff419d15a59..837698908e8f1 100644 Binary files a/docs/images/add-license-ui.png and b/docs/images/add-license-ui.png differ diff --git a/docs/images/admin/organizations/custom-roles.png b/docs/images/admin/organizations/custom-roles.png new file mode 100644 index 0000000000000..505fc5730ddd4 Binary files /dev/null and b/docs/images/admin/organizations/custom-roles.png differ diff --git a/docs/images/guides/using-organizations/default-organization.png b/docs/images/admin/organizations/default-organization.png similarity index 100% rename from docs/images/guides/using-organizations/default-organization.png rename to docs/images/admin/organizations/default-organization.png diff --git a/docs/images/guides/using-organizations/deployment-organizations.png b/docs/images/admin/organizations/deployment-organizations.png similarity index 100% rename from docs/images/guides/using-organizations/deployment-organizations.png rename to docs/images/admin/organizations/deployment-organizations.png diff --git a/docs/images/admin/organizations/diagram.png b/docs/images/admin/organizations/diagram.png new file mode 100644 index 0000000000000..b7d232c274b42 Binary files /dev/null and b/docs/images/admin/organizations/diagram.png differ diff --git a/docs/images/admin/organizations/group-sync.png b/docs/images/admin/organizations/group-sync.png new file mode 100644 index 0000000000000..a4013f2f15559 Binary files /dev/null and b/docs/images/admin/organizations/group-sync.png differ diff --git a/docs/images/guides/using-organizations/new-organization.png b/docs/images/admin/organizations/new-organization.png similarity index 100% rename from docs/images/guides/using-organizations/new-organization.png rename to docs/images/admin/organizations/new-organization.png diff --git a/docs/images/guides/using-organizations/organization-members.png b/docs/images/admin/organizations/organization-members.png similarity index 100% rename from docs/images/guides/using-organizations/organization-members.png rename to docs/images/admin/organizations/organization-members.png diff --git a/docs/images/admin/organizations/role-sync.png b/docs/images/admin/organizations/role-sync.png new file mode 100644 index 0000000000000..1b0fafb39fae1 Binary files /dev/null and b/docs/images/admin/organizations/role-sync.png differ diff --git a/docs/images/guides/using-organizations/template-org-picker.png b/docs/images/admin/organizations/template-org-picker.png similarity index 100% rename from docs/images/guides/using-organizations/template-org-picker.png rename to docs/images/admin/organizations/template-org-picker.png diff --git a/docs/images/guides/using-organizations/workspace-list.png b/docs/images/admin/organizations/workspace-list.png similarity index 100% rename from docs/images/guides/using-organizations/workspace-list.png rename to docs/images/admin/organizations/workspace-list.png diff --git a/docs/images/icons/licensing.svg b/docs/images/icons/licensing.svg new file mode 100644 index 0000000000000..6e876fd359583 --- /dev/null +++ b/docs/images/icons/licensing.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/images/icons/orgs.svg b/docs/images/icons/orgs.svg new file mode 100644 index 0000000000000..ff65ea99664b6 --- /dev/null +++ b/docs/images/icons/orgs.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/licensing.md b/docs/licensing.md new file mode 100644 index 0000000000000..e34e813c2a354 --- /dev/null +++ b/docs/licensing.md @@ -0,0 +1,47 @@ +# Licensing + +Some features are only accessible with a Premium or Enterprise license. See our +[pricing page](https://coder.com/pricing) for more details. To try Premium +features, you can [request a trial](https://coder.com/trial) or +[contact sales](https://coder.com/contact). + + + +> If you are an existing customer, you can learn more our new Premium plan in +> the [Coder v2.16 blog post](https://coder.com/blog/release-recap-2-16-0) + + + +## Adding your license key + +There are two ways to add a license to a Coder deployment: + +
+ +### Coder UI + +First, ensure you have a license key +([request a trial](https://coder.com/trial)). + +With an `Owner` account, navigate to `Deployment -> Licenses`, `Add a license` +then drag or select the license file with the `jwt` extension. + +![Add License UI](./images/add-license-ui.png) + +### Coder CLI + +First, ensure you have a license key +([request a trial](https://coder.com/trial)) and the +[Coder CLI](./install/index.md) installed. + +1. Save your license key to disk and make note of the path +2. Open a terminal +3. Ensure you are logged into your Coder deployment + + `coder login ` + +4. Run + + `coder licenses add -f ` + +
diff --git a/docs/manifest.json b/docs/manifest.json index 1826c6b62685e..7db5fc142191d 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -282,7 +282,7 @@ "title": "Process Logging", "description": "Audit commands in workspaces with exectrace", "path": "./templates/process-logging.md", - "state": "enterprise" + "state": ["enterprise", "premium"] }, { "title": "Icons", @@ -393,14 +393,21 @@ "description": "Learn how to manage user groups", "path": "./admin/groups.md", "icon_path": "./images/icons/group.svg", - "state": "enterprise" + "state": ["enterprise", "premium"] }, { - "title": "RBAC", - "description": "Learn how to use the role based access control", + "title": "Organizations", + "description": "Learn how to manage organizations", + "path": "./admin/organizations.md", + "icon_path": "./images/icons/orgs.svg", + "state": ["premium"] + }, + { + "title": "Template RBAC", + "description": "Learn how to use the role based access control against templates", "path": "./admin/rbac.md", "icon_path": "./images/icons/rbac.svg", - "state": "enterprise" + "state": ["enterprise", "beta"] }, { "title": "Configuration", @@ -443,14 +450,14 @@ "description": "Run provisioners isolated from the Coder server", "path": "./admin/provisioners.md", "icon_path": "./images/icons/queue.svg", - "state": "enterprise" + "state": ["enterprise", "premium"] }, { "title": "Workspace Proxies", "description": "Run geo distributed workspace proxies", "path": "./admin/workspace-proxies.md", "icon_path": "./images/icons/networking.svg", - "state": "enterprise" + "state": ["enterprise", "premium"] }, { "title": "Application Logs", @@ -463,21 +470,21 @@ "description": "Learn how to use Audit Logs in your Coder deployment", "path": "./admin/audit-logs.md", "icon_path": "./images/icons/radar.svg", - "state": "enterprise" + "state": ["enterprise", "premium"] }, { "title": "Quotas", "description": "Learn how to use Workspace Quotas in Coder", "path": "./admin/quotas.md", "icon_path": "./images/icons/dollar.svg", - "state": "enterprise" + "state": ["enterprise", "premium"] }, { "title": "High Availability", "description": "Learn how to configure Coder for High Availability", "path": "./admin/high-availability.md", "icon_path": "./images/icons/hydra.svg", - "state": "enterprise" + "state": ["enterprise", "premium"] }, { "title": "Prometheus", @@ -490,7 +497,7 @@ "description": "Learn how to configure the appearance of Coder", "path": "./admin/appearance.md", "icon_path": "./images/icons/info.svg", - "state": "enterprise" + "state": ["enterprise", "premium"] }, { "title": "Telemetry", @@ -503,7 +510,7 @@ "description": "Learn how to encrypt sensitive data at rest in Coder", "path": "./admin/encryption.md", "icon_path": "./images/icons/lock.svg", - "state": "enterprise" + "state": ["enterprise", "premium"] }, { "title": "Deployment Health", @@ -521,23 +528,23 @@ "title": "Slack Notifications", "description": "Learn how to setup Slack notifications", "path": "./admin/notifications/slack.md", - "state": "beta" + "state": ["beta"] }, { "title": "Microsoft Teams Notifications", "description": "Learn how to setup Microsoft Teams notifications", "path": "./admin/notifications/teams.md", - "state": "beta" + "state": ["beta"] } ] } ] }, { - "title": "Enterprise", - "description": "Learn how to enable Enterprise features", - "path": "./enterprise.md", - "icon_path": "./images/icons/group.svg" + "title": "Licensing", + "description": "Learn how to enable Premium features", + "path": "./licensing.md", + "icon_path": "./images/icons/licensing.svg" }, { "title": "Contributing", @@ -1337,11 +1344,6 @@ "title": "Cloning Git Repositories", "description": "Automatically clone Git repositories into your workspace", "path": "./guides/cloning-git-repositories.md" - }, - { - "title": "Using Organizations", - "description": "Learn how to use our (early access) Organizations functionality", - "path": "./guides/using-organizations.md" } ] } diff --git a/docs/networking/index.md b/docs/networking/index.md index d4abddc5718c8..781cb6aae6dca 100644 --- a/docs/networking/index.md +++ b/docs/networking/index.md @@ -157,10 +157,10 @@ $ coder server --derp-config-path derpmap.json The dashboard (and web apps opened through the dashboard) are served from the coder server, so they can only be geo-distributed with High Availability mode in -our Enterprise Edition. [Reach out to Sales](https://coder.com/contact) to learn -more. +our Enterprise and Premium Editions. +[Reach out to Sales](https://coder.com/contact) to learn more. -## Browser-only connections (enterprise) +## Browser-only connections (enterprise) (premium) Some Coder deployments require that all access is through the browser to comply with security policies. In these cases, pass the `--browser-only` flag to diff --git a/docs/networking/port-forwarding.md b/docs/networking/port-forwarding.md index 89454b8258e3d..b0e178708a9de 100644 --- a/docs/networking/port-forwarding.md +++ b/docs/networking/port-forwarding.md @@ -129,7 +129,7 @@ resource uses a different method of authentication and **is not impacted by the template's maximum sharing level**, nor the level of a shared port that points to the app. -### Configure maximum port sharing level (enterprise) +### Configure maximum port sharing level (enterprise) (premium) Enterprise-licensed template admins can control the maximum port sharing level for workspaces under a given template in the template settings. By default, the diff --git a/docs/workspaces.md b/docs/workspaces.md index 2968420022770..1ce503218b699 100644 --- a/docs/workspaces.md +++ b/docs/workspaces.md @@ -83,7 +83,7 @@ coder_app. ![Autostop UI](./images/autostop.png) -### Autostop requirement (enterprise) +### Autostop requirement (enterprise) (premium) Autostop requirement is a template setting that determines how often workspaces using the template must automatically stop. Autostop requirement ignores any @@ -113,7 +113,7 @@ Autostop requirement is disabled when the template is using the deprecated max lifetime feature. Templates can choose to use a max lifetime or an autostop requirement during the deprecation period, but only one can be used at a time. -### User quiet hours (enterprise) +### User quiet hours (enterprise) (premium) User quiet hours can be configured in the user's schedule settings page. Workspaces on templates with an autostop requirement will only be forcibly diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index 5127e6ec0887f..e0b8170c6ec2a 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -448,7 +448,6 @@ func New(ctx context.Context, options *Options) (_ *API, err error) { // with the below route, we need to register this route without any mounts or groups to make both work. r.With( apiKeyMiddleware, - httpmw.RequireExperiment(api.AGPL.Experiments, codersdk.ExperimentNotifications), httpmw.ExtractNotificationTemplateParam(options.Database), ).Put("/notifications/templates/{notification_template}/method", api.updateNotificationTemplateMethod) }) diff --git a/enterprise/coderd/notifications_test.go b/enterprise/coderd/notifications_test.go index 5546bec1dcb79..b71bde86a5736 100644 --- a/enterprise/coderd/notifications_test.go +++ b/enterprise/coderd/notifications_test.go @@ -23,7 +23,6 @@ func createOpts(t *testing.T) *coderdenttest.Options { t.Helper() dt := coderdtest.DeploymentValues(t) - dt.Experiments = []string{string(codersdk.ExperimentNotifications)} return &coderdenttest.Options{ Options: &coderdtest.Options{ DeploymentValues: dt, diff --git a/enterprise/coderd/provisionerkeys.go b/enterprise/coderd/provisionerkeys.go index ea89538965afa..08556e2eb24c2 100644 --- a/enterprise/coderd/provisionerkeys.go +++ b/enterprise/coderd/provisionerkeys.go @@ -147,9 +147,13 @@ func (api *API) provisionerKeyDaemons(rw http.ResponseWriter, r *http.Request) { pkDaemons := []codersdk.ProvisionerKeyDaemons{} for _, key := range sdkKeys { - // currently we exclude user-auth from this list + // The key.OrganizationID for the `user-auth` key is hardcoded to + // the default org in the database and we are overwriting it here + // to be the correct org we used to query the list. + // This will be changed when we update the `user-auth` keys to be + // directly tied to a user ID. if key.ID.String() == codersdk.ProvisionerKeyIDUserAuth { - continue + key.OrganizationID = organization.ID } daemons := []codersdk.ProvisionerDaemon{} for _, daemon := range recentDaemons { diff --git a/scripts/apidocgen/postprocess/main.go b/scripts/apidocgen/postprocess/main.go index 0af4f411a1e8f..a70a2091154c9 100644 --- a/scripts/apidocgen/postprocess/main.go +++ b/scripts/apidocgen/postprocess/main.go @@ -169,12 +169,12 @@ func writeDocs(sections [][]byte) error { // Update manifest.json type route struct { - Title string `json:"title,omitempty"` - Description string `json:"description,omitempty"` - Path string `json:"path,omitempty"` - IconPath string `json:"icon_path,omitempty"` - State string `json:"state,omitempty"` - Children []route `json:"children,omitempty"` + Title string `json:"title,omitempty"` + Description string `json:"description,omitempty"` + Path string `json:"path,omitempty"` + IconPath string `json:"icon_path,omitempty"` + State []string `json:"state,omitempty"` + Children []route `json:"children,omitempty"` } type manifest struct { diff --git a/scripts/clidocgen/main.go b/scripts/clidocgen/main.go index a5a48a9311df2..68b97b7f19a3c 100644 --- a/scripts/clidocgen/main.go +++ b/scripts/clidocgen/main.go @@ -14,12 +14,12 @@ import ( // route is an individual page object in the docs manifest.json. type route struct { - Title string `json:"title,omitempty"` - Description string `json:"description,omitempty"` - Path string `json:"path,omitempty"` - IconPath string `json:"icon_path,omitempty"` - State string `json:"state,omitempty"` - Children []route `json:"children,omitempty"` + Title string `json:"title,omitempty"` + Description string `json:"description,omitempty"` + Path string `json:"path,omitempty"` + IconPath string `json:"icon_path,omitempty"` + State []string `json:"state,omitempty"` + Children []route `json:"children,omitempty"` } // manifest describes the entire documentation index. diff --git a/site/src/components/FeatureStageBadge/FeatureStageBadge.tsx b/site/src/components/FeatureStageBadge/FeatureStageBadge.tsx index 52cdfaeb01a11..763b180d03bbe 100644 --- a/site/src/components/FeatureStageBadge/FeatureStageBadge.tsx +++ b/site/src/components/FeatureStageBadge/FeatureStageBadge.tsx @@ -10,7 +10,7 @@ import { docs } from "utils/docs"; * All types of feature that we are currently supporting. Defined as record to * ensure that we can't accidentally make typos when writing the badge text. */ -const featureStageBadgeTypes = { +export const featureStageBadgeTypes = { beta: "beta", experimental: "experimental", } as const satisfies Record; diff --git a/site/src/components/Paywall/Paywall.tsx b/site/src/components/Paywall/Paywall.tsx index 4ab955c856a3c..ae940ce515836 100644 --- a/site/src/components/Paywall/Paywall.tsx +++ b/site/src/components/Paywall/Paywall.tsx @@ -58,7 +58,7 @@ export const Paywall: FC = ({
@@ -335,11 +338,11 @@ const ProvisionerVersionPopover: FC = ({ ); }; -interface PskProvisionerTagsProps { +interface InlineProvisionerTagsProps { tags: Record; } -const PskProvisionerTags: FC = ({ tags }) => { +const InlineProvisionerTags: FC = ({ tags }) => { const daemonScope = tags.scope || "organization"; const iconScope = daemonScope === "organization" ? : ; @@ -413,6 +416,30 @@ const BuiltinProvisionerTitle: FC = () => { ); }; +const UserAuthProvisionerTitle: FC = () => { + return ( +

+ + User-authenticated provisioners + + + + User-authenticated provisioners + + These provisioners are connected by users using the{" "} + coder CLI, and are authorized by the users + credentials. They can be tagged to only run provisioner jobs for + that user. User-authenticated provisioners are only available for + the default organization.{" "} + Learn more… + + + + +

+ ); +}; + const PskProvisionerTitle: FC = () => { return (

diff --git a/site/src/pages/DeploySettingsPage/NotificationsPage/NotificationsPage.tsx b/site/src/pages/DeploySettingsPage/NotificationsPage/NotificationsPage.tsx index c073792248072..511959d3ec55f 100644 --- a/site/src/pages/DeploySettingsPage/NotificationsPage/NotificationsPage.tsx +++ b/site/src/pages/DeploySettingsPage/NotificationsPage/NotificationsPage.tsx @@ -43,6 +43,7 @@ export const NotificationsPage: FC = () => { title="Notifications" description="Control delivery methods for notifications on this deployment." layout="fluid" + featureStage={"beta"} > diff --git a/site/src/pages/DeploySettingsPage/Sidebar.tsx b/site/src/pages/DeploySettingsPage/Sidebar.tsx index 607920d65ee2f..1f1172834a5fa 100644 --- a/site/src/pages/DeploySettingsPage/Sidebar.tsx +++ b/site/src/pages/DeploySettingsPage/Sidebar.tsx @@ -7,6 +7,7 @@ import NotificationsIcon from "@mui/icons-material/NotificationsNoneOutlined"; import Globe from "@mui/icons-material/PublicOutlined"; import ApprovalIcon from "@mui/icons-material/VerifiedUserOutlined"; import VpnKeyOutlined from "@mui/icons-material/VpnKeyOutlined"; +import { FeatureStageBadge } from "components/FeatureStageBadge/FeatureStageBadge"; import { GitIcon } from "components/Icons/GitIcon"; import { Sidebar as BaseSidebar, @@ -51,11 +52,9 @@ export const Sidebar: FC = () => { Observability - {experiments.includes("notifications") && ( - - Notifications - - )} + + Notifications + ); }; diff --git a/site/src/pages/ManagementSettingsPage/CreateOrganizationPageView.tsx b/site/src/pages/ManagementSettingsPage/CreateOrganizationPageView.tsx index a73c2628cfbc1..7f4e41121d568 100644 --- a/site/src/pages/ManagementSettingsPage/CreateOrganizationPageView.tsx +++ b/site/src/pages/ManagementSettingsPage/CreateOrganizationPageView.tsx @@ -69,7 +69,7 @@ export const CreateOrganizationPageView: FC<
{Boolean(error) && !isApiValidationError(error) && ( @@ -92,7 +92,7 @@ export const CreateOrganizationPageView: FC< @@ -104,7 +104,7 @@ export const CreateOrganizationPageView: FC< diff --git a/site/src/pages/ManagementSettingsPage/IdpSyncPage/IdpSyncHelpTooltip.tsx b/site/src/pages/ManagementSettingsPage/IdpSyncPage/IdpSyncHelpTooltip.tsx index d2907e4d192f7..37ab1d9f317d9 100644 --- a/site/src/pages/ManagementSettingsPage/IdpSyncPage/IdpSyncHelpTooltip.tsx +++ b/site/src/pages/ManagementSettingsPage/IdpSyncPage/IdpSyncHelpTooltip.tsx @@ -21,7 +21,9 @@ export const IdpSyncHelpTooltip: FC = () => { Coder. Use the Coder CLI to configure these mappings. - + Configure IdP Sync diff --git a/site/src/pages/ManagementSettingsPage/IdpSyncPage/IdpSyncPage.tsx b/site/src/pages/ManagementSettingsPage/IdpSyncPage/IdpSyncPage.tsx index 5842f6d3696c8..487ef57c416b0 100644 --- a/site/src/pages/ManagementSettingsPage/IdpSyncPage/IdpSyncPage.tsx +++ b/site/src/pages/ManagementSettingsPage/IdpSyncPage/IdpSyncPage.tsx @@ -74,7 +74,7 @@ export const IdpSyncPage: FC = () => { } /> @@ -110,28 +110,16 @@ const ViewContent: FC = ({ buildInfo, provisioners }) => {
)} - {provisioners.map((group) => { - const type = getGroupType(group.key); - - // We intentionally hide user-authenticated provisioners for now - // because there are 1. some grouping issues on the backend and 2. we - // should ideally group them by the user who authenticated them, and - // not just lump them all together. - if (type === "userAuth") { - return null; - } - - return ( - - ); - })} + {provisioners.map((group) => ( + + ))} ); diff --git a/site/src/pages/ManagementSettingsPage/SidebarView.tsx b/site/src/pages/ManagementSettingsPage/SidebarView.tsx index f76b4da5b339f..37db583f23ba0 100644 --- a/site/src/pages/ManagementSettingsPage/SidebarView.tsx +++ b/site/src/pages/ManagementSettingsPage/SidebarView.tsx @@ -148,11 +148,12 @@ const DeploymentSettingsNavigation: FC = ({ Users )} - {experiments.includes("notifications") && ( + Notifications - )} + + )} diff --git a/site/src/pages/SetupPage/SetupPageView.tsx b/site/src/pages/SetupPage/SetupPageView.tsx index 3b0a7ea44b9a0..a4b0536ae0b85 100644 --- a/site/src/pages/SetupPage/SetupPageView.tsx +++ b/site/src/pages/SetupPage/SetupPageView.tsx @@ -211,7 +211,7 @@ export const SetupPageView: FC = ({ quotas, and more. diff --git a/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.tsx b/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.tsx index 49f01f1f00936..c67737fc00530 100644 --- a/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.tsx +++ b/site/src/pages/UserSettingsPage/NotificationsPage/NotificationsPage.tsx @@ -99,6 +99,7 @@ export const NotificationsPage: FC = () => { title="Notifications" description="Configure your notification preferences. Icons on the right of each notification indicate delivery method, either SMTP or Webhook." layout="fluid" + featureStage="beta" > {ready ? ( diff --git a/site/src/pages/UserSettingsPage/Section.tsx b/site/src/pages/UserSettingsPage/Section.tsx index edc2740537fbc..8c52aca1eb9cb 100644 --- a/site/src/pages/UserSettingsPage/Section.tsx +++ b/site/src/pages/UserSettingsPage/Section.tsx @@ -1,4 +1,9 @@ import type { Interpolation, Theme } from "@emotion/react"; +import { + FeatureStageBadge, + type featureStageBadgeTypes, +} from "components/FeatureStageBadge/FeatureStageBadge"; +import { Stack } from "components/Stack/Stack"; import type { FC, ReactNode } from "react"; type SectionLayout = "fixed" | "fluid"; @@ -13,6 +18,7 @@ export interface SectionProps { layout?: SectionLayout; className?: string; children?: ReactNode; + featureStage?: keyof typeof featureStageBadgeTypes; } export const Section: FC = ({ @@ -24,6 +30,7 @@ export const Section: FC = ({ className = "", children, layout = "fixed", + featureStage, }) => { return (
@@ -32,16 +39,25 @@ export const Section: FC = ({
{title && ( -

- {title} -

+ +

+ {title} +

+ {featureStage && ( + + )} +
)} {description && typeof description === "string" && (

{description}

diff --git a/site/src/pages/UserSettingsPage/Sidebar.tsx b/site/src/pages/UserSettingsPage/Sidebar.tsx index 2580e00f02e07..196f34d5ce0e1 100644 --- a/site/src/pages/UserSettingsPage/Sidebar.tsx +++ b/site/src/pages/UserSettingsPage/Sidebar.tsx @@ -6,6 +6,7 @@ import NotificationsIcon from "@mui/icons-material/NotificationsNoneOutlined"; import AccountIcon from "@mui/icons-material/Person"; import VpnKeyOutlined from "@mui/icons-material/VpnKeyOutlined"; import type { User } from "api/typesGenerated"; +import { FeatureStageBadge } from "components/FeatureStageBadge/FeatureStageBadge"; import { GitIcon } from "components/Icons/GitIcon"; import { Sidebar as BaseSidebar, @@ -57,11 +58,9 @@ export const Sidebar: FC = ({ user }) => { Tokens - {experiments.includes("notifications") && ( - - Notifications - - )} + + Notifications + ); }; diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index be2a1ccdda196..7b654e54c48a2 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -610,7 +610,7 @@ export const MockProvisioner2: TypesGen.ProvisionerDaemon = { }; export const MockUserProvisioner: TypesGen.ProvisionerDaemon = { - ...MockProvisioner, + ...MockUserAuthProvisioner, id: "test-user-provisioner", name: "Test User Provisioner", tags: { scope: "user", owner: "12345678-abcd-1234-abcd-1234567890abcd" },