gitea-github-proxy/api/middleware.go

165 lines
4.0 KiB
Go
Raw Normal View History

package api
import (
"bytes"
"code.gitea.io/sdk/gitea"
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"git.lumeweb.com/LumeWeb/gitea-github-proxy/config"
"github.com/gorilla/mux"
"go.uber.org/zap"
"io"
"net/http"
"strings"
)
const AUTHED_CONTEXT_KEY = "authed"
const REDIRECT_AFTER_AUTH = "redirect-after-auth"
const AuthCookieName = "auth-token"
func findAuthToken(r *http.Request) string {
authHeader := parseAuthTokenHeader(r.Header)
if authHeader != "" {
return authHeader
}
cookie := getCookie(r, AuthCookieName)
if cookie != "" {
return cookie
}
return r.FormValue(AuthCookieName)
}
func giteaOauthVerifyMiddleware(cfg *config.Config) mux.MiddlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := findAuthToken(r)
if token == "" {
addAuthStatusToRequestServ(false, r, w, next)
return
}
client, err := getClient(ClientParams{
Config: cfg,
AuthToken: token,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
_, _, err = client.AdminListUsers(gitea.AdminListUsersOptions{})
if err != nil {
addAuthStatusToRequestServ(false, r, w, next)
return
}
addAuthStatusToRequestServ(true, r, w, next)
})
}
}
func requireAuthMiddleware(cfg *config.Config) mux.MiddlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
status := getAuthedStatusFromRequest(r)
if !status {
setCookie(w, REDIRECT_AFTER_AUTH, cfg.Domain, r.Referer(), 0, http.SameSiteLaxMode)
http.Redirect(w, r, "/setup", http.StatusFound)
return
}
next.ServeHTTP(w, r)
})
}
}
func loggingMiddleware(logger *zap.Logger) mux.MiddlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Do stuff here
logger.Debug("Request", zap.String("method", r.Method), zap.String("url", r.RequestURI))
// Call the next handler, which can be another middleware in the chain, or the final handler.
next.ServeHTTP(w, r)
})
}
}
func giteaVerifyWebhookMiddleware(cfg *config.Config, logger *zap.Logger) mux.MiddlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
signature := r.Header.Get("X-Gitea-Signature")
if signature == "" {
http.Error(w, "No signature provided", http.StatusBadRequest)
return
}
decodedSignature, err := hex.DecodeString(signature)
if err != nil {
http.Error(w, "Error decoding signature", http.StatusBadRequest)
logger.Error("Error decoding signature", zap.Error(err))
return
}
payload, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading body", http.StatusBadRequest)
logger.Error("Error reading body", zap.Error(err))
return
}
defer func(body io.ReadCloser) {
err := body.Close()
if err != nil {
logger.Error("Error closing body", zap.Error(err))
}
}(r.Body)
r.Body = io.NopCloser(bytes.NewBuffer(payload))
mac := hmac.New(sha256.New, []byte(cfg.GiteaWebHookSecret))
mac.Write(payload)
expectedMAC := mac.Sum(nil)
if !hmac.Equal(decodedSignature, expectedMAC) {
http.Error(w, "Invalid signature", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
}
func addAuthStatusToRequestServ(status bool, r *http.Request, w http.ResponseWriter, next http.Handler) {
ctx := context.WithValue(r.Context(), AUTHED_CONTEXT_KEY, status)
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
}
func parseAuthTokenHeader(headers http.Header) string {
authHeader := headers.Get("Authorization")
if authHeader == "" {
return ""
}
authHeader = strings.TrimPrefix(authHeader, "Bearer ")
return authHeader
}
func getAuthedStatusFromRequest(r *http.Request) bool {
authed, ok := r.Context().Value(AUTHED_CONTEXT_KEY).(bool)
if !ok {
return false
}
return authed
}