Skip to content

Commit

Permalink
add set as default functionality to auth connectors
Browse files Browse the repository at this point in the history
  • Loading branch information
rudream committed Jan 18, 2025
1 parent 50c659f commit d335dad
Show file tree
Hide file tree
Showing 18 changed files with 679 additions and 70 deletions.
2 changes: 2 additions & 0 deletions api/client/webclient/webconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ type WebConfigAuthSettings struct {
AllowPasswordless bool `json:"allowPasswordless,omitempty"`
// AuthType is the authentication type.
AuthType string `json:"authType"`
// DefaultConnectorName is the name of the default connector in the auth preferences. This will be empty if the default is "local".
DefaultConnectorName string `json:"defaultConnectorName"`
// PreferredLocalMFA is a server-side hint for clients to pick an MFA method
// when various options are available.
// It is empty if there is nothing to suggest.
Expand Down
51 changes: 42 additions & 9 deletions lib/web/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,9 @@ func (h *Handler) bindDefaultEndpoints() {
h.PUT("/webapi/github/:name", h.WithAuth(h.updateGithubConnectorHandle))
h.DELETE("/webapi/github/:name", h.WithAuth(h.deleteGithubConnector))

// Sets the default connector in the auth preference.
h.PUT("/webapi/authconnector/default", h.WithAuth(h.setDefaultConnectorHandle))

h.GET("/webapi/trustedcluster", h.WithAuth(h.getTrustedClustersHandle))
h.POST("/webapi/trustedcluster", h.WithAuth(h.upsertTrustedClusterHandle))
h.PUT("/webapi/trustedcluster/:name", h.WithAuth(h.upsertTrustedClusterHandle))
Expand Down Expand Up @@ -1792,21 +1795,25 @@ func (h *Handler) getWebConfig(w http.ResponseWriter, r *http.Request, p httprou
} else {
authType := cap.GetType()
var localConnectorName string
var defaultConnectorName string

if authType == constants.Local {
localConnectorName = cap.GetConnectorName()
} else {
defaultConnectorName = cap.GetConnectorName()
}

authSettings = webclient.WebConfigAuthSettings{
Providers: authProviders,
SecondFactor: types.LegacySecondFactorFromSecondFactors(cap.GetSecondFactors()),
LocalAuthEnabled: cap.GetAllowLocalAuth(),
AllowPasswordless: cap.GetAllowPasswordless(),
AuthType: authType,
PreferredLocalMFA: cap.GetPreferredLocalMFA(),
LocalConnectorName: localConnectorName,
PrivateKeyPolicy: cap.GetPrivateKeyPolicy(),
MOTD: cap.GetMessageOfTheDay(),
Providers: authProviders,
SecondFactor: types.LegacySecondFactorFromSecondFactors(cap.GetSecondFactors()),
LocalAuthEnabled: cap.GetAllowLocalAuth(),
AllowPasswordless: cap.GetAllowPasswordless(),
AuthType: authType,
DefaultConnectorName: defaultConnectorName,
PreferredLocalMFA: cap.GetPreferredLocalMFA(),
LocalConnectorName: localConnectorName,
PrivateKeyPolicy: cap.GetPrivateKeyPolicy(),
MOTD: cap.GetMessageOfTheDay(),
}
}

Expand Down Expand Up @@ -3667,6 +3674,32 @@ func (h *Handler) siteNodeConnect(
return nil, nil
}

func (h *Handler) setDefaultConnectorHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
var req ui.SetDefaultAuthConnectorRequest
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}

clt, err := ctx.GetClient()
if err != nil {
return nil, trace.Wrap(err)
}
authPref, err := clt.GetAuthPreference(r.Context())
if err != nil {
return nil, trace.Wrap(err, "failed to get auth preference")
}

authPref.SetConnectorName(req.Name)
authPref.SetType(req.Type)

_, err = clt.UpsertAuthPreference(r.Context(), authPref)
if err != nil {
return nil, trace.Wrap(err)
}

return OK(), nil
}

type podConnectParams struct {
// Term is the initial PTY size.
Term session.TerminalParams `json:"term"`
Expand Down
6 changes: 3 additions & 3 deletions lib/web/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10652,10 +10652,10 @@ func TestGithubConnector(t *testing.T) {
resp, err = pack.clt.Get(ctx, pack.clt.Endpoint("webapi", "github"), nil)
assert.NoError(t, err, "unexpected error listing github connectors")

var item []webui.ResourceItem
require.NoError(t, json.Unmarshal(resp.Bytes(), &item), "invalid resource item received")
authConnectorsResp := webui.ListAuthConnectorsResponse{}
require.NoError(t, json.Unmarshal(resp.Bytes(), &authConnectorsResp), "invalid response received")

assert.Empty(t, item)
assert.Empty(t, authConnectorsResp.Connectors)
assert.Equal(t, http.StatusOK, resp.Code(), "unexpected status code getting connectors")
}

Expand Down
83 changes: 82 additions & 1 deletion lib/web/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ import (
kyaml "k8s.io/apimachinery/pkg/util/yaml"

"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/constants"
kubeproto "github.com/gravitational/teleport/api/gen/proto/go/teleport/kube/v1"
"github.com/gravitational/teleport/api/mfa"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/auth/authclient"
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/httplib"
Expand Down Expand Up @@ -212,7 +214,66 @@ func (h *Handler) getGithubConnectorsHandle(w http.ResponseWriter, r *http.Reque
return nil, trace.Wrap(err)
}

return getGithubConnectors(r.Context(), clt)
connectors, err := getGithubConnectors(r.Context(), clt)
if err != nil {
return nil, trace.Wrap(err)
}

defaultConnectorName, defaultConnectorType, err := ProcessDefaultConnector(r.Context(), clt, connectors)
if err != nil {
return nil, trace.Wrap(err)
}

return &ui.ListAuthConnectorsResponse{
DefaultConnectorName: defaultConnectorName,
DefaultConnectorType: defaultConnectorType,
Connectors: connectors,
}, nil
}

// ProcessDefaultConnector returns the default connector type and validates that the provided connectors list contains the default connector that is set in the auth preference.
// If it isn't, it will return a fallback connector which should be used as the default, as well as update the actual auth preference to refect the change.
func ProcessDefaultConnector(ctx context.Context, clt authclient.ClientI, connectors []ui.ResourceItem) (connectorName string, connectorType string, err error) {
authPref, err := clt.GetAuthPreference(ctx)
if err != nil {
return "", "", trace.Wrap(err, "failed to get auth preference")
}

defaultConnectorName := authPref.GetConnectorName()
defaultConnectorType := authPref.GetType()

if len(connectors) == 0 || defaultConnectorType == constants.Local {
// If there are no connectors or the default is already local, default to 'local' as the default connector.
defaultConnectorType = constants.Local
defaultConnectorName = ""
} else {
// Ensure that the default connector set in the auth preference exists in the list.
found := false
for _, c := range connectors {
if c.Name == defaultConnectorName && c.Kind == defaultConnectorType {
found = true
break
}
}
// If the default connector set in the auth preference doesn't exist, use the last connector in the list as the default.
if !found {
defaultConnectorName = connectors[len(connectors)-1].Name
defaultConnectorType = connectors[len(connectors)-1].Kind
}
}

// If the default connector we are returning here is different from the initial, also update the actual auth preference so that it's in sync.
if defaultConnectorName != authPref.GetConnectorName() || defaultConnectorType != authPref.GetType() {
authPref.SetConnectorName(defaultConnectorName)
authPref.SetType(defaultConnectorType)

_, err = clt.UpsertAuthPreference(ctx, authPref)
if err != nil {
return "", "", trace.Wrap(err, "failed to set fallback auth preference")
}
}

return defaultConnectorName, defaultConnectorType, nil
}

func getGithubConnectors(ctx context.Context, clt resourcesAPIGetter) ([]ui.ResourceItem, error) {
Expand All @@ -235,6 +296,26 @@ func (h *Handler) deleteGithubConnector(w http.ResponseWriter, r *http.Request,
return nil, trace.Wrap(err)
}

authPref, err := clt.GetAuthPreference(r.Context())
if err != nil {
return nil, trace.Wrap(err, "failed to get auth preference")
}

defaultConnectorName := authPref.GetConnectorName()
defaultConnectorType := authPref.GetType()
// If the connector being deleted is the default, have the auth preference fallback to another connector.
if defaultConnectorType == constants.Github && defaultConnectorName == connectorName {
connectors, err := getGithubConnectors(r.Context(), clt)
if err != nil {
return nil, trace.Wrap(err)
}

_, _, err = ProcessDefaultConnector(r.Context(), clt, connectors)
if err != nil {
return nil, trace.Wrap(err)
}
}

return OK(), nil
}

Expand Down
Loading

0 comments on commit d335dad

Please sign in to comment.