summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMihai Parparita <mihai@tailscale.com>2022-11-09 13:22:32 -0800
committerMihai Parparita <mihai@tailscale.com>2022-11-09 13:22:32 -0800
commitbc361df175385aca3248234ddd90db5f21657961 (patch)
tree95b65ebecc27cae8c1ebe0b92e61ac330d3e59cc
parent42464392ba80e79a82ed3717bbcc43480a45ec3c (diff)
downloadtailscale-mihaip/fas.tar.xz
tailscale-mihaip/fas.zip
ipn/localapi: basic LocalAPI endpoints for profilesmihaip/fas
Signed-off-by: Mihai Parparita <mihai@tailscale.com>
-rw-r--r--ipn/localapi/localapi.go81
1 files changed, 71 insertions, 10 deletions
diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go
index 0a5b5c027..14912d79d 100644
--- a/ipn/localapi/localapi.go
+++ b/ipn/localapi/localapi.go
@@ -71,16 +71,19 @@ var handler = map[string]localAPIHandler{
"metrics": (*Handler).serveMetrics,
"ping": (*Handler).servePing,
"prefs": (*Handler).servePrefs,
- "profile": (*Handler).serveProfile,
- "set-dns": (*Handler).serveSetDNS,
- "set-expiry-sooner": (*Handler).serveSetExpirySooner,
- "status": (*Handler).serveStatus,
- "tka/init": (*Handler).serveTKAInit,
- "tka/modify": (*Handler).serveTKAModify,
- "tka/sign": (*Handler).serveTKASign,
- "tka/status": (*Handler).serveTKAStatus,
- "upload-client-metrics": (*Handler).serveUploadClientMetrics,
- "whois": (*Handler).serveWhoIs,
+ // TODO: rename to debug-profile or pprof-profile to separatate it from the
+ // (user) profile functionality.
+ "profile": (*Handler).serveProfile,
+ "set-dns": (*Handler).serveSetDNS,
+ "set-expiry-sooner": (*Handler).serveSetExpirySooner,
+ "status": (*Handler).serveStatus,
+ "tka/init": (*Handler).serveTKAInit,
+ "tka/modify": (*Handler).serveTKAModify,
+ "tka/sign": (*Handler).serveTKASign,
+ "tka/status": (*Handler).serveTKAStatus,
+ "upload-client-metrics": (*Handler).serveUploadClientMetrics,
+ "whois": (*Handler).serveWhoIs,
+ "profiles": (*Handler).serveProfiles,
}
func randHex(n int) string {
@@ -1037,6 +1040,64 @@ func (h *Handler) serveTKAModify(w http.ResponseWriter, r *http.Request) {
w.Write(j)
}
+func (h *Handler) serveProfiles(w http.ResponseWriter, r *http.Request) {
+ if !h.PermitRead {
+ http.Error(w, "profiles access denied", http.StatusForbidden)
+ return
+ }
+ switch r.Method {
+ case "GET": // List profiles
+ profiles := make([]tailcfg.UserProfile, 0)
+ for i, name := range h.b.ListProfiles() {
+ profiles = append(profiles, tailcfg.UserProfile{
+ ID: tailcfg.UserID(i),
+ LoginName: name,
+ DisplayName: name,
+ ProfilePicURL: "",
+ })
+ }
+ w.Header().Set("Content-Type", "application/json")
+ e := json.NewEncoder(w)
+ e.SetIndent("", "\t")
+ e.Encode(profiles)
+ case "POST": // Switch profile
+ // TODO: it would be more REST-y to get the profile name from the path
+ profile := r.FormValue("profile")
+ if profile == "" {
+ http.Error(w, "missing profile", http.StatusBadRequest)
+ return
+ }
+ err := h.b.SwitchProfile(profile)
+ if err != nil {
+ http.Error(w, err.Error(), 500)
+ return
+ }
+ w.Header().Set("Content-Type", "text/plain")
+ io.WriteString(w, "done\n")
+ case "PUT": // Add profile
+ h.b.NewProfile()
+ w.Header().Set("Content-Type", "text/plain")
+ io.WriteString(w, "done\n")
+ case "DELETE": // Delete profile
+ // TODO: it would be more REST-y to get the profile name from the path
+ profile := r.FormValue("profile")
+ if profile == "" {
+ http.Error(w, "missing profile", http.StatusBadRequest)
+ return
+ }
+ err := h.b.DeleteProfile(profile)
+ if err != nil {
+ http.Error(w, err.Error(), 500)
+ return
+ }
+ w.Header().Set("Content-Type", "text/plain")
+ io.WriteString(w, "done\n")
+ default:
+ http.Error(w, "unsupported method", http.StatusMethodNotAllowed)
+ return
+ }
+}
+
func defBool(a string, def bool) bool {
if a == "" {
return def