@ -178,10 +178,15 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
func ( s * Server ) serve ( w http . ResponseWriter , r * http . Request ) {
func ( s * Server ) serve ( w http . ResponseWriter , r * http . Request ) {
if ok := s . authorizeRequest ( w , r ) ; ! ok {
return
}
if strings . HasPrefix ( r . URL . Path , "/api/" ) {
if strings . HasPrefix ( r . URL . Path , "/api/" ) {
if r . Method == httpm . GET && r . URL . Path == "/api/auth" {
s . serveAPIAuth ( w , r )
return
}
if ok := s . authorizeRequest ( w , r ) ; ! ok {
http . Error ( w , "not authorized" , http . StatusUnauthorized )
return
}
// Pass API requests through to the API handler.
// Pass API requests through to the API handler.
s . apiHandler . ServeHTTP ( w , r )
s . apiHandler . ServeHTTP ( w , r )
return
return
@ -208,9 +213,6 @@ func (s *Server) authorizeRequest(w http.ResponseWriter, r *http.Request) (ok bo
case r . URL . Path == "/api/data" && r . Method == httpm . GET :
case r . URL . Path == "/api/data" && r . Method == httpm . GET :
// Readonly endpoint allowed without browser session.
// Readonly endpoint allowed without browser session.
return true
return true
case r . URL . Path == "/api/auth" :
// Endpoint for browser to request auth allowed without browser session.
return true
case strings . HasPrefix ( r . URL . Path , "/api/" ) :
case strings . HasPrefix ( r . URL . Path , "/api/" ) :
// All other /api/ endpoints require a valid browser session.
// All other /api/ endpoints require a valid browser session.
//
//
@ -229,15 +231,13 @@ func (s *Server) authorizeRequest(w http.ResponseWriter, r *http.Request) (ok bo
}
}
}
}
// Client using system-specific auth.
// Client using system-specific auth.
d := distro . Get ( )
switch distro . Get ( ) {
switch {
case distro . Synology :
case strings . HasPrefix ( r . URL . Path , "/assets/" ) && r . Method == httpm . GET :
resp , _ := authorizeSynology ( r )
// Don't require authorization for static assets.
return resp . OK
return true
case distro . QNAP :
case d == distro . Synology :
resp , _ := authorizeQNAP ( r )
return authorizeSynology ( w , r )
return resp . OK
case d == distro . QNAP :
return authorizeQNAP ( w , r )
default :
default :
return true // no additional auth for this distro
return true // no additional auth for this distro
}
}
@ -252,11 +252,6 @@ func (s *Server) serveLoginAPI(w http.ResponseWriter, r *http.Request) {
http . Error ( w , "invalid endpoint" , http . StatusNotFound )
http . Error ( w , "invalid endpoint" , http . StatusNotFound )
return
return
}
}
if r . URL . Path != "/api/auth" {
// empty JSON response until we serve auth for the login client
fmt . Fprintf ( w , "{}" )
return
}
switch r . Method {
switch r . Method {
case httpm . GET :
case httpm . GET :
// TODO(soniaappasamy): we may want a minimal node data response here
// TODO(soniaappasamy): we may want a minimal node data response here
@ -282,7 +277,9 @@ type authResponse struct {
AuthNeeded authType ` json:"authNeeded,omitempty" ` // filled when user needs to complete a specific type of auth
AuthNeeded authType ` json:"authNeeded,omitempty" ` // filled when user needs to complete a specific type of auth
}
}
func ( s * Server ) serveTailscaleAuth ( w http . ResponseWriter , r * http . Request ) {
// serverAPIAuth handles requests to the /api/auth endpoint
// and returns an authResponse indicating the current auth state and any steps the user needs to take.
func ( s * Server ) serveAPIAuth ( w http . ResponseWriter , r * http . Request ) {
if r . Method != httpm . GET {
if r . Method != httpm . GET {
http . Error ( w , "method not allowed" , http . StatusMethodNotAllowed )
http . Error ( w , "method not allowed" , http . StatusMethodNotAllowed )
return
return
@ -291,6 +288,24 @@ func (s *Server) serveTailscaleAuth(w http.ResponseWriter, r *http.Request) {
session , whois , err := s . getSession ( r )
session , whois , err := s . getSession ( r )
switch {
switch {
case err != nil && errors . Is ( err , errNotUsingTailscale ) :
// not using tailscale, so perform platform auth
switch distro . Get ( ) {
case distro . Synology :
resp , err = authorizeSynology ( r )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusUnauthorized )
return
}
case distro . QNAP :
resp , err = authorizeQNAP ( r )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusUnauthorized )
return
}
default :
resp . OK = true // no additional auth for this distro
}
case err != nil && ! errors . Is ( err , errNoSession ) :
case err != nil && ! errors . Is ( err , errNoSession ) :
http . Error ( w , err . Error ( ) , http . StatusUnauthorized )
http . Error ( w , err . Error ( ) , http . StatusUnauthorized )
return
return
@ -341,14 +356,6 @@ func (s *Server) serveAPI(w http.ResponseWriter, r *http.Request) {
w . Header ( ) . Set ( "X-CSRF-Token" , csrf . Token ( r ) )
w . Header ( ) . Set ( "X-CSRF-Token" , csrf . Token ( r ) )
path := strings . TrimPrefix ( r . URL . Path , "/api" )
path := strings . TrimPrefix ( r . URL . Path , "/api" )
switch {
switch {
case path == "/auth" :
if s . tsDebugMode == "full" { // behind debug flag
s . serveTailscaleAuth ( w , r )
} else {
// empty JSON response until we serve auth for other modes
fmt . Fprintf ( w , "{}" )
}
return
case path == "/data" :
case path == "/data" :
switch r . Method {
switch r . Method {
case httpm . GET :
case httpm . GET :