Node wizard: no Yandex sign-in for the node

The apps no longer hand a node a Yandex sign-in (the wizard takes a link
to your own document), so the whole path goes:

- node-install.sh: cookies= and set-cookies (write_cookies); a node's
  own cookie store (CookieStore in node.conf, the transports' state)
  stays.
- provision: Channel.Cookies, CookieStore, ChannelCookies, SetCookies;
  Plan takes the channel only.
- --node-wizard: withCookies, cookies, setCookies, signedIn.
- mobile: NodePlan/NodeApply lose withCookies/cookieHeader;
  NodeSetCookies and NodeSignedIn are removed.

Apps already out pin their own script and bundle their own core, so
they are not affected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
p1neappleXpress
2026-09-29 21:45:06 +03:00
co-authored by Claude Opus 5.5
parent a691a5fe54
commit b6b0e3c838
8 changed files with 24 additions and 227 deletions
+4 -49
View File
@@ -31,19 +31,14 @@
# script installed)
# node-install.sh upgrade (run as root: move every
# channel to this core)
# node-install.sh set-cookies CONFIG_FILE (run as root: replace a
# channel's Yandex login)
# node-install.sh update (run as root, by the timer:
# move every channel to the
# newest node-v* release)
# node-install.sh autoupdate on|off (run as root: the updater)
# The config is "key=value" lines: channel, key, port, the transports
# (vyandex= or its old name url=: a Yandex document; mailru=: a Mail.ru
# public document; cupsonline=: the packed room list), autoupdate=yes|no,
# and optionally cookies: the channel's Yandex sign-in as the core's cookie store JSON,
# base64-encoded. It goes to /var/lib/openflux-node/<channel>/cookies.json
# (0600, owned by the node user) and is never printed. apply and remove
# take it from a 0600 temp file, which they delete after reading, so that
# public document; cupsonline=: the packed room list) and autoupdate=yes|no.
# apply and remove take it from a 0600 temp file, which they delete after reading, so that
# stdin stays free for `sudo -S` (a wrong sudo password would otherwise make
# sudo read the config as further password attempts). Secrets never appear
# in arguments, so they stay out of ps and shell history.
@@ -261,7 +256,7 @@ pick_port() {
# ---- input ------------------------------------------------------------------
CHANNEL=""; URL=""; MAILRU=""; CUPS=""; KEY=""; PORT=""; COOKIES=""; AUTOUPDATE=""
CHANNEL=""; URL=""; MAILRU=""; CUPS=""; KEY=""; PORT=""; AUTOUPDATE=""
# read_config [FILE]: reads stdin, or FILE and then deletes it.
read_config() {
@@ -280,7 +275,6 @@ read_config() {
cupsonline=*) CUPS=${line#cupsonline=} ;;
key=*) KEY=${line#key=} ;;
port=*) PORT=${line#port=} ;;
cookies=*) COOKIES=${line#cookies=} ;;
autoupdate=*) AUTOUPDATE=${line#autoupdate=} ;;
"") ;;
*) fail input "неизвестная строка конфигурации" ;;
@@ -307,7 +301,6 @@ check_transports() {
[ -z "$URL" ] || valid_url "$URL" || fail input "адрес документа должен быть вида https://docs.yandex.ru/edit/d/..."
[ -z "$MAILRU" ] || valid_mailru "$MAILRU" || fail input "ссылка Mail.ru должна быть вида https://cloud.mail.ru/public/..."
[ -z "$CUPS" ] || valid_rooms "$CUPS" || fail input "неверный список комнат cups.online"
[ -z "$COOKIES" ] || [ -n "$URL" ] || fail input "вход в Яндекс нужен только каналу с документом Яндекса"
case "$AUTOUPDATE" in ""|yes|no) ;; *) fail input "autoupdate: yes или no" ;; esac
}
@@ -327,26 +320,6 @@ session_context() {
if [ -n "$URL" ]; then printf '%s' "$URL"; elif [ -n "$MAILRU" ]; then printf '%s' "$MAILRU"; fi
}
# write_cookies: decodes COOKIES into the channel's cookie store, which the
# node loads at start. Needs the node user to exist.
write_cookies() {
printf '%s' "$COOKIES" | grep -Eq '^[A-Za-z0-9+/]+={0,2}$' || return 1
[ ${#COOKIES} -le 65536 ] || return 1
dir="$STATE_ROOT/$CHANNEL"
# umask 077 would make the parent 0700 and lock the node user out of
# its own state directory (systemd only creates it when missing).
mkdir -p "$STATE_ROOT" && chmod 0755 "$STATE_ROOT" || return 1
mkdir -p "$dir" || return 1
tmp="$dir/.cookies.json.new"
if have base64; then
printf '%s' "$COOKIES" | base64 -d > "$tmp" 2>/dev/null || { rm -f "$tmp"; return 1; }
else
printf '%s' "$COOKIES" | openssl base64 -d -A > "$tmp" 2>/dev/null || { rm -f "$tmp"; return 1; }
fi
head -c 1 "$tmp" | grep -q '{' || { rm -f "$tmp"; return 1; }
chown "$NODE_USER:$NODE_USER" "$dir" "$tmp" && chmod 0600 "$tmp" && mv -f "$tmp" "$dir/cookies.json"
}
check_channel() {
[ -n "$CHANNEL" ] || fail input "не указан канал"
valid_channel "$CHANNEL" || fail input "имя канала: только a-z, 0-9 и дефис, до 31 символа"
@@ -400,7 +373,6 @@ cmd_plan() {
set -- "$@" "Скачать ядро OpenFlux $core (linux-$arch) из релизов $RELEASE_REPO на GitHub и сверить SHA-256 в $BIN_DIR"
fi
set -- "$@" "Создать $CONF_ROOT/$CHANNEL: node.conf и ключ шифрования канала (права 0640)"
[ -n "$COOKIES" ] && set -- "$@" "Сохранить вход в Яндекс для этого канала в $STATE_ROOT/$CHANNEL/cookies.json (права 0600, только для ноды)"
[ -f "$UNIT_FILE" ] || set -- "$@" "Установить шаблон systemd $UNIT_FILE"
names=$(transport_names)
if [ -n "$names" ]; then
@@ -610,7 +582,6 @@ cmd_apply() {
valid_key "$KEY" || fail input "ключ канала должен быть 64 hex-символа"
check_transports
valid_port "$PORT" || fail input "не указан порт из плана"
[ -z "$COOKIES" ] || printf '%s' "$COOKIES" | grep -Eq '^[A-Za-z0-9+/]+={0,2}$' || fail input "cookies должны быть в base64"
arch=$(detect_arch)
[ -n "$arch" ] || fail apply "архитектура $(uname -m) не поддерживается"
[ -d "$CONF_ROOT/$CHANNEL" ] && fail apply "канал $CHANNEL уже существует на сервере"
@@ -649,9 +620,6 @@ cmd_apply() {
# The node's state directory (systemd before 235 does not make it).
mkdir -p "$STATE_ROOT/$CHANNEL" && chmod 0755 "$STATE_ROOT" && chown "$NODE_USER:$NODE_USER" "$STATE_ROOT/$CHANNEL" \
&& chmod 0750 "$STATE_ROOT/$CHANNEL" || apply_fail config "не удалось создать $STATE_ROOT/$CHANNEL"
if [ -n "$COOKIES" ]; then
write_cookies || apply_fail cookies "не удалось сохранить вход в Яндекс на сервере"
fi
if [ ! -f "$UNIT_FILE" ]; then
write_unit
@@ -901,18 +869,6 @@ cmd_update() {
"$latest" "$(json_escape "${prev#openflux-}")" "$(json_list "$@")"
}
# set-cookies: replaces a channel's Yandex sign-in and restarts it.
cmd_set_cookies() {
[ "$(id -u)" = 0 ] || fail set-cookies "нужны права root (sudo)"
read_config "$@"
check_channel
[ -d "$CONF_ROOT/$CHANNEL" ] || fail set-cookies "канала $CHANNEL нет на сервере"
[ -n "$COOKIES" ] || fail set-cookies "нет cookies"
write_cookies || fail set-cookies "не удалось сохранить вход в Яндекс на сервере"
systemctl restart "openflux-node@$CHANNEL" || fail set-cookies "не удалось перезапустить openflux-node@$CHANNEL"
printf '{"ok":true,"channel":"%s"}\n' "$CHANNEL"
}
# autoupdate on|off: turns the core updater on or off for the whole server,
# e.g. on channels installed before the wizard offered it.
cmd_autoupdate() {
@@ -947,8 +903,7 @@ case "${1:-}" in
list) cmd_list ;;
status) cmd_status ;;
upgrade) cmd_upgrade ;;
set-cookies) shift; cmd_set_cookies "$@" ;;
update) cmd_update ;;
autoupdate) shift; cmd_autoupdate "$@" ;;
*) fail usage "usage: node-install.sh probe|plan|apply|remove|list|uninstall|status|upgrade|set-cookies|update|autoupdate" ;;
*) fail usage "usage: node-install.sh probe|plan|apply|remove|list|uninstall|status|upgrade|update|autoupdate" ;;
esac
+5 -37
View File
@@ -144,9 +144,9 @@ func nodeTransports(transportsJSON string) ([]provision.ChannelTransport, error)
}
// NodePlan asks the VDS what installing the channel would change: {"plan"}.
// transportsJSON: the carriers (see nodeTransports); withCookies: the Yandex
// sign-in goes to the node too; autoUpdate: the server's core updater.
func NodePlan(channel string, port int, transportsJSON string, withCookies, autoUpdate bool) string {
// transportsJSON: the carriers (see nodeTransports); autoUpdate: the
// server's core updater.
func NodePlan(channel string, port int, transportsJSON string, autoUpdate bool) string {
conn, err := nodeConn()
if err != nil {
return failure(err, nil)
@@ -155,7 +155,7 @@ func NodePlan(channel string, port int, transportsJSON string, withCookies, auto
if err != nil {
return failure(err, nil)
}
plan, err := conn.Plan(provision.Channel{ID: channel, Port: port, Transports: ts, AutoUpdate: autoUpdate}, withCookies)
plan, err := conn.Plan(provision.Channel{ID: channel, Port: port, Transports: ts, AutoUpdate: autoUpdate})
if err != nil {
return failure(err, nil)
}
@@ -164,10 +164,7 @@ func NodePlan(channel string, port int, transportsJSON string, withCookies, auto
// NodeApply installs and starts the channel. sudoPassword is only used when
// the account needs one; a wrong one comes back with "sudo": true.
// cookieHeader is the Yandex sign-in for the node ("" for none): the node
// then opens the Yandex document as that account, which gets it past the
// checks Yandex shows a server's address.
func NodeApply(channel, transportsJSON, key string, port int, autoUpdate bool, sudoPassword, cookieHeader string) string {
func NodeApply(channel, transportsJSON, key string, port int, autoUpdate bool, sudoPassword string) string {
conn, err := nodeConn()
if err != nil {
return failure(err, nil)
@@ -177,11 +174,6 @@ func NodeApply(channel, transportsJSON, key string, port int, autoUpdate bool, s
return failure(err, nil)
}
ch := provision.Channel{ID: channel, Transports: ts, Key: key, Port: port, AutoUpdate: autoUpdate}
if cookieHeader != "" {
if ch.Cookies, err = provision.ChannelCookies(ts, cookieHeader); err != nil {
return failure(err, nil)
}
}
appendLog("[NODE] Установка канала " + channel)
if err := conn.Apply(ch, sudoPassword); err != nil {
appendLog("[NODE] Установка не удалась")
@@ -191,30 +183,6 @@ func NodeApply(channel, transportsJSON, key string, port int, autoUpdate bool, s
return result(nil)
}
// NodeSetCookies gives an installed channel's node a new Yandex sign-in
// and restarts it.
func NodeSetCookies(channel, documentURL, cookieHeader, sudoPassword string) string {
conn, err := nodeConn()
if err != nil {
return failure(err, nil)
}
cookies, _, err := provision.CookieStore(documentURL, cookieHeader)
if err != nil {
return failure(err, nil)
}
if err := conn.SetCookies(channel, cookies, sudoPassword); err != nil {
return failure(err, map[string]interface{}{"sudo": errors.Is(err, provision.ErrSudoPassword)})
}
appendLog("[NODE] Вход в Яндекс передан каналу " + channel)
return result(nil)
}
// NodeSignedIn reports whether a WebView Cookie header holds a Yandex login.
func NodeSignedIn(cookieHeader string) bool {
_, signedIn, err := provision.CookieStore("x", cookieHeader)
return err == nil && signedIn
}
// NodeRemove deletes the channel from the VDS (a failed verification's
// cleanup).
func NodeRemove(channel, sudoPassword string) string {
+4 -27
View File
@@ -11,8 +11,8 @@ package main
// Request: {"id": 1, "method": "connect", "params": {...}}
// Response: {"id": 1, "ok": true, ...} or {"id": 1, "ok": false, "error": "..."}
//
// Secrets (SSH and sudo passwords, private key, channel key, Yandex
// cookies) arrive only on stdin and never go to the log or the command line.
// Secrets (SSH and sudo passwords, private key, channel key) arrive only
// on stdin and never go to the log or the command line.
import (
"bufio"
@@ -45,11 +45,9 @@ type wizardParams struct {
HostKey string `json:"hostKey"`
Channel string `json:"channel"`
ChannelPort int `json:"channelPort"`
WithCookies bool `json:"withCookies"`
DocumentURL string `json:"documentUrl"`
Key string `json:"key"`
SudoPassword string `json:"sudoPassword"`
Cookies string `json:"cookies"`
Name string `json:"name"`
// Transports are the channel's carriers besides direct. Without them,
// DocumentURL alone means a Yandex document (older apps).
@@ -65,7 +63,7 @@ func (p wizardParams) transports() []provision.ChannelTransport {
return p.Transports
}
// channel is the channel the request describes, key and cookies aside.
// channel is the channel the request describes, its key aside.
func (p wizardParams) channel() provision.Channel {
return provision.Channel{ID: p.Channel, Transports: p.transports(), Port: p.ChannelPort, AutoUpdate: p.AutoUpdate}
}
@@ -160,7 +158,7 @@ func (w *nodeWizard) handle(req wizardRequest) map[string]interface{} {
if err != nil {
return wizardFailure(err, nil)
}
plan, err := conn.Plan(p.channel(), p.WithCookies)
plan, err := conn.Plan(p.channel())
if err != nil {
return wizardFailure(err, nil)
}
@@ -172,28 +170,10 @@ func (w *nodeWizard) handle(req wizardRequest) map[string]interface{} {
}
ch := p.channel()
ch.Key = p.Key
if p.Cookies != "" {
if ch.Cookies, err = provision.ChannelCookies(ch.Transports, p.Cookies); err != nil {
return wizardFailure(err, nil)
}
}
if err := conn.Apply(ch, p.SudoPassword); err != nil {
return wizardFailure(err, map[string]interface{}{"sudo": errors.Is(err, provision.ErrSudoPassword)})
}
return wizardOK(nil)
case "setCookies":
conn, err := w.connected()
if err != nil {
return wizardFailure(err, nil)
}
cookies, _, err := provision.CookieStore(p.DocumentURL, p.Cookies)
if err != nil {
return wizardFailure(err, nil)
}
if err := conn.SetCookies(p.Channel, cookies, p.SudoPassword); err != nil {
return wizardFailure(err, map[string]interface{}{"sudo": errors.Is(err, provision.ErrSudoPassword)})
}
return wizardOK(nil)
case "remove":
conn, err := w.connected()
if err != nil {
@@ -203,9 +183,6 @@ func (w *nodeWizard) handle(req wizardRequest) map[string]interface{} {
return wizardFailure(err, map[string]interface{}{"sudo": errors.Is(err, provision.ErrSudoPassword)})
}
return wizardOK(nil)
case "signedIn":
_, signedIn, err := provision.CookieStore("x", p.Cookies)
return wizardOK(map[string]interface{}{"signedIn": err == nil && signedIn})
case "checkDocument":
return w.checkDocument(p.DocumentURL)
case "createRooms":
+1 -7
View File
@@ -55,7 +55,7 @@ func TestNodeWizardProtocolLines(t *testing.T) {
func TestNodeWizardNeedsConnection(t *testing.T) {
w := newNodeWizard()
for _, m := range []string{"plan", "apply", "setCookies", "remove"} {
for _, m := range []string{"plan", "apply", "remove"} {
r := wizardCall(t, w, m, map[string]string{"channel": "c1"})
if r["ok"] != false || r["error"] != "нет подключения к серверу" {
t.Fatalf("%s: %v", m, r)
@@ -137,12 +137,6 @@ func TestNodeWizardShareLinkAndSignIn(t *testing.T) {
if r := wizardCall(t, w, "shareLink", wizardParams{DocumentURL: doc, Key: key}); r["ok"] != false {
t.Fatalf("no host: %v", r)
}
if r := wizardCall(t, w, "signedIn", wizardParams{Cookies: "yandexuid=1; Session_id=s"}); r["signedIn"] != true {
t.Fatalf("signed in: %v", r)
}
if r := wizardCall(t, w, "signedIn", wizardParams{Cookies: "yandexuid=1"}); r["signedIn"] != false {
t.Fatalf("anonymous: %v", r)
}
}
func TestNodeWizardShareLinkWithChosenTransports(t *testing.T) {
+8 -28
View File
@@ -134,7 +134,7 @@ func TestInstallOnVDS(t *testing.T) {
{Type: "mailru", URL: "https://cloud.mail.ru/public/AbCd/EfGhIjKlM"},
{Type: "cupsonline", URL: "WyJyb29tLTEiLCJyb29tLTIiXQ"},
}
plan, err := c.Plan(Channel{ID: id, Transports: volga, AutoUpdate: true}, true)
plan, err := c.Plan(Channel{ID: id, Transports: volga, AutoUpdate: true})
if err != nil {
t.Fatal(err)
}
@@ -147,11 +147,7 @@ func TestInstallOnVDS(t *testing.T) {
t.Fatalf("plan actions: %s", actions)
}
key, _ := NewKey()
cookies, signedIn, err := CookieStore(os.Getenv("OPENFLUX_TEST_DOC"), "Session_id=test-login-value; spravka=pass")
if err != nil || !signedIn {
t.Fatalf("CookieStore: %v %v", signedIn, err)
}
ch := Channel{ID: id, Transports: volga, Key: key, Port: plan.Port, Cookies: cookies, AutoUpdate: true}
ch := Channel{ID: id, Transports: volga, Key: key, Port: plan.Port, AutoUpdate: true}
if err := c.Apply(ch, "wrong-password"); !errors.Is(err, ErrSudoPassword) {
t.Fatalf("wrong sudo password: got %v", err)
@@ -174,30 +170,14 @@ func TestInstallOnVDS(t *testing.T) {
if out, _, _ := c.run("ps -eo args", nil); strings.Contains(string(out), key) {
t.Fatal("channel key visible in the process list")
}
if out, _, _ := c.run("stat -c '%a %U' /var/lib/openflux-node/"+id+"/cookies.json", nil); strings.TrimSpace(string(out)) != "600 openflux-node" {
t.Fatalf("cookies.json: %q", out)
}
if out, _, _ := c.run("sudo -S -p '' cat /var/lib/openflux-node/"+id+"/cookies.json", []byte(userPass+"\n")); !strings.Contains(string(out), "test-login-value") {
t.Fatalf("cookies.json content: %q", out)
}
if out, _, _ := c.run("ps -eo args; sudo -S -p '' journalctl -u openflux-node@"+id+" --no-pager", []byte(userPass+"\n")); strings.Contains(string(out), "test-login-value") {
t.Fatal("the Yandex login leaked into ps or the node's log")
}
fresh, _, _ := CookieStore(os.Getenv("OPENFLUX_TEST_DOC"), "Session_id=renewed-login")
if err := c.SetCookies(id, fresh, userPass); err != nil {
t.Fatal(err)
}
if out, _, _ := c.run("sudo -S -p '' cat /var/lib/openflux-node/"+id+"/cookies.json", []byte(userPass+"\n")); !strings.Contains(string(out), "renewed-login") {
t.Fatalf("set-cookies did not replace the login: %q", out)
}
if _, err := c.Plan(Channel{ID: id, Transports: volga}, true); err == nil {
if _, err := c.Plan(Channel{ID: id, Transports: volga}); err == nil {
t.Fatal("planning an existing channel must fail")
}
again, err := c.Plan(Channel{ID: "other", Port: plan.Port}, false)
again, err := c.Plan(Channel{ID: "other", Port: plan.Port})
if err == nil {
t.Fatalf("the channel's port must count as taken: %+v", again)
}
next, err := c.Plan(Channel{ID: "other"}, false)
next, err := c.Plan(Channel{ID: "other"})
if err != nil {
t.Fatal(err)
}
@@ -265,7 +245,7 @@ func TestInstallOnVDS(t *testing.T) {
t.Fatalf("autoupdate on: %s", out)
}
// An app with an older pinned script must not take the server back.
older, err := c.Plan(Channel{ID: "other"}, false)
older, err := c.Plan(Channel{ID: "other"})
if err != nil || older.Core != "node-v1.1.0" {
t.Fatalf("plan after an update: %+v %v", older, err)
}
@@ -275,7 +255,7 @@ func TestInstallOnVDS(t *testing.T) {
if err := c.FetchScript(Script{URL: base + "/other-install.sh", SHA256: ScriptHash(otherRepo)}); err != nil {
t.Fatal(err)
}
switched, err := c.Plan(Channel{ID: "other"}, false)
switched, err := c.Plan(Channel{ID: "other"})
if err != nil || switched.Core != "node-v1.0.1" || !strings.Contains(strings.Join(switched.Actions, "\n"), "someone/OpenFlux") {
t.Fatalf("plan from another repository: %+v %v", switched, err)
}
@@ -296,7 +276,7 @@ func TestInstallOnVDS(t *testing.T) {
t.Fatal(err)
}
for _, name := range []string{"first", "second"} {
p, err := c.Plan(Channel{ID: name, AutoUpdate: true}, false)
p, err := c.Plan(Channel{ID: name, AutoUpdate: true})
if err != nil {
t.Fatal(err)
}
+2 -42
View File
@@ -14,7 +14,6 @@ import (
"context"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
@@ -106,9 +105,6 @@ type Channel struct {
Key string
// Port is direct's port; 0 in a plan lets the script pick one.
Port int
// Cookies is the channel's Yandex sign-in for the node: the core's
// cookie store JSON, base64-encoded (see CookieStore). Empty: none.
Cookies string
// AutoUpdate turns the server's core updater on, or off: it is one
// timer for every channel on the server.
AutoUpdate bool
@@ -299,15 +295,12 @@ func (c *Conn) Probe() (*Probe, error) {
}
// Plan asks what apply would change: ch without its key, Port 0 lets the
// script pick one. withCookies adds the Yandex sign-in step.
func (c *Conn) Plan(ch Channel, withCookies bool) (*Plan, error) {
// script pick one.
func (c *Conn) Plan(ch Channel) (*Plan, error) {
cfg, err := ch.config()
if err != nil {
return nil, err
}
if withCookies {
cfg += "cookies=yes\n"
}
var p Plan
if err := c.script_("plan", []byte(cfg), &p); err != nil {
return nil, err
@@ -326,42 +319,9 @@ func (c *Conn) Apply(ch Channel, sudoPassword string) error {
return err
}
cfg += "key=" + ch.Key + "\n"
if ch.Cookies != "" {
cfg += "cookies=" + ch.Cookies + "\n"
}
return c.asRoot("apply", cfg, sudoPassword)
}
// SetCookies replaces an installed channel's Yandex sign-in (base64 cookie
// store JSON, see CookieStore) and restarts its node.
func (c *Conn) SetCookies(channel, cookies, sudoPassword string) error {
return c.asRoot("set-cookies", "channel="+channel+"\ncookies="+cookies+"\n", sudoPassword)
}
// CookieStore turns a Cookie header ("a=1; b=2", as the WebView keeps it
// for the document) into what Channel.Cookies takes: the core's cookie
// store JSON, keyed by the document URL like the node looks it up, then
// base64. signedIn reports whether the header holds a Yandex login.
func CookieStore(documentURL, header string) (cookies string, signedIn bool, err error) {
jar := map[string]string{}
for _, part := range strings.Split(header, ";") {
name, value, ok := strings.Cut(strings.TrimSpace(part), "=")
if !ok || name == "" || strings.ContainsAny(name+value, "\r\n") {
continue
}
jar[name] = value
}
if len(jar) == 0 {
return "", false, errors.New("нет cookies Яндекса")
}
raw, err := json.Marshal(map[string]map[string]string{documentURL: jar})
if err != nil {
return "", false, err
}
_, signedIn = jar["Session_id"]
return base64.StdEncoding.EncodeToString(raw), signedIn, nil
}
// Remove stops and deletes the channel; the last one also removes the
// binaries, unit and system user the script installed.
func (c *Conn) Remove(channel, sudoPassword string) error {
-21
View File
@@ -1,8 +1,6 @@
package provision
import (
"encoding/base64"
"encoding/json"
"os"
"regexp"
"testing"
@@ -66,22 +64,3 @@ func TestSudoRefusal(t *testing.T) {
t.Fatal("unexpected refusal")
}
}
func TestCookieStoreFormat(t *testing.T) {
b64, signedIn, err := CookieStore("https://docs.yandex.ru/edit/d/x", " Session_id=a=b ; yandexuid=1;bad;\nx=y")
if err != nil || !signedIn {
t.Fatalf("%v %v", signedIn, err)
}
raw, _ := base64.StdEncoding.DecodeString(b64)
var store map[string]map[string]string
if err := json.Unmarshal(raw, &store); err != nil {
t.Fatal(err)
}
jar := store["https://docs.yandex.ru/edit/d/x"]
if jar["Session_id"] != "a=b" || jar["yandexuid"] != "1" || len(jar) != 3 {
t.Fatalf("jar = %v", jar)
}
if _, signedIn, _ := CookieStore("u", "yandexuid=1"); signedIn {
t.Fatal("no Session_id means not signed in")
}
}
-16
View File
@@ -133,19 +133,3 @@ func transportLines(ts []ChannelTransport) string {
}
return b.String()
}
// ChannelCookies turns the app's Cookie header for the channel's Yandex
// document into what Channel.Cookies takes (see CookieStore), keyed by the
// document as the node's config names it.
func ChannelCookies(ts []ChannelTransport, header string) (string, error) {
ts, err := CheckTransports(ts)
if err != nil {
return "", err
}
doc := TransportURL(ts, "vyandex")
if doc == "" {
return "", errors.New("вход в Яндекс нужен только каналу с документом Яндекса")
}
cookies, _, err := CookieStore(doc, header)
return cookies, err
}