mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-07-02 16:20:19 +00:00
feat: automatically generate context ignore paths
This commit is contained in:
@@ -36,9 +36,9 @@ jobs:
|
|||||||
- name: Check codegen is up to date
|
- name: Check codegen is up to date
|
||||||
run: |
|
run: |
|
||||||
sqlc generate
|
sqlc generate
|
||||||
go generate ./internal/repository/...
|
go generate ./...
|
||||||
git diff --exit-code -- internal/repository/
|
git diff --exit-code
|
||||||
git status --porcelain -- internal/repository/ | grep -q . && echo "untracked files in internal/repository/" && exit 1 || true
|
git status --porcelain | grep -q . && echo "untracked files in git diff" && exit 1 || true
|
||||||
|
|
||||||
- name: Install frontend dependencies
|
- name: Install frontend dependencies
|
||||||
working-directory: ./frontend
|
working-directory: ./frontend
|
||||||
|
|||||||
@@ -94,5 +94,4 @@ sql:
|
|||||||
|
|
||||||
# Go gen
|
# Go gen
|
||||||
generate:
|
generate:
|
||||||
go run ./gen
|
go generate ./...
|
||||||
go generate ./internal/repository/...
|
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
// gen/context_paths generates the ignore paths for the user context since
|
||||||
|
// gin will not less apply the middleware to only specific paths.
|
||||||
|
//
|
||||||
|
// The generator reads every controller and looks for the //context:ignore comment.
|
||||||
|
// The format for the context ignore comment is:
|
||||||
|
//
|
||||||
|
// //contxt:ignore /api/mypath GET,POST
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/format"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
_ "embed"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/packages"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed paths.tmpl
|
||||||
|
var pathsTmplSrc string
|
||||||
|
|
||||||
|
var pathsTmpl = template.Must(template.New("paths").Parse(pathsTmplSrc))
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := run(); err != nil {
|
||||||
|
fmt.Printf("Failed to generate: %s", err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() error {
|
||||||
|
// load pkg
|
||||||
|
pkgConfig := &packages.Config{
|
||||||
|
Mode: packages.NeedFiles,
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgs, err := packages.Load(pkgConfig, "github.com/tinyauthapp/tinyauth/internal/controller")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to load pkg: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pkgs) == 0 {
|
||||||
|
return fmt.Errorf("failed to get controllers package")
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg := pkgs[0]
|
||||||
|
|
||||||
|
// for each file we check the comments and either add or remove the context
|
||||||
|
var contextIgnorePaths []string
|
||||||
|
|
||||||
|
for _, gofile := range pkg.GoFiles {
|
||||||
|
// read the file
|
||||||
|
file, err := os.ReadFile(gofile)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to read %s, ignoring", gofile)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the comment lines
|
||||||
|
lines := strings.SplitSeq(string(file), "\n")
|
||||||
|
|
||||||
|
for line := range lines {
|
||||||
|
if !strings.HasPrefix(strings.TrimSpace(line), "//context:ignore") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
path, methods, ok := parseContextIgnoreLine(line)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
fmt.Printf("Failed to parse %s rule, ignore", line)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range methods {
|
||||||
|
contextIgnorePaths = append(contextIgnorePaths, m+" "+path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate out
|
||||||
|
type tmplData struct {
|
||||||
|
IgnorePaths []string
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
if err := pathsTmpl.Execute(&buf, tmplData{
|
||||||
|
IgnorePaths: contextIgnorePaths,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
formatted, err := format.Source(buf.Bytes())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("gofmt failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// write out
|
||||||
|
err = os.WriteFile("context_paths.go", formatted, 0666)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to write out: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseContextIgnoreLine(line string) (string, []string, bool) {
|
||||||
|
line = strings.TrimPrefix(line, "//context:ignore ")
|
||||||
|
path, methodStr, ok := strings.Cut(line, " ")
|
||||||
|
if !ok {
|
||||||
|
return "", []string{}, false
|
||||||
|
}
|
||||||
|
var methodsParsed []string
|
||||||
|
methodParts := strings.SplitSeq(methodStr, ",")
|
||||||
|
for m := range methodParts {
|
||||||
|
if strings.TrimSpace(m) == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m = strings.ToUpper(m)
|
||||||
|
methodsParsed = append(methodsParsed, m)
|
||||||
|
}
|
||||||
|
return path, methodsParsed, true
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Code generated by gen/context_paths. DO NOT EDIT.
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
var contextSkipPathsPrefix = []string{
|
||||||
|
{{range .IgnorePaths}}"{{.}}",
|
||||||
|
{{end}}}
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
// gen/docs generates the .env.example and config.gen.md
|
||||||
|
// files for the configuration of Tinyauth. Run via:
|
||||||
|
//
|
||||||
|
// The generator reads the Tinyauth configuration package and using reflection it generates the
|
||||||
|
// example files. The .env.example is used in this repo while the config.gen.md is used in the
|
||||||
|
// documentaton alongside some warnings that are added later.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
// gen/sqlc-wrapper generates store.go wrapper files for each sqlc driver package under
|
// gen/sqlc_wrapper generates store.go wrapper files for each sqlc driver package under
|
||||||
// internal/repository/<driver>/. Run via:
|
// internal/repository/<driver>/.
|
||||||
//
|
|
||||||
// go generate ./internal/repository/...
|
|
||||||
//
|
//
|
||||||
// The generator introspects *Queries methods and the model/params types in the
|
// The generator introspects *Queries methods and the model/params types in the
|
||||||
// driver package, then emits a store.go that wraps *Queries so it satisfies
|
// driver package, then emits a store.go that wraps *Queries so it satisfies
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by cmd/gen/sqlc-wrapper. DO NOT EDIT.
|
// Code generated by cmd/gen/sqlc_wrapper. DO NOT EDIT.
|
||||||
package {{.PkgName}}
|
package {{.PkgName}}
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package docs
|
||||||
|
|
||||||
|
//go:generate go run github.com/tinyauthapp/tinyauth/gen/docs
|
||||||
@@ -147,6 +147,7 @@ func (controller *ContextController) userContextHandler(c *gin.Context) {
|
|||||||
c.JSON(200, userContext)
|
c.JSON(200, userContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//context:ignore /api/context/app GET
|
||||||
func (controller *ContextController) appContextHandler(c *gin.Context) {
|
func (controller *ContextController) appContextHandler(c *gin.Context) {
|
||||||
c.JSON(200, AppContextResponse{
|
c.JSON(200, AppContextResponse{
|
||||||
Status: 200,
|
Status: 200,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ func NewHealthController(i HealthControllerInput) *HealthController {
|
|||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//context:ignore /api/healthz GET,HEAD
|
||||||
func (controller *HealthController) healthHandler(c *gin.Context) {
|
func (controller *HealthController) healthHandler(c *gin.Context) {
|
||||||
c.JSON(200, gin.H{
|
c.JSON(200, gin.H{
|
||||||
"status": 200,
|
"status": 200,
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ func NewOAuthController(i OAuthControllerInput) *OAuthController {
|
|||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//context:ignore /api/oauth/url GET
|
||||||
func (controller *OAuthController) oauthURLHandler(c *gin.Context) {
|
func (controller *OAuthController) oauthURLHandler(c *gin.Context) {
|
||||||
var req OAuthRequest
|
var req OAuthRequest
|
||||||
|
|
||||||
@@ -118,6 +119,7 @@ func (controller *OAuthController) oauthURLHandler(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//context:ignore /api/oauth/callback GET
|
||||||
func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
|
func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
|
||||||
var req OAuthRequest
|
var req OAuthRequest
|
||||||
|
|
||||||
|
|||||||
@@ -367,6 +367,7 @@ func (controller *OIDCController) authorizeComplete(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//context:ignore /api/oidc/token POST
|
||||||
func (controller *OIDCController) Token(c *gin.Context) {
|
func (controller *OIDCController) Token(c *gin.Context) {
|
||||||
if controller.oidc == nil {
|
if controller.oidc == nil {
|
||||||
controller.log.App.Warn().Msg("Received OIDC request but OIDC server is not configured")
|
controller.log.App.Warn().Msg("Received OIDC request but OIDC server is not configured")
|
||||||
@@ -538,6 +539,7 @@ func (controller *OIDCController) Token(c *gin.Context) {
|
|||||||
c.JSON(200, tokenResponse)
|
c.JSON(200, tokenResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//context:ignore /api/oidc/userinfo GET,POST
|
||||||
func (controller *OIDCController) Userinfo(c *gin.Context) {
|
func (controller *OIDCController) Userinfo(c *gin.Context) {
|
||||||
if controller.oidc == nil {
|
if controller.oidc == nil {
|
||||||
controller.log.App.Warn().Msg("Received OIDC userinfo request but OIDC server is not configured")
|
controller.log.App.Warn().Msg("Received OIDC userinfo request but OIDC server is not configured")
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ func NewResourcesController(i ResourcesControllerInput) *ResourcesController {
|
|||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//context:ignore /resources GET
|
||||||
func (controller *ResourcesController) resourcesHandler(c *gin.Context) {
|
func (controller *ResourcesController) resourcesHandler(c *gin.Context) {
|
||||||
if controller.config.Resources.Path == "" {
|
if controller.config.Resources.Path == "" {
|
||||||
c.JSON(404, gin.H{
|
c.JSON(404, gin.H{
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ func NewUserController(i UserControllerInput) *UserController {
|
|||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//context:ignore /api/user/login POST
|
||||||
func (controller *UserController) loginHandler(c *gin.Context) {
|
func (controller *UserController) loginHandler(c *gin.Context) {
|
||||||
var req LoginRequest
|
var req LoginRequest
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ func NewWellKnownController(i WellKnownControllerInput) *WellKnownController {
|
|||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//context:ignore /.well-known/openid-configuration GET
|
||||||
func (controller *WellKnownController) OpenIDConnectConfiguration(c *gin.Context) {
|
func (controller *WellKnownController) OpenIDConnectConfiguration(c *gin.Context) {
|
||||||
if controller.oidc == nil {
|
if controller.oidc == nil {
|
||||||
c.JSON(500, gin.H{
|
c.JSON(500, gin.H{
|
||||||
@@ -94,6 +95,7 @@ func (controller *WellKnownController) OpenIDConnectConfiguration(c *gin.Context
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//context:ignore /.well-known/jwks.json GET
|
||||||
func (controller *WellKnownController) JWKS(c *gin.Context) {
|
func (controller *WellKnownController) JWKS(c *gin.Context) {
|
||||||
if controller.oidc == nil {
|
if controller.oidc == nil {
|
||||||
c.JSON(500, gin.H{
|
c.JSON(500, gin.H{
|
||||||
@@ -122,6 +124,7 @@ func (controller *WellKnownController) JWKS(c *gin.Context) {
|
|||||||
c.Status(http.StatusOK)
|
c.Status(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//context:ignore /.well-known/webfinger GET
|
||||||
func (controller *WellKnownController) WebFinger(c *gin.Context) {
|
func (controller *WellKnownController) WebFinger(c *gin.Context) {
|
||||||
c.Header("Content-Type", "application/jrd+json")
|
c.Header("Content-Type", "application/jrd+json")
|
||||||
c.Header("Access-Control-Allow-Origin", "*")
|
c.Header("Access-Control-Allow-Origin", "*")
|
||||||
|
|||||||
@@ -16,26 +16,6 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Gin won't let us set a middleware on a specific route (at least it doesn't work,
|
|
||||||
// see https://github.com/gin-gonic/gin/issues/531) so we have to do some hackery
|
|
||||||
var (
|
|
||||||
contextSkipPathsPrefix = []string{
|
|
||||||
"GET /api/context/app",
|
|
||||||
"GET /api/healthz",
|
|
||||||
"HEAD /api/healthz",
|
|
||||||
"GET /api/oauth/url",
|
|
||||||
"GET /api/oauth/callback",
|
|
||||||
"GET /api/oidc/clients",
|
|
||||||
"POST /api/oidc/token",
|
|
||||||
"GET /api/oidc/userinfo",
|
|
||||||
"POST /api/oidc/userinfo",
|
|
||||||
"GET /resources",
|
|
||||||
"POST /api/user/login",
|
|
||||||
"GET /.well-known/openid-configuration",
|
|
||||||
"GET /.well-known/jwks.json",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type ContextMiddleware struct {
|
type ContextMiddleware struct {
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
runtime *model.RuntimeConfig
|
runtime *model.RuntimeConfig
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// Code generated by gen/context_paths. DO NOT EDIT.
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
var contextSkipPathsPrefix = []string{
|
||||||
|
"GET /api/context/app",
|
||||||
|
"GET /api/healthz",
|
||||||
|
"HEAD /api/healthz",
|
||||||
|
"GET /api/oauth/url",
|
||||||
|
"GET /api/oauth/callback",
|
||||||
|
"POST /api/oidc/token",
|
||||||
|
"GET /api/oidc/userinfo",
|
||||||
|
"POST /api/oidc/userinfo",
|
||||||
|
"GET /resources",
|
||||||
|
"POST /api/user/login",
|
||||||
|
"GET /.well-known/openid-configuration",
|
||||||
|
"GET /.well-known/jwks.json",
|
||||||
|
"GET /.well-known/webfinger",
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
//go:generate go run github.com/tinyauthapp/tinyauth/gen/context_paths
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
package postgres
|
package postgres
|
||||||
|
|
||||||
//go:generate go run github.com/tinyauthapp/tinyauth/gen/sqlc-wrapper -pkg github.com/tinyauthapp/tinyauth/internal/repository/postgres
|
//go:generate go run github.com/tinyauthapp/tinyauth/gen/sqlc_wrapper -pkg github.com/tinyauthapp/tinyauth/internal/repository/postgres
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by cmd/gen/sqlc-wrapper. DO NOT EDIT.
|
// Code generated by cmd/gen/sqlc_wrapper. DO NOT EDIT.
|
||||||
package postgres
|
package postgres
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package sqlite
|
package sqlite
|
||||||
|
|
||||||
//go:generate go run github.com/tinyauthapp/tinyauth/gen/sqlc-wrapper -pkg github.com/tinyauthapp/tinyauth/internal/repository/sqlite
|
//go:generate go run github.com/tinyauthapp/tinyauth/gen/sqlc_wrapper -pkg github.com/tinyauthapp/tinyauth/internal/repository/sqlite
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by cmd/gen/sqlc-wrapper. DO NOT EDIT.
|
// Code generated by cmd/gen/sqlc_wrapper. DO NOT EDIT.
|
||||||
package sqlite
|
package sqlite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
Reference in New Issue
Block a user