mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-24 16:17:11 +08:00
tctl resource: convert saml_idp_service_provider handler (#61386)
This commit is contained in:
@@ -692,28 +692,6 @@ func (l *loginRuleCollection) Resources() []types.Resource {
|
||||
return resources
|
||||
}
|
||||
|
||||
//nolint:revive // Because we want this to be IdP.
|
||||
type samlIdPServiceProviderCollection struct {
|
||||
serviceProviders []types.SAMLIdPServiceProvider
|
||||
}
|
||||
|
||||
func (c *samlIdPServiceProviderCollection) Resources() []types.Resource {
|
||||
r := make([]types.Resource, len(c.serviceProviders))
|
||||
for i, resource := range c.serviceProviders {
|
||||
r[i] = resource
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (c *samlIdPServiceProviderCollection) WriteText(w io.Writer, verbose bool) error {
|
||||
t := asciitable.MakeTable([]string{"Name"})
|
||||
for _, serviceProvider := range c.serviceProviders {
|
||||
t.AddRow([]string{serviceProvider.GetName()})
|
||||
}
|
||||
_, err := t.AsBuffer().WriteTo(w)
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
type deviceCollection struct {
|
||||
devices []*devicepb.Device
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/crewjam/saml/samlsp"
|
||||
"github.com/gravitational/trace"
|
||||
kyaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
|
||||
@@ -131,7 +130,6 @@ func (rc *ResourceCommand) Initialize(app *kingpin.Application, _ *tctlcfg.Globa
|
||||
types.KindNetworkRestrictions: rc.createNetworkRestrictions,
|
||||
types.KindKubernetesCluster: rc.createKubeCluster,
|
||||
types.KindLoginRule: rc.createLoginRule,
|
||||
types.KindSAMLIdPServiceProvider: rc.createSAMLIdPServiceProvider,
|
||||
types.KindDevice: rc.createDevice,
|
||||
types.KindOktaImportRule: rc.createOktaImportRule,
|
||||
types.KindIntegration: rc.createIntegration,
|
||||
@@ -776,46 +774,6 @@ func (rc *ResourceCommand) createLoginRule(ctx context.Context, client *authclie
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rc *ResourceCommand) createSAMLIdPServiceProvider(ctx context.Context, client *authclient.Client, raw services.UnknownResource) error {
|
||||
// Create services.SAMLIdPServiceProvider from raw YAML to extract the service provider name.
|
||||
sp, err := services.UnmarshalSAMLIdPServiceProvider(raw.Raw, services.DisallowUnknown())
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
if sp.GetEntityDescriptor() != "" {
|
||||
// verify that entity descriptor parses
|
||||
ed, err := samlsp.ParseMetadata([]byte(sp.GetEntityDescriptor()))
|
||||
if err != nil {
|
||||
return trace.BadParameter("invalid entity descriptor for SAML IdP Service Provider %q: %v", sp.GetEntityID(), err)
|
||||
}
|
||||
|
||||
// issue warning about unsupported ACS bindings.
|
||||
if err := services.FilterSAMLEntityDescriptor(ed, false /* quiet */); err != nil {
|
||||
slog.WarnContext(ctx, "Entity descriptor for SAML IdP service provider contains unsupported ACS bindings",
|
||||
"entity_id", sp.GetEntityID(),
|
||||
"error", err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
serviceProviderName := sp.GetName()
|
||||
|
||||
exists := false
|
||||
if err = client.CreateSAMLIdPServiceProvider(ctx, sp); err != nil {
|
||||
if trace.IsAlreadyExists(err) {
|
||||
exists = true
|
||||
err = client.UpdateSAMLIdPServiceProvider(ctx, sp)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
fmt.Printf("SAML IdP service provider %q has been %s\n", serviceProviderName, UpsertVerb(exists, rc.IsForced()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rc *ResourceCommand) createDevice(ctx context.Context, client *authclient.Client, raw services.UnknownResource) error {
|
||||
res, err := services.UnmarshalDevice(raw.Raw)
|
||||
if err != nil {
|
||||
@@ -1204,11 +1162,6 @@ func (rc *ResourceCommand) Delete(ctx context.Context, client *authclient.Client
|
||||
return trail.FromGRPC(err)
|
||||
}
|
||||
fmt.Printf("login rule %q has been deleted\n", rc.ref.Name)
|
||||
case types.KindSAMLIdPServiceProvider:
|
||||
if err := client.DeleteSAMLIdPServiceProvider(ctx, rc.ref.Name); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
fmt.Printf("SAML IdP service provider %q has been deleted\n", rc.ref.Name)
|
||||
case types.KindDevice:
|
||||
remote := client.DevicesClient()
|
||||
device, err := findDeviceByIDOrTag(ctx, remote, rc.ref.Name)
|
||||
@@ -1667,20 +1620,6 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient
|
||||
Name: rc.ref.Name,
|
||||
})
|
||||
return &loginRuleCollection{[]*loginrulepb.LoginRule{rule}}, trail.FromGRPC(err)
|
||||
case types.KindSAMLIdPServiceProvider:
|
||||
if rc.ref.Name != "" {
|
||||
serviceProvider, err := client.GetSAMLIdPServiceProvider(ctx, rc.ref.Name)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return &samlIdPServiceProviderCollection{serviceProviders: []types.SAMLIdPServiceProvider{serviceProvider}}, nil
|
||||
}
|
||||
|
||||
resources, err := stream.Collect(clientutils.Resources(ctx, client.ListSAMLIdPServiceProviders))
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return &samlIdPServiceProviderCollection{serviceProviders: resources}, nil
|
||||
case types.KindDevice:
|
||||
remote := client.DevicesClient()
|
||||
if rc.ref.Name != "" {
|
||||
|
||||
@@ -63,6 +63,7 @@ func Handlers() map[string]Handler {
|
||||
types.KindProxy: proxyHandler(),
|
||||
types.KindRole: roleHandler(),
|
||||
types.KindSAMLConnector: samlConnectorHandler(),
|
||||
types.KindSAMLIdPServiceProvider: samlIdPServiceProviderHandler(),
|
||||
types.KindSessionRecordingConfig: sessionRecordingConfigHandler(),
|
||||
types.KindSigstorePolicy: sigstorePolicyHandler(),
|
||||
types.KindSPIFFEFederation: spiffeFederationHandler(),
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Teleport
|
||||
* Copyright (C) 2025 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/crewjam/saml/samlsp"
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/api/utils/clientutils"
|
||||
"github.com/gravitational/teleport/lib/asciitable"
|
||||
"github.com/gravitational/teleport/lib/auth/authclient"
|
||||
"github.com/gravitational/teleport/lib/itertools/stream"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
)
|
||||
|
||||
type samlIdPServiceProviderCollection struct {
|
||||
serviceProviders []types.SAMLIdPServiceProvider
|
||||
}
|
||||
|
||||
// Resources returns collection of SAML IdP service provider resource.
|
||||
func (c *samlIdPServiceProviderCollection) Resources() []types.Resource {
|
||||
r := make([]types.Resource, len(c.serviceProviders))
|
||||
for i, resource := range c.serviceProviders {
|
||||
r[i] = resource
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// WriteText writes collection of SAML IdP service provider resource to [w].
|
||||
func (c *samlIdPServiceProviderCollection) WriteText(w io.Writer, verbose bool) error {
|
||||
t := asciitable.MakeTable([]string{"Name"})
|
||||
for _, serviceProvider := range c.serviceProviders {
|
||||
t.AddRow([]string{serviceProvider.GetName()})
|
||||
}
|
||||
_, err := t.AsBuffer().WriteTo(w)
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
func samlIdPServiceProviderHandler() Handler {
|
||||
return Handler{
|
||||
getHandler: getSAMLIdPServiceProvider,
|
||||
createHandler: createSAMLIdPServiceProvider,
|
||||
updateHandler: updateSAMLIdPServiceProvider,
|
||||
deleteHandler: deleteSAMLIdPServiceProvider,
|
||||
|
||||
singleton: false,
|
||||
// MFA not enforced in Auth.
|
||||
mfaRequired: false,
|
||||
description: "Configure service provider for the Teleport SAML IdP",
|
||||
}
|
||||
}
|
||||
|
||||
func getSAMLIdPServiceProvider(ctx context.Context, client *authclient.Client, ref services.Ref, opts GetOpts) (Collection, error) {
|
||||
if ref.Name != "" {
|
||||
serviceProvider, err := client.GetSAMLIdPServiceProvider(ctx, ref.Name)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return &samlIdPServiceProviderCollection{serviceProviders: []types.SAMLIdPServiceProvider{serviceProvider}}, nil
|
||||
}
|
||||
|
||||
resources, err := stream.Collect(clientutils.Resources(ctx, client.ListSAMLIdPServiceProviders))
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return &samlIdPServiceProviderCollection{serviceProviders: resources}, nil
|
||||
}
|
||||
|
||||
func createSAMLIdPServiceProvider(ctx context.Context, client *authclient.Client, raw services.UnknownResource, opts CreateOpts) error {
|
||||
// Create services.SAMLIdPServiceProvider from raw YAML to extract the service provider name.
|
||||
sp, err := services.UnmarshalSAMLIdPServiceProvider(raw.Raw, services.DisallowUnknown())
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
if sp.GetEntityDescriptor() != "" {
|
||||
// verify that entity descriptor parses
|
||||
ed, err := samlsp.ParseMetadata([]byte(sp.GetEntityDescriptor()))
|
||||
if err != nil {
|
||||
return trace.BadParameter("invalid entity descriptor for SAML IdP Service Provider %q: %v", sp.GetEntityID(), err)
|
||||
}
|
||||
|
||||
// issue warning about unsupported ACS bindings.
|
||||
if err := services.FilterSAMLEntityDescriptor(ed, false /* quiet */); err != nil {
|
||||
slog.WarnContext(ctx, "Entity descriptor for SAML IdP service provider contains unsupported ACS bindings",
|
||||
"entity_id", sp.GetEntityID(),
|
||||
"error", err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
exists := false
|
||||
if err = client.CreateSAMLIdPServiceProvider(ctx, sp); err != nil {
|
||||
if trace.IsAlreadyExists(err) {
|
||||
exists = true
|
||||
err = client.UpdateSAMLIdPServiceProvider(ctx, sp)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
fmt.Printf("SAML IdP service provider %q has been %s\n", sp.GetName(), upsertVerb(exists, opts.Force))
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateSAMLIdPServiceProvider(ctx context.Context, client *authclient.Client, raw services.UnknownResource, opts CreateOpts) error {
|
||||
// Create services.SAMLIdPServiceProvider from raw YAML to extract the service provider name.
|
||||
sp, err := services.UnmarshalSAMLIdPServiceProvider(raw.Raw, services.DisallowUnknown())
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
if sp.GetEntityDescriptor() != "" {
|
||||
// verify that entity descriptor parses
|
||||
ed, err := samlsp.ParseMetadata([]byte(sp.GetEntityDescriptor()))
|
||||
if err != nil {
|
||||
return trace.BadParameter("invalid entity descriptor for SAML IdP Service Provider %q: %v", sp.GetEntityID(), err)
|
||||
}
|
||||
|
||||
// issue warning about unsupported ACS bindings.
|
||||
if err := services.FilterSAMLEntityDescriptor(ed, false /* quiet */); err != nil {
|
||||
slog.WarnContext(ctx, "Entity descriptor for SAML IdP service provider contains unsupported ACS bindings",
|
||||
"entity_id", sp.GetEntityID(),
|
||||
"error", err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if err = client.UpdateSAMLIdPServiceProvider(ctx, sp); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
fmt.Printf("SAML IdP service provider %q has been updated\n", sp.GetName())
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteSAMLIdPServiceProvider(ctx context.Context, client *authclient.Client, ref services.Ref) error {
|
||||
if err := client.DeleteSAMLIdPServiceProvider(ctx, ref.Name); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
fmt.Printf("SAML IdP service provider %q has been deleted\n", ref.Name)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user