<commit_before>package main

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"

	"github.com/gorilla/mux"
	"github.com/trusch/jwtd/jwt"
)

type SingleProxy struct {
	service string
	project string
	router  *mux.Router
	proxy   *httputil.ReverseProxy
	jwtdCrt interface{}
}

func NewSingleProxy(project, serviceName, backend string, routes []*Route, jwtdCrt interface{}) (*SingleProxy, error) {
	backendUrl, err := url.Parse(backend)
	if err != nil {
		return nil, err
	}
	proxy := &SingleProxy{
		service: serviceName,
		project: project,
		proxy:   httputil.NewSingleHostReverseProxy(backendUrl),
		jwtdCrt: jwtdCrt,
	}
	proxy.constructRouter(routes)
	return proxy, nil
}

func (proxy *SingleProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Printf("service: %v url: %v", proxy.service, r.URL)
	proxy.router.ServeHTTP(w, r)
}

func (proxy *SingleProxy) constructRouter(routes []*Route) {
	r := mux.NewRouter()
	for _, route := range routes {
		log.Printf("service: %v url: %v", proxy.service, route.Path)
		sub := r.PathPrefix(route.Path)
		if len(route.Methods) > 0 {
			sub = sub.Methods(route.Methods...)
		}
		sub.HandlerFunc(proxy.buildHandler(route.Require))
	}
	proxy.router = r
}

func (proxy *SingleProxy) buildHandler(required map[string]string) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		if len(required) == 0 {
			proxy.proxy.ServeHTTP(w, r)
			return
		}
		claims, err := jwt.GetClaimsFromRequest(r, proxy.jwtdCrt)
		if err != nil {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte(err.Error() + "\n"))
			return
		}
		if service, ok := claims["service"].(string); ok {
			if service != proxy.service {
				w.WriteHeader(http.StatusUnauthorized)
				w.Write([]byte("service mismatch\n"))
				return
			}
		} else {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("no valid service field in token\n"))
			return
		}
		if project, ok := claims["project"].(string); ok {
			if project != proxy.project {
				w.WriteHeader(http.StatusUnauthorized)
				w.Write([]byte("project mismatch\n"))
				return
			}
		} else {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("no valid project field in token\n"))
			return
		}
		if err = proxy.validateNbf(claims); err != nil {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte(err.Error() + "\n"))
			return
		}
		if err = proxy.validateExp(claims); err != nil {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte(err.Error() + "\n"))
			return
		}
		if err = proxy.validateLabels(claims, proxy.resolveVariables(required, mux.Vars(r))); err != nil {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte(err.Error() + "\n"))
			return
		}
		proxy.proxy.ServeHTTP(w, r)
	}
}<commit_msg>make logging in proxy more verbose and informative;<commit_after>package main

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"

	"github.com/gorilla/mux"
	"github.com/trusch/jwtd/jwt"
)

type SingleProxy struct {
	service string
	project string
	router  *mux.Router
	proxy   *httputil.ReverseProxy
	jwtdCrt interface{}
}

func NewSingleProxy(project, serviceName, backend string, routes []*Route, jwtdCrt interface{}) (*SingleProxy, error) {
	backendUrl, err := url.Parse(backend)
	if err != nil {
		return nil, err
	}
	proxy := &SingleProxy{
		service: serviceName,
		project: project,
		proxy:   httputil.NewSingleHostReverseProxy(backendUrl),
		jwtdCrt: jwtdCrt,
	}
	proxy.constructRouter(routes)
	return proxy, nil
}

func (proxy *SingleProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Printf("%v: incoming request: %v", proxy.service, r.URL)
	proxy.router.ServeHTTP(w, r)
}

func (proxy *SingleProxy) constructRouter(routes []*Route) {
	r := mux.NewRouter()
	for _, route := range routes {
		sub := r.PathPrefix(route.Path)
		if len(route.Methods) > 0 {
			sub = sub.Methods(route.Methods...)
		}
		sub.HandlerFunc(proxy.buildHandler(route.Require))
	}
	proxy.router = r
}

func (proxy *SingleProxy) buildHandler(required map[string]string) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		if len(required) == 0 {
			log.Print("no labels required for this reques, now forwarding...")
			proxy.proxy.ServeHTTP(w, r)
			return
		}
		claims, err := jwt.GetClaimsFromRequest(r, proxy.jwtdCrt)
		if err != nil {
			log.Printf("can not get claims from request (%v), return 401", err)
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte(err.Error() + "\n"))
			return
		}
		if service, ok := claims["service"].(string); ok {
			if service != proxy.service {
				log.Printf("service in claim doesn't match, return 401")
				w.WriteHeader(http.StatusUnauthorized)
				w.Write([]byte("service mismatch\n"))
				return
			}
		} else {
			log.Printf("service in claim not valid, return 401")
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("no valid service field in token\n"))
			return
		}
		if project, ok := claims["project"].(string); ok {
			if project != proxy.project {
				log.Printf("project in claim doesn't match, return 401")
				w.WriteHeader(http.StatusUnauthorized)
				w.Write([]byte("project mismatch\n"))
				return
			}
		} else {
			log.Printf("project in claim not valid, return 401")
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("no valid project field in token\n"))
			return
		}
		if err = proxy.validateNbf(claims); err != nil {
			log.Printf("NBF check failed: %v, return 401", err)
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte(err.Error() + "\n"))
			return
		}
		if err = proxy.validateExp(claims); err != nil {
			log.Printf("EXP check failed: %v, return 401", err)
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte(err.Error() + "\n"))
			return
		}
		if err = proxy.validateLabels(claims, proxy.resolveVariables(required, mux.Vars(r))); err != nil {
			log.Printf("claims do not have the required labels: %v, return 401", err)
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte(err.Error() + "\n"))
			return
		}
		log.Printf("all checks passed, forwarding...")
		proxy.proxy.ServeHTTP(w, r)
	}
}
{'fraction_non_alphanumeric': 0.15137836530710946, 'fraction_numerical': 0.004513944865387715, 'mean_word_length': 3.97514033680834, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1846726', 'n_tokens_mistral': 2418, 'n_tokens_neox': 2168, 'n_words': 486}