···
+
"github.com/bluesky-social/indigo/atproto/identity"
+
"github.com/go-chi/chi/v5"
+
"tangled.sh/tangled.sh/core/appview"
+
"tangled.sh/tangled.sh/core/appview/db"
"tangled.sh/tangled.sh/core/appview/oauth"
+
"tangled.sh/tangled.sh/core/appview/pages"
"tangled.sh/tangled.sh/core/appview/pagination"
+
"tangled.sh/tangled.sh/core/appview/reporesolver"
+
"tangled.sh/tangled.sh/core/rbac"
+
type Middleware struct {
+
repoResolver *reporesolver.RepoResolver
+
resolver *appview.Resolver
+
func New(oauth *oauth.OAuth, db *db.DB, enforcer rbac.Enforcer, repoResolver *reporesolver.RepoResolver, resolver *appview.Resolver, pages *pages.Pages) Middleware {
+
repoResolver: repoResolver,
+
type middlewareFunc func(http.Handler) http.Handler
+
func AuthMiddleware(a *oauth.OAuth) middlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
redirectFunc := func(w http.ResponseWriter, r *http.Request) {
···
next.ServeHTTP(w, r.WithContext(ctx))
+
func (mw Middleware) knotRoleMiddleware(group string) middlewareFunc {
+
return func(next http.Handler) http.Handler {
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
actor := mw.oauth.GetUser(r)
+
// we need a logged in user
+
log.Printf("not logged in, redirecting")
+
http.Error(w, "Forbiden", http.StatusUnauthorized)
+
domain := chi.URLParam(r, "domain")
+
http.Error(w, "malformed url", http.StatusBadRequest)
+
ok, err := mw.enforcer.E.HasGroupingPolicy(actor.Did, group, domain)
+
// we need a logged in user
+
log.Printf("%s does not have perms of a %s in domain %s", actor.Did, group, domain)
+
http.Error(w, "Forbiden", http.StatusUnauthorized)
+
func (mw Middleware) KnotOwner() middlewareFunc {
+
return mw.knotRoleMiddleware("server:owner")
+
func (mw Middleware) RepoPermissionMiddleware(requiredPerm string) middlewareFunc {
+
return func(next http.Handler) http.Handler {
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
actor := mw.oauth.GetUser(r)
+
// we need a logged in user
+
log.Printf("not logged in, redirecting")
+
http.Error(w, "Forbiden", http.StatusUnauthorized)
+
f, err := mw.repoResolver.Resolve(r)
+
http.Error(w, "malformed url", http.StatusBadRequest)
+
ok, err := mw.enforcer.E.Enforce(actor.Did, f.Knot, f.DidSlashRepo(), requiredPerm)
+
// we need a logged in user
+
log.Printf("%s does not have perms of a %s in repo %s", actor.Did, requiredPerm, f.OwnerSlashRepo())
+
http.Error(w, "Forbiden", http.StatusUnauthorized)
+
func StripLeadingAt(next http.Handler) http.Handler {
+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+
path := req.URL.EscapedPath()
+
if strings.HasPrefix(path, "/@") {
+
req.URL.RawPath = "/" + strings.TrimPrefix(path, "/@")
+
func (mw Middleware) ResolveIdent() middlewareFunc {
+
excluded := []string{"favicon.ico"}
+
return func(next http.Handler) http.Handler {
+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+
didOrHandle := chi.URLParam(req, "user")
+
if slices.Contains(excluded, didOrHandle) {
+
id, err := mw.resolver.ResolveIdent(req.Context(), didOrHandle)
+
// invalid did or handle
+
log.Println("failed to resolve did/handle:", err)
+
w.WriteHeader(http.StatusNotFound)
+
ctx := context.WithValue(req.Context(), "resolvedId", *id)
+
next.ServeHTTP(w, req.WithContext(ctx))
+
func (mw Middleware) ResolveRepo() middlewareFunc {
+
return func(next http.Handler) http.Handler {
+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+
repoName := chi.URLParam(req, "repo")
+
id, ok := req.Context().Value("resolvedId").(identity.Identity)
+
log.Println("malformed middleware")
+
w.WriteHeader(http.StatusInternalServerError)
+
repo, err := db.GetRepo(mw.db, id.DID.String(), repoName)
+
// invalid did or handle
+
log.Println("failed to resolve repo")
+
ctx := context.WithValue(req.Context(), "knot", repo.Knot)
+
ctx = context.WithValue(ctx, "repoAt", repo.AtUri)
+
ctx = context.WithValue(ctx, "repoDescription", repo.Description)
+
ctx = context.WithValue(ctx, "repoAddedAt", repo.Created.Format(time.RFC3339))
+
next.ServeHTTP(w, req.WithContext(ctx))
+
// middleware that is tacked on top of /{user}/{repo}/pulls/{pull}
+
func (mw Middleware) ResolvePull() middlewareFunc {
+
return func(next http.Handler) http.Handler {
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
f, err := mw.repoResolver.Resolve(r)
+
log.Println("failed to fully resolve repo", err)
+
http.Error(w, "invalid repo url", http.StatusNotFound)
+
prId := chi.URLParam(r, "pull")
+
prIdInt, err := strconv.Atoi(prId)
+
http.Error(w, "bad pr id", http.StatusBadRequest)
+
log.Println("failed to parse pr id", err)
+
pr, err := db.GetPull(mw.db, f.RepoAt, prIdInt)
+
log.Println("failed to get pull and comments", err)
+
ctx := context.WithValue(r.Context(), "pull", pr)
+
stack, err := db.GetStack(mw.db, pr.StackId)
+
log.Println("failed to get stack", err)
+
abandonedPulls, err := db.GetAbandonedPulls(mw.db, pr.StackId)
+
log.Println("failed to get abandoned pulls", err)
+
ctx = context.WithValue(ctx, "stack", stack)
+
ctx = context.WithValue(ctx, "abandonedPulls", abandonedPulls)
+
next.ServeHTTP(w, r.WithContext(ctx))
+
// this should serve the go-import meta tag even if the path is technically
+
// a 404 like tangled.sh/oppi.li/go-git/v5
+
func (mw Middleware) GoImport() middlewareFunc {
+
return func(next http.Handler) http.Handler {
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
f, err := mw.repoResolver.Resolve(r)
+
log.Println("failed to fully resolve repo", err)
+
http.Error(w, "invalid repo url", http.StatusNotFound)
+
fullName := f.OwnerHandle() + "/" + f.RepoName
+
if r.Header.Get("User-Agent") == "Go-http-client/1.1" {
+
if r.URL.Query().Get("go-get") == "1" {
+
`<meta name="go-import" content="tangled.sh/%s git https://tangled.sh/%s"/>`,
+
w.Header().Set("Content-Type", "text/html")