From 21bbab88c890baacc56e6c9b22847da7a74303d0 Mon Sep 17 00:00:00 2001 From: sawka Date: Mon, 19 Dec 2022 18:52:08 -0800 Subject: [PATCH] authkey get/generate. new schema for incognito/closed screens/sessions. rename scbase functions to use Prompt name --- cmd/main-server.go | 2 +- db/migrations/000001_init.up.sql | 8 +++-- db/schema.sql | 8 +++-- pkg/cmdrunner/cmdrunner.go | 13 ++++--- pkg/scbase/scbase.go | 62 ++++++++++++++++++++++++++------ pkg/sstore/dbops.go | 29 ++++++++++----- pkg/sstore/sstore.go | 12 +++++-- 7 files changed, 103 insertions(+), 31 deletions(-) diff --git a/cmd/main-server.go b/cmd/main-server.go index 43a6d20c..79309fc5 100644 --- a/cmd/main-server.go +++ b/cmd/main-server.go @@ -403,7 +403,7 @@ func main() { scHomeDir := scbase.GetPromptHomeDir() log.Printf("[prompt] homedir = %q\n", scHomeDir) - scLock, err := scbase.AcquireSCLock() + scLock, err := scbase.AcquirePromptLock() if err != nil || scLock == nil { log.Printf("[error] cannot acquire sh2 lock: %v\n", err) return diff --git a/db/migrations/000001_init.up.sql b/db/migrations/000001_init.up.sql index d4d65ed4..aa003337 100644 --- a/db/migrations/000001_init.up.sql +++ b/db/migrations/000001_init.up.sql @@ -13,6 +13,7 @@ CREATE TABLE session ( sessionidx int NOT NULL, activescreenid varchar(36) NOT NULL, notifynum int NOT NULL, + closed boolean NOT NULL, ownerid varchar(36) NOT NULL, sharemode varchar(12) NOT NULL, accesskey varchar(36) NOT NULL @@ -41,6 +42,8 @@ CREATE TABLE screen ( screenopts json NOT NULL, ownerid varchar(36) NOT NULL, sharemode varchar(12) NOT NULL, + incognito boolean NOT NULL, + closed boolean NOT NULL, PRIMARY KEY (sessionid, screenid) ); @@ -136,7 +139,7 @@ CREATE TABLE cmd ( startpk json NOT NULL, doneinfo json NOT NULL, runout json NOT NULL, - rtnstate bool NOT NULL, + rtnstate boolean NOT NULL, rtnbasehash varchar(36) NOT NULL, rtndiffhasharr json NOT NULL, PRIMARY KEY (sessionid, cmdid) @@ -156,5 +159,6 @@ CREATE TABLE history ( haderror boolean NOT NULL, cmdid varchar(36) NOT NULL, cmdstr text NOT NULL, - ismetacmd boolean + ismetacmd boolean, + incognito boolean ); diff --git a/db/schema.sql b/db/schema.sql index b35e347b..4f2f532c 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -14,6 +14,7 @@ CREATE TABLE session ( sessionidx int NOT NULL, activescreenid varchar(36) NOT NULL, notifynum int NOT NULL, + closed boolean NOT NULL, ownerid varchar(36) NOT NULL, sharemode varchar(12) NOT NULL, accesskey varchar(36) NOT NULL @@ -40,6 +41,8 @@ CREATE TABLE screen ( screenopts json NOT NULL, ownerid varchar(36) NOT NULL, sharemode varchar(12) NOT NULL, + incognito boolean NOT NULL, + closed boolean NOT NULL, PRIMARY KEY (sessionid, screenid) ); CREATE TABLE screen_window ( @@ -128,7 +131,7 @@ CREATE TABLE cmd ( startpk json NOT NULL, doneinfo json NOT NULL, runout json NOT NULL, - rtnstate bool NOT NULL, + rtnstate boolean NOT NULL, rtnbasehash varchar(36) NOT NULL, rtndiffhasharr json NOT NULL, PRIMARY KEY (sessionid, cmdid) @@ -147,5 +150,6 @@ CREATE TABLE history ( haderror boolean NOT NULL, cmdid varchar(36) NOT NULL, cmdstr text NOT NULL, - ismetacmd boolean + ismetacmd boolean, + incognito boolean ); diff --git a/pkg/cmdrunner/cmdrunner.go b/pkg/cmdrunner/cmdrunner.go index 21099435..a9d4d750 100644 --- a/pkg/cmdrunner/cmdrunner.go +++ b/pkg/cmdrunner/cmdrunner.go @@ -275,7 +275,7 @@ func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.U // runPacket.State is set in remote.RunCommand() runPacket := packet.MakeRunPacket() runPacket.ReqId = uuid.New().String() - runPacket.CK = base.MakeCommandKey(ids.SessionId, scbase.GenSCUUID()) + runPacket.CK = base.MakeCommandKey(ids.SessionId, scbase.GenPromptUUID()) runPacket.UsePty = true ptermVal := defaultStr(pk.Kwargs["pterm"], DefaultPTERM) runPacket.TermOpts, err = GetUITermOpts(pk.UIContext.WinSize, ptermVal) @@ -306,8 +306,12 @@ func addToHistory(ctx context.Context, pk *scpacket.FeCommandPacketType, history if err != nil { return err } + isIncognito, err := sstore.IsIncognitoScreen(ctx, ids.SessionId, ids.ScreenId) + if err != nil { + return fmt.Errorf("cannot add to history, error looking up incognito status of screen: %v", err) + } hitem := &sstore.HistoryItemType{ - HistoryId: scbase.GenSCUUID(), + HistoryId: scbase.GenPromptUUID(), Ts: time.Now().UnixMilli(), UserId: DefaultUserId, SessionId: ids.SessionId, @@ -318,6 +322,7 @@ func addToHistory(ctx context.Context, pk *scpacket.FeCommandPacketType, history CmdId: historyContext.CmdId, CmdStr: cmdStr, IsMetaCmd: isMetaCmd, + Incognito: isIncognito, } if !isMetaCmd && historyContext.RemotePtr != nil { hitem.Remote = *historyContext.RemotePtr @@ -785,7 +790,7 @@ func RemoteNewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ss return makeRemoteEditErrorReturn_new(visualEdit, fmt.Errorf("/remote:new %v", err)) } r := &sstore.RemoteType{ - RemoteId: scbase.GenSCUUID(), + RemoteId: scbase.GenPromptUUID(), PhysicalId: "", RemoteType: sstore.RemoteTypeSsh, RemoteAlias: editArgs.Alias, @@ -941,7 +946,7 @@ func CrCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.Up func makeStaticCmd(ctx context.Context, metaCmd string, ids resolvedIds, cmdStr string, cmdOutput []byte) (*sstore.CmdType, error) { cmd := &sstore.CmdType{ SessionId: ids.SessionId, - CmdId: scbase.GenSCUUID(), + CmdId: scbase.GenPromptUUID(), CmdStr: cmdStr, Remote: ids.Remote.RemotePtr, TermOpts: sstore.TermOpts{Rows: shexec.DefaultTermRows, Cols: shexec.DefaultTermCols, FlexRows: true, MaxPtySize: remote.DefaultMaxPtySize}, diff --git a/pkg/scbase/scbase.go b/pkg/scbase/scbase.go index a673655f..8199654b 100644 --- a/pkg/scbase/scbase.go +++ b/pkg/scbase/scbase.go @@ -20,10 +20,11 @@ import ( const HomeVarName = "HOME" const PromptHomeVarName = "PROMPT_HOME" const SessionsDirBaseName = "sessions" -const SCLockFile = "prompt.lock" +const PromptLockFile = "prompt.lock" const PromptDirName = "prompt" const PromptAppPathVarName = "PROMPT_APP_PATH" const PromptVersion = "v0.1.0" +const PromptAuthKeyFileName = "prompt.authkey" var SessionDirCache = make(map[string]string) var BaseLock = &sync.Mutex{} @@ -63,13 +64,54 @@ func MShellBinaryFromPackage(version string, goos string, goarch string) (io.Rea return fd, nil } -func AcquireSCLock() (*os.File, error) { +func createPromptAuthKeyFile(fileName string) (string, error) { + fd, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return "", err + } + defer fd.Close() + keyStr := GenPromptUUID() + _, err = fd.Write([]byte(keyStr)) + if err != nil { + return "", err + } + return keyStr, nil +} + +func ReadPromptAuthKey() (string, error) { + homeDir := GetPromptHomeDir() + err := ensureDir(homeDir) + if err != nil { + return "", fmt.Errorf("cannot find/create PROMPT_HOME directory %q", homeDir) + } + fileName := path.Join(homeDir, PromptAuthKeyFileName) + fd, err := os.Open(fileName) + if err != nil && errors.Is(err, fs.ErrNotExist) { + return createPromptAuthKeyFile(fileName) + } + if err != nil { + return "", fmt.Errorf("error opening prompt authkey:%s: %v", fileName, err) + } + defer fd.Close() + buf, err := io.ReadAll(fd) + if err != nil { + return "", fmt.Errorf("error reading prompt authkey:%s: %v", fileName, err) + } + keyStr := string(buf) + _, err = uuid.Parse(keyStr) + if err != nil { + return "", fmt.Errorf("invalid authkey:%s format: %v", fileName, err) + } + return keyStr, nil +} + +func AcquirePromptLock() (*os.File, error) { homeDir := GetPromptHomeDir() err := ensureDir(homeDir) if err != nil { return nil, fmt.Errorf("cannot find/create PROMPT_HOME directory %q", homeDir) } - lockFileName := path.Join(homeDir, SCLockFile) + lockFileName := path.Join(homeDir, PromptLockFile) fd, err := os.Create(lockFileName) if err != nil { return nil, err @@ -151,19 +193,19 @@ func RunOutFile(sessionId string, cmdId string) (string, error) { return fmt.Sprintf("%s/%s.runout", sdir, cmdId), nil } -type ScFileNameGenerator struct { - ScHome string +type PromptFileNameGenerator struct { + PromptHome string } -func (g ScFileNameGenerator) PtyOutFile(ck base.CommandKey) string { - return path.Join(g.ScHome, SessionsDirBaseName, ck.GetSessionId(), ck.GetCmdId()+".ptyout") +func (g PromptFileNameGenerator) PtyOutFile(ck base.CommandKey) string { + return path.Join(g.PromptHome, SessionsDirBaseName, ck.GetSessionId(), ck.GetCmdId()+".ptyout") } -func (g ScFileNameGenerator) RunOutFile(ck base.CommandKey) string { - return path.Join(g.ScHome, SessionsDirBaseName, ck.GetSessionId(), ck.GetCmdId()+".runout") +func (g PromptFileNameGenerator) RunOutFile(ck base.CommandKey) string { + return path.Join(g.PromptHome, SessionsDirBaseName, ck.GetSessionId(), ck.GetCmdId()+".runout") } -func GenSCUUID() string { +func GenPromptUUID() string { for { rtn := uuid.New().String() _, err := strconv.Atoi(rtn[0:8]) diff --git a/pkg/sstore/dbops.go b/pkg/sstore/dbops.go index d8af3c30..471eafa6 100644 --- a/pkg/sstore/dbops.go +++ b/pkg/sstore/dbops.go @@ -14,7 +14,7 @@ import ( "github.com/scripthaus-dev/sh2-server/pkg/scbase" ) -const HistoryCols = "historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr, remoteownerid, remoteid, remotename, ismetacmd" +const HistoryCols = "historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr, remoteownerid, remoteid, remotename, ismetacmd, incognito" const DefaultMaxHistoryItems = 1000 func NumSessions(ctx context.Context) (int, error) { @@ -157,14 +157,24 @@ func InsertHistoryItem(ctx context.Context, hitem *HistoryItemType) error { } txErr := WithTx(ctx, func(tx *TxWrap) error { query := `INSERT INTO history - ( historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr, remoteownerid, remoteid, remotename, ismetacmd) VALUES - (:historyid,:ts,:userid,:sessionid,:screenid,:windowid,:lineid,:cmdid,:haderror,:cmdstr,:remoteownerid,:remoteid,:remotename,:ismetacmd)` + ( historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr, remoteownerid, remoteid, remotename, ismetacmd, incognito) VALUES + (:historyid,:ts,:userid,:sessionid,:screenid,:windowid,:lineid,:cmdid,:haderror,:cmdstr,:remoteownerid,:remoteid,:remotename,:ismetacmd,:incognito)` tx.NamedExecWrap(query, hitem.ToMap()) return nil }) return txErr } +func IsIncognitoScreen(ctx context.Context, sessionId string, screenId string) (bool, error) { + var rtn bool + txErr := WithTx(ctx, func(tx *TxWrap) error { + query := `SELECT incognito FROM screen WHERE sessionid = ? AND screenid = ?` + tx.GetWrap(&rtn, query, sessionId, screenId) + return nil + }) + return rtn, txErr +} + func runHistoryQuery(tx *TxWrap, sessionId string, windowId string, opts HistoryQueryOpts) ([]*HistoryItemType, error) { // check sessionid/windowid format because we are directly inserting them into the SQL if sessionId != "" { @@ -390,12 +400,13 @@ func GetSessionByName(ctx context.Context, name string) (*SessionType, error) { // also creates default window, returns sessionId // if sessionName == "", it will be generated func InsertSessionWithName(ctx context.Context, sessionName string, activate bool) (UpdatePacket, error) { - newSessionId := scbase.GenSCUUID() + newSessionId := scbase.GenPromptUUID() txErr := WithTx(ctx, func(tx *TxWrap) error { names := tx.SelectStrings(`SELECT name FROM session`) sessionName = fmtUniqueName(sessionName, "session-%d", len(names)+1, names) maxSessionIdx := tx.GetInt(`SELECT COALESCE(max(sessionidx), 0) FROM session`) - query := `INSERT INTO session (sessionid, name, activescreenid, sessionidx, notifynum, ownerid, sharemode, accesskey) VALUES (?, ?, '', ?, ?, '', 'local', '')` + query := `INSERT INTO session (sessionid, name, activescreenid, sessionidx, notifynum, closed, ownerid, sharemode, accesskey) + VALUES (?, ?, '', ?, ?, 0, '', 'local', '')` tx.ExecWrap(query, newSessionId, sessionName, maxSessionIdx+1, 0) _, err := InsertScreen(tx.Context(), newSessionId, "", true) if err != nil { @@ -493,8 +504,8 @@ func InsertScreen(ctx context.Context, sessionId string, origScreenName string, maxScreenIdx := tx.GetInt(`SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ?`, sessionId) screenNames := tx.SelectStrings(`SELECT name FROM screen WHERE sessionid = ?`, sessionId) screenName := fmtUniqueName(origScreenName, "s%d", maxScreenIdx+1, screenNames) - newScreenId = scbase.GenSCUUID() - query = `INSERT INTO screen (sessionid, screenid, name, activewindowid, screenidx, screenopts, ownerid, sharemode) VALUES (?, ?, ?, ?, ?, ?, '', 'local')` + newScreenId = scbase.GenPromptUUID() + query = `INSERT INTO screen (sessionid, screenid, name, activewindowid, screenidx, screenopts, ownerid, sharemode, incognito, closed) VALUES (?, ?, ?, ?, ?, ?, '', 'local', 0, 0)` tx.ExecWrap(query, sessionId, newScreenId, screenName, newWindowId, maxScreenIdx+1, ScreenOptsType{}) layout := LayoutType{Type: LayoutFull} query = `INSERT INTO screen_window (sessionid, screenid, windowid, name, layout, selectedline, anchor, focustype) VALUES (?, ?, ?, ?, ?, ?, ?, ?)` @@ -541,7 +552,7 @@ func GetScreenById(ctx context.Context, sessionId string, screenId string) (*Scr func txCreateWindow(tx *TxWrap, sessionId string, curRemote RemotePtrType) string { w := &WindowType{ SessionId: sessionId, - WindowId: scbase.GenSCUUID(), + WindowId: scbase.GenPromptUUID(), CurRemote: curRemote, NextLineNum: 1, WinOpts: WindowOptsType{}, @@ -949,7 +960,7 @@ func UpdateRemoteState(ctx context.Context, sessionId string, windowId string, r ri = RIFromMap(m) if ri == nil { ri = &RemoteInstance{ - RIId: scbase.GenSCUUID(), + RIId: scbase.GenPromptUUID(), Name: remotePtr.Name, SessionId: sessionId, WindowId: windowId, diff --git a/pkg/sstore/sstore.go b/pkg/sstore/sstore.go index bc76fb98..efa2636f 100644 --- a/pkg/sstore/sstore.go +++ b/pkg/sstore/sstore.go @@ -152,6 +152,7 @@ type SessionType struct { ShareMode string `json:"sharemode"` AccessKey string `json:"-"` NotifyNum int64 `json:"notifynum"` + Closed bool `json:"closed,omitempty"` Screens []*ScreenType `json:"screens"` Remotes []*RemoteInstance `json:"remotes"` @@ -305,6 +306,7 @@ func (h *HistoryItemType) ToMap() map[string]interface{} { rtn["remoteid"] = h.Remote.RemoteId rtn["remotename"] = h.Remote.Name rtn["ismetacmd"] = h.IsMetaCmd + rtn["incognito"] = h.Incognito return rtn } @@ -328,6 +330,7 @@ func HistoryItemFromMap(m map[string]interface{}) *HistoryItemType { quickSetStr(&h.Remote.Name, m, "remotename") quickSetBool(&h.IsMetaCmd, m, "ismetacmd") quickSetStr(&h.HistoryNum, m, "historynum") + quickSetBool(&h.Incognito, m, "incognito") return &h } @@ -352,6 +355,8 @@ type ScreenType struct { ScreenOpts *ScreenOptsType `json:"screenopts"` OwnerId string `json:"ownerid"` ShareMode string `json:"sharemode"` + Incognito bool `json:"incognito,omitempty"` + Closed bool `json:"closed,omitempty"` Windows []*ScreenWindowType `json:"windows"` // only for updates @@ -430,6 +435,7 @@ type HistoryItemType struct { CmdStr string `json:"cmdstr"` Remote RemotePtrType `json:"remote"` IsMetaCmd bool `json:"ismetacmd"` + Incognito bool `json:"incognito,omitempty"` // only for updates Remove bool `json:"remove"` @@ -761,7 +767,7 @@ func makeNewLineCmd(sessionId string, windowId string, userId string, cmdId stri rtn.SessionId = sessionId rtn.WindowId = windowId rtn.UserId = userId - rtn.LineId = scbase.GenSCUUID() + rtn.LineId = scbase.GenPromptUUID() rtn.Ts = time.Now().UnixMilli() rtn.LineLocal = true rtn.LineType = LineTypeCmd @@ -775,7 +781,7 @@ func makeNewLineText(sessionId string, windowId string, userId string, text stri rtn.SessionId = sessionId rtn.WindowId = windowId rtn.UserId = userId - rtn.LineId = scbase.GenSCUUID() + rtn.LineId = scbase.GenPromptUUID() rtn.Ts = time.Now().UnixMilli() rtn.LineLocal = true rtn.LineType = LineTypeText @@ -824,7 +830,7 @@ func EnsureLocalRemote(ctx context.Context) error { } // create the local remote localRemote := &RemoteType{ - RemoteId: scbase.GenSCUUID(), + RemoteId: scbase.GenPromptUUID(), PhysicalId: physicalId, RemoteType: RemoteTypeSsh, RemoteAlias: LocalRemoteAlias,