Skip to content

Commit

Permalink
Add Allow and GCP fields to ui join token (#44408)
Browse files Browse the repository at this point in the history
  • Loading branch information
avatus authored Jul 26, 2024
1 parent ef06b41 commit 8e9cfc6
Show file tree
Hide file tree
Showing 16 changed files with 1,195 additions and 119 deletions.
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")
}

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)
}

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

0 comments on commit 8e9cfc6

Please sign in to comment.