Skip to content

Commit

Permalink
add GH action workflows:
Browse files Browse the repository at this point in the history
- dependabot to keep dependencies up to date
- PR workflow to run tests and lint

fix: linter findings
Signed-off-by: Sandor Szücs <[email protected]>
  • Loading branch information
szuecs committed Jan 17, 2024
1 parent 6bd0eb7 commit ba6389b
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 112 deletions.
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 ]
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:
- 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 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
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 = ""
}
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

0 comments on commit ba6389b

Please sign in to comment.