From 43fcd3b9e84befe3667324e5f4c2d813a7c2ad3a Mon Sep 17 00:00:00 2001 From: Tovi Jaeschke-Rogers Date: Thu, 22 Sep 2022 19:27:40 +0930 Subject: [PATCH] Add tests for CreateConversation and UpdateConversation --- Backend/Api/Auth/Logout_test.go | 1 - .../Api/Messages/CreateConversation_test.go | 1 - Backend/Api/Messages/CreateMessage_test.go | 2 + Backend/Api/Messages/MessageThread_test.go | 117 +++++++++++ .../Api/Messages/UpdateConversation_test.go | 183 ++++++++++++++++++ Backend/Database/Seeder/FriendSeeder.go | 2 +- Backend/Database/Seeder/MessageSeeder.go | 10 +- Backend/Database/Seeder/UserSeeder.go | 2 +- Backend/Database/Seeder/encryption.go | 18 +- 9 files changed, 318 insertions(+), 18 deletions(-) create mode 100644 Backend/Api/Messages/MessageThread_test.go create mode 100644 Backend/Api/Messages/UpdateConversation_test.go diff --git a/Backend/Api/Auth/Logout_test.go b/Backend/Api/Auth/Logout_test.go index 2903bb3..ca43f0c 100644 --- a/Backend/Api/Auth/Logout_test.go +++ b/Backend/Api/Auth/Logout_test.go @@ -18,7 +18,6 @@ func Test_Logout(t *testing.T) { } resp, err := client.Get(ts.URL + "/api/v1/logout") - if err != nil { t.Errorf("Expected user record, recieved %s", err.Error()) return diff --git a/Backend/Api/Messages/CreateConversation_test.go b/Backend/Api/Messages/CreateConversation_test.go index 7c0ee80..5659dff 100644 --- a/Backend/Api/Messages/CreateConversation_test.go +++ b/Backend/Api/Messages/CreateConversation_test.go @@ -121,7 +121,6 @@ func Test_CreateConversation(t *testing.T) { var c Models.ConversationDetail err = Database.DB.First(&c, "id = ?", id.String()).Error - if err != nil { t.Errorf("Expected conversation detail record, received %s", err.Error()) return diff --git a/Backend/Api/Messages/CreateMessage_test.go b/Backend/Api/Messages/CreateMessage_test.go index 35cfc51..81666c5 100644 --- a/Backend/Api/Messages/CreateMessage_test.go +++ b/Backend/Api/Messages/CreateMessage_test.go @@ -15,6 +15,8 @@ import ( "github.com/gofrs/uuid" ) +// TODO: Write test for message expiry + func Test_CreateMessage(t *testing.T) { client, ts, err := Tests.InitTestEnv() if err != nil { diff --git a/Backend/Api/Messages/MessageThread_test.go b/Backend/Api/Messages/MessageThread_test.go new file mode 100644 index 0000000..3d3dada --- /dev/null +++ b/Backend/Api/Messages/MessageThread_test.go @@ -0,0 +1,117 @@ +package Messages_test + +import ( + "encoding/base64" + "encoding/json" + "io/ioutil" + "net/http" + "testing" + + "git.tovijaeschke.xyz/tovi/Capsule/Backend/Database" + "git.tovijaeschke.xyz/tovi/Capsule/Backend/Database/Seeder" + "git.tovijaeschke.xyz/tovi/Capsule/Backend/Models" + "git.tovijaeschke.xyz/tovi/Capsule/Backend/Tests" +) + +func Test_Messages(t *testing.T) { + client, ts, err := Tests.InitTestEnv() + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + u, err := Database.GetUserByUsername("test") + + userKey, err := Seeder.GenerateAesKey() + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + key, err := Seeder.GenerateAesKey() + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + dataCiphertext, err := key.AesEncrypt([]byte("Test message")) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + senderIDCiphertext, err := key.AesEncrypt([]byte(u.ID.String())) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + keyCiphertext, err := userKey.AesEncrypt( + []byte(base64.StdEncoding.EncodeToString(key.Key)), + ) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + pubKey := Seeder.GetPubKey() + + message := Models.Message{ + MessageData: Models.MessageData{ + Data: base64.StdEncoding.EncodeToString(dataCiphertext), + SenderID: base64.StdEncoding.EncodeToString(senderIDCiphertext), + SymmetricKey: base64.StdEncoding.EncodeToString(keyCiphertext), + }, + SymmetricKey: base64.StdEncoding.EncodeToString( + Seeder.EncryptWithPublicKey(userKey.Key, pubKey), + ), + AssociationKey: "AssociationKey", + } + + err = Database.CreateMessage(&message) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + resp, err := client.Get(ts.URL + "/api/v1/auth/messages/AssociationKey") + if err != nil { + t.Errorf("Expected user record, recieved %s", err.Error()) + return + } + + if resp.StatusCode != http.StatusOK { + t.Errorf("Expected %d, recieved %d", http.StatusOK, resp.StatusCode) + return + } + + requestBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + var m []Models.Message + err = json.Unmarshal(requestBody, &m) + + if len(m) != 1 { + t.Errorf("Expected %d, recieved %d", 1, len(m)) + } + + msg := m[0] + + decodedData, err := base64.StdEncoding.DecodeString(msg.MessageData.Data) + if err != nil { + t.Errorf("Expected %d, recieved %d", http.StatusOK, resp.StatusCode) + return + } + decrypedData, err := key.AesDecrypt(decodedData) + if err != nil { + t.Errorf("Expected %d, recieved %d", http.StatusOK, resp.StatusCode) + return + } + + if string(decrypedData) != "Test message" { + t.Errorf("Expected %s, recieved %s", "Test converation", string(decrypedData)) + } +} diff --git a/Backend/Api/Messages/UpdateConversation_test.go b/Backend/Api/Messages/UpdateConversation_test.go new file mode 100644 index 0000000..cdc3684 --- /dev/null +++ b/Backend/Api/Messages/UpdateConversation_test.go @@ -0,0 +1,183 @@ +package Messages_test + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "net/http" + "testing" + + "git.tovijaeschke.xyz/tovi/Capsule/Backend/Database" + "git.tovijaeschke.xyz/tovi/Capsule/Backend/Database/Seeder" + "git.tovijaeschke.xyz/tovi/Capsule/Backend/Models" + "git.tovijaeschke.xyz/tovi/Capsule/Backend/Tests" +) + +func createConversation(key Seeder.AesKey) (Models.ConversationDetail, Models.UserConversation, Models.ConversationDetailUser, error) { + var ( + cd Models.ConversationDetail + uc Models.UserConversation + cdu Models.ConversationDetailUser + ) + + u, err := Database.GetUserByUsername("test") + + nameCiphertext, err := key.AesEncrypt([]byte("Test conversation")) + if err != nil { + return cd, uc, cdu, err + } + + twoUserCiphertext, err := key.AesEncrypt([]byte("false")) + if err != nil { + return cd, uc, cdu, err + } + + cd = Models.ConversationDetail{ + Name: base64.StdEncoding.EncodeToString(nameCiphertext), + TwoUser: base64.StdEncoding.EncodeToString(twoUserCiphertext), + } + + err = Database.CreateConversationDetail(&cd) + if err != nil { + return cd, uc, cdu, err + } + + conversationDetailIDCiphertext, err := key.AesEncrypt([]byte(cd.ID.String())) + if err != nil { + return cd, uc, cdu, err + } + + adminCiphertext, err := key.AesEncrypt([]byte("true")) + if err != nil { + return cd, uc, cdu, err + } + + pubKey := Seeder.GetPubKey() + + uc = Models.UserConversation{ + UserID: u.ID, + ConversationDetailID: base64.StdEncoding.EncodeToString(conversationDetailIDCiphertext), + Admin: base64.StdEncoding.EncodeToString(adminCiphertext), + SymmetricKey: base64.StdEncoding.EncodeToString( + Seeder.EncryptWithPublicKey(key.Key, pubKey), + ), + } + + err = Database.CreateUserConversation(&uc) + if err != nil { + return cd, uc, cdu, err + } + + userIDCiphertext, err := key.AesEncrypt([]byte(u.ID.String())) + if err != nil { + return cd, uc, cdu, err + } + + usernameCiphertext, err := key.AesEncrypt([]byte(u.Username)) + if err != nil { + return cd, uc, cdu, err + } + + adminCiphertext, err = key.AesEncrypt([]byte("true")) + if err != nil { + return cd, uc, cdu, err + } + + associationKeyCiphertext, err := key.AesEncrypt([]byte("association")) + if err != nil { + return cd, uc, cdu, err + } + + publicKeyCiphertext, err := key.AesEncrypt([]byte(u.AsymmetricPublicKey)) + if err != nil { + return cd, uc, cdu, err + } + + cdu = Models.ConversationDetailUser{ + ConversationDetailID: cd.ID, + UserID: base64.StdEncoding.EncodeToString(userIDCiphertext), + Username: base64.StdEncoding.EncodeToString(usernameCiphertext), + Admin: base64.StdEncoding.EncodeToString(adminCiphertext), + AssociationKey: base64.StdEncoding.EncodeToString(associationKeyCiphertext), + PublicKey: base64.StdEncoding.EncodeToString(publicKeyCiphertext), + } + + err = Database.CreateConversationDetailUser(&cdu) + return cd, uc, cdu, err +} + +func Test_UpdateConversation(t *testing.T) { + client, ts, err := Tests.InitTestEnv() + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + // u, err := Database.GetUserByUsername("test") + + key, err := Seeder.GenerateAesKey() + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + cd, uc, cdu, err := createConversation(key) + + nameCiphertext, err := key.AesEncrypt([]byte("Not test conversation")) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + } + + d := struct { + ID string `json:"id"` + Name string `json:"name"` + Users []Models.ConversationDetailUser + UserConversations []Models.UserConversation + }{ + ID: cd.ID.String(), + Name: base64.StdEncoding.EncodeToString(nameCiphertext), + Users: []Models.ConversationDetailUser{ + cdu, + }, + UserConversations: []Models.UserConversation{ + uc, + }, + } + + jsonStr, _ := json.Marshal(d) + req, _ := http.NewRequest("PUT", ts.URL+"/api/v1/auth/conversations", bytes.NewBuffer(jsonStr)) + req.Header.Set("Content-Type", "application/json") + + resp, err := client.Do(req) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Expected %d, recieved %d", http.StatusNoContent, resp.StatusCode) + } + + var ncd Models.ConversationDetail + err = Database.DB.First(&ncd, "id = ?", cd.ID.String()).Error + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + decodedName, err := base64.StdEncoding.DecodeString(ncd.Name) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + decrypedName, err := key.AesDecrypt(decodedName) + if err != nil { + t.Errorf("Expected %d, recieved %d", http.StatusOK, resp.StatusCode) + return + } + + if string(decrypedName) != "Not test conversation" { + t.Errorf("Expected %s, recieved %s", "Not test converation", string(decrypedName)) + } + +} diff --git a/Backend/Database/Seeder/FriendSeeder.go b/Backend/Database/Seeder/FriendSeeder.go index b4cacd5..43cdd0f 100644 --- a/Backend/Database/Seeder/FriendSeeder.go +++ b/Backend/Database/Seeder/FriendSeeder.go @@ -13,7 +13,7 @@ import ( func seedFriend(userRequestTo, userRequestFrom Models.User, accepted bool) error { var ( friendRequest Models.FriendRequest - symKey aesKey + symKey AesKey encPublicKey []byte err error ) diff --git a/Backend/Database/Seeder/MessageSeeder.go b/Backend/Database/Seeder/MessageSeeder.go index 1b4b0a8..a117742 100644 --- a/Backend/Database/Seeder/MessageSeeder.go +++ b/Backend/Database/Seeder/MessageSeeder.go @@ -17,7 +17,7 @@ func seedMessage( var ( message Models.Message messageData Models.MessageData - key, userKey aesKey + key, userKey AesKey keyCiphertext []byte plaintext string dataCiphertext []byte @@ -91,7 +91,7 @@ func seedMessage( return Database.CreateMessage(&message) } -func seedConversationDetail(key aesKey) (Models.ConversationDetail, error) { +func seedConversationDetail(key AesKey) (Models.ConversationDetail, error) { var ( messageThread Models.ConversationDetail name string @@ -124,7 +124,7 @@ func seedConversationDetail(key aesKey) (Models.ConversationDetail, error) { func seedUserConversation( user Models.User, threadID uuid.UUID, - key aesKey, + key AesKey, ) (Models.UserConversation, error) { var ( messageThreadUser Models.UserConversation @@ -161,7 +161,7 @@ func seedConversationDetailUser( conversationDetail Models.ConversationDetail, associationKey uuid.UUID, admin bool, - key aesKey, + key AesKey, ) (Models.ConversationDetailUser, error) { var ( conversationDetailUser Models.ConversationDetailUser @@ -224,7 +224,7 @@ func seedConversationDetailUser( func SeedMessages() { var ( conversationDetail Models.ConversationDetail - key aesKey + key AesKey primaryUser Models.User primaryUserAssociationKey uuid.UUID secondaryUser Models.User diff --git a/Backend/Database/Seeder/UserSeeder.go b/Backend/Database/Seeder/UserSeeder.go index e47f983..eae651f 100644 --- a/Backend/Database/Seeder/UserSeeder.go +++ b/Backend/Database/Seeder/UserSeeder.go @@ -25,7 +25,7 @@ var userNames = []string{ func createUser(username string) (Models.User, error) { var ( userData Models.User - userKey aesKey + userKey AesKey password string err error ) diff --git a/Backend/Database/Seeder/encryption.go b/Backend/Database/Seeder/encryption.go index 101e5a4..16afc11 100644 --- a/Backend/Database/Seeder/encryption.go +++ b/Backend/Database/Seeder/encryption.go @@ -17,12 +17,12 @@ import ( "golang.org/x/crypto/pbkdf2" ) -type aesKey struct { +type AesKey struct { Key []byte Iv []byte } -func (key aesKey) encode() string { +func (key AesKey) encode() string { return base64.StdEncoding.EncodeToString(key.Key) } @@ -71,7 +71,7 @@ func pkcs7strip(data []byte, blockSize int) ([]byte, error) { return data[:length-padLen], nil } -func GenerateAesKey() (aesKey, error) { +func GenerateAesKey() (AesKey, error) { var ( saltBytes []byte = []byte{} password []byte @@ -83,22 +83,22 @@ func GenerateAesKey() (aesKey, error) { password = make([]byte, 64) _, err = rand.Read(password) if err != nil { - return aesKey{}, err + return AesKey{}, err } seed = make([]byte, 64) _, err = rand.Read(seed) if err != nil { - return aesKey{}, err + return AesKey{}, err } iv = make([]byte, 16) _, err = rand.Read(iv) if err != nil { - return aesKey{}, err + return AesKey{}, err } - return aesKey{ + return AesKey{ Key: pbkdf2.Key( password, saltBytes, @@ -110,7 +110,7 @@ func GenerateAesKey() (aesKey, error) { }, nil } -func (key aesKey) AesEncrypt(plaintext []byte) ([]byte, error) { +func (key AesKey) AesEncrypt(plaintext []byte) ([]byte, error) { var ( bPlaintext []byte ciphertext []byte @@ -134,7 +134,7 @@ func (key aesKey) AesEncrypt(plaintext []byte) ([]byte, error) { return ciphertext, nil } -func (key aesKey) AesDecrypt(ciphertext []byte) ([]byte, error) { +func (key AesKey) AesDecrypt(ciphertext []byte) ([]byte, error) { var ( plaintext []byte iv []byte