Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add token create forms to Join Tokens UI #44408

Merged
merged 1 commit into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions api/types/provisioning.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ type ProvisionToken interface {
GetAllowRules() []*TokenRule
// SetAllowRules sets the allow rules
SetAllowRules([]*TokenRule)
// GetGCPRules will return the GCP rules within this token.
GetGCPRules() *ProvisionTokenSpecV2GCP
// GetAWSIIDTTL returns the TTL of EC2 IIDs
GetAWSIIDTTL() Duration
// GetJoinMethod returns joining method that must be used with this token.
Expand Down Expand Up @@ -385,6 +387,11 @@ func (p *ProvisionTokenV2) SetAllowRules(rules []*TokenRule) {
p.Spec.Allow = rules
}

// GetGCPRules will return the GCP rules within this token.
func (p *ProvisionTokenV2) GetGCPRules() *ProvisionTokenSpecV2GCP {
return p.Spec.GCP
}

// GetAWSIIDTTL returns the TTL of EC2 IIDs
func (p *ProvisionTokenV2) GetAWSIIDTTL() Duration {
return p.Spec.AWSIIDTTL
Expand Down
9 changes: 7 additions & 2 deletions lib/web/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -771,8 +771,13 @@ func (h *Handler) bindDefaultEndpoints() {
h.GET("/webapi/auth/export", h.authExportPublic)

// join token handlers
h.PUT("/webapi/token/yaml", h.WithAuth(h.upsertTokenContent))
h.POST("/webapi/token", h.WithAuth(h.createTokenHandle))
h.PUT("/webapi/tokens/yaml", h.WithAuth(h.updateTokenYAML))
// used for creating a new token
h.POST("/webapi/tokens", h.WithAuth(h.upsertTokenHandle))
// used for updating a token
h.PUT("/webapi/tokens", h.WithAuth(h.upsertTokenHandle))
// used for creating tokens used during guided discover flows
h.POST("/webapi/token", h.WithAuth(h.createTokenForDiscoveryHandle))
h.GET("/webapi/tokens", h.WithAuth(h.getTokens))
h.DELETE("/webapi/tokens", h.WithAuth(h.deleteToken))

Expand Down
75 changes: 73 additions & 2 deletions lib/web/join_tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,12 @@ type CreateTokenRequest struct {
Content string `json:"content"`
}

func (h *Handler) upsertTokenContent(w http.ResponseWriter, r *http.Request, params httprouter.Params, sctx *SessionContext) (interface{}, error) {
func (h *Handler) updateTokenYAML(w http.ResponseWriter, r *http.Request, params httprouter.Params, sctx *SessionContext) (interface{}, error) {
tokenId := r.Header.Get(HeaderTokenName)
if tokenId == "" {
return nil, trace.BadParameter("requires a token name to edit")
}
Comment on lines +151 to +154
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this make the endpoint not backwards compatible? Should this use a new endpoint instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are new endpoints (I'm editing this one but it isn't actually used/released yet, just from my last PR) so no worry about backward compatibility here.


var yaml CreateTokenRequest
if err := httplib.ReadJSON(r, &yaml); err != nil {
return nil, trace.Wrap(err)
Expand All @@ -158,6 +163,10 @@ func (h *Handler) upsertTokenContent(w http.ResponseWriter, r *http.Request, par
return nil, trace.Wrap(err)
}

if tokenId != extractedRes.Metadata.Name {
return nil, trace.BadParameter("renaming tokens is not supported")
}

token, err := services.UnmarshalProvisionToken(extractedRes.Raw)
if err != nil {
return nil, trace.Wrap(err)
Expand All @@ -182,7 +191,69 @@ func (h *Handler) upsertTokenContent(w http.ResponseWriter, r *http.Request, par

}

func (h *Handler) createTokenHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
type upsertTokenHandleRequest struct {
types.ProvisionTokenSpecV2
Name string `json:"name"`
}

func (h *Handler) upsertTokenHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
// if using the PUT route, tokenId will be present
// in the X-Teleport-TokenName header
editing := r.Method == "PUT"
tokenId := r.Header.Get(HeaderTokenName)
if editing && tokenId == "" {
return nil, trace.BadParameter("requires a token name to edit")
}

var req upsertTokenHandleRequest
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}

if editing && tokenId != req.Name {
return nil, trace.BadParameter("renaming tokens is not supported")
}

// set expires time to default node join token TTL
expires := time.Now().UTC().Add(defaults.NodeJoinTokenTTL)
// IAM and GCP tokens should never expire
if req.JoinMethod == types.JoinMethodGCP || req.JoinMethod == types.JoinMethodIAM {
expires = time.Now().UTC().AddDate(1000, 0, 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a const for 1000? to consistently set a "never expires"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add a constant but the time package doesn't have Year (at least my editor doesn't seem to think so) so it'd end up being only a const of 1000 which might not get across that its a "time" so probably best to leave out

}

name := req.Name
if name == "" {
randName, err := utils.CryptoRandomHex(defaults.TokenLenBytes)
if err != nil {
return nil, trace.Wrap(err)
}
name = randName
}

token, err := types.NewProvisionTokenFromSpec(name, expires, req.ProvisionTokenSpecV2)
if err != nil {
return nil, trace.Wrap(err)
}

clt, err := ctx.GetClient()
if err != nil {
return nil, trace.Wrap(err)
}

err = clt.UpsertToken(r.Context(), token)
if err != nil {
return nil, trace.Wrap(err)
}

uiToken, err := ui.MakeJoinToken(token)
if err != nil {
return nil, trace.Wrap(err)
}

return uiToken, nil
}

func (h *Handler) createTokenForDiscoveryHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
clt, err := ctx.GetClient()
if err != nil {
return nil, trace.Wrap(err)
Expand Down
19 changes: 15 additions & 4 deletions lib/web/ui/join_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type JoinToken struct {
// SafeName returns the name of the token, sanitized appropriately for
// join methods where the name is secret.
SafeName string `json:"safeName"`
// BotName is the name of the bot this token grants access to, if any
BotName string `json:"bot_name"`
// Expiry is the time that the token resource expires. Tokens that do not expire
// should expect a zero value time to be returned.
Expiry time.Time `json:"expiry"`
Expand All @@ -41,8 +43,10 @@ type JoinToken struct {
IsStatic bool `json:"isStatic"`
// Method is the join method that the token supports
Method types.JoinMethod `json:"method"`
// AllowRules is a list of allow rules
AllowRules []string `json:"allowRules,omitempty"`
// Allow is a list of allow rules
Allow []*types.TokenRule `json:"allow,omitempty"`
// GCP allows the configuration of options specific to the "gcp" join method.
GCP *types.ProvisionTokenSpecV2GCP `json:"gcp,omitempty"`
// Content is resource yaml content.
Content string `json:"content"`
}
Expand All @@ -52,15 +56,22 @@ func MakeJoinToken(token types.ProvisionToken) (*JoinToken, error) {
if err != nil {
return nil, trace.Wrap(err)
}
return &JoinToken{
uiToken := &JoinToken{
ID: token.GetName(),
SafeName: token.GetSafeName(),
BotName: token.GetBotName(),
Expiry: token.Expiry(),
Roles: token.GetRoles(),
IsStatic: token.IsStatic(),
Method: token.GetJoinMethod(),
Allow: token.GetAllowRules(),
Content: string(content[:]),
}, nil
}

if uiToken.Method == types.JoinMethodGCP {
uiToken.GCP = token.GetGCPRules()
}
return uiToken, nil
}

func MakeJoinTokens(tokens []types.ProvisionToken) (joinTokens []JoinToken, err error) {
Expand Down
Loading
Loading