Commit dabcc517 authored by Kirill Smelkov's avatar Kirill Smelkov

X auth refreshing draftly works

parent d528e25c
Pipeline #113 failed with stage
...@@ -51,8 +51,6 @@ func preAuthorizeHandler(handleFunc serviceHandleFunc, suffix string) serviceHan ...@@ -51,8 +51,6 @@ func preAuthorizeHandler(handleFunc serviceHandleFunc, suffix string) serviceHan
// The auth backend validated the client request and told us additional // The auth backend validated the client request and told us additional
// request metadata. We must extract this information from the auth // request metadata. We must extract this information from the auth
// response body. // response body.
//log.Printf("resp body: %s", authResponse.Body)
//io.Copy(os.Stdout, authResponse.Body)
if err := json.NewDecoder(authResponse.Body).Decode(&r.authorizationResponse); err != nil { if err := json.NewDecoder(authResponse.Body).Decode(&r.authorizationResponse); err != nil {
fail500(w, "decode authorization response", err) fail500(w, "decode authorization response", err)
return return
......
...@@ -2,6 +2,17 @@ ...@@ -2,6 +2,17 @@
Handler for raw blob downloads Handler for raw blob downloads
*/ */
/*
Cache-Control: private
ETag: "4c10677531b44f555ebbdaff24a9b2d6"
X-Content-Type-Options: nosniff
Content-Disposition: inline
Content-Transfer-Encoding: binary
Content-Type: text/plain; charset=utf-8
*/
package main package main
import ( import (
...@@ -12,85 +23,96 @@ import ( ...@@ -12,85 +23,96 @@ import (
"strings" "strings"
"regexp" "regexp"
"net/http" "net/http"
"net/http/httptest"
) )
/*
Cache-Control: private
ETag: "4c10677531b44f555ebbdaff24a9b2d6"
X-Content-Type-Options: nosniff // auth backend reply
Content-Disposition: inline type backendAuthReply struct {
Content-Transfer-Encoding: binary w *httptest.ResponseRecorder // output of backend/preAuthorizeHandler
authorizationResponse
}
Content-Type: text/plain; charset=utf-8 // ask auth backend whether download is ok for project
*/ func askAuthBackend(u *upstream, project string) backendAuthReply {
authReply := backendAuthReply{
w: httptest.NewRecorder(),
}
// request to verify whether download is possible via asking as git fetch would do
// XXX privateToken not propagated, etc ...
reqDownloadAccess, err := http.NewRequest("GET", project + ".git/info/refs?service=git-upload-pack", nil)
if err != nil {
fail500(authReply.w, "GET git-upload-pack", err)
//return false // XXX not cache as it is just we cannot create request
return authReply
}
func blobPreAuthorizeHandler(handleFunc serviceHandleFunc) serviceHandleFunc { // swap original request to 'verify-download' one XXX "swap" not correct
return func (w http.ResponseWriter, r *gitRequest) { r := &gitRequest{
Request: reqDownloadAccess,
u: u,
} }
// downloadOk := false
preAuthorizeHandler(
func(w http.ResponseWriter, r *gitRequest) {
// if we ever get to this point - auth handler approved
// access and thus it is ok to download
// downloadOk = true
}, "") (authReply.w, r)
return authReply
} }
// authorization info, as replied by authBackend for a request
type authInfo struct { type authInfo struct {
authResponse authorizationResponse authReply backendAuthReply
// XXX no need?
Tauth int64 // in seconds Tauth int64 // in seconds
Naccess int64 Naccess int64
} }
// project -> authInfo // {} project -> authInfo
// FIXME it have to be not only project (privateToken etc...) // FIXME should be not only project (privateToken etc...)
var authCache = make(map[string]authInfo) var authCache = make(map[string]*authInfo)
const authCacheRefresh = 30 // in seconds const authCacheRefresh = 10 * time.Second // XXX -> 30
// refresh cache entry periodically while it is used // refresh auth cache entry periodically while it is used
// if the entry is detected to be not used - remove it from cache and stop rereshing // if the entry is detected to be not used - remove it from cache and stop rereshing
func authRefresh(u *upstream, project string) { func authRefresh(u *upstream, project string) {
for ;; { for ;; {
log.Printf("AUTH refresh sleep ...")
time.Sleep(authCacheRefresh) time.Sleep(authCacheRefresh)
// XXX lock? // XXX lock?
auth, ok := authCache[project] auth, ok := authCache[project]
if !ok { // someone removed the entry from cache - no if !ok { // someone removed the entry from cache - no
log.Printf("AUTH refresh - %v entry removed", project)
break // need to further refresh XXX ok? break // need to further refresh XXX ok?
} }
log.Printf("AUTH refresh - %v Naccess: %v", project, auth.Naccess)
if auth.Naccess == 0 { // not used - we can remove and stop refreshing if auth.Naccess == 0 { // not used - we can remove and stop refreshing
// XXX lock?
delete(authCache, project) delete(authCache, project)
break break
} }
askAuthBackend(u, project) log.Printf("AUTH - refreshing %v", project)
} authReply := askAuthBackend(u, project)
}
// ask auth backend whether download is ok for project
func askAuthBackend(u *upstream, project string) authorizationResponse {
// request to verify whether download is possible via asking as git fetch would do
// XXX privateToken not propagated, etc ...
reqDownloadAccess, err := http.NewRequest("GET", project + ".git/info/refs?service=git-upload-pack", nil)
if err != nil {
fail500(w, "GET git-upload-pack", err)
return false // XXX not cache as it is just we cannot create request
}
// swap original request to 'verify-download' one // XXX lock ?
//requestBlob := r.Request auth.authReply = authReply
r := &gitRequest{ auth.Tauth = time.Now().Unix()
Request: reqDownloadAccess, auth.Naccess = 0
u: u,
} }
// downloadOk := false
preAuthorizeHandler(
func(w http.ResponseWriter, r *gitRequest) {
// if we ever get to this point - auth handler approved
// access and thus it is ok to download
// downloadOk = true
}, "") (w, r)
return r.authorizationResponse
} }
...@@ -99,18 +121,21 @@ func verifyDownloadAccess(w http.ResponseWriter, r *gitRequest, project string) ...@@ -99,18 +121,21 @@ func verifyDownloadAccess(w http.ResponseWriter, r *gitRequest, project string)
// XXX do we need mutex to lock authCache ? // XXX do we need mutex to lock authCache ?
auth, ok := authCache[project] auth, ok := authCache[project]
if ok { if ok {
log.Printf("downloadOk cached %v ago: %v", auth.Naccess++
log.Printf("authReply cached %v ago: %v (hits: %v)",
time.Since(time.Unix(auth.Tauth, 0)), time.Since(time.Unix(auth.Tauth, 0)),
auth.authResponse) auth.authReply.authorizationResponse,
r.authorizationResponse = auth.authResponse auth.Naccess)
return (auth.authResponse.RepoPath != "") // XXX ok? r.authorizationResponse = auth.authReply.authorizationResponse
return (auth.authReply.RepoPath != "") // XXX ok?
} }
r.authorizationResponse = askAuthBackend(r.u, project) authReply := askAuthBackend(r.u, project)
// XXX do we need to lock authCache ? // XXX do we need to lock authCache ?
authCache[project] = authInfo{r.authorizationResponse, time.Now().Unix()} authCache[project] = &authInfo{authReply, time.Now().Unix(), 0}
return downloadOk go authRefresh(r.u, project)
return (authReply.RepoPath != "")
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment