coverage/server/auth.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
}