From f2529581e18c847a4387eee830311a41e1a3db7d Mon Sep 17 00:00:00 2001 From: sawka Date: Wed, 8 Mar 2023 17:16:06 -0800 Subject: [PATCH] checkpoint, cloud sessions --- db/migrations/000008_cloudsession.down.sql | 4 ++ db/migrations/000008_cloudsession.up.sql | 13 ++++ pkg/pcloud/pcloud.go | 80 +++++++++++++++++++++- pkg/promptenc/promptenc.go | 18 +++++ pkg/sstore/dbops.go | 4 +- pkg/sstore/sstore.go | 17 ++++- 6 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 db/migrations/000008_cloudsession.down.sql create mode 100644 db/migrations/000008_cloudsession.up.sql diff --git a/db/migrations/000008_cloudsession.down.sql b/db/migrations/000008_cloudsession.down.sql new file mode 100644 index 00000000..afb70c9b --- /dev/null +++ b/db/migrations/000008_cloudsession.down.sql @@ -0,0 +1,4 @@ +ALTER TABLE session ADD COLUMN accesskey DEFAULT ''; +ALTER TABLE session ADD COLUMN ownerid DEFAULT ''; + +DROP TABLE cloud_session; diff --git a/db/migrations/000008_cloudsession.up.sql b/db/migrations/000008_cloudsession.up.sql new file mode 100644 index 00000000..4fba6464 --- /dev/null +++ b/db/migrations/000008_cloudsession.up.sql @@ -0,0 +1,13 @@ +ALTER TABLE session DROP COLUMN accesskey; +ALTER TABLE session DROP COLUMN ownerid; + +CREATE TABLE cloud_session ( + sessionid varchar(36) PRIMARY KEY, + viewkey varchar(50) NOT NULL, + writekey varchar(50) NOT NULL, + enckey varchar(100) NOT NULL, + enctype varchar(50) NOT NULL, + vts bigint NOT NULL, + acl json NOT NULL +); + diff --git a/pkg/pcloud/pcloud.go b/pkg/pcloud/pcloud.go index e55d4230..3ca08b8f 100644 --- a/pkg/pcloud/pcloud.go +++ b/pkg/pcloud/pcloud.go @@ -20,6 +20,10 @@ const PCloudEndpoint = "https://api.getprompt.dev/central" const PCloudEndpointVarName = "PCLOUD_ENDPOINT" const APIVersion = 1 +const TelemetryUrl = "/telemetry" +const NoTelemetryUrl = "/no-telemetry" +const CreateCloudSessionUrl = "/auth/create-cloud-session" + type NoTelemetryInputType struct { ClientId string `json:"clientid"` Value bool `json:"value"` @@ -32,6 +36,27 @@ type TelemetryInputType struct { Activity []*sstore.ActivityType `json:"activity"` } +type CloudSession struct { + SessionId string `json:"sessionid"` + ViewKey string `json:"viewkey"` + WriteKey string `json:"writekey"` + EncType string `json:"enctype"` + UpdateVTS int64 `json:"updatevts"` + + EncSessionData []byte `json:"enc_sessiondata" enc:"*"` + Name string `json:"-" enc:"name"` +} + +func (cs *CloudSession) GetOData() string { + return fmt.Sprintf("session:%s", cs.SessionId) +} + +type AuthInfo struct { + UserId string `json:"userid"` + ClientId string `json:"clientid"` + AuthKey string `json:"authkey"` +} + func GetEndpoint() string { if !scbase.IsDevMode() { return PCloudEndpoint @@ -43,7 +68,31 @@ func GetEndpoint() string { return endpoint } -func makePostReq(ctx context.Context, apiUrl string, data interface{}) (*http.Request, error) { +func makeAuthPostReq(ctx context.Context, apiUrl string, authInfo AuthInfo, data interface{}) (*http.Request, error) { + var dataReader io.Reader + if data != nil { + byteArr, err := json.Marshal(data) + if err != nil { + return nil, fmt.Errorf("error marshaling json for %s request: %v", apiUrl, err) + } + dataReader = bytes.NewReader(byteArr) + } + fullUrl := GetEndpoint() + apiUrl + req, err := http.NewRequestWithContext(ctx, "POST", fullUrl, dataReader) + if err != nil { + return nil, fmt.Errorf("error creating %s request: %v", apiUrl, err) + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-PromptAPIVersion", strconv.Itoa(APIVersion)) + req.Header.Set("X-PromptAPIUrl", apiUrl) + req.Header.Set("X-PromptUserId", authInfo.UserId) + req.Header.Set("X-PromptClientId", authInfo.ClientId) + req.Header.Set("X-PromptAuthKey", authInfo.AuthKey) + req.Close = true + return req, nil +} + +func makeAnonPostReq(ctx context.Context, apiUrl string, data interface{}) (*http.Request, error) { var dataReader io.Reader if data != nil { byteArr, err := json.Marshal(data) @@ -106,7 +155,7 @@ func SendTelemetry(ctx context.Context, force bool) error { log.Printf("sending telemetry data\n") dayStr := sstore.GetCurDayStr() input := TelemetryInputType{UserId: clientData.UserId, ClientId: clientData.ClientId, CurDay: dayStr, Activity: activity} - req, err := makePostReq(ctx, "/telemetry", input) + req, err := makeAnonPostReq(ctx, TelemetryUrl, input) if err != nil { return err } @@ -126,7 +175,32 @@ func SendNoTelemetryUpdate(ctx context.Context, noTelemetryVal bool) error { if err != nil { return fmt.Errorf("cannot retrieve client data: %v", err) } - req, err := makePostReq(ctx, "/no-telemetry", NoTelemetryInputType{ClientId: clientData.ClientId, Value: noTelemetryVal}) + req, err := makeAnonPostReq(ctx, NoTelemetryUrl, NoTelemetryInputType{ClientId: clientData.ClientId, Value: noTelemetryVal}) + if err != nil { + return err + } + _, err = doRequest(req, nil) + if err != nil { + return err + } + return nil +} + +func getAuthInfo(ctx context.Context) (AuthInfo, error) { + clientData, err := sstore.EnsureClientData(ctx) + if err != nil { + return AuthInfo{}, fmt.Errorf("cannot retrieve client data: %v", err) + } + return AuthInfo{UserId: clientData.UserId, ClientId: clientData.ClientId}, nil +} + +func CreateCloudSession(ctx context.Context) error { + authInfo, err := getAuthInfo(ctx) + if err != nil { + return err + } + fmt.Printf("authinfo: %v\n", authInfo) + req, err := makeAuthPostReq(ctx, CreateCloudSessionUrl, authInfo, nil) if err != nil { return err } diff --git a/pkg/promptenc/promptenc.go b/pkg/promptenc/promptenc.go index f7338e34..bd74216d 100644 --- a/pkg/promptenc/promptenc.go +++ b/pkg/promptenc/promptenc.go @@ -3,6 +3,7 @@ package promptenc import ( "crypto/cipher" "crypto/rand" + "encoding/base64" "encoding/json" "fmt" "io" @@ -42,6 +43,23 @@ func MakeRandomEncryptor() (*Encryptor, error) { return rtn, nil } +func MakeEncryptor(key []byte) (*Encryptor, error) { + rtn := &Encryptor{Key: key} + rtn.AEAD, err = ccp.NewX(rtn.Key) + if err != nil { + return nil, err + } + return rtn, nil +} + +func MakeEncryptorB64(key64 string) (*Encryptor, error) { + keyBytes, err := base64.RawURLEncoding.DecodeString(key64) + if err != nil { + return nil, err + } + return MakeEncryptor(keyBytes) +} + func (enc *Encryptor) EncryptData(plainText []byte, odata string) ([]byte, error) { outputBuf := make([]byte, enc.AEAD.NonceSize()+enc.AEAD.Overhead()+len(plainText)) nonce := outputBuf[0:enc.AEAD.NonceSize()] diff --git a/pkg/sstore/dbops.go b/pkg/sstore/dbops.go index 780406e8..30b55a4e 100644 --- a/pkg/sstore/dbops.go +++ b/pkg/sstore/dbops.go @@ -527,8 +527,8 @@ func InsertSessionWithName(ctx context.Context, sessionName string, activate boo 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, archived, archivedts, ownerid, sharemode, accesskey) - VALUES (?, ?, '', ?, ?, 0, 0, '', 'local', '')` + query := `INSERT INTO session (sessionid, name, activescreenid, sessionidx, notifynum, archived, archivedts, sharemode) + VALUES (?, ?, '', ?, ?, 0, 0, 'local')` tx.Exec(query, newSessionId, sessionName, maxSessionIdx+1, 0) _, err := InsertScreen(tx.Context(), newSessionId, "", true) if err != nil { diff --git a/pkg/sstore/sstore.go b/pkg/sstore/sstore.go index 9fe5c1ba..45e39db5 100644 --- a/pkg/sstore/sstore.go +++ b/pkg/sstore/sstore.go @@ -212,14 +212,17 @@ func ClientDataFromMap(m map[string]interface{}) *ClientData { return &c } +type CloudAclType struct { + UserId string `json:"userid"` + Role string `json:"role"` +} + type SessionType struct { SessionId string `json:"sessionid"` Name string `json:"name"` SessionIdx int64 `json:"sessionidx"` ActiveScreenId string `json:"activescreenid"` - OwnerId string `json:"ownerid"` ShareMode string `json:"sharemode"` - AccessKey string `json:"-"` NotifyNum int64 `json:"notifynum"` Archived bool `json:"archived,omitempty"` ArchivedTs int64 `json:"archivedts,omitempty"` @@ -231,6 +234,16 @@ type SessionType struct { Full bool `json:"full,omitempty"` } +type CloudSessionType struct { + SessionId string + ViewKey string + WriteKey string + EncKey string + EncType string + Vts int64 + Acl []*CloudAclType +} + type SessionStatsType struct { SessionId string `json:"sessionid"` NumScreens int `json:"numscreens"`