Files

76 lines
2.3 KiB
Go

package api
import (
"net"
"net/http"
"strings"
"time"
)
// requestIdentity belongs to one authorized request, including its confirmation binding.
type requestIdentity struct {
session session
sessionID string
}
// authorize adapts bearer, local-development and cookie authentication to one
// request identity. CSRF validation remains after authentication and scope checks.
func (s *Server) authorize(w http.ResponseWriter, r *http.Request, path string) (http.ResponseWriter, requestIdentity, bool) {
var sess session
cookie := &http.Cookie{Value: "development"}
bearer := r.Header.Get("Authorization") != ""
if bearer {
value := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
credential, err := s.store.VerifyToken(value)
if err != nil {
fail(w, 401, "invalid or expired API token")
return w, requestIdentity{}, false
}
if !tokenPermits(credential, path, r.Method) {
fail(w, 403, "token scope does not permit this operation")
return w, requestIdentity{}, false
}
sess = session{User: credential.Owner, Revision: credential.Revision}
cookie.Value = "token:" + credential.ID
w = tokenResponseWriter{w}
} else if !s.authEnabled {
ip := net.ParseIP(source(r))
host := r.Host
if h, _, e := net.SplitHostPort(host); e == nil {
host = h
}
hostIP := net.ParseIP(host)
if ip == nil || !ip.IsLoopback() || (host != "localhost" && (hostIP == nil || !hostIP.IsLoopback())) {
fail(w, 403, "development mode is local only")
return w, requestIdentity{}, false
}
sess = session{User: "开发者", CSRF: s.devCSRF}
} else {
var err error
cookie, err = r.Cookie("linkport_session")
if err != nil {
fail(w, 401, "login required")
return w, requestIdentity{}, false
}
s.mu.Lock()
var ok bool
sess, ok = s.sessions[cookie.Value]
if time.Now().After(sess.Expires) {
delete(s.sessions, cookie.Value)
ok = false
}
s.mu.Unlock()
rev, err := s.store.PrimaryRevision(sess.User)
if !ok || err != nil || rev != sess.Revision {
fail(w, 401, "session expired")
return w, requestIdentity{}, false
}
}
if !bearer && r.Method != "GET" && r.Header.Get("X-CSRF-Token") != sess.CSRF {
fail(w, 403, "CSRF token required")
return w, requestIdentity{}, false
}
return w, requestIdentity{session: sess, sessionID: cookie.Value}, true
}