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

feat!: Vela OIDC provider #1120

Merged
merged 43 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
e910998
init commit
ecrupper Apr 29, 2024
9a35107
some renaming and comment fixing
ecrupper Apr 29, 2024
a089ee2
tests and renaming things here and there, plus pull in types
ecrupper Apr 30, 2024
e99d1c3
update subject
ecrupper May 2, 2024
a2ef469
integration test
ecrupper May 3, 2024
095208a
Merge branch 'main' into feat/oidc-provider
ecrupper May 3, 2024
ebc9518
Merge branch 'main' into feat/oidc-provider
ecrupper May 16, 2024
9b451c6
pull in types and add event to subject
ecrupper May 16, 2024
9be3a5a
address lint review feedback
ecrupper May 16, 2024
43cae89
fix tests
ecrupper May 16, 2024
7c605ab
more integration test fixes
ecrupper May 16, 2024
a595daf
bytes buffer for exponent
ecrupper May 17, 2024
5582b26
correct issuer and add commands claim
ecrupper May 20, 2024
396396c
Merge branch 'main' into feat/oidc-provider
ecrupper May 20, 2024
a7d8b9b
sender to actor
ecrupper May 21, 2024
ab20991
Merge branch 'feat/oidc-provider' of github.com:go-vela/server into f…
ecrupper May 21, 2024
75770cd
use lestrrat jwx lib for jwks
ecrupper May 21, 2024
e974df9
fixes
ecrupper May 22, 2024
34d115f
more fixes
ecrupper May 22, 2024
e5b9af2
use wrapper for swagger jwk set
ecrupper May 22, 2024
9d6aeb1
address feedback
ecrupper May 23, 2024
ce82c32
enhance: add build_id and actor_id to claims
plyr4 May 29, 2024
a82dd61
enhance: complete adding build_id and actor_id to claims
plyr4 May 30, 2024
5e88941
enhance: complete adding build_id and actor_id to claims
plyr4 May 30, 2024
0699f60
fix: apply context to GenerateRSA
plyr4 May 30, 2024
169b103
fix: add err check to ParseBool
plyr4 May 30, 2024
3f46a1b
enhance: audience validation
plyr4 May 30, 2024
8eed526
enhance: better audience validation
plyr4 May 30, 2024
ae353bf
enhance: add query parameter input validation
plyr4 May 30, 2024
36983c6
tweak: order of operations, move sanitize lower
plyr4 May 30, 2024
32861c6
enhance: add scm user id to build obj
plyr4 Jun 3, 2024
a8958cf
enhance: add GetUserID to scm interface
plyr4 Jun 3, 2024
242ebf9
fix: apply missing scm id using scm client lookups
plyr4 Jun 3, 2024
27f52be
chore: merge with main scm user id
plyr4 Jun 4, 2024
7625a96
chore: verbose comment on fallback user fetch
plyr4 Jun 4, 2024
a04a62f
Merge branch 'enhance/build-sender-id' of github.com:go-vela/server i…
plyr4 Jun 4, 2024
82c8578
chore: comment typo
plyr4 Jun 4, 2024
3d50f56
Merge branch 'enhance/build-sender-id' of github.com:go-vela/server i…
plyr4 Jun 4, 2024
167c95b
enhance: use repo owner token in schedule processing
plyr4 Jun 4, 2024
37d5dfc
enhance: use repo owner token in restart build
plyr4 Jun 4, 2024
4ad8f5a
Merge branch 'enhance/build-sender-id' of github.com:go-vela/server i…
plyr4 Jun 4, 2024
4d69c9b
enhance: change claims actor_id to actor_scm_id
plyr4 Jun 4, 2024
922ffd0
chore: merge with main
plyr4 Jun 4, 2024
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
53 changes: 53 additions & 0 deletions api/admin/rotate_keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: Apache-2.0

package admin

import (
"fmt"
"net/http"

"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"

"github.com/go-vela/server/database"
"github.com/go-vela/server/util"
)

// swagger:operation POST /api/v1/admin/rotate_oidc_keys admin AdminRotateOIDCKeys
//
// Rotate RSA Keys
//
// ---
// produces:
// - application/json
// parameters:
// security:
// - ApiKeyAuth: []
// responses:
// '200':
// description: Successfully rotated OIDC provider keys
// schema:
// type: string
// '500':
// description: Error unable to rotate OIDC provider keys
// schema:
// "$ref": "#/definitions/Error"

// RotateOIDCKeys represents the API handler to
// rotate RSA keys in OIDC provider service.
func RotateOIDCKeys(c *gin.Context) {
logrus.Info("Admin: rotating keys for OIDC provider")

// capture middleware values
ctx := c.Request.Context()

err := database.FromContext(c).RotateKeys(ctx)
if err != nil {
retErr := fmt.Errorf("unable to rotate keys: %w", err)
util.HandleError(c, http.StatusInternalServerError, retErr)

return
}

c.JSON(http.StatusOK, "keys rotated successfully")
}
134 changes: 134 additions & 0 deletions api/build/id_request_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: Apache-2.0

package build

import (
"fmt"
"net/http"
"strconv"
"time"

"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"

"github.com/go-vela/server/constants"
"github.com/go-vela/server/internal/token"
"github.com/go-vela/server/router/middleware/build"
"github.com/go-vela/server/router/middleware/claims"
"github.com/go-vela/server/util"
"github.com/go-vela/types/library"
)

// swagger:operation GET /api/v1/repos/{org}/{repo}/builds/{build}/id_request_token builds GetIDRequestToken
//
// Get a Vela OIDC request token associated with a build
//
// ---
// produces:
// - application/json
// parameters:
// - in: path
// name: repo
// description: Name of the repo
// required: true
// type: string
// - in: path
// name: org
// description: Name of the org
// required: true
// type: string
// - in: path
// name: build
// description: Build number
// required: true
// type: integer
// - in: query
// name: image
// description: Add image to token claims
// type: string
// - in: query
// name: request
// description: Add request input to token claims
// type: string
// - in: query
// name: commands
// description: Add commands input to token claims
// type: boolean
// security:
// - ApiKeyAuth: []
// responses:
// '200':
// description: Successfully retrieved ID Request token
// schema:
// "$ref": "#/definitions/Token"
// '400':
// description: Bad request
// schema:
// "$ref": "#/definitions/Error"
// '401':
// description: Unauthorized request
// schema:
// "$ref": "#/definitions/Error"
// '404':
// description: Unable to find build
// schema:
// "$ref": "#/definitions/Error"
// '500':
// description: Unable to generate ID request token
// schema:
// "$ref": "#/definitions/Error"

// GetIDRequestToken represents the API handler to generate and return an ID request token.
func GetIDRequestToken(c *gin.Context) {
// capture middleware values
b := build.Retrieve(c)
cl := claims.Retrieve(c)

// update engine logger with API metadata
//
// https://pkg.go.dev/github.com/sirupsen/logrus?tab=doc#Entry.WithFields
logrus.WithFields(logrus.Fields{
"build": b.GetNumber(),
"org": b.GetRepo().GetOrg(),
"repo": b.GetRepo().GetName(),
"user": cl.Subject,
}).Infof("generating ID request token for build %s/%d", b.GetRepo().GetFullName(), b.GetNumber())

image := c.Query("image")
request := c.Query("request")
plyr4 marked this conversation as resolved.
Show resolved Hide resolved
commands, err := strconv.ParseBool(c.Query("commands"))
if err != nil {

Check failure on line 100 in api/build/id_request_token.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] api/build/id_request_token.go#L100

only one cuddle assignment allowed before if statement (wsl)
Raw output
api/build/id_request_token.go:100:2: only one cuddle assignment allowed before if statement (wsl)
	if err != nil {
	^
plyr4 marked this conversation as resolved.
Show resolved Hide resolved
retErr := fmt.Errorf("unable to parse 'commands' query %s: %w", c.Query("commands"), err)

util.HandleError(c, http.StatusBadRequest, retErr)

return
}

// retrieve token manager from context
tm := c.MustGet("token-manager").(*token.Manager)

exp := (time.Duration(b.GetRepo().GetTimeout()) * time.Minute) + tm.BuildTokenBufferDuration

// set mint token options
idmto := &token.MintTokenOpts{
Build: b,
Repo: b.GetRepo().GetFullName(),
TokenType: constants.IDRequestTokenType,
TokenDuration: exp,
Image: image,
Request: request,
Commands: commands,
}

// mint token
idrt, err := tm.MintToken(idmto)
if err != nil {
retErr := fmt.Errorf("unable to generate ID request token: %w", err)
util.HandleError(c, http.StatusInternalServerError, retErr)

return
}

c.JSON(http.StatusOK, library.Token{Token: &idrt})
}
127 changes: 127 additions & 0 deletions api/build/id_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: Apache-2.0

package build

import (
"fmt"
"net/http"

"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"

"github.com/go-vela/server/constants"
"github.com/go-vela/server/database"
"github.com/go-vela/server/internal/token"
"github.com/go-vela/server/router/middleware/build"
"github.com/go-vela/server/router/middleware/claims"
"github.com/go-vela/server/util"
"github.com/go-vela/types/library"
)

// swagger:operation GET /api/v1/repos/{org}/{repo}/builds/{build}/id_token builds GetIDToken
//
// Get a Vela OIDC token associated with a build
//
// ---
// produces:
// - application/json
// parameters:
// - in: path
// name: repo
// description: Name of the repo
// required: true
// type: string
// - in: path
// name: org
// description: Name of the org
// required: true
// type: string
// - in: path
// name: build
// description: Build number
// required: true
// type: integer
// - in: query
// name: audience
// description: Add audience to token claims
// type: array
// items:
// type: string
// collectionFormat: multi
// security:
// - ApiKeyAuth: []
// responses:
// '200':
// description: Successfully retrieved ID token
// schema:
// "$ref": "#/definitions/Token"
// '400':
// description: Bad request
// schema:
// "$ref": "#/definitions/Error"
// '401':
// description: Unauthorized request
// schema:
// "$ref": "#/definitions/Error"
// '404':
// description: Unable to find build
// schema:
// "$ref": "#/definitions/Error"
// '500':
// description: Unable to generate id token
// schema:
// "$ref": "#/definitions/Error"

// GetIDToken represents the API handler to generate a id token.
func GetIDToken(c *gin.Context) {
// capture middleware values
b := build.Retrieve(c)
cl := claims.Retrieve(c)
ctx := c.Request.Context()

// update engine logger with API metadata
//
// https://pkg.go.dev/github.com/sirupsen/logrus?tab=doc#Entry.WithFields
logrus.WithFields(logrus.Fields{
"build": b.GetNumber(),
"org": b.GetRepo().GetOrg(),
"repo": b.GetRepo().GetName(),
"subject": cl.Subject,
}).Infof("generating ID token for build %s/%d", b.GetRepo().GetFullName(), b.GetNumber())

// retrieve token manager from context
tm := c.MustGet("token-manager").(*token.Manager)

// set mint token options
idmto := &token.MintTokenOpts{
Build: b,
Repo: b.GetRepo().GetFullName(),
TokenType: constants.IDTokenType,
TokenDuration: tm.IDTokenDuration,
Image: cl.Image,
Request: cl.Request,
Commands: cl.Commands,
}

// if audience is provided, include that in claims
if len(c.QueryArray("audience")) > 0 {
ecrupper marked this conversation as resolved.
Show resolved Hide resolved
audience := []string{}
for _, a := range c.QueryArray("audience") {

Check failure on line 109 in api/build/id_token.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] api/build/id_token.go#L109

ranges should only be cuddled with assignments used in the iteration (wsl)
Raw output
api/build/id_token.go:109:3: ranges should only be cuddled with assignments used in the iteration (wsl)
		for _, a := range c.QueryArray("audience") {
		^
plyr4 marked this conversation as resolved.
Show resolved Hide resolved
if len(a) > 0 {
audience = append(audience, util.Sanitize(a))
}
}
idmto.Audience = audience

Check failure on line 114 in api/build/id_token.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] api/build/id_token.go#L114

assignments should only be cuddled with other assignments (wsl)
Raw output
api/build/id_token.go:114:3: assignments should only be cuddled with other assignments (wsl)
		idmto.Audience = audience
		^
plyr4 marked this conversation as resolved.
Show resolved Hide resolved
}

// mint token
idt, err := tm.MintIDToken(ctx, idmto, database.FromContext(c))
if err != nil {
retErr := fmt.Errorf("unable to generate build token: %w", err)
util.HandleError(c, http.StatusInternalServerError, retErr)

return
}

c.JSON(http.StatusOK, library.Token{Token: &idt})
}
2 changes: 1 addition & 1 deletion api/build/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func GetBuildToken(c *gin.Context) {
// set mint token options
bmto := &token.MintTokenOpts{
Hostname: cl.Subject,
BuildID: b.GetID(),
Build: b,
Repo: r.GetFullName(),
TokenType: constants.WorkerBuildTokenType,
TokenDuration: exp,
Expand Down
47 changes: 47 additions & 0 deletions api/jwks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: Apache-2.0

package api

import (
"fmt"
"net/http"

"github.com/gin-gonic/gin"

"github.com/go-vela/server/database"
"github.com/go-vela/server/util"
)

// swagger:operation GET /_services/token/.well-known/jwks token GetJWKS
//
// Get the JWKS for the Vela OIDC service
//
// ---
// produces:
// - application/json
// parameters:
// security:
// - ApiKeyAuth: []
// responses:
// '200':
// description: Successfully retrieved the Vela JWKS
// schema:
// "$ref": "#/definitions/JWKSet"
ecrupper marked this conversation as resolved.
Show resolved Hide resolved
// '500':
// description: Unable to get the Vela JWKS
// schema:
// "$ref": "#/definitions/Error"

// GetJWKS represents the API handler for requests to public keys in the Vela OpenID service.
func GetJWKS(c *gin.Context) {
// retrieve JWKs from the database
keys, err := database.FromContext(c).ListJWKs(c)
if err != nil {
retErr := fmt.Errorf("unable to get key set: %w", err)
util.HandleError(c, http.StatusInternalServerError, retErr)

return
}

c.JSON(http.StatusOK, keys)
}
Loading
Loading