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 GH action workflows #80

Merged
merged 2 commits into from
Jan 18, 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
15 changes: 15 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# * https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
# * https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/" # For GitHub Actions, set the directory to / to check for workflow files in .github/workflows
schedule:
interval: "weekly"
43 changes: 43 additions & 0 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: pr
on: [ pull_request ]
Copy link
Member

Choose a reason for hiding this comment

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

shouldn't this be [ push ] to compile on any new commit?

Copy link
Member Author

Choose a reason for hiding this comment

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

It updates on each push but online if there's an open pr

permissions:
contents: read
jobs:
semgrep:
if: ${{ github.actor != 'dependabot[bot]' }}
runs-on: ubuntu-latest
container:
image: returntocorp/semgrep
steps:
# Retrieve the source code for the repository
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
# Fetch the semgrep rules
- run: git clone https://github.com/dgryski/semgrep-go.git
# Run the rule checker using the fetched rules
- run: semgrep ci -f semgrep-go

check-race:
runs-on: ubuntu-latest
steps:
Comment on lines +19 to +21
Copy link
Member

Choose a reason for hiding this comment

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

why do you propose to test two times: with and without -race?

Copy link
Member Author

Choose a reason for hiding this comment

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

Race takes significant longer and that's why it makes sense to break out.

- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
Comment on lines +22 to +23
Copy link
Member

Choose a reason for hiding this comment

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

Why not just an official release version instead of an unclear commit?

      uses: actions/checkout@v3
      uses: actions/setup-go@v3

Copy link
Member

Choose a reason for hiding this comment

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

for security reasons. Usage of v3 opens up possibility for script injections into CI/CD. +1 for commit usage in Zalando context.

with:
# https://www.npmjs.com/package/semver#caret-ranges-123-025-004
go-version: '^1.21'
check-latest: true
- run: go version
- run: go test -race ./...
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
with:
# https://www.npmjs.com/package/semver#caret-ranges-123-025-004
go-version: '^1.21'
check-latest: true
- run: go version
- run: go vet ./github ./google ./zalando
- run: go test ./...
- run: go install honnef.co/go/tools/cmd/staticcheck@latest
- run: staticcheck -checks "all" ./github ./google ./zalando
Copy link
Member

Choose a reason for hiding this comment

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

Should the staticcheck be in own step. You can use the action from dominikh/staticcheck-action@xxx

8 changes: 4 additions & 4 deletions example/zalando/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (

"github.com/gin-gonic/gin"
"github.com/golang/glog"
"github.com/szuecs/gin-glog"
"github.com/zalando/gin-oauth2"
ginglog "github.com/szuecs/gin-glog"
ginoauth2 "github.com/zalando/gin-oauth2"
"github.com/zalando/gin-oauth2/zalando"
)

Expand Down Expand Up @@ -57,8 +57,8 @@ func main() {
c.JSON(200, gin.H{"message": "Hello from private for groups and users"})
})
privateGroup.GET("/", func(c *gin.Context) {
uid, okUid := c.Get("uid")
if team, ok := c.Get("team"); ok && okUid {
uid, okUID := c.Get("uid")
if team, ok := c.Get("team"); ok && okUID {
c.JSON(200, gin.H{"message": fmt.Sprintf("Hello from private for groups to %s member of %s", uid, team)})
} else {
c.JSON(200, gin.H{"message": "Hello from private for groups without uid and team"})
Expand Down
183 changes: 90 additions & 93 deletions ginoauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,53 @@
// webframework.
//
// Example:
// package main
// import (
// "flag"
// "time"
// "github.com/gin-gonic/gin"
// "github.com/golang/glog"
// "github.com/szuecs/gin-glog"
// "github.com/zalando/gin-oauth2"
// "golang.org/x/oauth2"
// )
//
// var OAuth2Endpoint = oauth2.Endpoint{
// AuthURL: "https://token.oauth2.corp.com/access_token",
// TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
// }
// package main
// import (
// "flag"
// "time"
// "github.com/gin-gonic/gin"
// "github.com/golang/glog"
// "github.com/szuecs/gin-glog"
// "github.com/zalando/gin-oauth2"
// "golang.org/x/oauth2"
// )
//
// func UidCheck(tc *TokenContainer, ctx *gin.Context) bool {
// uid := tc.Scopes["uid"].(string)
// if uid != "sszuecs" {
// return false
// }
// ctx.Set("uid", uid)
// return true
// }
// var OAuth2Endpoint = oauth2.Endpoint{
// AuthURL: "https://token.oauth2.corp.com/access_token",
// TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
// }
//
// func main() {
// flag.Parse()
// router := gin.New()
// router.Use(ginglog.Logger(3 * time.Second))
// router.Use(gin.Recovery())
// func UidCheck(tc *TokenContainer, ctx *gin.Context) bool {
// uid := tc.Scopes["uid"].(string)
// if uid != "sszuecs" {
// return false
// }
// ctx.Set("uid", uid)
// return true
// }
//
// ginoauth2.VarianceTimer = 300 * time.Millisecond // defaults to 30s
// func main() {
// flag.Parse()
// router := gin.New()
// router.Use(ginglog.Logger(3 * time.Second))
// router.Use(gin.Recovery())
//
// public := router.Group("/api")
// public.GET("/", func(c *gin.Context) {
// c.JSON(200, gin.H{"message": "Hello to public world"})
// })
// ginoauth2.VarianceTimer = 300 * time.Millisecond // defaults to 30s
//
// private := router.Group("/api/private")
// private.Use(ginoauth2.Auth(UidCheck, OAuth2Endpoint))
// private.GET("/", func(c *gin.Context) {
// c.JSON(200, gin.H{"message": "Hello from private"})
// })
// public := router.Group("/api")
// public.GET("/", func(c *gin.Context) {
// c.JSON(200, gin.H{"message": "Hello to public world"})
// })
//
// glog.Info("bootstrapped application")
// router.Run(":8081")
// private := router.Group("/api/private")
// private.Use(ginoauth2.Auth(UidCheck, OAuth2Endpoint))
// private.GET("/", func(c *gin.Context) {
// c.JSON(200, gin.H{"message": "Hello from private"})
// })
//
// glog.Info("bootstrapped application")
// router.Run(":8081")
package ginoauth2

import (
Expand Down Expand Up @@ -125,12 +125,12 @@ func infofv2(f string, args ...interface{}) {
func extractToken(r *http.Request) (*oauth2.Token, error) {
hdr := r.Header.Get("Authorization")
if hdr == "" {
return nil, errors.New("No authorization header")
return nil, errors.New("no authorization header")
}

th := strings.Split(hdr, " ")
if len(th) != 2 {
return nil, errors.New("Incomplete authorization header")
return nil, errors.New("incomplete authorization header")
}

return &oauth2.Token{AccessToken: th[1], TokenType: th[0]}, nil
Expand Down Expand Up @@ -179,10 +179,10 @@ func ParseTokenContainer(t *oauth2.Token, data map[string]interface{}) (*TokenCo
exp := data["expires_in"].(float64)
tok := data["access_token"].(string)
if ttype != t.TokenType {
return nil, errors.New("Token type mismatch")
return nil, errors.New("token type mismatch")
}
if tok != t.AccessToken {
return nil, errors.New("Mismatch between verify request and answer")
return nil, errors.New("mismatch between verify request and answer")
}

scopes := data["scope"].([]interface{})
Expand Down Expand Up @@ -219,9 +219,11 @@ func getTokenContainerForToken(o Options, token *oauth2.Token) (*TokenContainer,
errorf("[Gin-OAuth] JSON.Unmarshal failed caused by: %s", err)
return nil, err
}
if _, ok := data["error_description"]; ok {
var s string
s = data["error_description"].(string)
if si, ok := data["error_description"]; ok {
s, ok := si.(string)
if !ok {
s = ""

Choose a reason for hiding this comment

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

Do you expect this line to be executed? If yes, why is it okay to ignore data["error_description"]?

}
errorf("[Gin-OAuth] RequestAuthInfo returned an error: %s", s)
return nil, errors.New(s)
}
Expand Down Expand Up @@ -254,32 +256,30 @@ func getTokenContainer(o Options, ctx *gin.Context) (*TokenContainer, bool) {
return tc, true
}

//
// TokenContainer
//
// Validates that the AccessToken within TokenContainer is not expired and not empty.
// Valid validates that the AccessToken within TokenContainer is not
// expired and not empty.
func (t *TokenContainer) Valid() bool {
if t.Token == nil {
return false
}
return t.Token.Valid()
}

// Router middleware that can be used to get an authenticated and authorized service for the whole router group.
// Auth implements a router middleware that can be used to get an
// authenticated and authorized service for the whole router group.
// Example:
//
// var endpoints oauth2.Endpoint = oauth2.Endpoint{
// AuthURL: "https://token.oauth2.corp.com/access_token",
// TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
// }
// var acl []ginoauth2.AccessTuple = []ginoauth2.AccessTuple{{"employee", 1070, "sszuecs"}, {"employee", 1114, "njuettner"}}
// router := gin.Default()
// private := router.Group("")
// private.Use(ginoauth2.Auth(ginoauth2.UidCheck, ginoauth2.endpoints))
// private.GET("/api/private", func(c *gin.Context) {
// c.JSON(200, gin.H{"message": "Hello from private"})
// })
//
// var endpoints oauth2.Endpoint = oauth2.Endpoint{
// AuthURL: "https://token.oauth2.corp.com/access_token",
// TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
// }
// var acl []ginoauth2.AccessTuple = []ginoauth2.AccessTuple{{"employee", 1070, "sszuecs"}, {"employee", 1114, "njuettner"}}
// router := gin.Default()
// private := router.Group("")
// private.Use(ginoauth2.Auth(ginoauth2.UidCheck, ginoauth2.endpoints))
// private.GET("/api/private", func(c *gin.Context) {
// c.JSON(200, gin.H{"message": "Hello from private"})
// })
func Auth(accessCheckFunction AccessCheckFunction, endpoints oauth2.Endpoint) gin.HandlerFunc {
return AuthChain(endpoints, accessCheckFunction)
}
Expand All @@ -289,22 +289,21 @@ func Auth(accessCheckFunction AccessCheckFunction, endpoints oauth2.Endpoint) gi
// takes a chain of AccessCheckFunctions and only fails if all of them fails.
// Example:
//
// var endpoints oauth2.Endpoint = oauth2.Endpoint{
// AuthURL: "https://token.oauth2.corp.com/access_token",
// TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
// }
// var acl []ginoauth2.AccessTuple = []ginoauth2.AccessTuple{{"employee", 1070, "sszuecs"}, {"employee", 1114, "njuettner"}}
// router := gin.Default()
// private := router.Group("")
// checkChain := []AccessCheckFunction{
// ginoauth2.UidCheck,
// ginoauth2.GroupCheck,
// }
// private.Use(ginoauth2.AuthChain(checkChain, ginoauth2.endpoints))
// private.GET("/api/private", func(c *gin.Context) {
// c.JSON(200, gin.H{"message": "Hello from private"})
// })
//
// var endpoints oauth2.Endpoint = oauth2.Endpoint{
// AuthURL: "https://token.oauth2.corp.com/access_token",
// TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
// }
// var acl []ginoauth2.AccessTuple = []ginoauth2.AccessTuple{{"employee", 1070, "sszuecs"}, {"employee", 1114, "njuettner"}}
// router := gin.Default()
// private := router.Group("")
// checkChain := []AccessCheckFunction{
// ginoauth2.UidCheck,
// ginoauth2.GroupCheck,
// }
// private.Use(ginoauth2.AuthChain(checkChain, ginoauth2.endpoints))
// private.GET("/api/private", func(c *gin.Context) {
// c.JSON(200, gin.H{"message": "Hello from private"})
// })
func AuthChain(endpoint oauth2.Endpoint, accessCheckFunctions ...AccessCheckFunction) gin.HandlerFunc {
return AuthChainOptions(Options{Endpoint: endpoint}, accessCheckFunctions...)
}
Expand All @@ -322,15 +321,15 @@ func AuthChainOptions(o Options, accessCheckFunctions ...AccessCheckFunction) gi
if !ok {
// set LOCATION header to auth endpoint such that the user can easily get a new access-token
ctx.Writer.Header().Set("Location", o.Endpoint.AuthURL)
ctx.AbortWithError(http.StatusUnauthorized, errors.New("No token in context"))
ctx.AbortWithError(http.StatusUnauthorized, errors.New("no token in context"))
varianceControl <- false
return
}

if !tokenContainer.Valid() {
// set LOCATION header to auth endpoint such that the user can easily get a new access-token
ctx.Writer.Header().Set("Location", o.Endpoint.AuthURL)
ctx.AbortWithError(http.StatusUnauthorized, errors.New("Invalid Token"))
ctx.AbortWithError(http.StatusUnauthorized, errors.New("invalid Token"))
varianceControl <- false
return
}
Expand All @@ -342,7 +341,7 @@ func AuthChainOptions(o Options, accessCheckFunctions ...AccessCheckFunction) gi
}

if len(accessCheckFunctions)-1 == i {
ctx.AbortWithError(http.StatusForbidden, errors.New("Access to the Resource is forbidden"))
ctx.AbortWithError(http.StatusForbidden, errors.New("access to the Resource is forbidden"))
varianceControl <- false
return
}
Expand All @@ -356,7 +355,7 @@ func AuthChainOptions(o Options, accessCheckFunctions ...AccessCheckFunction) gi
return
}
case <-time.After(VarianceTimer):
ctx.AbortWithError(http.StatusGatewayTimeout, errors.New("Authorization check overtime"))
ctx.AbortWithError(http.StatusGatewayTimeout, errors.New("authorization check overtime"))
infofv2("[Gin-OAuth] %12v %s overtime", time.Since(t), ctx.Request.URL.Path)
return
}
Expand All @@ -375,22 +374,20 @@ func AuthChainOptions(o Options, accessCheckFunctions ...AccessCheckFunction) gi
//
// Example:
//
// var endpoints oauth2.Endpoint = oauth2.Endpoint{
// AuthURL: "https://token.oauth2.corp.com/access_token",
// TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
// }
// var acl []ginoauth2.AccessTuple = []ginoauth2.AccessTuple{{"employee", 1070, "sszuecs"}, {"employee", 1114, "njuettner"}}
// router := gin.Default()
// router.Use(ginoauth2.RequestLogger([]string{"uid"}, "data"))
//
// var endpoints oauth2.Endpoint = oauth2.Endpoint{
// AuthURL: "https://token.oauth2.corp.com/access_token",
// TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
// }
// var acl []ginoauth2.AccessTuple = []ginoauth2.AccessTuple{{"employee", 1070, "sszuecs"}, {"employee", 1114, "njuettner"}}
// router := gin.Default()
// router.Use(ginoauth2.RequestLogger([]string{"uid"}, "data"))
func RequestLogger(keys []string, contentKey string) gin.HandlerFunc {
return func(c *gin.Context) {
request := c.Request
c.Next()
err := c.Errors
if request.Method != "GET" && err == nil {
data, e := c.Get(contentKey)
if e != false { //key is non existent
if data, ok := c.Get(contentKey); !ok {
values := make([]string, 0)
for _, key := range keys {
val, keyPresent := c.Get(key)
Expand Down
Loading