mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-26 03:41:01 +00:00
Extend Logging & Report to WebHook Caller back if pulls are disabled (#369)
* Add more logging * Format Code * Add TODOs * Fix nits * Delete two unused functions * Report to WebHook Caller back if pulls are disabled
This commit is contained in:
parent
a94807efeb
commit
f81bd8c656
12 changed files with 67 additions and 64 deletions
|
@ -54,7 +54,9 @@ import (
|
||||||
func loop(c *cli.Context) error {
|
func loop(c *cli.Context) error {
|
||||||
|
|
||||||
// debug level if requested by user
|
// debug level if requested by user
|
||||||
|
// TODO: format output & options to switch to json aka. option to add channels to send logs to
|
||||||
if c.Bool("debug") {
|
if c.Bool("debug") {
|
||||||
|
logrus.SetReportCaller(true)
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
} else {
|
} else {
|
||||||
logrus.SetLevel(logrus.WarnLevel)
|
logrus.SetLevel(logrus.WarnLevel)
|
||||||
|
|
|
@ -79,7 +79,7 @@ func BlockTilQueueHasRunningItem(c *gin.Context) {
|
||||||
func PostHook(c *gin.Context) {
|
func PostHook(c *gin.Context) {
|
||||||
remote_ := remote.FromContext(c)
|
remote_ := remote.FromContext(c)
|
||||||
|
|
||||||
tmprepo, build, err := remote_.Hook(c.Request)
|
tmpRepo, build, err := remote_.Hook(c.Request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("failure to parse hook. %s", err)
|
logrus.Errorf("failure to parse hook. %s", err)
|
||||||
c.AbortWithError(400, err)
|
c.AbortWithError(400, err)
|
||||||
|
@ -89,7 +89,7 @@ func PostHook(c *gin.Context) {
|
||||||
c.Writer.WriteHeader(200)
|
c.Writer.WriteHeader(200)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if tmprepo == nil {
|
if tmpRepo == nil {
|
||||||
logrus.Errorf("failure to ascertain repo from hook.")
|
logrus.Errorf("failure to ascertain repo from hook.")
|
||||||
c.Writer.WriteHeader(400)
|
c.Writer.WriteHeader(400)
|
||||||
return
|
return
|
||||||
|
@ -104,14 +104,14 @@ func PostHook(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
repo, err := store.GetRepoOwnerName(c, tmprepo.Owner, tmprepo.Name)
|
repo, err := store.GetRepoOwnerName(c, tmpRepo.Owner, tmpRepo.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("failure to find repo %s/%s from hook. %s", tmprepo.Owner, tmprepo.Name, err)
|
logrus.Errorf("failure to find repo %s/%s from hook. %s", tmpRepo.Owner, tmpRepo.Name, err)
|
||||||
c.AbortWithError(404, err)
|
c.AbortWithError(404, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !repo.IsActive {
|
if !repo.IsActive {
|
||||||
logrus.Errorf("ignoring hook. %s/%s is inactive.", tmprepo.Owner, tmprepo.Name)
|
logrus.Errorf("ignoring hook. %s/%s is inactive.", tmpRepo.Owner, tmpRepo.Name)
|
||||||
c.AbortWithError(204, err)
|
c.AbortWithError(204, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -139,6 +139,7 @@ func PostHook(c *gin.Context) {
|
||||||
|
|
||||||
if build.Event == model.EventPull && !repo.AllowPull {
|
if build.Event == model.EventPull && !repo.AllowPull {
|
||||||
logrus.Infof("ignoring hook. repo %s is disabled for pull requests.", repo.FullName)
|
logrus.Infof("ignoring hook. repo %s is disabled for pull requests.", repo.FullName)
|
||||||
|
c.Writer.Write([]byte("pulls are disabled on woodpecker for this repo"))
|
||||||
c.Writer.WriteHeader(204)
|
c.Writer.WriteHeader(204)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -154,9 +155,14 @@ func PostHook(c *gin.Context) {
|
||||||
// may be stale. Therefore, we should refresh prior to dispatching
|
// may be stale. Therefore, we should refresh prior to dispatching
|
||||||
// the build.
|
// the build.
|
||||||
if refresher, ok := remote_.(remote.Refresher); ok {
|
if refresher, ok := remote_.(remote.Refresher); ok {
|
||||||
ok, _ := refresher.Refresh(user)
|
ok, err := refresher.Refresh(user)
|
||||||
if ok {
|
if err != nil {
|
||||||
store.UpdateUser(c, user)
|
logrus.Errorf("failed to refresh oauth2 token: %s", err)
|
||||||
|
} else if ok {
|
||||||
|
if err := store.UpdateUser(c, user); err != nil {
|
||||||
|
logrus.Errorf("error while updating user: %s", err)
|
||||||
|
// move forward
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,12 +292,16 @@ func PostHook(c *gin.Context) {
|
||||||
queueBuild(build, repo, buildItems)
|
queueBuild(build, repo, buildItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: parse yaml once and not for each filter function
|
||||||
func branchFiltered(build *model.Build, remoteYamlConfigs []*remote.FileMeta) (bool, error) {
|
func branchFiltered(build *model.Build, remoteYamlConfigs []*remote.FileMeta) (bool, error) {
|
||||||
|
logrus.Tracef("hook.branchFiltered(): build branch: '%s' build event: '%s' config count: %d", build.Branch, build.Event, len(remoteYamlConfigs))
|
||||||
for _, remoteYamlConfig := range remoteYamlConfigs {
|
for _, remoteYamlConfig := range remoteYamlConfigs {
|
||||||
parsedPipelineConfig, err := yaml.ParseString(string(remoteYamlConfig.Data))
|
parsedPipelineConfig, err := yaml.ParseString(string(remoteYamlConfig.Data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logrus.Tracef("parse config '%s': %s", remoteYamlConfig.Name, err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
logrus.Tracef("config '%s': %#v", remoteYamlConfig.Name, parsedPipelineConfig)
|
||||||
|
|
||||||
if !parsedPipelineConfig.Branches.Match(build.Branch) && build.Event != model.EventTag && build.Event != model.EventDeploy {
|
if !parsedPipelineConfig.Branches.Match(build.Branch) && build.Event != model.EventTag && build.Event != model.EventDeploy {
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -15,9 +15,10 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/woodpecker-ci/woodpecker/server/store"
|
"github.com/woodpecker-ci/woodpecker/server/store"
|
||||||
"github.com/woodpecker-ci/woodpecker/version"
|
"github.com/woodpecker-ci/woodpecker/version"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Health endpoint returns a 500 if the server state is unhealthy.
|
// Health endpoint returns a 500 if the server state is unhealthy.
|
||||||
|
|
|
@ -21,6 +21,8 @@ const (
|
||||||
EventDeploy = "deployment"
|
EventDeploy = "deployment"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: type StatusValue string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StatusSkipped = "skipped"
|
StatusSkipped = "skipped"
|
||||||
StatusPending = "pending"
|
StatusPending = "pending"
|
||||||
|
|
|
@ -19,13 +19,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RepoLite struct {
|
|
||||||
Owner string `json:"owner"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
FullName string `json:"full_name"`
|
|
||||||
Avatar string `json:"avatar_url"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repo represents a repository.
|
// Repo represents a repository.
|
||||||
//
|
//
|
||||||
// swagger:model repo
|
// swagger:model repo
|
||||||
|
|
|
@ -289,15 +289,6 @@ var (
|
||||||
Name: "not_found_project",
|
Name: "not_found_project",
|
||||||
}
|
}
|
||||||
|
|
||||||
fakeRepos = []*model.RepoLite{
|
|
||||||
&model.RepoLite{
|
|
||||||
Owner: "demo1",
|
|
||||||
Name: "test1",
|
|
||||||
FullName: "demo1/test1",
|
|
||||||
Avatar: "/static/project_icon/scenery-5.png",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
fakeBuild = &model.Build{
|
fakeBuild = &model.Build{
|
||||||
Commit: "4504a072cc",
|
Commit: "4504a072cc",
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,10 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: use pagination
|
||||||
|
// TODO: use context
|
||||||
|
// TODO: add Driver() who return source forge back
|
||||||
|
|
||||||
type Remote interface {
|
type Remote interface {
|
||||||
// Login authenticates the session and returns the
|
// Login authenticates the session and returns the
|
||||||
// remote user details.
|
// remote user details.
|
||||||
|
|
|
@ -38,18 +38,6 @@ func Repo(c *gin.Context) *model.Repo {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func Repos(c *gin.Context) []*model.RepoLite {
|
|
||||||
v, ok := c.Get("repos")
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
r, ok := v.([]*model.RepoLite)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetRepo() gin.HandlerFunc {
|
func SetRepo() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
var (
|
var (
|
||||||
|
@ -93,7 +81,6 @@ func Perm(c *gin.Context) *model.Perm {
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetPerm() gin.HandlerFunc {
|
func SetPerm() gin.HandlerFunc {
|
||||||
|
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
user := User(c)
|
user := User(c)
|
||||||
repo := Repo(c)
|
repo := Repo(c)
|
||||||
|
|
|
@ -36,18 +36,6 @@ func User(c *gin.Context) *model.User {
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
func Token(c *gin.Context) *token.Token {
|
|
||||||
v, ok := c.Get("token")
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
u, ok := v.(*token.Token)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetUser() gin.HandlerFunc {
|
func SetUser() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
var user *model.User
|
var user *model.User
|
||||||
|
|
|
@ -17,8 +17,6 @@ package router
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"github.com/woodpecker-ci/woodpecker/server/api"
|
"github.com/woodpecker-ci/woodpecker/server/api"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/api/debug"
|
"github.com/woodpecker-ci/woodpecker/server/api/debug"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/api/metrics"
|
"github.com/woodpecker-ci/woodpecker/server/api/metrics"
|
||||||
|
@ -26,6 +24,9 @@ import (
|
||||||
"github.com/woodpecker-ci/woodpecker/server/router/middleware/session"
|
"github.com/woodpecker-ci/woodpecker/server/router/middleware/session"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/router/middleware/token"
|
"github.com/woodpecker-ci/woodpecker/server/router/middleware/token"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/web"
|
"github.com/woodpecker-ci/woodpecker/server/web"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Load loads the router
|
// Load loads the router
|
||||||
|
@ -34,6 +35,11 @@ func Load(serveHTTP func(w http.ResponseWriter, r *http.Request), middleware ...
|
||||||
e := gin.New()
|
e := gin.New()
|
||||||
e.Use(gin.Recovery())
|
e.Use(gin.Recovery())
|
||||||
|
|
||||||
|
e.Use(func(c *gin.Context) {
|
||||||
|
logrus.Tracef("[%s] %s", c.Request.Method, c.Request.URL.String())
|
||||||
|
c.Next()
|
||||||
|
})
|
||||||
|
|
||||||
e.Use(header.NoCache)
|
e.Use(header.NoCache)
|
||||||
e.Use(header.Options)
|
e.Use(header.Options)
|
||||||
e.Use(header.Secure)
|
e.Use(header.Secure)
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
|
|
||||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/remote"
|
"github.com/woodpecker-ci/woodpecker/server/remote"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type configFetcher struct {
|
type configFetcher struct {
|
||||||
|
@ -25,10 +27,14 @@ func NewConfigFetcher(remote remote.Remote, user *model.User, repo *model.Repo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch
|
||||||
|
// TODO: dedupe code
|
||||||
func (cf *configFetcher) Fetch() (files []*remote.FileMeta, err error) {
|
func (cf *configFetcher) Fetch() (files []*remote.FileMeta, err error) {
|
||||||
var file []byte
|
var file []byte
|
||||||
config := strings.TrimSpace(cf.repo.Config)
|
config := strings.TrimSpace(cf.repo.Config)
|
||||||
|
|
||||||
|
logrus.Tracef("Start Fetching config for '%s'", cf.repo.FullName)
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
select {
|
select {
|
||||||
case <-time.After(time.Second * time.Duration(i)):
|
case <-time.After(time.Second * time.Duration(i)):
|
||||||
|
@ -37,6 +43,7 @@ func (cf *configFetcher) Fetch() (files []*remote.FileMeta, err error) {
|
||||||
if !strings.HasSuffix(config, "/") {
|
if !strings.HasSuffix(config, "/") {
|
||||||
file, err = cf.remote_.File(cf.user, cf.repo, cf.build, config)
|
file, err = cf.remote_.File(cf.user, cf.repo, cf.build, config)
|
||||||
if err == nil && len(file) != 0 {
|
if err == nil && len(file) != 0 {
|
||||||
|
logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config)
|
||||||
return []*remote.FileMeta{{
|
return []*remote.FileMeta{{
|
||||||
Name: config,
|
Name: config,
|
||||||
Data: file,
|
Data: file,
|
||||||
|
@ -47,29 +54,37 @@ func (cf *configFetcher) Fetch() (files []*remote.FileMeta, err error) {
|
||||||
// or a folder
|
// or a folder
|
||||||
files, err = cf.remote_.Dir(cf.user, cf.repo, cf.build, strings.TrimSuffix(config, "/"))
|
files, err = cf.remote_.Dir(cf.user, cf.repo, cf.build, strings.TrimSuffix(config, "/"))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
logrus.Tracef("ConfigFetch[%s]: found %d files in '%s'", cf.repo.FullName, len(files), config)
|
||||||
return filterPipelineFiles(files), nil
|
return filterPipelineFiles(files), nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
logrus.Tracef("ConfigFetch[%s]: user did not defined own config follow default procedure", cf.repo.FullName)
|
||||||
// no user defined config so try .woodpecker/*.yml -> .woodpecker.yml -> .drone.yml
|
// no user defined config so try .woodpecker/*.yml -> .woodpecker.yml -> .drone.yml
|
||||||
|
|
||||||
// test .woodpecker/ folder
|
// test .woodpecker/ folder
|
||||||
// if folder is not supported we will get a "Not implemented" error and continue
|
// if folder is not supported we will get a "Not implemented" error and continue
|
||||||
files, err = cf.remote_.Dir(cf.user, cf.repo, cf.build, ".woodpecker")
|
config = ".woodpecker"
|
||||||
|
files, err = cf.remote_.Dir(cf.user, cf.repo, cf.build, config)
|
||||||
|
logrus.Tracef("ConfigFetch[%s]: found %d files in '%s'", cf.repo.FullName, len(files), config)
|
||||||
files = filterPipelineFiles(files)
|
files = filterPipelineFiles(files)
|
||||||
if err == nil && len(files) != 0 {
|
if err == nil && len(files) != 0 {
|
||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err = cf.remote_.File(cf.user, cf.repo, cf.build, ".woodpecker.yml")
|
config = ".woodpecker.yml"
|
||||||
|
file, err = cf.remote_.File(cf.user, cf.repo, cf.build, config)
|
||||||
if err == nil && len(file) != 0 {
|
if err == nil && len(file) != 0 {
|
||||||
|
logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config)
|
||||||
return []*remote.FileMeta{{
|
return []*remote.FileMeta{{
|
||||||
Name: ".woodpecker.yml",
|
Name: ".woodpecker.yml",
|
||||||
Data: file,
|
Data: file,
|
||||||
}}, nil
|
}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err = cf.remote_.File(cf.user, cf.repo, cf.build, ".drone.yml")
|
config = ".drone.yml"
|
||||||
|
file, err = cf.remote_.File(cf.user, cf.repo, cf.build, config)
|
||||||
if err == nil && len(file) != 0 {
|
if err == nil && len(file) != 0 {
|
||||||
|
logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config)
|
||||||
return []*remote.FileMeta{{
|
return []*remote.FileMeta{{
|
||||||
Name: ".drone.yml",
|
Name: ".drone.yml",
|
||||||
Data: file,
|
Data: file,
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SecretFunc func(*Token) (string, error)
|
type SecretFunc func(*Token) (string, error)
|
||||||
|
@ -31,7 +32,7 @@ const (
|
||||||
AgentToken = "agent"
|
AgentToken = "agent"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Default algorithm used to sign JWT tokens.
|
// SignerAlgo id default algorithm used to sign JWT tokens.
|
||||||
const SignerAlgo = "HS256"
|
const SignerAlgo = "HS256"
|
||||||
|
|
||||||
type Token struct {
|
type Token struct {
|
||||||
|
@ -39,7 +40,7 @@ type Token struct {
|
||||||
Text string
|
Text string
|
||||||
}
|
}
|
||||||
|
|
||||||
func Parse(raw string, fn SecretFunc) (*Token, error) {
|
func parse(raw string, fn SecretFunc) (*Token, error) {
|
||||||
token := &Token{}
|
token := &Token{}
|
||||||
parsed, err := jwt.Parse(raw, keyFunc(token, fn))
|
parsed, err := jwt.Parse(raw, keyFunc(token, fn))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -51,21 +52,24 @@ func Parse(raw string, fn SecretFunc) (*Token, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseRequest(r *http.Request, fn SecretFunc) (*Token, error) {
|
func ParseRequest(r *http.Request, fn SecretFunc) (*Token, error) {
|
||||||
var token = r.Header.Get("Authorization")
|
token := r.Header.Get("Authorization")
|
||||||
|
|
||||||
// first we attempt to get the token from the
|
// first we attempt to get the token from the
|
||||||
// authorization header.
|
// authorization header.
|
||||||
if len(token) != 0 {
|
if len(token) != 0 {
|
||||||
token = r.Header.Get("Authorization")
|
logrus.Tracef("token.ParseRequest: found token in header: %s", token)
|
||||||
fmt.Sscanf(token, "Bearer %s", &token)
|
bearer := token
|
||||||
return Parse(token, fn)
|
if _, err := fmt.Sscanf(token, "Bearer %s", &bearer); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return parse(bearer, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// then we attempt to get the token from the
|
// then we attempt to get the token from the
|
||||||
// access_token url query parameter
|
// access_token url query parameter
|
||||||
token = r.FormValue("access_token")
|
token = r.FormValue("access_token")
|
||||||
if len(token) != 0 {
|
if len(token) != 0 {
|
||||||
return Parse(token, fn)
|
return parse(token, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// and finally we attempt to get the token from
|
// and finally we attempt to get the token from
|
||||||
|
@ -74,7 +78,7 @@ func ParseRequest(r *http.Request, fn SecretFunc) (*Token, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return Parse(cookie.Value, fn)
|
return parse(cookie.Value, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckCsrf(r *http.Request, fn SecretFunc) error {
|
func CheckCsrf(r *http.Request, fn SecretFunc) error {
|
||||||
|
@ -88,7 +92,7 @@ func CheckCsrf(r *http.Request, fn SecretFunc) error {
|
||||||
|
|
||||||
// parse the raw CSRF token value and validate
|
// parse the raw CSRF token value and validate
|
||||||
raw := r.Header.Get("X-CSRF-TOKEN")
|
raw := r.Header.Get("X-CSRF-TOKEN")
|
||||||
_, err := Parse(raw, fn)
|
_, err := parse(raw, fn)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue