diff --git a/build/go.sum b/build/go.sum index 177c246..90855df 100644 --- a/build/go.sum +++ b/build/go.sum @@ -57,6 +57,7 @@ github.com/aws/aws-sdk-go v1.44.233/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8 github.com/aws/aws-sdk-go v1.44.256/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go v1.45.24/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go v1.48.0/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go v1.48.2/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= @@ -178,6 +179,7 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -225,6 +227,7 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -240,6 +243,7 @@ github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPK github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -279,6 +283,7 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/r3labs/sse/v2 v2.10.0/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -422,6 +427,7 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -782,6 +788,16 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= +modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI= +modernc.org/libc v1.34.9/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.27.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/configuration/database/Database_test.go b/internal/configuration/database/Database_test.go index d73c7f6..9c31a84 100644 --- a/internal/configuration/database/Database_test.go +++ b/internal/configuration/database/Database_test.go @@ -1,9 +1,12 @@ package database import ( + "github.com/forceu/gokapi/internal/helper" "github.com/forceu/gokapi/internal/models" "github.com/forceu/gokapi/internal/test" + "math" "os" + "sync" "testing" "time" ) @@ -279,3 +282,155 @@ func TestGarbageCollectionSessions(t *testing.T) { test.IsEqualBool(t, result, true) } } + +func TestEnd2EndInfo(t *testing.T) { + info := GetEnd2EndInfo() + test.IsEqualInt(t, info.Version, 0) + test.IsEqualBool(t, info.HasBeenSetUp(), false) + + SaveEnd2EndInfo(models.E2EInfoEncrypted{ + Version: 1, + Nonce: []byte("testNonce1"), + Content: []byte("testContent1"), + AvailableFiles: []string{"file1_0", "file1_1"}, + }) + + info = GetEnd2EndInfo() + test.IsEqualInt(t, info.Version, 1) + test.IsEqualBool(t, info.HasBeenSetUp(), true) + test.IsEqualByteSlice(t, info.Nonce, []byte("testNonce1")) + test.IsEqualByteSlice(t, info.Content, []byte("testContent1")) + test.IsEqualBool(t, len(info.AvailableFiles) == 0, true) + + SaveEnd2EndInfo(models.E2EInfoEncrypted{ + Version: 2, + Nonce: []byte("testNonce2"), + Content: []byte("testContent2"), + AvailableFiles: []string{"file2_0", "file2_1"}, + }) + + info = GetEnd2EndInfo() + test.IsEqualInt(t, info.Version, 2) + test.IsEqualBool(t, info.HasBeenSetUp(), true) + test.IsEqualByteSlice(t, info.Nonce, []byte("testNonce2")) + test.IsEqualByteSlice(t, info.Content, []byte("testContent2")) + test.IsEqualBool(t, len(info.AvailableFiles) == 0, true) + + DeleteEnd2EndInfo() + info = GetEnd2EndInfo() + test.IsEqualInt(t, info.Version, 0) + test.IsEqualBool(t, info.HasBeenSetUp(), false) +} + +func TestUpdateTimeApiKey(t *testing.T) { + + retrievedKey, ok := GetApiKey("key1") + test.IsEqualBool(t, ok, false) + test.IsEqualString(t, retrievedKey.Id, "") + + key := models.ApiKey{ + Id: "key1", + FriendlyName: "key1", + LastUsed: 100, + LastUsedString: "last1", + } + SaveApiKey(key) + key = models.ApiKey{ + Id: "key2", + FriendlyName: "key2", + LastUsed: 200, + LastUsedString: "last2", + } + SaveApiKey(key) + + retrievedKey, ok = GetApiKey("key1") + test.IsEqualBool(t, ok, true) + test.IsEqualString(t, retrievedKey.Id, "key1") + test.IsEqualInt64(t, retrievedKey.LastUsed, 100) + test.IsEqualString(t, retrievedKey.LastUsedString, "last1") + retrievedKey, ok = GetApiKey("key2") + test.IsEqualBool(t, ok, true) + test.IsEqualString(t, retrievedKey.Id, "key2") + test.IsEqualInt64(t, retrievedKey.LastUsed, 200) + test.IsEqualString(t, retrievedKey.LastUsedString, "last2") + + key.LastUsed = 300 + key.LastUsedString = "last2_1" + UpdateTimeApiKey(key) + + retrievedKey, ok = GetApiKey("key1") + test.IsEqualBool(t, ok, true) + test.IsEqualString(t, retrievedKey.Id, "key1") + test.IsEqualInt64(t, retrievedKey.LastUsed, 100) + test.IsEqualString(t, retrievedKey.LastUsedString, "last1") + retrievedKey, ok = GetApiKey("key2") + test.IsEqualBool(t, ok, true) + test.IsEqualString(t, retrievedKey.Id, "key2") + test.IsEqualInt64(t, retrievedKey.LastUsed, 300) + test.IsEqualString(t, retrievedKey.LastUsedString, "last2_1") +} + +func TestParallelConnectionsWritingAndReading(t *testing.T) { + var wg sync.WaitGroup + + simulatedConnection := func(t *testing.T) { + file := models.File{ + Id: helper.GenerateRandomString(10), + Name: helper.GenerateRandomString(10), + Size: "10B", + SHA1: "1289423794287598237489", + ExpireAt: math.MaxInt, + SizeBytes: 10, + ExpireAtString: "Never", + DownloadsRemaining: 10, + DownloadCount: 10, + PasswordHash: "", + HotlinkId: "", + ContentType: "", + AwsBucket: "", + Encryption: models.EncryptionInfo{}, + UnlimitedDownloads: false, + UnlimitedTime: false, + } + SaveMetaData(file) + retrievedFile, ok := GetMetaDataById(file.Id) + test.IsEqualBool(t, ok, true) + test.IsEqualString(t, retrievedFile.Name, file.Name) + DeleteMetaData(file.Id) + _, ok = GetMetaDataById(file.Id) + test.IsEqualBool(t, ok, false) + } + + for i := 1; i <= 4000; i++ { + wg.Add(1) + go func() { + defer wg.Done() + simulatedConnection(t) + }() + } + wg.Wait() +} + +func TestParallelConnectionsReading(t *testing.T) { + var wg sync.WaitGroup + + SaveApiKey(models.ApiKey{ + Id: "readtest", + FriendlyName: "readtest", + LastUsed: 40000, + LastUsedString: "readtest", + }) + simulatedConnection := func(t *testing.T) { + _, ok := GetApiKey("readtest") + test.IsEqualBool(t, ok, true) + } + + for i := 1; i <= 100000; i++ { + wg.Add(1) + go func() { + defer wg.Done() + simulatedConnection(t) + }() + } + wg.Wait() +} diff --git a/internal/configuration/database/apikeys.go b/internal/configuration/database/apikeys.go index 4bcf823..9c7dcb3 100644 --- a/internal/configuration/database/apikeys.go +++ b/internal/configuration/database/apikeys.go @@ -37,7 +37,6 @@ func GetAllApiKeys() map[string]models.ApiKey { // GetApiKey returns a models.ApiKey if valid or false if the ID is not valid func GetApiKey(id string) (models.ApiKey, bool) { - var rowResult schemaApiKeys row := sqliteDb.QueryRow("SELECT * FROM ApiKeys WHERE Id = ?", id) err := row.Scan(&rowResult.Id, &rowResult.FriendlyName, &rowResult.LastUsed, &rowResult.LastUsedString) diff --git a/internal/configuration/database/e2econfig.go b/internal/configuration/database/e2econfig.go index 4cabb92..2d98607 100644 --- a/internal/configuration/database/e2econfig.go +++ b/internal/configuration/database/e2econfig.go @@ -17,6 +17,7 @@ type schemaE2EConfig struct { // SaveEnd2EndInfo stores the encrypted e2e info func SaveEnd2EndInfo(info models.E2EInfoEncrypted) { + info.AvailableFiles = nil var buf bytes.Buffer enc := gob.NewEncoder(&buf) err := enc.Encode(info) diff --git a/internal/models/End2EndEncryption.go b/internal/models/End2EndEncryption.go index f13bd02..b6ad0b8 100644 --- a/internal/models/End2EndEncryption.go +++ b/internal/models/End2EndEncryption.go @@ -7,9 +7,14 @@ type E2EInfoPlainText struct { // E2EInfoEncrypted is the struct that is stored on the server and decrypted locally type E2EInfoEncrypted struct { - Version int `json:"version"` - Nonce []byte `json:"nonce"` - Content []byte `json:"content"` + // Version of the E2E used, must be at least 1 + Version int `json:"version"` + // Nonce used for encryption + Nonce []byte `json:"nonce"` + // Content that is encrypted + Content []byte `json:"content"` + // AvailableFiles contains a list of all files on the webserver and will be populated + // when reading from the database, but will not be saved to the database AvailableFiles []string `json:"availablefiles"` } diff --git a/internal/test/TestHelper.go b/internal/test/TestHelper.go index 23e5cb3..1d3106d 100644 --- a/internal/test/TestHelper.go +++ b/internal/test/TestHelper.go @@ -22,6 +22,13 @@ type MockT interface { Helper() } +func IsEqualByteSlice(t MockT, got, want []byte) { + t.Helper() + if !bytes.Equal(got, want) { + t.Errorf("Assertion failed, GOT: %s, WANT: %s.", string(got), string(want)) + } +} + // IsEqualString fails test if got and want are not identical func IsEqualString(t MockT, got, want string) { t.Helper() diff --git a/internal/webserver/fileupload/FileUpload_test.go b/internal/webserver/fileupload/FileUpload_test.go index 8157160..34cd4b5 100644 --- a/internal/webserver/fileupload/FileUpload_test.go +++ b/internal/webserver/fileupload/FileUpload_test.go @@ -85,9 +85,6 @@ func TestProcess(t *testing.T) { err := Process(w, r, false, 20) test.IsNotNil(t, err) - data := url.Values{} - data.Set("file", "invalid") - w = httptest.NewRecorder() r = getFileUploadRecorder(false) err = Process(w, r, false, 20)