Skip to content

Commit

Permalink
feat(traefik-plugin): ensure the correct is always sent and add local…
Browse files Browse the repository at this point in the history
… dockerfile for testing
  • Loading branch information
luizfonseca committed Nov 27, 2023
1 parent 8c02c3e commit 0fa2208
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 42 deletions.
20 changes: 20 additions & 0 deletions Dockerfile.local
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM golang:1.21.4-alpine3.17 AS builder

RUN apk add --no-cache ca-certificates && update-ca-certificates

WORKDIR /build

COPY go.mod go.sum ./

RUN go mod download
RUN go mod verify

COPY . .

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /app/server ./cmd/traefik-github-oauth-server

WORKDIR /app

EXPOSE 80

ENTRYPOINT ["/app/server"]
12 changes: 6 additions & 6 deletions internal/app/traefik-github-oauth-server/router/oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package router

import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
Expand All @@ -25,9 +26,10 @@ var (
// GET /oauth/page-url
func OauthPageUrlHandler(app *server.App) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
input := r.Context().Value(httpin.Input).(*model.RequestGenerateOAuthPageURL)
var reqBody *model.RequestGenerateOAuthPageURL

if input == nil {
err := json.NewDecoder(r.Body).Decode(&reqBody)
if err != nil || reqBody == nil || reqBody.AuthURL == "" || reqBody.RedirectURI == "" {
app.Logger.Error().Msgf("Missing required input params")
w.WriteHeader(http.StatusBadRequest)
render.JSON(w, r, model.ResponseError{
Expand All @@ -36,11 +38,9 @@ func OauthPageUrlHandler(app *server.App) http.HandlerFunc {
return
}

app.Logger.Info().Msgf("Generating OAuth page URL for %v+", input)

rid := app.AuthRequestManager.Insert(&model.AuthRequest{
RedirectURI: input.RedirectURI,
AuthURL: input.AuthURL,
RedirectURI: reqBody.RedirectURI,
AuthURL: reqBody.AuthURL,
})

redirectURI, err := buildRedirectURI(app.Config.ApiBaseURL, rid)
Expand Down
2 changes: 1 addition & 1 deletion internal/app/traefik-github-oauth-server/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func RegisterRoutes(app *server.App) {

app.Router.Route("/oauth", func(r chi.Router) {
r.With(httpin.NewInput(model.RequestRedirect{})).Get("/redirect", OauthRedirectHandler(app))
r.With(apiSecretKeyMiddleware, httpin.NewInput(model.RequestGenerateOAuthPageURL{})).Post("/page-url", OauthPageUrlHandler(app))
r.With(apiSecretKeyMiddleware).Post("/page-url", OauthPageUrlHandler(app))
r.With(apiSecretKeyMiddleware, httpin.NewInput(model.RequestGetAuthResult{})).Get("/result", OauthAuthResultHandler(app))
})
}
60 changes: 25 additions & 35 deletions middleware_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"net/url"
"os"
Expand All @@ -17,7 +18,6 @@ import (
"github.com/luizfonseca/traefik-github-oauth-plugin/internal/app/traefik-github-oauth-server/model"
"github.com/luizfonseca/traefik-github-oauth-plugin/internal/pkg/constant"
"github.com/luizfonseca/traefik-github-oauth-plugin/internal/pkg/jwt"
"github.com/rs/zerolog"
"github.com/scylladb/go-set/strset"
)

Expand Down Expand Up @@ -70,67 +70,56 @@ type TraefikGithubOauthMiddleware struct {
whitelistIdSet *strset.Set
whitelistLoginSet *strset.Set

logger *zerolog.Logger
logger *log.Logger
}

var _ http.Handler = (*TraefikGithubOauthMiddleware)(nil)

// New creates a new TraefikGithubOauthMiddleware.
func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
// region Setup logger
logLevel := zerolog.InfoLevel
switch config.LogLevel {
case "DEBUG", "debug":
logLevel = zerolog.DebugLevel
case "INFO", "info":
logLevel = zerolog.InfoLevel
case "WARNING", "warning", "WARN", "warn":
logLevel = zerolog.WarnLevel
case "ERROR", "error":
logLevel = zerolog.ErrorLevel
}
logger := zerolog.New(os.Stdout).Level(logLevel).With().Str("service", "TraefikGithubOauthMiddleware").Timestamp().Logger()
logger := log.New(os.Stdout, "service=traefik-github-oauth-middleware level=debug msg=", 0)
// endregion Setup logger

authPath := config.AuthPath
if !strings.HasPrefix(authPath, "/") {
authPath = "/" + authPath
}

baseUrl := strings.TrimSuffix(config.ApiBaseUrl, "/")

return &TraefikGithubOauthMiddleware{
ctx: ctx,
next: next,
name: name,

apiBaseUrl: config.ApiBaseUrl,
apiBaseUrl: baseUrl,
apiSecretKey: config.ApiSecretKey,
authPath: authPath,
jwtSecretKey: config.JwtSecretKey,
whitelistIdSet: strset.New(config.Whitelist.Ids...),
whitelistLoginSet: strset.New(config.Whitelist.Logins...),

logger: &logger,
logger: logger,
}, nil
}

// ServeHTTP implements http.Handler.
func (middleware *TraefikGithubOauthMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
func (tg *TraefikGithubOauthMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
// If the request matches the injected `/_auth` path, handle it as an authentication request.
if req.URL.Path == middleware.authPath {
middleware.handleAuthRequest(rw, req)
if req.URL.Path == tg.authPath {
tg.handleAuthRequest(rw, req)
return
}

// Otherwise, handle it as oauth-start request
middleware.handleRequest(rw, req)
tg.handleRequest(rw, req)
}

// handleRequest
func (middleware *TraefikGithubOauthMiddleware) handleRequest(rw http.ResponseWriter, req *http.Request) {
user, err := middleware.getGitHubUserFromCookie(req)
// If cookie is missing, re-trigger oauth flow
if err != nil {
middleware.logger.Debug().Msgf("handleRequest: getGitHubUserFromCookie: %s\n", err.Error())
if req.Method == http.MethodGet {
middleware.redirectToOAuthPage(rw, req)
}
Expand All @@ -155,15 +144,13 @@ func (p TraefikGithubOauthMiddleware) handleAuthRequest(rw http.ResponseWriter,
rid := req.URL.Query().Get(constant.QUERY_KEY_REQUEST_ID)
result, err := p.getAuthResult(rid)
if err != nil {
p.logger.Debug().Msgf("handleAuthRequest: getAuthResult: %s\n", err.Error())
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}

// Generate JWTs
tokenString, err := jwt.GenerateJwtTokenString(result.GitHubUserID, result.GitHubUserLogin, p.jwtSecretKey)
if err != nil {
p.logger.Debug().Msgf("handleAuthRequest: GenerateJwtTokenString: %s\n", err.Error())
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
Expand All @@ -181,7 +168,6 @@ func (p TraefikGithubOauthMiddleware) redirectToOAuthPage(rw http.ResponseWriter

oAuthPageURL, err := p.generateOAuthPageURL(getRawRequestUrl(req), p.getAuthURL(req))
if err != nil {
p.logger.Debug().Msgf("redirectToOAuthPage: generateOAuthPageURL: %s\n", err.Error())
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
Expand All @@ -195,7 +181,7 @@ func (tg TraefikGithubOauthMiddleware) generateOAuthPageURL(redirectURI, authURL
}

httpClient := http.Client{Timeout: 5 * time.Second}
reqBodyJson, err := json.Marshal(reqBody)
reqBodyJson, err := json.Marshal(&reqBody)
if err != nil {
return "", err
}
Expand All @@ -222,7 +208,7 @@ func (tg TraefikGithubOauthMiddleware) generateOAuthPageURL(redirectURI, authURL

err = json.NewDecoder(resp.Body).Decode(&respBody)
if err != nil {
tg.logger.Error().Msgf("Failed to decode response from oauth server: %s", err.Error())
tg.logger.Printf("Failed to decode response from oauth server: %s", err.Error())
return "", errors.New("unprocessable entity")
}

Expand Down Expand Up @@ -255,7 +241,7 @@ func (tg TraefikGithubOauthMiddleware) getAuthResult(rid string) (*model.Respons
respBody := model.ResponseGetAuthResult{}
err = json.NewDecoder(resp.Body).Decode(&respBody)
if err != nil {
tg.logger.Error().Msgf("Failed to decode response from oauth server: %s", err.Error())
tg.logger.Printf("Failed to decode response from oauth server: %s", err.Error())
return nil, err
}

Expand All @@ -272,15 +258,15 @@ func (p *TraefikGithubOauthMiddleware) getGitHubUserFromCookie(req *http.Request

// Returns base_url + '/oauth/page-url'
func (p TraefikGithubOauthMiddleware) getOauthPageUrl() string {
return p.apiBaseUrl + constant.ROUTER_GROUP_PATH_OAUTH + "/" + constant.ROUTER_PATH_OAUTH_PAGE_URL
return fmt.Sprintf("%s/%s/%s", p.apiBaseUrl, constant.ROUTER_GROUP_PATH_OAUTH, constant.ROUTER_PATH_OAUTH_PAGE_URL)
}

// Returns base_url + '/oauth/result'
func (p TraefikGithubOauthMiddleware) getOauthResultUrl() string {
return p.apiBaseUrl + constant.ROUTER_GROUP_PATH_OAUTH + "/" + constant.ROUTER_PATH_OAUTH_RESULT
return fmt.Sprintf("%s/%s/%s", p.apiBaseUrl, constant.ROUTER_GROUP_PATH_OAUTH, constant.ROUTER_PATH_OAUTH_RESULT)
}

func (p *TraefikGithubOauthMiddleware) getAuthURL(originalReq *http.Request) string {
func (tg TraefikGithubOauthMiddleware) getAuthURL(originalReq *http.Request) string {
scheme := "http"
if originalReq.TLS != nil {
scheme = "https"
Expand All @@ -289,7 +275,7 @@ func (p *TraefikGithubOauthMiddleware) getAuthURL(originalReq *http.Request) str
gen := url.URL{
Scheme: scheme,
Host: originalReq.Host,
Path: p.authPath,
Path: tg.authPath,
}

return gen.String()
Expand All @@ -302,14 +288,18 @@ func setNoCacheHeaders(rw http.ResponseWriter) {
}

func getRawRequestUrl(originalReq *http.Request) string {
originalUrl := originalReq.URL
url := url.URL{}

scheme := "http"
if originalReq.TLS != nil {
scheme = "https"
}
originalUrl.Scheme = scheme

return originalUrl.String()
url.Scheme = scheme
url.Host = originalReq.Host
url.Path = originalReq.URL.Path

return url.String()
}

func getRandomString32() string {
Expand Down

0 comments on commit 0fa2208

Please sign in to comment.