mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-26 03:25:35 +00:00
0c3d343ea3
Add separate server methods for synology and qnap, and enforce authentication and authorization checks before calling into the actual serving handlers. This allows us to remove all of the auth logic from those handlers, since all requests will already be authenticated by that point. Also simplify the Synology token redirect handler by using fetch. Remove the SynologyUser from nodeData, since it was never used in the frontend anyway. Updates tailscale/corp#13775 Signed-off-by: Will Norris <will@tailscale.com>
77 lines
1.9 KiB
Go
77 lines
1.9 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// synology.go contains handlers and logic, such as authentication,
|
|
// that is specific to running the web client on Synology.
|
|
|
|
package web
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"tailscale.com/util/groupmember"
|
|
)
|
|
|
|
// authorizeSynology authenticates the logged-in Synology user and verifies
|
|
// that they are authorized to use the web client. It returns true if the
|
|
// request was handled and no further processing is required.
|
|
func authorizeSynology(w http.ResponseWriter, r *http.Request) (handled bool) {
|
|
if synoTokenRedirect(w, r) {
|
|
return true
|
|
}
|
|
|
|
// authenticate the Synology user
|
|
cmd := exec.Command("/usr/syno/synoman/webman/modules/authenticate.cgi")
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
http.Error(w, fmt.Sprintf("auth: %v: %s", err, out), http.StatusUnauthorized)
|
|
return true
|
|
}
|
|
user := strings.TrimSpace(string(out))
|
|
|
|
// check if the user is in the administrators group
|
|
isAdmin, err := groupmember.IsMemberOfGroup("administrators", user)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusForbidden)
|
|
return true
|
|
}
|
|
if !isAdmin {
|
|
http.Error(w, "not a member of administrators group", http.StatusForbidden)
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func synoTokenRedirect(w http.ResponseWriter, r *http.Request) bool {
|
|
if r.Header.Get("X-Syno-Token") != "" {
|
|
return false
|
|
}
|
|
if r.URL.Query().Get("SynoToken") != "" {
|
|
return false
|
|
}
|
|
if r.Method == "POST" && r.FormValue("SynoToken") != "" {
|
|
return false
|
|
}
|
|
// We need a SynoToken for authenticate.cgi.
|
|
// So we tell the client to get one.
|
|
_, _ = fmt.Fprint(w, synoTokenRedirectHTML)
|
|
return true
|
|
}
|
|
|
|
const synoTokenRedirectHTML = `<html>
|
|
Redirecting with session token...
|
|
<script>
|
|
fetch("/webman/login.cgi")
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
u = new URL(window.location)
|
|
u.searchParams.set("SynoToken", data.SynoToken)
|
|
document.location = u
|
|
})
|
|
</script>
|
|
`
|