109 lines
2.6 KiB
Go
109 lines
2.6 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
"gitea.teamortix.com/team-ortix/coverage/db"
|
|
)
|
|
|
|
const cookieName = "gitea_m_lord"
|
|
|
|
// AuthorizationMiddleware is a middleware for requests to report endpoints to validate that users are authorized.
|
|
// It automatically sets a refresh cookie to keep it updated.
|
|
func AuthorizationMiddleware(resource func(http.ResponseWriter, *http.Request)) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
vars := mux.Vars(r)
|
|
namespace := vars["namespace"]
|
|
project := vars["project"]
|
|
|
|
// It's a public repository :).
|
|
if canAccessRepo(http.DefaultClient, namespace, project) {
|
|
resource(w, r)
|
|
return
|
|
}
|
|
|
|
cookie, err := r.Cookie(cookieName)
|
|
if err != nil && err != http.ErrNoCookie {
|
|
log.Printf("Could not get cookie: %v\n", err)
|
|
http.Error(w, "could not process this request", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// They don't have the cookie, so we redirect them to log in.
|
|
if err == http.ErrNoCookie {
|
|
url := db.GenerateNewToken(r.URL.String())
|
|
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
|
|
return
|
|
}
|
|
|
|
// Convert the cookie token to an OAuth token.
|
|
client, newToken, err := db.Authorize(cookie.Value)
|
|
if err != nil {
|
|
url := db.GenerateNewToken(r.URL.String())
|
|
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
|
|
return
|
|
}
|
|
http.SetCookie(w, &http.Cookie{
|
|
Name: cookieName,
|
|
Value: newToken,
|
|
Expires: time.Now().Add(time.Hour * 24 * 7),
|
|
})
|
|
|
|
// We can use the configured http client to check if they can access with credentials.
|
|
if !canAccessRepo(client, namespace, project) {
|
|
http.Error(w, "404 page not found", http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
resource(w, r)
|
|
})
|
|
}
|
|
|
|
const repoURL = "https://gitea.teamortix.com/api/v1/repos/%s/%s"
|
|
|
|
func canAccessRepo(client *http.Client, namespace, project string) bool {
|
|
req, cancel := newClientWithTimeout(fmt.Sprintf(repoURL, namespace, project))
|
|
defer cancel()
|
|
|
|
res, err := client.Do(req)
|
|
if err != nil {
|
|
log.Printf("could not do request: %s\n", err)
|
|
return false
|
|
}
|
|
|
|
err = res.Body.Close()
|
|
if err != nil {
|
|
log.Printf("could close req body: %s\n", err)
|
|
return false
|
|
}
|
|
|
|
if res.StatusCode != 200 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func newClientWithTimeout(url string) (*http.Request, context.CancelFunc) {
|
|
ctx, cancel := context.WithTimeout(
|
|
context.Background(),
|
|
time.Second*5,
|
|
)
|
|
|
|
r := strings.NewReader("")
|
|
req, err := http.NewRequestWithContext(ctx, "GET", url, r)
|
|
if err != nil {
|
|
cancel()
|
|
log.Fatalf("could not create new http request: %v", err)
|
|
}
|
|
|
|
return req, cancel
|
|
}
|