feat: carry the token tag restriction on the session
This commit is contained in:
@@ -74,8 +74,9 @@ func createToken(c *gin.Context) {
|
||||
var body struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Role string `json:"role" binding:"required"`
|
||||
Scopes []string `json:"scopes" binding:"required"`
|
||||
ExpiresInDays *int `json:"expires_in_days"`
|
||||
Scopes []string `json:"scopes" binding:"required"`
|
||||
ExpiresInDays *int `json:"expires_in_days"`
|
||||
TagSelector map[string]string `json:"tag_selector"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
@@ -108,9 +109,16 @@ func createToken(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
if !services.SelectorNarrowerOrEqual(body.TagSelector, auth.ServerScope(c)) {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": "a token cannot reach servers its creator cannot reach",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
tok, plaintext, err := services.CreateAPIToken(
|
||||
auth.InstanceID(c), auth.UserID(c),
|
||||
body.Name, body.Role, body.Scopes, nil, body.ExpiresInDays, c.ClientIP(),
|
||||
body.Name, body.Role, body.Scopes, body.TagSelector, body.ExpiresInDays, c.ClientIP(),
|
||||
)
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTokenNameTaken):
|
||||
@@ -137,8 +145,12 @@ func createToken(c *gin.Context) {
|
||||
if tok.ExpiresAt != nil {
|
||||
expiry = "expires " + tok.ExpiresAt.Format("2006-01-02")
|
||||
}
|
||||
services.LogEvent(auth.InstanceID(c), "token.created", actorFromCtx(c), "", "",
|
||||
fmt.Sprintf("API token '%s' created with role %s, scopes %v, %s", tok.Name, tok.Role, tok.Scopes, expiry))
|
||||
detail := fmt.Sprintf("API token '%s' created with role %s, scopes %v, %s",
|
||||
tok.Name, tok.Role, tok.Scopes, expiry)
|
||||
if len(tok.TagSelector) > 0 {
|
||||
detail += fmt.Sprintf(", restricted to %v", tok.TagSelector)
|
||||
}
|
||||
services.LogEvent(auth.InstanceID(c), "token.created", actorFromCtx(c), "", "", detail)
|
||||
|
||||
// The plaintext is returned exactly once and is not stored anywhere.
|
||||
c.JSON(http.StatusCreated, CreateTokenResponse{Token: plaintext, Record: *tok})
|
||||
|
||||
@@ -167,9 +167,10 @@ func sessionFromToken(c *gin.Context) (*Session, bool) {
|
||||
Role: services.LowerRole(user.Role, tok.Role),
|
||||
Email: user.Email,
|
||||
Name: user.Email,
|
||||
TokenID: tok.TokenID,
|
||||
TokenName: tok.Name,
|
||||
Scopes: tok.Scopes,
|
||||
TokenID: tok.TokenID,
|
||||
TokenName: tok.Name,
|
||||
Scopes: tok.Scopes,
|
||||
TokenScope: tok.TagSelector,
|
||||
}, true
|
||||
}
|
||||
|
||||
@@ -199,3 +200,14 @@ func Scopes(c *gin.Context) []string {
|
||||
// IsToken reports whether this request authenticated with an API token rather
|
||||
// than a browser session.
|
||||
func IsToken(c *gin.Context) bool { return TokenID(c) != "" }
|
||||
|
||||
// ServerScope is the tag restriction the acting credential carries, or nil for
|
||||
// an unrestricted token and for every cookie session. Callers pass it to
|
||||
// services.ServerInTokenScope or services.IntersectSelectors — nil means the
|
||||
// whole fleet, never nothing.
|
||||
func ServerScope(c *gin.Context) map[string]string {
|
||||
if s := GetSessionFromContext(c); s != nil {
|
||||
return s.TokenScope
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -23,13 +23,14 @@ type Session struct {
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
|
||||
// The three fields below are set only when the request authenticated with
|
||||
// The four fields below are set only when the request authenticated with
|
||||
// an API token. They are never persisted to Redis — a token authenticates
|
||||
// per request and mints no session, so a revoked token stops working
|
||||
// immediately rather than at the end of a session TTL.
|
||||
TokenID string `json:"-"`
|
||||
TokenName string `json:"-"`
|
||||
Scopes []string `json:"-"`
|
||||
TokenID string `json:"-"`
|
||||
TokenName string `json:"-"`
|
||||
Scopes []string `json:"-"`
|
||||
TokenScope map[string]string `json:"-"`
|
||||
}
|
||||
|
||||
var rdb *redis.Client
|
||||
|
||||
Reference in New Issue
Block a user