package main import ( "encoding/json" "errors" "log" "net/http" "net/url" "path" "regexp" "strings" "time" "github.com/andreimarcu/linx-server/backends" "github.com/flosch/pongo2" "github.com/zenazn/goji/web" ) type accessKeySource int const ( accessKeySourceNone accessKeySource = iota accessKeySourceCookie accessKeySourceHeader accessKeySourceForm accessKeySourceQuery ) const accessKeyHeaderName = "Linx-Access-Key" const accessKeyParamName = "access_key" var ( errInvalidAccessKey = errors.New("invalid access key") cliUserAgentRe = regexp.MustCompile("(?i)(lib)?curl|wget") ) func checkAccessKey(r *http.Request, metadata *backends.Metadata) (accessKeySource, error) { key := metadata.AccessKey if key == "" { return accessKeySourceNone, nil } cookieKey, err := r.Cookie(accessKeyHeaderName) if err == nil { if cookieKey.Value == key { return accessKeySourceCookie, nil } return accessKeySourceCookie, errInvalidAccessKey } headerKey := r.Header.Get(accessKeyHeaderName) if headerKey == key { return accessKeySourceHeader, nil } else if headerKey != "" { return accessKeySourceHeader, errInvalidAccessKey } formKey := r.PostFormValue(accessKeyParamName) if formKey == key { return accessKeySourceForm, nil } else if formKey != "" { return accessKeySourceForm, errInvalidAccessKey } queryKey := r.URL.Query().Get(accessKeyParamName) if queryKey == key { return accessKeySourceQuery, nil } else if formKey != "" { return accessKeySourceQuery, errInvalidAccessKey } return accessKeySourceNone, errInvalidAccessKey } func setAccessKeyCookies(w http.ResponseWriter, siteURL, fileName, value string, expires time.Time) { u, err := url.Parse(siteURL) if err != nil { log.Printf("cant parse siteURL (%v): %v", siteURL, err) return } cookie := http.Cookie{ Name: accessKeyHeaderName, Value: value, HttpOnly: true, Domain: u.Hostname(), Expires: expires, } cookie.Path = path.Join(u.Path, fileName) http.SetCookie(w, &cookie) cookie.Path = path.Join(u.Path, Config.selifPath, fileName) http.SetCookie(w, &cookie) } func fileAccessHandler(c web.C, w http.ResponseWriter, r *http.Request) { if !Config.noDirectAgents && cliUserAgentRe.MatchString(r.Header.Get("User-Agent")) && !strings.EqualFold("application/json", r.Header.Get("Accept")) { fileServeHandler(c, w, r) return } fileName := c.URLParams["name"] metadata, err := checkFile(fileName) if err == backends.NotFoundErr { notFoundHandler(c, w, r) return } else if err != nil { oopsHandler(c, w, r, RespAUTO, "Corrupt metadata.") return } if src, err := checkAccessKey(r, &metadata); err != nil { // remove invalid cookie if src == accessKeySourceCookie { setAccessKeyCookies(w, getSiteURL(r), fileName, "", time.Unix(0, 0)) } if strings.EqualFold("application/json", r.Header.Get("Accept")) { dec := json.NewEncoder(w) _ = dec.Encode(map[string]string{ "error": errInvalidAccessKey.Error(), }) return } _ = renderTemplate(Templates["access.html"], pongo2.Context{ "filename": fileName, "accesspath": fileName, }, r, w) return } if metadata.AccessKey != "" { var expiry time.Time if Config.accessKeyCookieExpiry != 0 { expiry = time.Now().Add(time.Duration(Config.accessKeyCookieExpiry) * time.Second) } setAccessKeyCookies(w, getSiteURL(r), fileName, metadata.AccessKey, expiry) } fileDisplayHandler(c, w, r, fileName, metadata) }