diff --git a/docs/config.json b/docs/config.json index 2b39965a1ea1f..d405d216200d0 100644 --- a/docs/config.json +++ b/docs/config.json @@ -28,7 +28,7 @@ }, { "title": "Set up Single Sign-On", - "slug": "/access-controls/sso/overview/" + "slug": "/admin-guides/authentication/sso/overview/" }, { "title": "Use Infrastructure as Code", diff --git a/docs/pages/access-controls.mdx b/docs/pages/access-controls.mdx deleted file mode 100644 index 80bec5bebd699..0000000000000 --- a/docs/pages/access-controls.mdx +++ /dev/null @@ -1,118 +0,0 @@ ---- -title: Teleport Access Controls -description: Configure Teleport to implement the principal of least privilege in your infrastructure. ---- - -{/*TOPICS*/} - -- [Getting Started With Access Controls](access-controls/getting-started.mdx): Get started using Teleport Access Controls. -- [Getting Started with Access Monitoring](access-controls/access-monitoring.mdx): Learn how to use Access Monitoring. -- [Manage Access to your Cluster](access-controls/introduction.mdx): How to provide role-based access control (RBAC) for servers, databases, Kubernetes clusters, and other resources in your infrastructure -- [Teleport Access Controls Reference](access-controls/reference.mdx): Explains the configuration settings that you can include in a Teleport role, which enables you to apply access controls for your infrastructure. - -## Access Lists - -Use Access Lists in Teleport ([more info](access-controls/access-lists.mdx)) - -- [Access List Reference](access-controls/access-lists/reference.mdx): An explanation and overview of Access Lists in Teleport. -- [Getting Started with Access Lists](access-controls/access-lists/guide.mdx): Learn how to use Access Lists to manage and audit long lived access to Teleport resources. - -## Cluster Access and RBAC - -How to configure access to specific resources in your infrastructure or your Teleport cluster as a whole. ([more info](access-controls/guides.mdx)) - -- [Dual Authorization](access-controls/guides/dual-authz.mdx): Dual Authorization for SSH and Kubernetes. -- [Hardware Key Support](access-controls/guides/hardware-key-support.mdx): Hardware Key Support -- [Headless WebAuthn](access-controls/guides/headless.mdx): Headless WebAuthn -- [IP Pinning ](access-controls/guides/ip-pinning.mdx): How to enable IP pinning for Teleport users -- [Impersonating Teleport Users](access-controls/guides/impersonation.mdx): How to issue short-lived certs on behalf of Teleport users using impersonation. -- [MFA for Administrative Actions](access-controls/guides/mfa-for-admin-actions.mdx): Require MFA checks to perform administrative actions. -- [Moderated Sessions](access-controls/guides/moderated-sessions.mdx): Describes the purpose of moderated sessions and how to configure roles to support moderated sessions in a Teleport cluster. -- [Passwordless](access-controls/guides/passwordless.mdx): Learn how to use passwordless authentication with Teleport. -- [Per-session MFA](access-controls/guides/per-session-mfa.mdx): Require MFA checks to initiate sessions. -- [Second Factor: WebAuthn](access-controls/guides/webauthn.mdx): Configuring WebAuthn support in Teleport clusters. -- [Session and Identity Locking](access-controls/guides/locking.mdx): How to lock compromised users or agents -- [Teleport Role Templates](access-controls/guides/role-templates.mdx): This guide explains templating in Teleport roles. Templates allow you to enable access to resources depending on the traits of a local or single sign-on user. - -## Compliance Frameworks - -How to use Teleport's access controls to streamline compliance without sacrificing productivity. ([more info](access-controls/compliance-frameworks.mdx)) - -- [FedRAMP Compliance for Infrastructure Access](access-controls/compliance-frameworks/fedramp.mdx): How to configure SSH, Kubernetes, database, and web app access to be FedRAMP compliant, including support for FIPS 140-2. -- [SOC 2 compliance for SSH, Kubernetes, and Databases](access-controls/compliance-frameworks/soc2.mdx): How to configure SOC 2-compliant access to SSH, Kubernetes, databases, desktops, and web apps - -## Configure Teleport as an identity provider - -How to set up Teleport's identity provider functionality ([more info](access-controls/idps.mdx)) - -- [Access GCP Web Console and API with a federated authentication.](access-controls/idps/saml-gcp-workforce-identity-federation.mdx): Manage Google Cloud Platform (GCP) web console access with Teleport SAMl IdP. -- [SAML IdP Attribute Mapping](access-controls/idps/saml-attribute-mapping.mdx): How to map user attributes to custom SAML response -- [SAML Identity Provider Reference](access-controls/idps/saml-reference.mdx): Reference documentation for the SAML identity provider -- [Use Teleport's SAML Provider to authenticate with Grafana](access-controls/idps/saml-grafana.mdx): Configure Grafana to use identities provided by Teleport. -- [Using Teleport as a SAML identity provider](access-controls/idps/saml-guide.mdx): How to configure and use Teleport as a SAML identity provider. - -## Device Trust - -Device Trust allows Teleport admins to enforce the use of trusted devices. ([more info](access-controls/device-trust.mdx)) - -- [Device Trust Overview](access-controls/device-trust/concepts.mdx): Teleport Device Trust Concepts -- [Enforce Device Trust](access-controls/device-trust/enforcing-device-trust.mdx): Learn how to enforce trusted devices with Teleport -- [Getting Started with Device Trust](access-controls/device-trust/guide.mdx): Get started with Teleport Device Trust -- [Jamf Pro Integration](access-controls/device-trust/jamf-integration.mdx): Sync your Jamf Pro inventory into Teleport -- [Manage Trusted Devices](access-controls/device-trust/device-management.mdx): Learn how to manage Trusted Devices - -## Just-in-Time Access Request Plugins - -Use Teleport's Access Request plugins to least-privilege access without sacrificing productivity. ([more info](access-controls/access-request-plugins.mdx)) - -- [Access Requests with Microsoft Teams](access-controls/access-request-plugins/ssh-approval-msteams.mdx): How to set up Teleport's Microsoft Teams plugin for privilege elevation approvals. -- [Access Requests with Opsgenie](access-controls/access-request-plugins/opsgenie.mdx): How to set up Teleport's Opsgenie plugin for privilege elevation approvals. -- [Access Requests with ServiceNow](access-controls/access-request-plugins/servicenow.mdx): How to set up Teleport's ServiceNow plugin for privilege elevation approvals. -- [Run the Discord Access Request Plugin](access-controls/access-request-plugins/ssh-approval-discord.mdx): How to set up Teleport's Discord plugin for privilege elevation approvals. -- [Run the Jira Access Request Plugin](access-controls/access-request-plugins/ssh-approval-jira.mdx): How to set up the Teleport Jira plugin to notify users when another user requests elevated privileges. -- [Run the Mattermost Access Request plugin](access-controls/access-request-plugins/ssh-approval-mattermost.mdx): How to set up Teleport's Mattermost plugin for privilege elevation approvals. -- [Run the PagerDuty Access Request Plugin](access-controls/access-request-plugins/ssh-approval-pagerduty.mdx): How to set up Teleport's PagerDuty plugin for privilege elevation approvals. -- [Run the Slack Access Request Plugin](access-controls/access-request-plugins/ssh-approval-slack.mdx): How to set up Teleport's Slack plugin for privilege elevation approvals. -- [Teleport Access Requests with Email](access-controls/access-request-plugins/ssh-approval-email.mdx): How to set up the Teleport email plugin to notify users when another user requests elevated privileges. - -## Just-in-Time Access Requests - -Just-in-time Access Requests allow Teleport users to request access to a resource or role depending on need. ([more info](access-controls/access-requests.mdx)) - -- [Configure Access Requests](access-controls/access-requests/access-request-configuration.mdx): Describes the options available for configuring just-in-time access to roles and resources in your Teleport cluster. -- [Just-in-Time Access Requests](access-controls/access-requests/overview.mdx): Use just-in-time Access Requests to request elevated privileges. -- [Resource Access Requests](access-controls/access-requests/resource-requests.mdx): Teleport allows users to request access to specific resources from the CLI or UI. Requests can be escalated via ChatOps or anywhere else via our flexible Authorization Workflow API. -- [Role Access Requests](access-controls/access-requests/role-requests.mdx): Use Just-in-time Access Requests to request new roles with elevated privileges. -- [Teleport Community Edition Role Access Requests](access-controls/access-requests/oss-role-requests.mdx): Teleport Community Edition allows users to request access to roles from the CLI. - -## Login Rules - -Transform User Traits with Login Rules ([more info](access-controls/login-rules.mdx)) - -- [Deploy Login Rules using Kubernetes Operator](access-controls/login-rules/kubernetes.mdx): Use Teleport's Kubernetes Operator to deploy Login Rules to your cluster -- [Deploy Login Rules via Terraform](access-controls/login-rules/terraform.mdx): Use Teleport's Terraform Provider to deploy Login Rules to your cluster -- [Login Rules Reference](access-controls/login-rules/reference.mdx): Reference documentation for Login Rules -- [Set Up Login Rules](access-controls/login-rules/guide.mdx): Set up Login Rules to transform user traits - -## Teleport Access Graph - -Includes guides for Teleport Access Graph, which allows you to visualize RBAC policies in your infrastructure. ([more info](access-controls/access-graph.mdx)) - -- [Discover AWS Access Patterns with Teleport Policy](access-controls/access-graph/aws-sync.mdx): Describes how to import and visualize AWS accounts access patterns using Teleport Policy and Access Graph. -- [Run Teleport Access Graph on Self-Hosted Clusters with Helm](access-controls/access-graph/self-hosted-helm.mdx): undefined -- [Run Teleport Access Graph on Self-Hosted Clusters](access-controls/access-graph/self-hosted.mdx): Describes how to deploy Teleport Access Graph on self-hosted clusters. -- [Teleport Policy](access-controls/access-graph/overview.mdx): A reference for Access Graph with Teleport Policy. - -## Teleport Single-Sign-On - -Learn how to configure your single sign-on provider to allow authentication to Teleport. ([more info](access-controls/sso.mdx)) - -- [Authentication With GitLab as an SSO provider](access-controls/sso/gitlab.mdx): How to configure Teleport access using GitLab for SSO -- [Authentication With Okta as an SSO Provider](access-controls/sso/okta.mdx): How to configure Teleport access using Okta for SSO -- [OAuth2 and OIDC authentication](access-controls/sso/oidc.mdx): How to configure Teleport access with OAuth2 or OpenID connect (OIDC) -- [SSO with Active Directory Federation Services](access-controls/sso/adfs.mdx): How to configure Teleport access with Active Directory Federation Services -- [Set up Single Sign-On with GitHub](access-controls/sso/github-sso.mdx): Setting up GitHub SSO -- [Teleport Authentication with Azure Active Directory (AD)](access-controls/sso/azuread.mdx): How to configure Teleport access with Azure Active Directory. -- [Teleport Authentication with Google Workspace (G Suite)](access-controls/sso/google-workspace.mdx): How to configure Teleport access with Google Workspace (formerly known as G Suite) -- [Teleport Authentication with OneLogin as an SSO Provider](access-controls/sso/one-login.mdx): How to configure Teleport access using OneLogin as an SSO provider -- [Teleport Single Sign-On Overview](access-controls/sso/overview.mdx): How to set up single sign-on (SSO) for SSH using Teleport diff --git a/docs/pages/access-controls/guides.mdx b/docs/pages/access-controls/guides.mdx deleted file mode 100644 index 4148e6fc98aa9..0000000000000 --- a/docs/pages/access-controls/guides.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: Cluster Access and RBAC -description: How to configure access to specific resources in your infrastructure or your Teleport cluster as a whole. -layout: tocless-doc ---- - -Teleport gives you fine-grained control over who can access resources in your -infrastructure as well as how they can access those resources. Once you have -deployed a Teleport cluster, configure access controls to achieve the right -security policies for your organization. - -{/*TOPICS*/} - -- [Dual Authorization](guides/dual-authz.mdx): Dual Authorization for SSH and Kubernetes. -- [Hardware Key Support](guides/hardware-key-support.mdx): Hardware Key Support -- [Headless WebAuthn](guides/headless.mdx): Headless WebAuthn -- [IP Pinning ](guides/ip-pinning.mdx): How to enable IP pinning for Teleport users -- [Impersonating Teleport Users](guides/impersonation.mdx): How to issue short-lived certs on behalf of Teleport users using impersonation. -- [MFA for Administrative Actions](guides/mfa-for-admin-actions.mdx): Require MFA checks to perform administrative actions. -- [Moderated Sessions](guides/moderated-sessions.mdx): Describes the purpose of moderated sessions and how to configure roles to support moderated sessions in a Teleport cluster. -- [Passwordless](guides/passwordless.mdx): Learn how to use passwordless authentication with Teleport. -- [Per-session MFA](guides/per-session-mfa.mdx): Require MFA checks to initiate sessions. -- [Second Factor: WebAuthn](guides/webauthn.mdx): Configuring WebAuthn support in Teleport clusters. -- [Session and Identity Locking](guides/locking.mdx): How to lock compromised users or agents -- [Teleport Role Templates](guides/role-templates.mdx): This guide explains templating in Teleport roles. Templates allow you to enable access to resources depending on the traits of a local or single sign-on user. diff --git a/docs/pages/access-controls/introduction.mdx b/docs/pages/access-controls/introduction.mdx deleted file mode 100644 index 3e1d53d274eab..0000000000000 --- a/docs/pages/access-controls/introduction.mdx +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Manage Access to your Cluster -description: How to provide role-based access control (RBAC) for servers, databases, Kubernetes clusters, and other resources in your infrastructure -layout: tocless-doc ---- - -After [deploying a Teleport cluster](../deploy-a-cluster/introduction.mdx), the -next step is to manage the access that Teleport users have to resources in your -infrastructure. - -Teleport's role-based access control (RBAC) enables you to set fine-grained -policies for who can perform certain actions against specific resources. For -example: - -- Analytics team members can SSH into a MongoDB read replica, but not the main - database. -- Interns can't access production databases. -- SREs can access a production server only when using a [trusted hardware - device](./device-trust/guide.mdx). -- Members of a team can access the production Kubernetes cluster if approved by - someone else from the same team. - -## Get started - -Configure Access Controls with our five-minute [Getting -Started](./getting-started.mdx) guide. - -## Set up Teleport roles - -The heart of Teleport's RBAC system is the **role**, a configuration document -that specifies access policies for resources in your Teleport cluster. -Assigning a role to a Teleport user applies the policies listed in the role to -the user. - -See the [Cluster Access and RBAC](./guides.mdx) section for instructions on -setting up Teleport roles. - -## Integrate with your Single Sign-On provider - -While you can create Teleport users directly on the Auth Service, the more -scalable approach is to integrate Teleport with a Single Sign-On identity -provider (IdP), such as Okta or GitHub. - -When a user authenticates to your Teleport cluster via your IdP, Teleport -automatically assigns roles to the user based on data provided by the IdP. This -means that you can implement a fully fledged infrastructure RBAC system based on -your existing Single Sign-On solution. - -Read our [Single Sign-On guide](./sso.mdx) to get started. - -## Enable Access Requests - -With Access Requests, your Teleport cluster can grant a user temporary access to -resources in your infrastructure based on the approval of other users. You can -set up your RBAC so all privileged access is short lived, and there are no -longstanding admin roles for attackers to hijack. - -[Get started with Access Requests](./access-requests.mdx). - -You can integrate Teleport with your existing communication tool, e.g., Slack, -PagerDuty, or Microsoft Teams, so Teleport users can easily create and approve -Access Requests. - -[Get started with Access Request plugins](./access-request-plugins/index.mdx). - -## Achieve compliance - -Teleport's RBAC features make it easier to manage access to your infrastructure -in order to satisfy compliance requirements. Learn how to use Teleport to -achieve compliance with: - -- [FedRAMP](./compliance-frameworks/fedramp.mdx) -- [SOC 2](./compliance-frameworks/soc2.mdx) - -## Find out more - -Find out more information on Teleport's RBAC features by reading the [Access -Controls Reference](./reference.mdx). diff --git a/docs/pages/admin-guides.mdx b/docs/pages/admin-guides.mdx index 967e99a782a96..ad76239765ba2 100644 --- a/docs/pages/admin-guides.mdx +++ b/docs/pages/admin-guides.mdx @@ -5,11 +5,14 @@ description: "Step-by-step guides to performing common Teleport tasks" {/*TOPICS*/} +- [Getting Started with Access Monitoring](admin-guides/access-monitoring.mdx): Learn how to use Access Monitoring. + ## Common Operations Contains guides for performing common tasks on a Teleport cluster after the initial setup phase. ([more info](admin-guides/common-operations.mdx)) - [Backup and Restore](admin-guides/common-operations/backup-restore.mdx): How to back up and restore your Teleport cluster state. +- [Configure Teleport as an identity provider (section)](admin-guides/common-operations/idps.mdx): How to set up Teleport's identity provider functionality - [Exporting Teleport Audit Events (section)](admin-guides/common-operations/export-audit-events.mdx): Learn how to export Teleport audit events to your log management solution. - [External Audit Storage](admin-guides/common-operations/external-audit-storage.mdx): Store audit logs and session recordings on your own infrastructure with Teleport Enterprise Cloud. - [Run Teleport as a Daemon](admin-guides/common-operations/daemon.mdx): Configure Teleport to run as a daemon using systemd @@ -52,7 +55,38 @@ Guides to deploying and managing the Teleport Auth Service and Proxy Service. ([ ## Teleport Access Controls -Guides to configuring the access that Teleport users have to infrastructure resources and cluster permissions. ([more info](admin-guides/rbac.mdx)) - -- [Configure Trusted Clusters](admin-guides/rbac/trustedclusters.mdx): Explains how you can configure a trust relationship and manage access between two Teleport clusters. -- [Teleport Label Guides (section)](admin-guides/rbac/labels.mdx): Guides to using Teleport labels, which underpin the Teleport role-based access controls system. +Guides to configuring the access that Teleport users have to infrastructure resources and cluster permissions. ([more info](admin-guides/access-controls.mdx)) + +- [Configure Trusted Clusters](admin-guides/access-controls/trustedclusters.mdx): Explains how you can configure a trust relationship and manage access between two Teleport clusters. +- [Getting Started With Access Controls](admin-guides/access-controls/access-controls-getting-started.mdx): Get started using Teleport Access Controls. +- [IP Pinning ](admin-guides/access-controls/ip-pinning.mdx): How to enable IP pinning for Teleport users +- [Impersonating Teleport Users](admin-guides/access-controls/impersonation.mdx): How to issue short-lived certs on behalf of Teleport users using impersonation. +- [Just-in-Time Access with Teleport (section)](admin-guides/access-controls/just-in-time.mdx): Provide elevated privileges for a limited period of time to allow users to access resources without the need for permanent admin roles. +- [Login Rules (section)](admin-guides/access-controls/login-rules.mdx): Transform User Traits with Login Rules +- [Moderated Sessions](admin-guides/access-controls/moderated-sessions.mdx): Describes the purpose of moderated sessions and how to configure roles to support moderated sessions in a Teleport cluster. +- [Session and Identity Locking](admin-guides/access-controls/locking.mdx): How to lock compromised users or agents +- [Teleport Label Guides (section)](admin-guides/access-controls/labels.mdx): Guides to using Teleport labels, which underpin the Teleport role-based access controls system. +- [Teleport Role Templates](admin-guides/access-controls/role-templates.mdx): This guide explains templating in Teleport roles. Templates allow you to enable access to resources depending on the traits of a local or single sign-on user. + +## Teleport Access Graph + +Includes guides for Teleport Access Graph, which allows you to visualize RBAC policies in your infrastructure. ([more info](admin-guides/access-graph.mdx)) + +- [Discover AWS Access Patterns with Teleport Policy](admin-guides/access-graph/aws-sync.mdx): Describes how to import and visualize AWS accounts access patterns using Teleport Policy and Access Graph. +- [Run Teleport Access Graph on Self-Hosted Clusters with Helm](admin-guides/access-graph/self-hosted-helm.mdx): undefined +- [Run Teleport Access Graph on Self-Hosted Clusters](admin-guides/access-graph/self-hosted.mdx): Describes how to deploy Teleport Access Graph on self-hosted clusters. +- [Teleport Policy](admin-guides/access-graph/overview.mdx): A reference for Access Graph with Teleport Policy. + +## Teleport Authentication + +Guides for configuring the way users authenticate to Teleport. ([more info](admin-guides/authentication.mdx)) + +- [Device Trust (section)](admin-guides/authentication/device-trust.mdx): Device Trust allows Teleport admins to enforce the use of trusted devices. +- [Dual Authorization](admin-guides/authentication/dual-authz.mdx): Dual Authorization for SSH and Kubernetes. +- [Hardware Key Support](admin-guides/authentication/hardware-key-support.mdx): Hardware Key Support +- [Headless WebAuthn](admin-guides/authentication/headless.mdx): Headless WebAuthn +- [MFA for Administrative Actions](admin-guides/authentication/mfa-for-admin-actions.mdx): Require MFA checks to perform administrative actions. +- [Passwordless](admin-guides/authentication/passwordless.mdx): Learn how to use passwordless authentication with Teleport. +- [Per-session MFA](admin-guides/authentication/per-session-mfa.mdx): Require MFA checks to initiate sessions. +- [Second Factor: WebAuthn](admin-guides/authentication/webauthn.mdx): Configuring WebAuthn support in Teleport clusters. +- [Teleport Single-Sign-On (section)](admin-guides/authentication/sso.mdx): Learn how to configure your single sign-on provider to allow authentication to Teleport. diff --git a/docs/pages/admin-guides/access-controls.mdx b/docs/pages/admin-guides/access-controls.mdx new file mode 100644 index 0000000000000..ecef8f72a3368 --- /dev/null +++ b/docs/pages/admin-guides/access-controls.mdx @@ -0,0 +1,38 @@ +--- +title: Teleport Access Controls +description: Guides to configuring the access that Teleport users have to infrastructure resources and cluster permissions. +--- + +{/*TOPICS*/} + +- [Configure Trusted Clusters](access-controls/trustedclusters.mdx): Explains how you can configure a trust relationship and manage access between two Teleport clusters. +- [Getting Started With Access Controls](access-controls/access-controls-getting-started.mdx): Get started using Teleport Access Controls. +- [IP Pinning ](access-controls/ip-pinning.mdx): How to enable IP pinning for Teleport users +- [Impersonating Teleport Users](access-controls/impersonation.mdx): How to issue short-lived certs on behalf of Teleport users using impersonation. +- [Moderated Sessions](access-controls/moderated-sessions.mdx): Describes the purpose of moderated sessions and how to configure roles to support moderated sessions in a Teleport cluster. +- [Session and Identity Locking](access-controls/locking.mdx): How to lock compromised users or agents +- [Teleport Role Templates](access-controls/role-templates.mdx): This guide explains templating in Teleport roles. Templates allow you to enable access to resources depending on the traits of a local or single sign-on user. + +## Just-in-Time Access with Teleport + +Provide elevated privileges for a limited period of time to allow users to access resources without the need for permanent admin roles. ([more info](access-controls/just-in-time.mdx)) + +- [Access Lists (section)](access-controls/just-in-time/access-lists.mdx): Use Access Lists in Teleport +- [Just-in-Time Access Request Plugins (section)](access-controls/just-in-time/access-request-plugins.mdx): Use Teleport's Access Request plugins to least-privilege access without sacrificing productivity. +- [Just-in-Time Access Requests (section)](access-controls/just-in-time/access-requests.mdx): Just-in-time Access Requests allow Teleport users to request access to a resource or role depending on need. + +## Login Rules + +Transform User Traits with Login Rules ([more info](access-controls/login-rules.mdx)) + +- [Deploy Login Rules using Kubernetes Operator](access-controls/login-rules/kubernetes.mdx): Use Teleport's Kubernetes Operator to deploy Login Rules to your cluster +- [Deploy Login Rules via Terraform](access-controls/login-rules/terraform.mdx): Use Teleport's Terraform Provider to deploy Login Rules to your cluster +- [Set Up Login Rules](access-controls/login-rules/guide.mdx): Set up Login Rules to transform user traits + +## Teleport Label Guides + +Guides to using Teleport labels, which underpin the Teleport role-based access controls system. ([more info](access-controls/labels.mdx)) + +- [Add Labels to Resources](access-controls/labels/labels.mdx): How to assign static and command-based dynamic labels to Teleport resources. +- [EC2 Tags as Teleport Node Labels](access-controls/labels/ec2-tags.mdx): How to set up Teleport Node labels based on EC2 tags +- [GCP Tags and Labels as Teleport Agent Labels](access-controls/labels/gcp-tags.mdx): How to set up Teleport agent labels based on GCP tags and labels diff --git a/docs/pages/access-controls/getting-started.mdx b/docs/pages/admin-guides/access-controls/access-controls-getting-started.mdx similarity index 100% rename from docs/pages/access-controls/getting-started.mdx rename to docs/pages/admin-guides/access-controls/access-controls-getting-started.mdx diff --git a/docs/pages/access-controls/guides/impersonation.mdx b/docs/pages/admin-guides/access-controls/impersonation.mdx similarity index 100% rename from docs/pages/access-controls/guides/impersonation.mdx rename to docs/pages/admin-guides/access-controls/impersonation.mdx diff --git a/docs/pages/access-controls/guides/ip-pinning.mdx b/docs/pages/admin-guides/access-controls/ip-pinning.mdx similarity index 100% rename from docs/pages/access-controls/guides/ip-pinning.mdx rename to docs/pages/admin-guides/access-controls/ip-pinning.mdx diff --git a/docs/pages/admin-guides/access-controls/just-in-time.mdx b/docs/pages/admin-guides/access-controls/just-in-time.mdx new file mode 100644 index 0000000000000..5ac42c6e1559f --- /dev/null +++ b/docs/pages/admin-guides/access-controls/just-in-time.mdx @@ -0,0 +1,36 @@ +--- +title: Just-in-Time Access with Teleport +description: Provide elevated privileges for a limited period of time to allow users to access resources without the need for permanent admin roles. +--- + +{/*TOPICS*/} + +## Access Lists + +Use Access Lists in Teleport ([more info](just-in-time/access-lists.mdx)) + +- [Getting Started with Access Lists](just-in-time/access-lists/guide.mdx): Learn how to use Access Lists to manage and audit long lived access to Teleport resources. + +## Just-in-Time Access Request Plugins + +Use Teleport's Access Request plugins to least-privilege access without sacrificing productivity. ([more info](just-in-time/access-request-plugins.mdx)) + +- [Access Requests with Microsoft Teams](just-in-time/access-request-plugins/ssh-approval-msteams.mdx): How to set up Teleport's Microsoft Teams plugin for privilege elevation approvals. +- [Access Requests with Opsgenie](just-in-time/access-request-plugins/opsgenie.mdx): How to set up Teleport's Opsgenie plugin for privilege elevation approvals. +- [Access Requests with ServiceNow](just-in-time/access-request-plugins/servicenow.mdx): How to set up Teleport's ServiceNow plugin for privilege elevation approvals. +- [Routing Access Request notifications](just-in-time/access-request-plugins/notification-routing-rules.mdx): How to set up Teleport's Access Monitoring Rules to route Access Request notifications +- [Run the Discord Access Request Plugin](just-in-time/access-request-plugins/ssh-approval-discord.mdx): How to set up Teleport's Discord plugin for privilege elevation approvals. +- [Run the Jira Access Request Plugin](just-in-time/access-request-plugins/ssh-approval-jira.mdx): How to set up the Teleport Jira plugin to notify users when another user requests elevated privileges. +- [Run the Mattermost Access Request plugin](just-in-time/access-request-plugins/ssh-approval-mattermost.mdx): How to set up Teleport's Mattermost plugin for privilege elevation approvals. +- [Run the PagerDuty Access Request Plugin](just-in-time/access-request-plugins/ssh-approval-pagerduty.mdx): How to set up Teleport's PagerDuty plugin for privilege elevation approvals. +- [Run the Slack Access Request Plugin](just-in-time/access-request-plugins/ssh-approval-slack.mdx): How to set up Teleport's Slack plugin for privilege elevation approvals. +- [Teleport Access Requests with Email](just-in-time/access-request-plugins/ssh-approval-email.mdx): How to set up the Teleport email plugin to notify users when another user requests elevated privileges. + +## Just-in-Time Access Requests + +Just-in-time Access Requests allow Teleport users to request access to a resource or role depending on need. ([more info](just-in-time/access-requests.mdx)) + +- [Just-in-Time Access Requests](just-in-time/access-requests/overview.mdx): Use just-in-time Access Requests to request elevated privileges. +- [Resource Access Requests](just-in-time/access-requests/resource-requests.mdx): Teleport allows users to request access to specific resources from the CLI or UI. Requests can be escalated via ChatOps or anywhere else via our flexible Authorization Workflow API. +- [Role Access Requests](just-in-time/access-requests/role-requests.mdx): Use Just-in-time Access Requests to request new roles with elevated privileges. +- [Teleport Community Edition Role Access Requests](just-in-time/access-requests/oss-role-requests.mdx): Teleport Community Edition allows users to request access to roles from the CLI. diff --git a/docs/pages/access-controls/access-lists.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-lists.mdx similarity index 83% rename from docs/pages/access-controls/access-lists.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-lists.mdx index 202a8107100b8..8ddb4859e989e 100644 --- a/docs/pages/access-controls/access-lists.mdx +++ b/docs/pages/admin-guides/access-controls/just-in-time/access-lists.mdx @@ -11,5 +11,4 @@ traits, which then tie easily back into Teleport's existing RBAC system. {/*TOPICS*/} -- [Access List Reference](access-lists/reference.mdx): An explanation and overview of Access Lists in Teleport. - [Getting Started with Access Lists](access-lists/guide.mdx): Learn how to use Access Lists to manage and audit long lived access to Teleport resources. diff --git a/docs/pages/access-controls/access-lists/guide.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-lists/guide.mdx similarity index 100% rename from docs/pages/access-controls/access-lists/guide.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-lists/guide.mdx diff --git a/docs/pages/access-controls/access-request-plugins.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins.mdx similarity index 95% rename from docs/pages/access-controls/access-request-plugins.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins.mdx index fd075ef6c63ca..f859e0aa96a2e 100644 --- a/docs/pages/access-controls/access-request-plugins.mdx +++ b/docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins.mdx @@ -56,6 +56,7 @@ communication workflows by reading our setup guides: - [Access Requests with Microsoft Teams](access-request-plugins/ssh-approval-msteams.mdx): How to set up Teleport's Microsoft Teams plugin for privilege elevation approvals. - [Access Requests with Opsgenie](access-request-plugins/opsgenie.mdx): How to set up Teleport's Opsgenie plugin for privilege elevation approvals. - [Access Requests with ServiceNow](access-request-plugins/servicenow.mdx): How to set up Teleport's ServiceNow plugin for privilege elevation approvals. +- [Routing Access Request notifications](access-request-plugins/notification-routing-rules.mdx): How to set up Teleport's Access Monitoring Rules to route Access Request notifications - [Run the Discord Access Request Plugin](access-request-plugins/ssh-approval-discord.mdx): How to set up Teleport's Discord plugin for privilege elevation approvals. - [Run the Jira Access Request Plugin](access-request-plugins/ssh-approval-jira.mdx): How to set up the Teleport Jira plugin to notify users when another user requests elevated privileges. - [Run the Mattermost Access Request plugin](access-request-plugins/ssh-approval-mattermost.mdx): How to set up Teleport's Mattermost plugin for privilege elevation approvals. diff --git a/docs/pages/access-controls/access-request-plugins/notification-routing-rules.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/notification-routing-rules.mdx similarity index 100% rename from docs/pages/access-controls/access-request-plugins/notification-routing-rules.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/notification-routing-rules.mdx diff --git a/docs/pages/access-controls/access-request-plugins/opsgenie.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/opsgenie.mdx similarity index 100% rename from docs/pages/access-controls/access-request-plugins/opsgenie.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/opsgenie.mdx diff --git a/docs/pages/access-controls/access-request-plugins/servicenow.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/servicenow.mdx similarity index 100% rename from docs/pages/access-controls/access-request-plugins/servicenow.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/servicenow.mdx diff --git a/docs/pages/access-controls/access-request-plugins/ssh-approval-discord.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/ssh-approval-discord.mdx similarity index 100% rename from docs/pages/access-controls/access-request-plugins/ssh-approval-discord.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/ssh-approval-discord.mdx diff --git a/docs/pages/access-controls/access-request-plugins/ssh-approval-email.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/ssh-approval-email.mdx similarity index 100% rename from docs/pages/access-controls/access-request-plugins/ssh-approval-email.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/ssh-approval-email.mdx diff --git a/docs/pages/access-controls/access-request-plugins/ssh-approval-jira.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/ssh-approval-jira.mdx similarity index 100% rename from docs/pages/access-controls/access-request-plugins/ssh-approval-jira.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/ssh-approval-jira.mdx diff --git a/docs/pages/access-controls/access-request-plugins/ssh-approval-mattermost.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/ssh-approval-mattermost.mdx similarity index 100% rename from docs/pages/access-controls/access-request-plugins/ssh-approval-mattermost.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/ssh-approval-mattermost.mdx diff --git a/docs/pages/access-controls/access-request-plugins/ssh-approval-msteams.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/ssh-approval-msteams.mdx similarity index 100% rename from docs/pages/access-controls/access-request-plugins/ssh-approval-msteams.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/ssh-approval-msteams.mdx diff --git a/docs/pages/access-controls/access-request-plugins/ssh-approval-pagerduty.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/ssh-approval-pagerduty.mdx similarity index 100% rename from docs/pages/access-controls/access-request-plugins/ssh-approval-pagerduty.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/ssh-approval-pagerduty.mdx diff --git a/docs/pages/access-controls/access-request-plugins/ssh-approval-slack.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/ssh-approval-slack.mdx similarity index 100% rename from docs/pages/access-controls/access-request-plugins/ssh-approval-slack.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-request-plugins/ssh-approval-slack.mdx diff --git a/docs/pages/access-controls/access-requests.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-requests.mdx similarity index 84% rename from docs/pages/access-controls/access-requests.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-requests.mdx index 9da7d487d39d4..39d0386ec3b70 100644 --- a/docs/pages/access-controls/access-requests.mdx +++ b/docs/pages/admin-guides/access-controls/just-in-time/access-requests.mdx @@ -9,7 +9,6 @@ based on a configurable number of approvers. {/*TOPICS*/} -- [Configure Access Requests](access-requests/access-request-configuration.mdx): Describes the options available for configuring just-in-time access to roles and resources in your Teleport cluster. - [Just-in-Time Access Requests](access-requests/overview.mdx): Use just-in-time Access Requests to request elevated privileges. - [Resource Access Requests](access-requests/resource-requests.mdx): Teleport allows users to request access to specific resources from the CLI or UI. Requests can be escalated via ChatOps or anywhere else via our flexible Authorization Workflow API. - [Role Access Requests](access-requests/role-requests.mdx): Use Just-in-time Access Requests to request new roles with elevated privileges. diff --git a/docs/pages/access-controls/access-requests/oss-role-requests.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-requests/oss-role-requests.mdx similarity index 100% rename from docs/pages/access-controls/access-requests/oss-role-requests.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-requests/oss-role-requests.mdx diff --git a/docs/pages/access-controls/access-requests/overview.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-requests/overview.mdx similarity index 100% rename from docs/pages/access-controls/access-requests/overview.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-requests/overview.mdx diff --git a/docs/pages/access-controls/access-requests/resource-requests.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-requests/resource-requests.mdx similarity index 100% rename from docs/pages/access-controls/access-requests/resource-requests.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-requests/resource-requests.mdx diff --git a/docs/pages/access-controls/access-requests/role-requests.mdx b/docs/pages/admin-guides/access-controls/just-in-time/access-requests/role-requests.mdx similarity index 100% rename from docs/pages/access-controls/access-requests/role-requests.mdx rename to docs/pages/admin-guides/access-controls/just-in-time/access-requests/role-requests.mdx diff --git a/docs/pages/admin-guides/rbac/labels.mdx b/docs/pages/admin-guides/access-controls/labels.mdx similarity index 100% rename from docs/pages/admin-guides/rbac/labels.mdx rename to docs/pages/admin-guides/access-controls/labels.mdx diff --git a/docs/pages/admin-guides/rbac/labels/ec2-tags.mdx b/docs/pages/admin-guides/access-controls/labels/ec2-tags.mdx similarity index 100% rename from docs/pages/admin-guides/rbac/labels/ec2-tags.mdx rename to docs/pages/admin-guides/access-controls/labels/ec2-tags.mdx diff --git a/docs/pages/admin-guides/rbac/labels/gcp-tags.mdx b/docs/pages/admin-guides/access-controls/labels/gcp-tags.mdx similarity index 100% rename from docs/pages/admin-guides/rbac/labels/gcp-tags.mdx rename to docs/pages/admin-guides/access-controls/labels/gcp-tags.mdx diff --git a/docs/pages/admin-guides/rbac/labels/labels.mdx b/docs/pages/admin-guides/access-controls/labels/labels.mdx similarity index 100% rename from docs/pages/admin-guides/rbac/labels/labels.mdx rename to docs/pages/admin-guides/access-controls/labels/labels.mdx diff --git a/docs/pages/access-controls/guides/locking.mdx b/docs/pages/admin-guides/access-controls/locking.mdx similarity index 100% rename from docs/pages/access-controls/guides/locking.mdx rename to docs/pages/admin-guides/access-controls/locking.mdx diff --git a/docs/pages/access-controls/login-rules.mdx b/docs/pages/admin-guides/access-controls/login-rules.mdx similarity index 83% rename from docs/pages/access-controls/login-rules.mdx rename to docs/pages/admin-guides/access-controls/login-rules.mdx index 681beadf07ff7..a03ff8bbdf908 100644 --- a/docs/pages/access-controls/login-rules.mdx +++ b/docs/pages/admin-guides/access-controls/login-rules.mdx @@ -7,5 +7,4 @@ description: Transform User Traits with Login Rules - [Deploy Login Rules using Kubernetes Operator](login-rules/kubernetes.mdx): Use Teleport's Kubernetes Operator to deploy Login Rules to your cluster - [Deploy Login Rules via Terraform](login-rules/terraform.mdx): Use Teleport's Terraform Provider to deploy Login Rules to your cluster -- [Login Rules Reference](login-rules/reference.mdx): Reference documentation for Login Rules - [Set Up Login Rules](login-rules/guide.mdx): Set up Login Rules to transform user traits diff --git a/docs/pages/access-controls/login-rules/guide.mdx b/docs/pages/admin-guides/access-controls/login-rules/guide.mdx similarity index 100% rename from docs/pages/access-controls/login-rules/guide.mdx rename to docs/pages/admin-guides/access-controls/login-rules/guide.mdx diff --git a/docs/pages/access-controls/login-rules/kubernetes.mdx b/docs/pages/admin-guides/access-controls/login-rules/kubernetes.mdx similarity index 100% rename from docs/pages/access-controls/login-rules/kubernetes.mdx rename to docs/pages/admin-guides/access-controls/login-rules/kubernetes.mdx diff --git a/docs/pages/access-controls/login-rules/terraform.mdx b/docs/pages/admin-guides/access-controls/login-rules/terraform.mdx similarity index 100% rename from docs/pages/access-controls/login-rules/terraform.mdx rename to docs/pages/admin-guides/access-controls/login-rules/terraform.mdx diff --git a/docs/pages/access-controls/guides/moderated-sessions.mdx b/docs/pages/admin-guides/access-controls/moderated-sessions.mdx similarity index 100% rename from docs/pages/access-controls/guides/moderated-sessions.mdx rename to docs/pages/admin-guides/access-controls/moderated-sessions.mdx diff --git a/docs/pages/access-controls/guides/role-templates.mdx b/docs/pages/admin-guides/access-controls/role-templates.mdx similarity index 100% rename from docs/pages/access-controls/guides/role-templates.mdx rename to docs/pages/admin-guides/access-controls/role-templates.mdx diff --git a/docs/pages/admin-guides/rbac/trustedclusters.mdx b/docs/pages/admin-guides/access-controls/trustedclusters.mdx similarity index 100% rename from docs/pages/admin-guides/rbac/trustedclusters.mdx rename to docs/pages/admin-guides/access-controls/trustedclusters.mdx diff --git a/docs/pages/access-controls/access-graph.mdx b/docs/pages/admin-guides/access-graph.mdx similarity index 100% rename from docs/pages/access-controls/access-graph.mdx rename to docs/pages/admin-guides/access-graph.mdx diff --git a/docs/pages/access-controls/access-graph/aws-sync.mdx b/docs/pages/admin-guides/access-graph/aws-sync.mdx similarity index 100% rename from docs/pages/access-controls/access-graph/aws-sync.mdx rename to docs/pages/admin-guides/access-graph/aws-sync.mdx diff --git a/docs/pages/access-controls/access-graph/overview.mdx b/docs/pages/admin-guides/access-graph/overview.mdx similarity index 100% rename from docs/pages/access-controls/access-graph/overview.mdx rename to docs/pages/admin-guides/access-graph/overview.mdx diff --git a/docs/pages/access-controls/access-graph/self-hosted-helm.mdx b/docs/pages/admin-guides/access-graph/self-hosted-helm.mdx similarity index 100% rename from docs/pages/access-controls/access-graph/self-hosted-helm.mdx rename to docs/pages/admin-guides/access-graph/self-hosted-helm.mdx diff --git a/docs/pages/access-controls/access-graph/self-hosted.mdx b/docs/pages/admin-guides/access-graph/self-hosted.mdx similarity index 100% rename from docs/pages/access-controls/access-graph/self-hosted.mdx rename to docs/pages/admin-guides/access-graph/self-hosted.mdx diff --git a/docs/pages/access-controls/access-monitoring.mdx b/docs/pages/admin-guides/access-monitoring.mdx similarity index 100% rename from docs/pages/access-controls/access-monitoring.mdx rename to docs/pages/admin-guides/access-monitoring.mdx diff --git a/docs/pages/admin-guides/authentication.mdx b/docs/pages/admin-guides/authentication.mdx new file mode 100644 index 0000000000000..0a2c45f2fe0c7 --- /dev/null +++ b/docs/pages/admin-guides/authentication.mdx @@ -0,0 +1,38 @@ +--- +title: "Teleport Authentication" +description: Guides for configuring the way users authenticate to Teleport. +--- + +{/*TOPICS*/} + +- [Dual Authorization](authentication/dual-authz.mdx): Dual Authorization for SSH and Kubernetes. +- [Hardware Key Support](authentication/hardware-key-support.mdx): Hardware Key Support +- [Headless WebAuthn](authentication/headless.mdx): Headless WebAuthn +- [MFA for Administrative Actions](authentication/mfa-for-admin-actions.mdx): Require MFA checks to perform administrative actions. +- [Passwordless](authentication/passwordless.mdx): Learn how to use passwordless authentication with Teleport. +- [Per-session MFA](authentication/per-session-mfa.mdx): Require MFA checks to initiate sessions. +- [Second Factor: WebAuthn](authentication/webauthn.mdx): Configuring WebAuthn support in Teleport clusters. + +## Device Trust + +Device Trust allows Teleport admins to enforce the use of trusted devices. ([more info](authentication/device-trust.mdx)) + +- [Device Trust Overview](authentication/device-trust/concepts.mdx): Teleport Device Trust Concepts +- [Enforce Device Trust](authentication/device-trust/enforcing-device-trust.mdx): Learn how to enforce trusted devices with Teleport +- [Getting Started with Device Trust](authentication/device-trust/guide.mdx): Get started with Teleport Device Trust +- [Jamf Pro Integration](authentication/device-trust/jamf-integration.mdx): Sync your Jamf Pro inventory into Teleport +- [Manage Trusted Devices](authentication/device-trust/device-management.mdx): Learn how to manage Trusted Devices + +## Teleport Single-Sign-On + +Learn how to configure your single sign-on provider to allow authentication to Teleport. ([more info](authentication/sso.mdx)) + +- [Authentication With GitLab as an SSO provider](authentication/sso/gitlab.mdx): How to configure Teleport access using GitLab for SSO +- [Authentication With Okta as an SSO Provider](authentication/sso/okta.mdx): How to configure Teleport access using Okta for SSO +- [OAuth2 and OIDC authentication](authentication/sso/oidc.mdx): How to configure Teleport access with OAuth2 or OpenID connect (OIDC) +- [SSO with Active Directory Federation Services](authentication/sso/adfs.mdx): How to configure Teleport access with Active Directory Federation Services +- [Set up Single Sign-On with GitHub](authentication/sso/github-sso.mdx): Setting up GitHub SSO +- [Teleport Authentication with Azure Active Directory (AD)](authentication/sso/azuread.mdx): How to configure Teleport access with Azure Active Directory. +- [Teleport Authentication with Google Workspace (G Suite)](authentication/sso/google-workspace.mdx): How to configure Teleport access with Google Workspace (formerly known as G Suite) +- [Teleport Authentication with OneLogin as an SSO Provider](authentication/sso/one-login.mdx): How to configure Teleport access using OneLogin as an SSO provider +- [Teleport Single Sign-On Overview](authentication/sso/overview.mdx): How to set up single sign-on (SSO) for SSH using Teleport diff --git a/docs/pages/access-controls/device-trust.mdx b/docs/pages/admin-guides/authentication/device-trust.mdx similarity index 100% rename from docs/pages/access-controls/device-trust.mdx rename to docs/pages/admin-guides/authentication/device-trust.mdx diff --git a/docs/pages/access-controls/device-trust/concepts.mdx b/docs/pages/admin-guides/authentication/device-trust/concepts.mdx similarity index 100% rename from docs/pages/access-controls/device-trust/concepts.mdx rename to docs/pages/admin-guides/authentication/device-trust/concepts.mdx diff --git a/docs/pages/access-controls/device-trust/device-management.mdx b/docs/pages/admin-guides/authentication/device-trust/device-management.mdx similarity index 100% rename from docs/pages/access-controls/device-trust/device-management.mdx rename to docs/pages/admin-guides/authentication/device-trust/device-management.mdx diff --git a/docs/pages/access-controls/device-trust/enforcing-device-trust.mdx b/docs/pages/admin-guides/authentication/device-trust/enforcing-device-trust.mdx similarity index 100% rename from docs/pages/access-controls/device-trust/enforcing-device-trust.mdx rename to docs/pages/admin-guides/authentication/device-trust/enforcing-device-trust.mdx diff --git a/docs/pages/access-controls/device-trust/guide.mdx b/docs/pages/admin-guides/authentication/device-trust/guide.mdx similarity index 100% rename from docs/pages/access-controls/device-trust/guide.mdx rename to docs/pages/admin-guides/authentication/device-trust/guide.mdx diff --git a/docs/pages/access-controls/device-trust/jamf-integration.mdx b/docs/pages/admin-guides/authentication/device-trust/jamf-integration.mdx similarity index 100% rename from docs/pages/access-controls/device-trust/jamf-integration.mdx rename to docs/pages/admin-guides/authentication/device-trust/jamf-integration.mdx diff --git a/docs/pages/access-controls/guides/dual-authz.mdx b/docs/pages/admin-guides/authentication/dual-authz.mdx similarity index 100% rename from docs/pages/access-controls/guides/dual-authz.mdx rename to docs/pages/admin-guides/authentication/dual-authz.mdx diff --git a/docs/pages/access-controls/guides/hardware-key-support.mdx b/docs/pages/admin-guides/authentication/hardware-key-support.mdx similarity index 100% rename from docs/pages/access-controls/guides/hardware-key-support.mdx rename to docs/pages/admin-guides/authentication/hardware-key-support.mdx diff --git a/docs/pages/access-controls/guides/headless.mdx b/docs/pages/admin-guides/authentication/headless.mdx similarity index 100% rename from docs/pages/access-controls/guides/headless.mdx rename to docs/pages/admin-guides/authentication/headless.mdx diff --git a/docs/pages/access-controls/guides/mfa-for-admin-actions.mdx b/docs/pages/admin-guides/authentication/mfa-for-admin-actions.mdx similarity index 100% rename from docs/pages/access-controls/guides/mfa-for-admin-actions.mdx rename to docs/pages/admin-guides/authentication/mfa-for-admin-actions.mdx diff --git a/docs/pages/access-controls/guides/passwordless.mdx b/docs/pages/admin-guides/authentication/passwordless.mdx similarity index 100% rename from docs/pages/access-controls/guides/passwordless.mdx rename to docs/pages/admin-guides/authentication/passwordless.mdx diff --git a/docs/pages/access-controls/guides/per-session-mfa.mdx b/docs/pages/admin-guides/authentication/per-session-mfa.mdx similarity index 100% rename from docs/pages/access-controls/guides/per-session-mfa.mdx rename to docs/pages/admin-guides/authentication/per-session-mfa.mdx diff --git a/docs/pages/access-controls/sso.mdx b/docs/pages/admin-guides/authentication/sso.mdx similarity index 100% rename from docs/pages/access-controls/sso.mdx rename to docs/pages/admin-guides/authentication/sso.mdx diff --git a/docs/pages/access-controls/sso/adfs.mdx b/docs/pages/admin-guides/authentication/sso/adfs.mdx similarity index 100% rename from docs/pages/access-controls/sso/adfs.mdx rename to docs/pages/admin-guides/authentication/sso/adfs.mdx diff --git a/docs/pages/access-controls/sso/azuread.mdx b/docs/pages/admin-guides/authentication/sso/azuread.mdx similarity index 100% rename from docs/pages/access-controls/sso/azuread.mdx rename to docs/pages/admin-guides/authentication/sso/azuread.mdx diff --git a/docs/pages/access-controls/sso/github-sso.mdx b/docs/pages/admin-guides/authentication/sso/github-sso.mdx similarity index 100% rename from docs/pages/access-controls/sso/github-sso.mdx rename to docs/pages/admin-guides/authentication/sso/github-sso.mdx diff --git a/docs/pages/access-controls/sso/gitlab.mdx b/docs/pages/admin-guides/authentication/sso/gitlab.mdx similarity index 100% rename from docs/pages/access-controls/sso/gitlab.mdx rename to docs/pages/admin-guides/authentication/sso/gitlab.mdx diff --git a/docs/pages/access-controls/sso/google-workspace.mdx b/docs/pages/admin-guides/authentication/sso/google-workspace.mdx similarity index 100% rename from docs/pages/access-controls/sso/google-workspace.mdx rename to docs/pages/admin-guides/authentication/sso/google-workspace.mdx diff --git a/docs/pages/access-controls/sso/oidc.mdx b/docs/pages/admin-guides/authentication/sso/oidc.mdx similarity index 100% rename from docs/pages/access-controls/sso/oidc.mdx rename to docs/pages/admin-guides/authentication/sso/oidc.mdx diff --git a/docs/pages/access-controls/sso/okta.mdx b/docs/pages/admin-guides/authentication/sso/okta.mdx similarity index 100% rename from docs/pages/access-controls/sso/okta.mdx rename to docs/pages/admin-guides/authentication/sso/okta.mdx diff --git a/docs/pages/access-controls/sso/one-login.mdx b/docs/pages/admin-guides/authentication/sso/one-login.mdx similarity index 100% rename from docs/pages/access-controls/sso/one-login.mdx rename to docs/pages/admin-guides/authentication/sso/one-login.mdx diff --git a/docs/pages/access-controls/sso/overview.mdx b/docs/pages/admin-guides/authentication/sso/overview.mdx similarity index 100% rename from docs/pages/access-controls/sso/overview.mdx rename to docs/pages/admin-guides/authentication/sso/overview.mdx diff --git a/docs/pages/access-controls/guides/webauthn.mdx b/docs/pages/admin-guides/authentication/webauthn.mdx similarity index 100% rename from docs/pages/access-controls/guides/webauthn.mdx rename to docs/pages/admin-guides/authentication/webauthn.mdx diff --git a/docs/pages/admin-guides/common-operations.mdx b/docs/pages/admin-guides/common-operations.mdx index 18fe018cb2c1e..9418afbf680d9 100644 --- a/docs/pages/admin-guides/common-operations.mdx +++ b/docs/pages/admin-guides/common-operations.mdx @@ -11,6 +11,16 @@ description: Contains guides for performing common tasks on a Teleport cluster a - [Troubleshooting](common-operations/troubleshooting.mdx): Troubleshooting and Collecting Metrics of Teleport Processes - [Uninstall Teleport](common-operations/uninstall-teleport.mdx): How to remove Teleport from your system +## Configure Teleport as an identity provider + +How to set up Teleport's identity provider functionality ([more info](common-operations/idps.mdx)) + +- [Access GCP Web Console and API with a federated authentication.](common-operations/idps/saml-gcp-workforce-identity-federation.mdx): Manage Google Cloud Platform (GCP) web console access with Teleport SAMl IdP. +- [SAML IdP Attribute Mapping](common-operations/idps/saml-attribute-mapping.mdx): How to map user attributes to custom SAML response +- [SAML Identity Provider Reference](common-operations/idps/saml-reference.mdx): Reference documentation for the SAML identity provider +- [Use Teleport's SAML Provider to authenticate with Grafana](common-operations/idps/saml-grafana.mdx): Configure Grafana to use identities provided by Teleport. +- [Using Teleport as a SAML identity provider](common-operations/idps/saml-guide.mdx): How to configure and use Teleport as a SAML identity provider. + ## Exporting Teleport Audit Events Learn how to export Teleport audit events to your log management solution. ([more info](common-operations/export-audit-events.mdx)) diff --git a/docs/pages/access-controls/idps.mdx b/docs/pages/admin-guides/common-operations/idps.mdx similarity index 100% rename from docs/pages/access-controls/idps.mdx rename to docs/pages/admin-guides/common-operations/idps.mdx diff --git a/docs/pages/access-controls/idps/saml-attribute-mapping.mdx b/docs/pages/admin-guides/common-operations/idps/saml-attribute-mapping.mdx similarity index 100% rename from docs/pages/access-controls/idps/saml-attribute-mapping.mdx rename to docs/pages/admin-guides/common-operations/idps/saml-attribute-mapping.mdx diff --git a/docs/pages/access-controls/idps/saml-gcp-workforce-identity-federation.mdx b/docs/pages/admin-guides/common-operations/idps/saml-gcp-workforce-identity-federation.mdx similarity index 100% rename from docs/pages/access-controls/idps/saml-gcp-workforce-identity-federation.mdx rename to docs/pages/admin-guides/common-operations/idps/saml-gcp-workforce-identity-federation.mdx diff --git a/docs/pages/access-controls/idps/saml-grafana.mdx b/docs/pages/admin-guides/common-operations/idps/saml-grafana.mdx similarity index 100% rename from docs/pages/access-controls/idps/saml-grafana.mdx rename to docs/pages/admin-guides/common-operations/idps/saml-grafana.mdx diff --git a/docs/pages/access-controls/idps/saml-guide.mdx b/docs/pages/admin-guides/common-operations/idps/saml-guide.mdx similarity index 100% rename from docs/pages/access-controls/idps/saml-guide.mdx rename to docs/pages/admin-guides/common-operations/idps/saml-guide.mdx diff --git a/docs/pages/access-controls/idps/saml-reference.mdx b/docs/pages/admin-guides/common-operations/idps/saml-reference.mdx similarity index 100% rename from docs/pages/access-controls/idps/saml-reference.mdx rename to docs/pages/admin-guides/common-operations/idps/saml-reference.mdx diff --git a/docs/pages/admin-guides/rbac.mdx b/docs/pages/admin-guides/rbac.mdx deleted file mode 100644 index 32481fe182fdc..0000000000000 --- a/docs/pages/admin-guides/rbac.mdx +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: Teleport Access Controls -description: Guides to configuring the access that Teleport users have to infrastructure resources and cluster permissions. ---- - -{/*TOPICS*/} - -- [Configure Trusted Clusters](rbac/trustedclusters.mdx): Explains how you can configure a trust relationship and manage access between two Teleport clusters. - -## Teleport Label Guides - -Guides to using Teleport labels, which underpin the Teleport role-based access controls system. ([more info](rbac/labels.mdx)) - -- [Add Labels to Resources](rbac/labels/labels.mdx): How to assign static and command-based dynamic labels to Teleport resources. -- [EC2 Tags as Teleport Node Labels](rbac/labels/ec2-tags.mdx): How to set up Teleport Node labels based on EC2 tags -- [GCP Tags and Labels as Teleport Agent Labels](rbac/labels/gcp-tags.mdx): How to set up Teleport agent labels based on GCP tags and labels diff --git a/docs/pages/reference.mdx b/docs/pages/reference.mdx index d86b3676ae83c..42d21c89eb382 100644 --- a/docs/pages/reference.mdx +++ b/docs/pages/reference.mdx @@ -32,6 +32,7 @@ Configuration and CLI reference for Teleport Machine ID. ([more info](reference/ References for concepts and tools available for operating Teleport. ([more info](reference/operations.mdx)) - [Authentication options](reference/operations/authentication.mdx): A reference for Teleport's authentication connectors +- [Compliance Frameworks (section)](reference/operations/compliance-frameworks.mdx): How to use Teleport's access controls to streamline compliance without sacrificing productivity. - [Join Methods and Token Reference](reference/operations/join-methods.mdx): Describes the different ways to configure a Teleport to join a cluster. - [Local Users](reference/operations/users.mdx): Learn how to manage local users in Teleport. Local users are stored on the Auth Service instead of a third-party identity provider. - [Networking](reference/operations/networking.mdx): This reference explains the networking requirements of a Teleport cluster, including its public address, ports, and support for HTTP CONNECT proxies. @@ -39,6 +40,15 @@ References for concepts and tools available for operating Teleport. ([more info] - [Teleport Signals Reference](reference/operations/signals.mdx): Signals you can send to a running teleport process. - [User Types](reference/operations/user-types.mdx): Describes the different types of Teleport users and their properties. +## Teleport Access Controls References + +Guides to configuring Teleport RBAC. ([more info](reference/rbac.mdx)) + +- [Access List Reference](reference/rbac/access-lists.mdx): An explanation and overview of Access Lists in Teleport. +- [Configure Access Requests](reference/rbac/access-request-configuration.mdx): Describes the options available for configuring just-in-time access to roles and resources in your Teleport cluster. +- [Login Rules Reference](reference/rbac/login-rules.mdx): Reference documentation for Login Rules +- [Teleport Access Controls Reference](reference/rbac/teleport-roles.mdx): Explains the configuration settings that you can include in a Teleport role, which enables you to apply access controls for your infrastructure. + ## Teleport Agent Service References Commands and configuration options for Teleport agent services, e.g., for protecting servers, databases, Kubernetes clusters, and Windows desktops. ([more info](reference/agents.mdx)) diff --git a/docs/pages/reference/operations.mdx b/docs/pages/reference/operations.mdx index 684bca91541c7..e6a6126b398cb 100644 --- a/docs/pages/reference/operations.mdx +++ b/docs/pages/reference/operations.mdx @@ -15,3 +15,10 @@ Teleport. - [Storage backends](operations/backends.mdx): How to configure Teleport deployment for high-availability using storage backends - [Teleport Signals Reference](operations/signals.mdx): Signals you can send to a running teleport process. - [User Types](operations/user-types.mdx): Describes the different types of Teleport users and their properties. + +## Compliance Frameworks + +How to use Teleport's access controls to streamline compliance without sacrificing productivity. ([more info](operations/compliance-frameworks.mdx)) + +- [FedRAMP Compliance for Infrastructure Access](operations/compliance-frameworks/fedramp.mdx): How to configure SSH, Kubernetes, database, and web app access to be FedRAMP compliant, including support for FIPS 140-2. +- [SOC 2 compliance for SSH, Kubernetes, and Databases](operations/compliance-frameworks/soc2.mdx): How to configure SOC 2-compliant access to SSH, Kubernetes, databases, desktops, and web apps diff --git a/docs/pages/access-controls/compliance-frameworks.mdx b/docs/pages/reference/operations/compliance-frameworks.mdx similarity index 100% rename from docs/pages/access-controls/compliance-frameworks.mdx rename to docs/pages/reference/operations/compliance-frameworks.mdx diff --git a/docs/pages/access-controls/compliance-frameworks/fedramp.mdx b/docs/pages/reference/operations/compliance-frameworks/fedramp.mdx similarity index 100% rename from docs/pages/access-controls/compliance-frameworks/fedramp.mdx rename to docs/pages/reference/operations/compliance-frameworks/fedramp.mdx diff --git a/docs/pages/access-controls/compliance-frameworks/soc2.mdx b/docs/pages/reference/operations/compliance-frameworks/soc2.mdx similarity index 100% rename from docs/pages/access-controls/compliance-frameworks/soc2.mdx rename to docs/pages/reference/operations/compliance-frameworks/soc2.mdx diff --git a/docs/pages/reference/rbac.mdx b/docs/pages/reference/rbac.mdx new file mode 100644 index 0000000000000..93d10ab787c41 --- /dev/null +++ b/docs/pages/reference/rbac.mdx @@ -0,0 +1,11 @@ +--- +title: Teleport Access Controls References +description: Guides to configuring Teleport RBAC. +--- + +{/*TOPICS*/} + +- [Access List Reference](rbac/access-lists.mdx): An explanation and overview of Access Lists in Teleport. +- [Configure Access Requests](rbac/access-request-configuration.mdx): Describes the options available for configuring just-in-time access to roles and resources in your Teleport cluster. +- [Login Rules Reference](rbac/login-rules.mdx): Reference documentation for Login Rules +- [Teleport Access Controls Reference](rbac/teleport-roles.mdx): Explains the configuration settings that you can include in a Teleport role, which enables you to apply access controls for your infrastructure. diff --git a/docs/pages/access-controls/access-lists/reference.mdx b/docs/pages/reference/rbac/access-lists.mdx similarity index 100% rename from docs/pages/access-controls/access-lists/reference.mdx rename to docs/pages/reference/rbac/access-lists.mdx diff --git a/docs/pages/access-controls/access-requests/access-request-configuration.mdx b/docs/pages/reference/rbac/access-request-configuration.mdx similarity index 100% rename from docs/pages/access-controls/access-requests/access-request-configuration.mdx rename to docs/pages/reference/rbac/access-request-configuration.mdx diff --git a/docs/pages/access-controls/login-rules/reference.mdx b/docs/pages/reference/rbac/login-rules.mdx similarity index 100% rename from docs/pages/access-controls/login-rules/reference.mdx rename to docs/pages/reference/rbac/login-rules.mdx diff --git a/docs/pages/access-controls/reference.mdx b/docs/pages/reference/rbac/teleport-roles.mdx similarity index 100% rename from docs/pages/access-controls/reference.mdx rename to docs/pages/reference/rbac/teleport-roles.mdx diff --git a/integration/hostuser_test.go b/integration/hostuser_test.go index 10041cb4a7631..076328c9e4441 100644 --- a/integration/hostuser_test.go +++ b/integration/hostuser_test.go @@ -28,6 +28,7 @@ import ( "os/exec" "os/user" "path/filepath" + "slices" "testing" "github.com/gravitational/trace" @@ -180,7 +181,7 @@ func TestRootHostUsersBackend(t *testing.T) { }) } -func requireUserInGroups(t *testing.T, u *user.User, requiredGroups []string) { +func getUserGroups(t *testing.T, u *user.User) []string { var userGroups []string userGids, err := u.GroupIds() require.NoError(t, err) @@ -189,7 +190,11 @@ func requireUserInGroups(t *testing.T, u *user.User, requiredGroups []string) { require.NoError(t, err) userGroups = append(userGroups, group.Name) } - require.Subset(t, userGroups, requiredGroups) + return userGroups +} + +func requireUserInGroups(t *testing.T, u *user.User, requiredGroups []string) { + require.Subset(t, getUserGroups(t, u), requiredGroups) } func cleanupUsersAndGroups(users []string, groups []string) func() { @@ -219,7 +224,7 @@ func TestRootHostUsers(t *testing.T) { users := srv.NewHostUsers(context.Background(), presence, "host_uuid") testGroups := []string{"group1", "group2"} - closer, err := users.CreateUser(testuser, &services.HostUsersInfo{Groups: testGroups, Mode: types.CreateHostUserMode_HOST_USER_MODE_INSECURE_DROP}) + closer, err := users.UpsertUser(testuser, &services.HostUsersInfo{Groups: testGroups, Mode: types.CreateHostUserMode_HOST_USER_MODE_INSECURE_DROP}) require.NoError(t, err) testGroups = append(testGroups, types.TeleportServiceGroup) @@ -244,7 +249,7 @@ func TestRootHostUsers(t *testing.T) { _, err := user.LookupGroupId(testGID) require.ErrorIs(t, err, user.UnknownGroupIdError(testGID)) - closer, err := users.CreateUser(testuser, &services.HostUsersInfo{ + closer, err := users.UpsertUser(testuser, &services.HostUsersInfo{ Mode: types.CreateHostUserMode_HOST_USER_MODE_INSECURE_DROP, UID: testUID, GID: testGID, @@ -273,7 +278,7 @@ func TestRootHostUsers(t *testing.T) { expectedHome := filepath.Join("/home", testuser) require.NoDirExists(t, expectedHome) - closer, err := users.CreateUser(testuser, &services.HostUsersInfo{Mode: types.CreateHostUserMode_HOST_USER_MODE_KEEP}) + closer, err := users.UpsertUser(testuser, &services.HostUsersInfo{Mode: types.CreateHostUserMode_HOST_USER_MODE_KEEP}) require.NoError(t, err) require.Nil(t, closer) t.Cleanup(cleanupUsersAndGroups([]string{testuser}, nil)) @@ -303,7 +308,7 @@ func TestRootHostUsers(t *testing.T) { os.Remove(sudoersPath(testuser, uuid)) host.UserDel(testuser) }) - closer, err := users.CreateUser(testuser, + closer, err := users.UpsertUser(testuser, &services.HostUsersInfo{ Mode: types.CreateHostUserMode_HOST_USER_MODE_INSECURE_DROP, }) @@ -334,12 +339,12 @@ func TestRootHostUsers(t *testing.T) { deleteableUsers := []string{"teleport-user1", "teleport-user2", "teleport-user3"} for _, user := range deleteableUsers { - _, err := users.CreateUser(user, &services.HostUsersInfo{Mode: types.CreateHostUserMode_HOST_USER_MODE_INSECURE_DROP}) + _, err := users.UpsertUser(user, &services.HostUsersInfo{Mode: types.CreateHostUserMode_HOST_USER_MODE_INSECURE_DROP}) require.NoError(t, err) } // this user should not be in the service group as it was created with mode keep. - closer, err := users.CreateUser("teleport-user4", &services.HostUsersInfo{ + closer, err := users.UpsertUser("teleport-user4", &services.HostUsersInfo{ Mode: types.CreateHostUserMode_HOST_USER_MODE_KEEP, }) require.NoError(t, err) @@ -360,4 +365,66 @@ func TestRootHostUsers(t *testing.T) { require.Equal(t, err, user.UnknownUserError(us)) } }) + + t.Run("test update changed groups", func(t *testing.T) { + tests := []struct { + name string + firstGroups []string + secondGroups []string + }{ + { + name: "add groups", + secondGroups: []string{"group1", "group2"}, + }, + { + name: "delete groups", + firstGroups: []string{"group1", "group2"}, + }, + { + name: "change groups", + firstGroups: []string{"group1", "group2"}, + secondGroups: []string{"group2", "group3"}, + }, + { + name: "no change", + firstGroups: []string{"group1", "group2"}, + secondGroups: []string{"group2", "group1"}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Cleanup(cleanupUsersAndGroups([]string{testuser}, slices.Concat(tc.firstGroups, tc.secondGroups))) + + // Verify that the user is created with the first set of groups. + users := srv.NewHostUsers(context.Background(), presence, "host_uuid") + _, err := users.UpsertUser(testuser, &services.HostUsersInfo{ + Groups: tc.firstGroups, + Mode: types.CreateHostUserMode_HOST_USER_MODE_KEEP, + }) + require.NoError(t, err) + u, err := user.Lookup(testuser) + require.NoError(t, err) + requireUserInGroups(t, u, tc.firstGroups) + + // Verify that the user is updated with the second set of groups. + _, err = users.UpsertUser(testuser, &services.HostUsersInfo{ + Groups: tc.secondGroups, + Mode: types.CreateHostUserMode_HOST_USER_MODE_KEEP, + }) + require.NoError(t, err) + u, err = user.Lookup(testuser) + require.NoError(t, err) + requireUserInGroups(t, u, tc.secondGroups) + + // Verify that the appropriate groups form the first set were deleted. + userGroups := getUserGroups(t, u) + for _, group := range tc.firstGroups { + if !slices.Contains(tc.secondGroups, group) { + require.NotContains(t, userGroups, group) + } + } + }) + } + }) } diff --git a/lib/backend/dynamo/atomicwrite.go b/lib/backend/dynamo/atomicwrite.go index d0a61cb1ee66c..78a506a1a5b13 100644 --- a/lib/backend/dynamo/atomicwrite.go +++ b/lib/backend/dynamo/atomicwrite.go @@ -199,6 +199,10 @@ TxnLoop: if err != nil { txnErr := &dynamodb.TransactionCanceledException{} if !errors.As(err, &txnErr) { + if s := err.Error(); strings.Contains(s, "AccessDenied") && strings.Contains(s, "dynamodb:ConditionCheckItem") { + b.Warnf("AtomicWrite failed with error that may indicate dynamodb is missing the required dynamodb:ConditionCheckItem permission (this permission is now required for teleport v16 and later). Consider updating your IAM policy to include this permission. Original error: %v", err) + return "", trace.Errorf("teleport is missing required AWS permission dynamodb:ConditionCheckItem, please contact your administrator to update permissions") + } return "", trace.Errorf("unexpected error during atomic write: %v", err) } diff --git a/lib/cloud/gcp/vm_test.go b/lib/cloud/gcp/vm_test.go index 77cc248f84ba7..8bfbecd08d994 100644 --- a/lib/cloud/gcp/vm_test.go +++ b/lib/cloud/gcp/vm_test.go @@ -73,7 +73,8 @@ func TestConvertAPIError(t *testing.T) { } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - require.True(t, trace.IsAccessDenied(convertAPIError(tc.err))) + err := convertAPIError(tc.err) + require.True(t, trace.IsAccessDenied(err), "unexpected error of type %T: %v", tc.err, err) require.Contains(t, tc.err.Error(), "abcd1234") }) } diff --git a/lib/cloud/imds/gcp/imds.go b/lib/cloud/imds/gcp/imds.go index 0830ab3938a9c..49525dc533794 100644 --- a/lib/cloud/imds/gcp/imds.go +++ b/lib/cloud/imds/gcp/imds.go @@ -76,12 +76,23 @@ func NewInstanceMetadataClient(ctx context.Context) (*InstanceMetadataClient, er // IsAvailable checks if instance metadata is available. func (client *InstanceMetadataClient) IsAvailable(ctx context.Context) bool { - instanceData, err := client.getMetadata(ctx, "instance") - return err == nil && instanceData != "" + _, err := client.getNumericID(ctx) + return err == nil } -// GetTags gets all of the GCP instance's labels (note: these are separate from -// its tags, which we do not use). +func (client *InstanceMetadataClient) getNumericID(ctx context.Context) (uint64, error) { + idStr, err := client.GetID(ctx) + if err != nil { + return 0, trace.Wrap(err) + } + id, err := strconv.ParseUint(idStr, 10, 64) + if err != nil || id == 0 { + return 0, trace.BadParameter("Invalid instance ID %q", idStr) + } + return id, nil +} + +// GetTags gets all of the GCP instance's labels and tags. func (client *InstanceMetadataClient) GetTags(ctx context.Context) (map[string]string, error) { // Get a bunch of info from instance metadata. projectID, err := client.GetProjectID(ctx) @@ -96,11 +107,7 @@ func (client *InstanceMetadataClient) GetTags(ctx context.Context) (map[string]s if err != nil { return nil, trace.Wrap(err) } - idStr, err := client.GetID(ctx) - if err != nil { - return nil, trace.Wrap(err) - } - id, err := strconv.ParseUint(idStr, 10, 64) + id, err := client.getNumericID(ctx) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/cloud/imds/gcp/imds_test.go b/lib/cloud/imds/gcp/imds_test.go index 10ee2c1a2f5f6..ab33e44b4ddec 100644 --- a/lib/cloud/imds/gcp/imds_test.go +++ b/lib/cloud/imds/gcp/imds_test.go @@ -58,16 +58,51 @@ func (m *mockInstanceGetter) GetInstanceTags(ctx context.Context, req *gcp.Insta func TestIsInstanceMetadataAvailable(t *testing.T) { t.Parallel() - t.Run("not available", func(t *testing.T) { - client := &InstanceMetadataClient{ + tests := []struct { + name string + getMetadata metadataGetter + assert require.BoolAssertionFunc + }{ + { + name: "not available", getMetadata: func(ctx context.Context, path string) (string, error) { return "", trace.NotFound("") }, - } - require.False(t, client.IsAvailable(context.Background())) - }) + assert: require.False, + }, + { + name: "not on gcp", + getMetadata: func(ctx context.Context, path string) (string, error) { + return "non-numeric id", nil + }, + assert: require.False, + }, + { + name: "zero ID", + getMetadata: func(ctx context.Context, path string) (string, error) { + return "0", nil + }, + assert: require.False, + }, + { + name: "on mocked gcp", + getMetadata: func(ctx context.Context, path string) (string, error) { + return "12345678", nil + }, + assert: require.True, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + client := &InstanceMetadataClient{ + getMetadata: tc.getMetadata, + } + tc.assert(t, client.IsAvailable(context.Background())) + }) + } - t.Run("on gcp", func(t *testing.T) { + t.Run("on real gcp", func(t *testing.T) { if os.Getenv("TELEPORT_TEST_GCP") == "" { t.Skip("not on gcp") } diff --git a/lib/service/service.go b/lib/service/service.go index 6fc03b7c985b2..ec6278904b7ca 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -955,8 +955,14 @@ func NewTeleport(cfg *servicecfg.Config) (*TeleportProcess, error) { imClient := cfg.InstanceMetadataClient if imClient == nil { imClient, err = cloud.DiscoverInstanceMetadata(supervisor.ExitContext()) - if err != nil && !trace.IsNotFound(err) { - return nil, trace.Wrap(err) + if err == nil { + cfg.Logger.InfoContext(supervisor.ExitContext(), + "Found an instance metadata service. Teleport will import labels from this cloud instance.", + "type", imClient.GetType()) + } else if !trace.IsNotFound(err) { + cfg.Logger.ErrorContext(supervisor.ExitContext(), "Error looking for cloud instance metadata", "error", err) + // Keep going. Not being able to fetch labels isn't necessarily an error (e.g. the user doesn't need imported + // labels and hasn't configured their cloud instance for it). } } @@ -965,15 +971,16 @@ func NewTeleport(cfg *servicecfg.Config) (*TeleportProcess, error) { if err == nil { cloudHostname = strings.ReplaceAll(cloudHostname, " ", "_") if utils.IsValidHostname(cloudHostname) { - cfg.Logger.InfoContext(supervisor.ExitContext(), "Overriding hostname with value from cloud tag TeleportHostname.", "hostname", cloudHostname) + cfg.Logger.InfoContext(supervisor.ExitContext(), "Overriding hostname with value from cloud tag TeleportHostname", "hostname", cloudHostname) cfg.Hostname = cloudHostname // cloudHostname exists but is not a valid hostname. } else if cloudHostname != "" { - cfg.Logger.InfoContext(supervisor.ExitContext(), "Found invalid hostname in cloud tag TeleportHostname.", "hostname", cloudHostname) + cfg.Logger.InfoContext(supervisor.ExitContext(), "Found invalid hostname in cloud tag TeleportHostname", "hostname", cloudHostname) } } else if !trace.IsNotFound(err) { - return nil, trace.Wrap(err) + cfg.Logger.ErrorContext(supervisor.ExitContext(), "Error looking for hostname tag", "error", err) + // Keep going. } cloudLabels, err = labels.NewCloudImporter(supervisor.ExitContext(), &labels.CloudConfig{ @@ -981,7 +988,8 @@ func NewTeleport(cfg *servicecfg.Config) (*TeleportProcess, error) { Clock: cfg.Clock, }) if err != nil { - return nil, trace.Wrap(err) + cfg.Logger.ErrorContext(supervisor.ExitContext(), "Cloud labels will not be imported", "error", err) + // Keep going. } } diff --git a/lib/service/service_test.go b/lib/service/service_test.go index ec276e8bd1660..b42014e491124 100644 --- a/lib/service/service_test.go +++ b/lib/service/service_test.go @@ -1620,3 +1620,112 @@ func TestDebugServiceStartSocket(t *testing.T) { defer req.Body.Close() require.Equal(t, http.StatusNotFound, req.StatusCode) } + +type mockInstanceMetadata struct { + hostname string + hostnameErr error +} + +func (m *mockInstanceMetadata) IsAvailable(ctx context.Context) bool { + return true +} + +func (m *mockInstanceMetadata) GetTags(ctx context.Context) (map[string]string, error) { + return nil, nil +} + +func (m *mockInstanceMetadata) GetHostname(ctx context.Context) (string, error) { + return m.hostname, m.hostnameErr +} + +func (m *mockInstanceMetadata) GetType() types.InstanceMetadataType { + return "mock" +} + +func (m *mockInstanceMetadata) GetID(ctx context.Context) (string, error) { + return "", nil +} + +func TestInstanceMetadata(t *testing.T) { + t.Parallel() + + newCfg := func() *servicecfg.Config { + cfg := servicecfg.MakeDefaultConfig() + cfg.Hostname = "default.example.com" + cfg.SetAuthServerAddress(utils.NetAddr{AddrNetwork: "tcp", Addr: "127.0.0.1:0"}) + cfg.Auth.ListenAddr = utils.NetAddr{AddrNetwork: "tcp", Addr: "127.0.0.1:0"} + cfg.Proxy.Enabled = false + cfg.SSH.Enabled = false + cfg.CircuitBreakerConfig = breaker.NoopBreakerConfig() + return cfg + } + + tests := []struct { + name string + imClient imds.Client + expectCloudLabels bool + expectedHostname string + }{ + { + name: "no instance metadata", + imClient: imds.NewDisabledIMDSClient(), + expectCloudLabels: false, + expectedHostname: "default.example.com", + }, + { + name: "instance metadata with valid hostname", + imClient: &mockInstanceMetadata{ + hostname: "new.example.com", + }, + expectCloudLabels: true, + expectedHostname: "new.example.com", + }, + { + name: "instance metadata with no hostname", + imClient: &mockInstanceMetadata{ + hostnameErr: trace.NotFound(""), + }, + expectCloudLabels: true, + expectedHostname: "default.example.com", + }, + { + name: "instance metadata with invalid hostname", + imClient: &mockInstanceMetadata{ + hostname: ")7%#(*&@())", + }, + expectCloudLabels: true, + expectedHostname: "default.example.com", + }, + { + name: "instance metadata with hostname error", + imClient: &mockInstanceMetadata{ + hostnameErr: trace.Errorf(""), + }, + expectCloudLabels: true, + expectedHostname: "default.example.com", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + cfg := newCfg() + cfg.DataDir = t.TempDir() + cfg.Auth.StorageConfig.Params["path"] = t.TempDir() + cfg.InstanceMetadataClient = tc.imClient + + process, err := NewTeleport(cfg) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, process.Close()) + }) + + if tc.expectCloudLabels { + require.NotNil(t, process.cloudLabels) + } else { + require.Nil(t, process.cloudLabels) + } + + require.Equal(t, tc.expectedHostname, cfg.Hostname) + }) + } +} diff --git a/lib/srv/sess.go b/lib/srv/sess.go index 461935b0455eb..1ba3e436fc801 100644 --- a/lib/srv/sess.go +++ b/lib/srv/sess.go @@ -281,7 +281,7 @@ func (s *SessionRegistry) TryCreateHostUser(ctx *ServerContext) error { if trace.IsAccessDenied(err) && existsErr != nil { return trace.WrapWithMessage(err, "Insufficient permission for host user creation") } - userCloser, err := s.users.CreateUser(ctx.Identity.Login, ui) + userCloser, err := s.users.UpsertUser(ctx.Identity.Login, ui) if userCloser != nil { ctx.AddCloser(userCloser) } diff --git a/lib/srv/usermgmt.go b/lib/srv/usermgmt.go index af2ac11c812ea..d8ce41205908f 100644 --- a/lib/srv/usermgmt.go +++ b/lib/srv/usermgmt.go @@ -96,6 +96,8 @@ type HostUsersBackend interface { LookupGroup(group string) (*user.Group, error) // LookupGroupByID retrieves a group by its ID. LookupGroupByID(gid string) (*user.Group, error) + // SetUserGroups sets a user's groups, replacing their existing groups. + SetUserGroups(name string, groups []string) error // CreateGroup creates a group on a host. CreateGroup(group string, gid string) error // CreateUser creates a user on a host. @@ -145,8 +147,8 @@ func (*HostSudoersNotImplemented) RemoveSudoers(name string) error { } type HostUsers interface { - // CreateUser creates a temporary Teleport user in the TeleportServiceGroup - CreateUser(name string, hostRoleInfo *services.HostUsersInfo) (io.Closer, error) + // UpsertUser creates a temporary Teleport user in the TeleportServiceGroup + UpsertUser(name string, hostRoleInfo *services.HostUsersInfo) (io.Closer, error) // DeleteUser deletes a temporary Teleport user only if they are // in a specified group DeleteUser(name string, gid string) error @@ -221,29 +223,71 @@ func (u *HostSudoersManagement) RemoveSudoers(name string) error { return nil } -// CreateUser creates a temporary Teleport user in the TeleportServiceGroup -func (u *HostUserManagement) CreateUser(name string, ui *services.HostUsersInfo) (io.Closer, error) { +// UpsertUser creates a temporary Teleport user in the TeleportServiceGroup +func (u *HostUserManagement) UpsertUser(name string, ui *services.HostUsersInfo) (io.Closer, error) { if ui.Mode == types.CreateHostUserMode_HOST_USER_MODE_UNSPECIFIED { return nil, trace.BadParameter("Mode is a required argument to CreateUser") } + groups := make([]string, 0, len(ui.Groups)) + for _, group := range ui.Groups { + if group == name { + // this causes an error as useradd expects the group with the same name as the user to be available + log.Debugf("Skipping group creation with name the same as login user (%q, %q).", name, group) + continue + } + groups = append(groups, group) + } + if ui.Mode == types.CreateHostUserMode_HOST_USER_MODE_INSECURE_DROP { + groups = append(groups, types.TeleportServiceGroup) + } + var errs []error + for _, group := range groups { + if err := u.createGroupIfNotExist(group); err != nil { + errs = append(errs, err) + continue + } + } + if err := trace.NewAggregate(errs...); err != nil { + return nil, trace.WrapWithMessage(err, "error while creating groups") + } + tempUser, err := u.backend.Lookup(name) if err != nil && !errors.Is(err, user.UnknownUserError(name)) { return nil, trace.Wrap(err) } if tempUser != nil { - gids, err := u.backend.UserGIDs(tempUser) - if err != nil { - return nil, trace.Wrap(err) + // Collect actions that need to be done together under a lock on the user. + actionsUnderLock := []func() error{ + func() error { + // If the user exists, set user groups again as they might have changed. + return trace.Wrap(u.backend.SetUserGroups(name, groups)) + }, + } + doWithUserLock := func() error { + return trace.Wrap(u.doWithUserLock(func(_ types.SemaphoreLease) error { + for _, action := range actionsUnderLock { + if err := action(); err != nil { + return trace.Wrap(err) + } + } + return nil + })) } + systemGroup, err := u.backend.LookupGroup(types.TeleportServiceGroup) if err != nil { if isUnknownGroupError(err, types.TeleportServiceGroup) { - return nil, trace.AlreadyExists("User %q already exists, however no users are currently managed by teleport", name) + // Teleport service group doesn't exist, so we don't need to update interaction time. + return nil, trace.Wrap(doWithUserLock()) } return nil, trace.Wrap(err) } + gids, err := u.backend.UserGIDs(tempUser) + if err != nil { + return nil, trace.Wrap(err) + } var found bool for _, gid := range gids { if gid == systemGroup.Gid { @@ -252,16 +296,14 @@ func (u *HostUserManagement) CreateUser(name string, ui *services.HostUsersInfo) } } if !found { - return nil, trace.AlreadyExists("User %q already exists and is not managed by teleport", name) + // User isn't managed by Teleport, so we don't need to update interaction time. + return nil, trace.Wrap(doWithUserLock()) } - err = u.doWithUserLock(func(_ types.SemaphoreLease) error { - if err := u.storage.UpsertHostUserInteractionTime(u.ctx, name, time.Now()); err != nil { - return trace.Wrap(err) - } - return nil + actionsUnderLock = append(actionsUnderLock, func() error { + return trace.Wrap(u.storage.UpsertHostUserInteractionTime(u.ctx, name, time.Now())) }) - if err != nil { + if err := doWithUserLock(); err != nil { return nil, trace.Wrap(err) } // try to delete even if the user already exists as only users @@ -272,30 +314,7 @@ func (u *HostUserManagement) CreateUser(name string, ui *services.HostUsersInfo) username: name, users: u, backend: u.backend, - }, trace.AlreadyExists("User %q already exists", name) - } - - groups := make([]string, 0, len(ui.Groups)) - for _, group := range ui.Groups { - if group == name { - // this causes an error as useradd expects the group with the same name as the user to be available - log.Debugf("Skipping group creation with name the same as login user (%q, %q).", name, group) - continue - } - groups = append(groups, group) - } - if ui.Mode == types.CreateHostUserMode_HOST_USER_MODE_INSECURE_DROP { - groups = append(groups, types.TeleportServiceGroup) - } - var errs []error - for _, group := range groups { - if err := u.createGroupIfNotExist(group); err != nil { - errs = append(errs, err) - continue - } - } - if err := trace.NewAggregate(errs...); err != nil { - return nil, trace.WrapWithMessage(err, "error while creating groups") + }, nil } var home string @@ -307,8 +326,10 @@ func (u *HostUserManagement) CreateUser(name string, ui *services.HostUsersInfo) } err = u.doWithUserLock(func(_ types.SemaphoreLease) error { - if err := u.storage.UpsertHostUserInteractionTime(u.ctx, name, time.Now()); err != nil { - return trace.Wrap(err) + if ui.Mode != types.CreateHostUserMode_HOST_USER_MODE_KEEP { + if err := u.storage.UpsertHostUserInteractionTime(u.ctx, name, time.Now()); err != nil { + return trace.Wrap(err) + } } if ui.GID != "" { // if gid is specified a group must already exist @@ -341,7 +362,7 @@ func (u *HostUserManagement) CreateUser(name string, ui *services.HostUsersInfo) } if ui.Mode == types.CreateHostUserMode_HOST_USER_MODE_KEEP { - return nil, trace.Wrap(err) + return nil, nil } closer := &userCloser{ diff --git a/lib/srv/usermgmt_linux.go b/lib/srv/usermgmt_linux.go index 3a2e8acb6a01b..6dcf085743233 100644 --- a/lib/srv/usermgmt_linux.go +++ b/lib/srv/usermgmt_linux.go @@ -90,6 +90,12 @@ func (*HostUsersProvisioningBackend) LookupGroupByID(gid string) (*user.Group, e return user.LookupGroupId(gid) } +// SetUserGroups sets a user's groups, replacing their existing groups. +func (*HostUsersProvisioningBackend) SetUserGroups(name string, groups []string) error { + _, err := host.SetUserGroups(name, groups) + return trace.Wrap(err) +} + // GetAllUsers returns a full list of users present on a system func (*HostUsersProvisioningBackend) GetAllUsers() ([]string, error) { users, _, err := host.GetAllUsers() diff --git a/lib/srv/usermgmt_test.go b/lib/srv/usermgmt_test.go index cd54df30f17f9..7003237105e2a 100644 --- a/lib/srv/usermgmt_test.go +++ b/lib/srv/usermgmt_test.go @@ -89,6 +89,14 @@ func (tm *testHostUserBackend) LookupGroupByID(gid string) (*user.Group, error) }, nil } +func (tm *testHostUserBackend) SetUserGroups(name string, groups []string) error { + if _, ok := tm.users[name]; !ok { + return trace.NotFound("User %q doesn't exist", name) + } + tm.users[name] = groups + return nil +} + func (tm *testHostUserBackend) UserGIDs(u *user.User) ([]string, error) { ids := make([]string, 0, len(tm.users[u.Username])) for _, id := range tm.users[u.Username] { @@ -175,7 +183,7 @@ func TestUserMgmt_CreateTemporaryUser(t *testing.T) { Mode: types.CreateHostUserMode_HOST_USER_MODE_INSECURE_DROP, } // create a user with some groups - closer, err := users.CreateUser("bob", userinfo) + closer, err := users.UpsertUser("bob", userinfo) require.NoError(t, err) require.NotNil(t, closer, "user closer was nil") @@ -185,8 +193,8 @@ func TestUserMgmt_CreateTemporaryUser(t *testing.T) { }, backend.users["bob"]) // try creat the same user again - secondCloser, err := users.CreateUser("bob", userinfo) - require.True(t, trace.IsAlreadyExists(err)) + secondCloser, err := users.UpsertUser("bob", userinfo) + require.NoError(t, err) require.NotNil(t, secondCloser) // Close will remove the user if the user is in the teleport-system group @@ -197,8 +205,8 @@ func TestUserMgmt_CreateTemporaryUser(t *testing.T) { backend.CreateUser("simon", []string{}, "", "", "") // try to create a temporary user for simon - closer, err = users.CreateUser("simon", userinfo) - require.True(t, trace.IsAlreadyExists(err)) + closer, err = users.UpsertUser("simon", userinfo) + require.NoError(t, err) require.Nil(t, closer) } @@ -217,7 +225,7 @@ func TestUserMgmtSudoers_CreateTemporaryUser(t *testing.T) { backend: backend, } - closer, err := users.CreateUser("bob", &services.HostUsersInfo{ + closer, err := users.UpsertUser("bob", &services.HostUsersInfo{ Groups: []string{"hello", "sudo"}, Mode: types.CreateHostUserMode_HOST_USER_MODE_INSECURE_DROP, }) @@ -241,16 +249,15 @@ func TestUserMgmtSudoers_CreateTemporaryUser(t *testing.T) { // test user already exists but teleport-service group has not yet // been created backend.CreateUser("testuser", nil, "", "", "") - _, err := users.CreateUser("testuser", &services.HostUsersInfo{ + _, err := users.UpsertUser("testuser", &services.HostUsersInfo{ Mode: types.CreateHostUserMode_HOST_USER_MODE_INSECURE_DROP, }) - require.True(t, trace.IsAlreadyExists(err)) + require.NoError(t, err) backend.CreateGroup(types.TeleportServiceGroup, "") - // IsAlreadyExists error when teleport-service group now exists - _, err = users.CreateUser("testuser", &services.HostUsersInfo{ + _, err = users.UpsertUser("testuser", &services.HostUsersInfo{ Mode: types.CreateHostUserMode_HOST_USER_MODE_INSECURE_DROP, }) - require.True(t, trace.IsAlreadyExists(err)) + require.NoError(t, err) }) } @@ -286,7 +293,7 @@ func TestUserMgmt_DeleteAllTeleportSystemUsers(t *testing.T) { mgmt.CreateGroup(group, "") } if slices.Contains(user.groups, types.TeleportServiceGroup) { - users.CreateUser(user.user, &services.HostUsersInfo{Groups: user.groups}) + users.UpsertUser(user.user, &services.HostUsersInfo{Groups: user.groups}) } else { mgmt.CreateUser(user.user, user.groups, "", "", "") } diff --git a/lib/utils/host/hostusers.go b/lib/utils/host/hostusers.go index f6bacf0028636..92b2bd15e90b9 100644 --- a/lib/utils/host/hostusers.go +++ b/lib/utils/host/hostusers.go @@ -92,17 +92,15 @@ func UserAdd(username string, groups []string, home, uid, gid string) (exitCode return cmd.ProcessState.ExitCode(), trace.Wrap(err) } -// AddUserToGroups adds a user to a list of specified groups on a host using `usermod` -func AddUserToGroups(username string, groups []string) (exitCode int, err error) { +// SetUserGroups adds a user to a list of specified groups on a host using `usermod`, +// overriding any existing supplementary groups. +func SetUserGroups(username string, groups []string) (exitCode int, err error) { usermodBin, err := exec.LookPath("usermod") if err != nil { return -1, trace.Wrap(err, "cant find usermod binary") } - args := []string{"-aG"} - args = append(args, groups...) - args = append(args, username) - // usermod -aG (append groups) (username) - cmd := exec.Command(usermodBin, args...) + // usermod -G (replace groups) (username) + cmd := exec.Command(usermodBin, "-G", strings.Join(groups, ","), username) output, err := cmd.CombinedOutput() log.Debugf("%s output: %s", cmd.Path, string(output)) return cmd.ProcessState.ExitCode(), trace.Wrap(err) diff --git a/package.json b/package.json index 1f9b9a4204483..5bd1a0557d9f5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "teleport-ui", "version": "1.0.0", "scripts": { - "all-topics": "node docs/gen-topic-pages/index.js --in docs/pages/admin-guides,docs/pages/access-controls,docs/pages/connect-your-client,docs/pages/reference", + "all-topics": "node docs/gen-topic-pages/index.js --in docs/pages/admin-guides,docs/pages/connect-your-client,docs/pages/reference", "build-ui": "yarn build-ui-oss && yarn build-ui-e", "build-ui-oss": "yarn workspace @gravitational/teleport build", "build-ui-e": "yarn workspace @gravitational/teleport.e build", diff --git a/web/packages/shared/components/AccessRequests/AccessDuration/AccessDurationRequest.tsx b/web/packages/shared/components/AccessRequests/AccessDuration/AccessDurationRequest.tsx index ed17b5b5b7b1c..1a141d7050491 100644 --- a/web/packages/shared/components/AccessRequests/AccessDuration/AccessDurationRequest.tsx +++ b/web/packages/shared/components/AccessRequests/AccessDuration/AccessDurationRequest.tsx @@ -16,70 +16,21 @@ * along with this program. If not, see . */ -import { useState, useEffect } from 'react'; +import React from 'react'; import { Flex, LabelInput, Text } from 'design'; import Select, { Option } from 'shared/components/Select'; import { ToolTipInfo } from 'shared/components/ToolTip'; -import { AccessRequest } from 'shared/services/accessRequests'; - -import { - getDurationOptionIndexClosestToOneWeek, - getDurationOptionsFromStartTime, -} from './durationOptions'; - export function AccessDurationRequest({ - assumeStartTime, - accessRequest, maxDuration, - setMaxDuration, + onMaxDurationChange, + maxDurationOptions, }: { - assumeStartTime: Date; - accessRequest: AccessRequest; + maxDurationOptions: Option[]; maxDuration: Option; - setMaxDuration(s: Option): void; + onMaxDurationChange(s: Option): void; }) { - // Options for extending or shortening the access request duration. - const [durationOptions, setDurationOptions] = useState[]>([]); - - useEffect(() => { - if (!assumeStartTime) { - defaultDuration(); - } else { - updateAccessDuration(assumeStartTime); - } - }, [assumeStartTime]); - - function defaultDuration() { - const created = accessRequest.created; - const options = getDurationOptionsFromStartTime(created, accessRequest); - - setDurationOptions(options); - if (options.length > 0) { - const durationIndex = getDurationOptionIndexClosestToOneWeek( - options, - accessRequest.created - ); - setMaxDuration(options[durationIndex]); - } - } - - function updateAccessDuration(start: Date) { - const updatedDurationOpts = getDurationOptionsFromStartTime( - start, - accessRequest - ); - - const durationIndex = getDurationOptionIndexClosestToOneWeek( - updatedDurationOpts, - start - ); - - setMaxDuration(updatedDurationOpts[durationIndex]); - setDurationOptions(updatedDurationOpts); - } - return ( @@ -92,8 +43,8 @@ export function AccessDurationRequest({ ) => setRequestTTL(option)} - value={requestTTL} + options={pendingRequestTtlOptions} + onChange={(option: Option) => + setPendingRequestTtl(option) + } + value={pendingRequestTtl} /> )} diff --git a/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.story.tsx b/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.story.tsx index c91e41cf1a592..bf1b3b4fa04e3 100644 --- a/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.story.tsx +++ b/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.story.tsx @@ -24,6 +24,7 @@ import { Box, ButtonPrimary, ButtonText } from 'design'; import { Option } from 'shared/components/Select'; import { dryRunResponse } from '../../fixtures'; +import { useSpecifiableFields } from '../useSpecifiableFields'; import { RequestCheckoutWithSlider, @@ -60,22 +61,15 @@ function SuccessActionComponent({ reset, onClose }) { } export const Loaded = () => { - const [selectedReviewers, setSelectedReviewers] = useState( - props.selectedReviewers - ); - const [maxDuration, setMaxDuration] = useState>(); - const [requestTTL, setRequestTTL] = useState>(); + const props = useSpecifiableFields(); + if (!props.dryRunResponse) { + props.onDryRunChange(dryRunResponse); + } return ( - + + + ); }; export const Empty = () => { @@ -84,74 +78,84 @@ export const Empty = () => { const [requestTTL, setRequestTTL] = useState>(); return ( - + + + ); }; export const Failed = () => ( - + + + ); export const LoadedResourceRequest = () => { const [selectedReviewers, setSelectedReviewers] = useState( - props.selectedReviewers + baseProps.selectedReviewers ); const [selectedResourceRequestRoles, setSelectedResourceRequestRoles] = - useState(props.resourceRequestRoles); + useState(baseProps.resourceRequestRoles); return ( - + + + ); }; export const ProcessingResourceRequest = () => ( - + + + ); export const FailedResourceRequest = () => ( - + + + ); export const Success = () => ( ( ); -const props: RequestCheckoutWithSliderProps = { +const baseProps: RequestCheckoutWithSliderProps = { createAttempt: { status: '' }, fetchResourceRequestRolesAttempt: { status: '' }, isResourceRequest: false, requireReason: true, - reviewers: ['bob', 'cat', 'george washington'], selectedReviewers: [ - { value: 'bob', label: 'bob', isSelected: true }, - { value: 'cat', label: 'cat', isSelected: true }, { value: 'george washington', label: 'george washington', @@ -214,8 +215,12 @@ const props: RequestCheckoutWithSliderProps = { setSelectedResourceRequestRoles: () => null, fetchStatus: 'loaded', maxDuration: { value: 0, label: '12 hours' }, - setMaxDuration: () => null, - requestTTL: { value: 0, label: '1 hour' }, - setRequestTTL: () => null, + onMaxDurationChange: () => null, + maxDurationOptions: [], + pendingRequestTtl: { value: 0, label: '1 hour' }, + setPendingRequestTtl: () => null, + pendingRequestTtlOptions: [], dryRunResponse, + startTime: null, + onStartTimeChange: () => null, }; diff --git a/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.test.tsx b/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.test.tsx index 7b33cc9a851cd..89fca74f4ff92 100644 --- a/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.test.tsx +++ b/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.test.tsx @@ -16,21 +16,19 @@ * along with this program. If not, see . */ -import { useState } from 'react'; import { render, screen, userEvent, fireEvent } from 'design/utils/testing'; -import { Option } from 'shared/components/Select'; +import { RequestState } from 'shared/services/accessRequests'; import { dryRunResponse } from '../../fixtures'; - -import { ReviewerOption } from './types'; +import { useSpecifiableFields } from '../useSpecifiableFields'; import { RequestCheckoutWithSlider as RequestCheckoutComp, RequestCheckoutWithSliderProps, } from './RequestCheckout'; -test('start with no suggested reviewers', async () => { +test('adding a reviewer and then removing it afterwards when there are no suggested reviewers', async () => { render(); // Test init renders no reviewers. @@ -55,10 +53,10 @@ test('start with no suggested reviewers', async () => { expect(reviewers.childNodes).toHaveLength(0); }); -test('start with suggested reviewers', async () => { +test('adding a reviewer and removing a suggested reviewer does not remove it from the suggested section', async () => { render(); - // Test init renders reviewers. + // Initially, all suggested reviewers are pre-selected. let reviewers = screen.getByTestId('reviewers'); expect(reviewers.childNodes).toHaveLength(1); expect(reviewers.childNodes[0]).toHaveTextContent('llama'); @@ -99,7 +97,7 @@ test('start with suggested reviewers', async () => { expect(reviewers.childNodes[1]).toHaveTextContent('llama'); }); -test('assume start time + additional info access request lifetime', () => { +test('changing access duration, also changes pending TTL options and the request lifetime text', () => { jest.useFakeTimers().setSystemTime(dryRunResponse.created); render(); @@ -109,39 +107,40 @@ test('assume start time + additional info access request lifetime', () => { expect(screen.queryByText(/start time/i)).not.toBeInTheDocument(); expect(screen.getByText(/access duration/i)).toBeInTheDocument(); expect(screen.getAllByText(/2 days/i)).toHaveLength(1); - const calendarBtn = screen.getByText(/immediately/i); - fireEvent.click(calendarBtn); // Expand the additional info box where the access lifetime // gets displayed. fireEvent.click(infoBtn); expect(screen.getByText(/Access Request Lifetime/i)).toBeInTheDocument(); - expect(screen.getAllByText(/2 days/i)).toHaveLength(2); + + // Test that all three fields display the same time info (max duration, pending, and lifetime) + expect(screen.getAllByText(/2 days/i)).toHaveLength(3); // Changing the "access duration" to a shorter time - // should reduce the "access lifetime". + // should also update pending and lifetime text fireEvent.keyDown(screen.getAllByText(/2 days/i)[0], { key: 'ArrowDown' }); fireEvent.click(screen.getByText(/1 day/i)); - expect(screen.getAllByText(/1 day/i)).toHaveLength(2); + expect(screen.getAllByText(/1 day/i)).toHaveLength(3); }); const RequestCheckout = ({ reviewers = [] }: { reviewers?: string[] }) => { - const [selectedReviewers, setSelectedReviewers] = useState( - () => reviewers.map(r => ({ label: r, value: r, isSelected: true })) - ); - const [maxDuration, setMaxDuration] = useState>(); + const specifiableProps = useSpecifiableFields(); + + if (!specifiableProps.dryRunResponse) { + const request = { + ...dryRunResponse, + reviewers: reviewers.map(r => ({ name: r, state: '' as RequestState })), + }; + specifiableProps.onDryRunChange(request); + } return (
); @@ -152,7 +151,6 @@ const props: RequestCheckoutWithSliderProps = { fetchResourceRequestRolesAttempt: { status: '' }, isResourceRequest: false, requireReason: true, - reviewers: [], selectedReviewers: [], setSelectedReviewers: () => null, createRequest: () => null, @@ -168,8 +166,12 @@ const props: RequestCheckoutWithSliderProps = { setSelectedResourceRequestRoles: () => null, fetchStatus: 'loaded', maxDuration: { value: 0, label: '12 hours' }, - setMaxDuration: () => null, - requestTTL: { value: 0, label: '1 hour' }, - setRequestTTL: () => null, - dryRunResponse, + onMaxDurationChange: () => null, + maxDurationOptions: [], + pendingRequestTtlOptions: [], + pendingRequestTtl: { value: 0, label: '1 hour' }, + setPendingRequestTtl: () => null, + dryRunResponse: null, + startTime: null, + onStartTimeChange: () => null, }; diff --git a/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.tsx b/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.tsx index 9de44e13b7616..13907708821c9 100644 --- a/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.tsx +++ b/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/RequestCheckout.tsx @@ -126,15 +126,16 @@ export function RequestCheckout({ appsGrantedByUserGroup = [], userGroupFetchAttempt, clearAttempt, - reviewers, setSelectedReviewers, SuccessComponent, requireReason, numRequestedResources, setSelectedResourceRequestRoles, fetchStatus, - setMaxDuration, - setRequestTTL, + onMaxDurationChange, + maxDurationOptions, + setPendingRequestTtl, + pendingRequestTtlOptions, dryRunResponse, data, showClusterNameColumn, @@ -143,14 +144,14 @@ export function RequestCheckout({ createRequest, selectedReviewers, maxDuration, - requestTTL, + pendingRequestTtl, resourceRequestRoles, isResourceRequest, selectedResourceRequestRoles, Header, + startTime, + onStartTimeChange, }: RequestCheckoutProps) { - // Specifies the start date/time a requestor requested for. - const [start, setStart] = useState(); const [reason, setReason] = useState(''); function updateReason(reason: string) { setReason(reason); @@ -165,8 +166,8 @@ export function RequestCheckout({ reason, suggestedReviewers: selectedReviewers.map(r => r.value), maxDuration: maxDuration ? new Date(maxDuration.value) : null, - requestTTL: requestTTL ? new Date(requestTTL.value) : null, - start: start, + requestTTL: pendingRequestTtl ? new Date(pendingRequestTtl.value) : null, + start: startTime, }); } @@ -313,7 +314,7 @@ export function RequestCheckout({ )} r.name) ?? []} selectedReviewers={selectedReviewers} setSelectedReviewers={setSelectedReviewers} /> @@ -324,15 +325,14 @@ export function RequestCheckout({ {dryRunResponse && ( )} @@ -343,11 +343,11 @@ export function RequestCheckout({ /> {dryRunResponse && maxDuration && ( )} = selectedReviewers: ReviewerOption[]; data: T[]; showClusterNameColumn?: boolean; - setRequestTTL: (value: Option) => void; createRequest: (req: CreateRequest) => void; fetchStatus: 'loading' | 'loaded'; fetchResourceRequestRolesAttempt: Attempt; - requestTTL: Option; + pendingRequestTtl: Option; + setPendingRequestTtl: (value: Option) => void; + pendingRequestTtlOptions: Option[]; resourceRequestRoles: string[]; - reviewers: string[]; + maxDuration: Option; + onMaxDurationChange: (value: Option) => void; + maxDurationOptions: Option[]; setSelectedReviewers: (value: ReviewerOption[]) => void; - setMaxDuration: (value: Option) => void; clearAttempt: () => void; createAttempt: Attempt; setSelectedResourceRequestRoles: (value: string[]) => void; numRequestedResources: number; selectedResourceRequestRoles: string[]; dryRunResponse: AccessRequest; - maxDuration: Option; Header?: () => JSX.Element; + startTime: Date; + onStartTimeChange(t?: Date): void; }; type SuccessComponentParams = { diff --git a/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/__snapshots__/RequestCheckout.story.test.tsx.snap b/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/__snapshots__/RequestCheckout.story.test.tsx.snap index 12be5f63a98f6..d8d732f03ea82 100644 --- a/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/__snapshots__/RequestCheckout.story.test.tsx.snap +++ b/web/packages/shared/components/AccessRequests/NewRequest/RequestCheckout/__snapshots__/RequestCheckout.story.test.tsx.snap @@ -1488,12 +1488,6 @@ exports[`loaded state 1`] = ` border-color: rgba(255,255,255,0.54); } -.c48 { - box-sizing: border-box; - padding-top: 24px; - padding-bottom: 24px; -} - .c46 { box-sizing: border-box; margin-bottom: 8px; @@ -1502,6 +1496,12 @@ exports[`loaded state 1`] = ` height: 34px; } +.c48 { + box-sizing: border-box; + padding-top: 24px; + padding-bottom: 24px; +} + .c24 { line-height: 1.5; margin: 0; @@ -2615,7 +2615,7 @@ exports[`loaded state 1`] = ` />
diff --git a/web/packages/shared/components/AccessRequests/NewRequest/useSpecifiableFields.ts b/web/packages/shared/components/AccessRequests/NewRequest/useSpecifiableFields.ts new file mode 100644 index 0000000000000..ff3dc5463f9ec --- /dev/null +++ b/web/packages/shared/components/AccessRequests/NewRequest/useSpecifiableFields.ts @@ -0,0 +1,176 @@ +import { useState } from 'react'; + +import { Option } from 'shared/components/Select'; +import { + getPendingRequestDurationOptions, + ReviewerOption, +} from 'shared/components/AccessRequests/NewRequest'; +import { AccessRequest } from 'shared/services/accessRequests'; + +import { + getDurationOptionIndexClosestToOneWeek, + getDurationOptionsFromStartTime, +} from '../AccessDuration/durationOptions'; + +export function useSpecifiableFields() { + /** + * Fetched response from `createAccessRequest` with dry run enabled. + * Contains max time options (to calculate max duration and requestTTL options) + * and suggested reviewers that were available both statically (from roles) + * and dynamically (from access lists). + */ + const [dryRunResponse, setDryRunResponse] = useState(); + + /** + * Fetched response from `fetchResourceRequestRoles`. + * Required roles to gain access to the resources requested. + */ + const [resourceRequestRoles, setResourceRequestRoles] = useState( + [] + ); + + const [selectedResourceRequestRoles, setSelectedResourceRequestRoles] = + useState([]); + + /** + * User selected reviewers from suggested reviewers options and/or + * any other reviewers they manually added. + */ + const [selectedReviewers, setSelectedReviewers] = useState( + [] + ); + + /** + * Specifies when the access request can be "assumed" by. + * No startTime means access request is to be available + * to assume immediately. + */ + const [startTime, setStartTime] = useState(); + + /** + * Specifies how long the access request should last for. + * Duration countdown starts from access request creation. + * Resets when startTime changes. + */ + const [maxDuration, setMaxDuration] = useState>(); + + /** + * How long the request can be in a PENDING state before it expires. + * Resets when startTime changes. + */ + const [pendingRequestTtl, setPendingRequestTtl] = useState>(); + + /** + * Options for shortening or extending pending TTL. + */ + let pendingRequestTtlOptions: Option[] = []; + /** + * Options for extending or shortening the access request duration. + */ + let maxDurationOptions: Option[] = []; + + if (dryRunResponse) { + pendingRequestTtlOptions = getPendingRequestDurationOptions( + dryRunResponse.created, + maxDuration.value + ); + maxDurationOptions = getDurationOptionsFromStartTime( + startTime, + dryRunResponse + ); + } + + function preselectPendingRequestTtlOption( + newMaxDuration: number, + dryRequestCreated: Date + ) { + const newRequestTtlOptions = getPendingRequestDurationOptions( + dryRequestCreated, + newMaxDuration + ); + const ttlIndex = getDurationOptionIndexClosestToOneWeek( + newRequestTtlOptions, + dryRequestCreated + ); + setPendingRequestTtl(newRequestTtlOptions[ttlIndex]); + } + + function onMaxDurationChange(newMaxDurationOption: Option) { + setMaxDuration(newMaxDurationOption); + // Re-set pending request TTL, since pending duration + // can't be greater than max duration. + preselectPendingRequestTtlOption( + newMaxDurationOption.value, + dryRunResponse.created + ); + } + + /** + * An empty start date will default to dry run's response created date. + */ + function onStartTimeChange(start?: Date) { + updateStartAndDurationFields(start, dryRunResponse); + } + + function updateStartAndDurationFields( + start: Date, + dryRequest: AccessRequest + ) { + setStartTime(start); + + // Pre-select default max duration. + const newDurationOpts = getDurationOptionsFromStartTime(start, dryRequest); + + const durationIndex = getDurationOptionIndexClosestToOneWeek( + newDurationOpts, + start || dryRequest.created + ); + const newMaxDuration = newDurationOpts[durationIndex]; + setMaxDuration(newMaxDuration); + + preselectPendingRequestTtlOption(newMaxDuration.value, dryRequest.created); + } + + /** + * An empty dry run request will just set the response to null. + * Teleterm requires clearing this response when user closes + * checkout. + */ + function onDryRunChange(dryRequest?: AccessRequest) { + setDryRunResponse(dryRequest); + + if (dryRequest) { + // Initialize fields. + updateStartAndDurationFields(null /* startTime */, dryRequest); + + // Initially select suggested reviewers for the requestor. + const reviewers = dryRequest.reviewers.map(r => r.name); + setSelectedReviewers( + reviewers.map(r => ({ + value: r, + label: r, + isSelected: true, + })) + ); + } + } + + return { + selectedReviewers, + setSelectedReviewers, + resourceRequestRoles, + setResourceRequestRoles, + selectedResourceRequestRoles, + setSelectedResourceRequestRoles, + maxDuration, + onMaxDurationChange, + maxDurationOptions, + pendingRequestTtl, + setPendingRequestTtl, + pendingRequestTtlOptions, + dryRunResponse, + startTime, + onStartTimeChange, + onDryRunChange, + }; +} diff --git a/web/packages/shared/components/AccessRequests/fixtures/index.ts b/web/packages/shared/components/AccessRequests/fixtures/index.ts index 241d7a2752bbf..24aabcd94d5fe 100644 --- a/web/packages/shared/components/AccessRequests/fixtures/index.ts +++ b/web/packages/shared/components/AccessRequests/fixtures/index.ts @@ -36,7 +36,11 @@ export const dryRunResponse: AccessRequest = { sessionTTL: new Date('2024-02-15T14:51:03.999893Z'), sessionTTLDuration: '', reviews: [], - reviewers: [], + reviewers: [ + { name: 'bob', state: '' }, + { name: 'cat', state: '' }, + { name: 'george washington', state: '' }, + ], thresholdNames: ['default'], resources: [], assumeStartTime: null, diff --git a/web/packages/teleterm/src/ui/AccessRequestCheckout/AccessRequestCheckout.tsx b/web/packages/teleterm/src/ui/AccessRequestCheckout/AccessRequestCheckout.tsx index 5e2678e967e20..2217609b32b7b 100644 --- a/web/packages/teleterm/src/ui/AccessRequestCheckout/AccessRequestCheckout.tsx +++ b/web/packages/teleterm/src/ui/AccessRequestCheckout/AccessRequestCheckout.tsx @@ -87,7 +87,6 @@ export function AccessRequestCheckout() { clearCreateAttempt, data, shouldShowClusterNameColumn, - suggestedReviewers, selectedReviewers, setSelectedReviewers, assumedRequests, @@ -95,10 +94,14 @@ export function AccessRequestCheckout() { goToRequestsList, setShowCheckout, maxDuration, - setMaxDuration, + onMaxDurationChange, + maxDurationOptions, dryRunResponse, - requestTTL, - setRequestTTL, + pendingRequestTtl, + setPendingRequestTtl, + pendingRequestTtlOptions, + startTime, + onStartTimeChange, } = useAccessRequestCheckout(); const isRoleRequest = data[0]?.kind === 'role'; @@ -110,7 +113,6 @@ export function AccessRequestCheckout() { // We should rather detect how much space we have, // but for simplicity we only count items. const moreToShow = Math.max(data.length - MAX_RESOURCES_IN_BAR_TO_SHOW, 0); - return ( <> {data.length > 0 && !isCollapsed() && ( @@ -241,7 +243,6 @@ export function AccessRequestCheckout() { setSelectedResourceRequestRoles={setSelectedResourceRequestRoles} createRequest={createRequest} clearAttempt={clearCreateAttempt} - reviewers={suggestedReviewers} selectedReviewers={selectedReviewers} setSelectedReviewers={setSelectedReviewers} requireReason={false} @@ -250,9 +251,13 @@ export function AccessRequestCheckout() { fetchStatus={'loaded'} dryRunResponse={dryRunResponse} maxDuration={maxDuration} - setMaxDuration={setMaxDuration} - requestTTL={requestTTL} - setRequestTTL={setRequestTTL} + onMaxDurationChange={onMaxDurationChange} + maxDurationOptions={maxDurationOptions} + pendingRequestTtl={pendingRequestTtl} + pendingRequestTtlOptions={pendingRequestTtlOptions} + setPendingRequestTtl={setPendingRequestTtl} + startTime={startTime} + onStartTimeChange={onStartTimeChange} /> )} diff --git a/web/packages/teleterm/src/ui/AccessRequestCheckout/useAccessRequestCheckout.ts b/web/packages/teleterm/src/ui/AccessRequestCheckout/useAccessRequestCheckout.ts index fd638cf528bd1..11f78ea5ebd09 100644 --- a/web/packages/teleterm/src/ui/AccessRequestCheckout/useAccessRequestCheckout.ts +++ b/web/packages/teleterm/src/ui/AccessRequestCheckout/useAccessRequestCheckout.ts @@ -22,15 +22,13 @@ import { Timestamp } from 'gen-proto-ts/google/protobuf/timestamp_pb'; import useAttempt from 'shared/hooks/useAttemptNext'; import { - ReviewerOption, getDryRunMaxDuration, PendingListItem, } from 'shared/components/AccessRequests/NewRequest'; +import { useSpecifiableFields } from 'shared/components/AccessRequests/NewRequest/useSpecifiableFields'; import { CreateRequest } from 'shared/components/AccessRequests/Shared/types'; -import { Option } from 'shared/components/Select'; - import { useAppContext } from 'teleterm/ui/appContextProvider'; import { PendingAccessRequest, @@ -49,8 +47,6 @@ import { ResourceKind } from '../DocumentAccessRequests/NewRequest/useNewRequest import { makeUiAccessRequest } from '../DocumentAccessRequests/useAccessRequests'; -import type { AccessRequest } from 'shared/services/accessRequests'; - export default function useAccessRequestCheckout() { const ctx = useAppContext(); ctx.workspacesService.useState(); @@ -59,33 +55,28 @@ export default function useAccessRequestCheckout() { ctx.workspacesService?.getActiveWorkspace()?.localClusterUri; const rootClusterUri = ctx.workspacesService?.getRootClusterUri(); - // Contains max time options (to calculate max duration and requestTTL options) - // and suggested reviewers that were available both statically (from roles) - // and dynamically (from access lists). - const [dryRunResponse, setDryRunResponse] = useState(); - // The reviewers defined in the users roles (static) and access list owners - // (dynamic). - const [suggestedReviewers, setSuggestedReviewers] = useState([]); - // User selected reviewers from suggested reviewers options and/or - // any other reviewers they manually added. - const [selectedReviewers, setSelectedReviewers] = useState( - [] - ); - - // Access request lifetime upon creation. - // Duration countdown starts from access request creation. - const [maxDuration, setMaxDuration] = useState>(); - // How long the request can be in a PENDING state before it expires. - const [requestTTL, setRequestTTL] = useState>(); + const { + selectedReviewers, + setSelectedReviewers, + resourceRequestRoles, + setResourceRequestRoles, + selectedResourceRequestRoles, + setSelectedResourceRequestRoles, + maxDuration, + onMaxDurationChange, + maxDurationOptions, + pendingRequestTtl, + setPendingRequestTtl, + pendingRequestTtlOptions, + dryRunResponse, + onDryRunChange, + startTime, + onStartTimeChange, + } = useSpecifiableFields(); const [showCheckout, setShowCheckout] = useState(false); const [hasExited, setHasExited] = useState(false); const [requestedCount, setRequestedCount] = useState(0); - const [resourceRequestRoles, setResourceRequestRoles] = useState( - [] - ); - const [selectedResourceRequestRoles, setSelectedResourceRequestRoles] = - useState([]); const { attempt: createRequestAttempt, setAttempt: setCreateRequestAttempt } = useAttempt(''); @@ -100,15 +91,18 @@ export default function useAccessRequestCheckout() { workspaceAccessRequest?.getPendingAccessRequest(); useEffect(() => { - // Do a new dry run per checkout to get the latest time options - // and latest calculated suggested reviewers. - if (showCheckout) { + // Do a new dry run per changes to pending data + // to get the latest time options and latest calculated + // suggested reviewers. + // Options and reviewers can change depending on the selected + // roles or resources. + if (showCheckout && requestedCount == 0) { performDryRun(); } - }, [showCheckout]); + }, [showCheckout, pendingAccessRequest]); useEffect(() => { - if (!pendingAccessRequest) { + if (!pendingAccessRequest || requestedCount > 0) { return; } @@ -147,7 +141,7 @@ export default function useAccessRequestCheckout() { ) { clearCreateAttempt(); setRequestedCount(0); - setDryRunResponse(null); + onDryRunChange(null /* set dryRunResponse to null */); } }, [showCheckout, hasExited, createRequestAttempt.status]); @@ -280,18 +274,7 @@ export default function useAccessRequestCheckout() { setCreateRequestAttempt({ status: '' }); const accessRequest = makeUiAccessRequest(teletermAccessRequest); - setDryRunResponse(accessRequest); - - const reviewers = accessRequest.reviewers.map(r => r.name).sort(); - setSuggestedReviewers(reviewers); - // Initially select suggested reviewers for the requestor. - setSelectedReviewers( - reviewers.map(r => ({ - value: r, - label: r, - isSelected: true, - })) - ); + onDryRunChange(accessRequest); } async function createRequest(req: CreateRequest) { @@ -371,14 +354,17 @@ export default function useAccessRequestCheckout() { createRequestAttempt, collapseBar, setShowCheckout, - suggestedReviewers, selectedReviewers, setSelectedReviewers, dryRunResponse, maxDuration, - setMaxDuration, - requestTTL, - setRequestTTL, + onMaxDurationChange, + maxDurationOptions, + pendingRequestTtl, + setPendingRequestTtl, + pendingRequestTtlOptions, + startTime, + onStartTimeChange, }; }