#4 feature/dockerize-server

Merged
tovi merged 6 commits from feature/dockerize-server into develop 2 years ago
  1. +13
    -3
      Backend/Api/Auth/AddProfileImage.go
  2. +79
    -0
      Backend/Api/Auth/AddProfileImage_test.go
  3. +8
    -4
      Backend/Api/Auth/ChangeMessageExpiry.go
  4. +89
    -0
      Backend/Api/Auth/ChangeMessageExpiry_test.go
  5. +3
    -3
      Backend/Api/Auth/ChangePassword.go
  6. +128
    -0
      Backend/Api/Auth/ChangePassword_test.go
  7. +2
    -2
      Backend/Api/Auth/Login.go
  8. +95
    -0
      Backend/Api/Auth/Login_test.go
  9. +5
    -2
      Backend/Api/Auth/Logout.go
  10. +44
    -0
      Backend/Api/Auth/Logout_test.go
  11. +3
    -3
      Backend/Api/Auth/Session.go
  12. +29
    -57
      Backend/Api/Auth/Signup.go
  13. +168
    -0
      Backend/Api/Auth/Signup_test.go
  14. BIN
      Backend/Api/Auth/profile_picture_test.png
  15. +2
    -2
      Backend/Api/Friends/AcceptFriendRequest.go
  16. +87
    -0
      Backend/Api/Friends/CreateFriendRequest.go
  17. +0
    -41
      Backend/Api/Friends/EncryptedFriendsList.go
  18. +3
    -3
      Backend/Api/Friends/FriendRequest.go
  19. +17
    -56
      Backend/Api/Friends/Friends.go
  20. +124
    -0
      Backend/Api/Friends/Friends_test.go
  21. +2
    -2
      Backend/Api/Friends/RejectFriendRequest.go
  22. +0
    -76
      Backend/Api/JsonSerialization/DeserializeUserJson.go
  23. +0
    -109
      Backend/Api/JsonSerialization/VerifyJson.go
  24. +3
    -3
      Backend/Api/Messages/AddConversationImage.go
  25. +17
    -10
      Backend/Api/Messages/Conversations.go
  26. +256
    -0
      Backend/Api/Messages/Conversations_test.go
  27. +3
    -3
      Backend/Api/Messages/CreateConversation.go
  28. +128
    -0
      Backend/Api/Messages/CreateConversation_test.go
  29. +21
    -4
      Backend/Api/Messages/CreateMessage.go
  30. +133
    -0
      Backend/Api/Messages/CreateMessage_test.go
  31. +14
    -3
      Backend/Api/Messages/MessageThread.go
  32. +205
    -0
      Backend/Api/Messages/MessageThread_test.go
  33. +2
    -2
      Backend/Api/Messages/UpdateConversation.go
  34. +183
    -0
      Backend/Api/Messages/UpdateConversation_test.go
  35. +7
    -7
      Backend/Api/Routes.go
  36. +2
    -3
      Backend/Api/Users/SearchUsers.go
  37. +106
    -0
      Backend/Api/Users/SearchUsers_test.go
  38. +1
    -1
      Backend/Database/Attachments.go
  39. +1
    -1
      Backend/Database/ConversationDetailUsers.go
  40. +1
    -1
      Backend/Database/ConversationDetails.go
  41. +8
    -2
      Backend/Database/FriendRequests.go
  42. +14
    -14
      Backend/Database/Init.go
  43. +1
    -1
      Backend/Database/MessageData.go
  44. +8
    -3
      Backend/Database/Messages.go
  45. +8
    -8
      Backend/Database/Seeder/FriendSeeder.go
  46. +26
    -26
      Backend/Database/Seeder/MessageSeeder.go
  47. +35
    -12
      Backend/Database/Seeder/Seed.go
  48. +8
    -8
      Backend/Database/Seeder/UserSeeder.go
  49. +15
    -10
      Backend/Database/Seeder/encryption.go
  50. +12
    -4
      Backend/Database/Sessions.go
  51. +9
    -3
      Backend/Database/UserConversations.go
  52. +1
    -1
      Backend/Database/Users.go
  53. +16
    -0
      Backend/Dockerfile
  54. +3
    -0
      Backend/Models/Conversations.go
  55. +2
    -0
      Backend/Models/Friends.go
  56. +2
    -1
      Backend/Models/Messages.go
  57. +31
    -1
      Backend/Models/Users.go
  58. +92
    -0
      Backend/Tests/Init.go
  59. +1
    -8
      Backend/Util/Files.go
  60. +6
    -0
      Backend/Util/Strings.go
  61. +2
    -2
      Backend/Util/UserHelper.go
  62. +1
    -2
      Backend/go.mod
  63. +3
    -3
      Backend/main.go
  64. +1
    -1
      README.md
  65. +49
    -0
      docker-compose.yml
  66. +1
    -1
      mobile/android/app/src/main/AndroidManifest.xml
  67. +2
    -2
      mobile/lib/components/view_image.dart
  68. +1
    -1
      mobile/lib/main.dart
  69. +2
    -2
      mobile/lib/models/conversation_users.dart
  70. +2
    -2
      mobile/lib/models/conversations.dart
  71. +3
    -3
      mobile/lib/models/image_message.dart
  72. +1
    -1
      mobile/lib/models/my_profile.dart
  73. +2
    -2
      mobile/lib/utils/storage/conversations.dart
  74. +2
    -2
      mobile/lib/utils/storage/database.dart
  75. +2
    -2
      mobile/lib/utils/storage/messages.dart
  76. +1
    -1
      mobile/lib/views/authentication/login.dart
  77. +3
    -3
      mobile/lib/views/authentication/signup.dart
  78. +1
    -1
      mobile/lib/views/authentication/unauthenticated_landing.dart
  79. +2
    -2
      mobile/lib/views/main/conversation/create_add_users_list.dart
  80. +1
    -1
      mobile/lib/views/main/conversation/detail.dart
  81. +1
    -1
      mobile/lib/views/main/conversation/edit_details.dart
  82. +3
    -3
      mobile/lib/views/main/conversation/list.dart
  83. +4
    -4
      mobile/lib/views/main/conversation/message.dart
  84. +7
    -7
      mobile/lib/views/main/conversation/settings.dart
  85. +4
    -4
      mobile/lib/views/main/friend/list.dart
  86. +6
    -6
      mobile/lib/views/main/friend/list_item.dart
  87. +1
    -1
      mobile/lib/views/main/profile/change_server_url.dart
  88. +5
    -5
      mobile/lib/views/main/profile/profile.dart
  89. +2
    -2
      mobile/pubspec.yaml
  90. +3
    -0
      test.sh

+ 13
- 3
Backend/Api/Auth/AddProfileImage.go View File

@ -5,9 +5,9 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Util"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Util"
) )
// AddProfileImage adds a profile image // AddProfileImage adds a profile image
@ -35,7 +35,17 @@ func AddProfileImage(w http.ResponseWriter, r *http.Request) {
} }
decodedFile, err = base64.StdEncoding.DecodeString(attachment.Data) decodedFile, err = base64.StdEncoding.DecodeString(attachment.Data)
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
fileName, err = Util.WriteFile(decodedFile) fileName, err = Util.WriteFile(decodedFile)
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
attachment.FilePath = fileName attachment.FilePath = fileName
user.Attachment = attachment user.Attachment = attachment


+ 79
- 0
Backend/Api/Auth/AddProfileImage_test.go View File

@ -0,0 +1,79 @@
package Auth_test
import (
"bytes"
"encoding/base64"
"encoding/json"
"net/http"
"os"
"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_AddProfileImage(t *testing.T) {
client, ts, err := Tests.InitTestEnv()
defer ts.Close()
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
}
dat, err := os.ReadFile("./profile_picture_test.png")
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
encDat, err := key.AesEncrypt(dat)
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
a := Models.Attachment{
Mimetype: "image/png",
Extension: "png",
Data: base64.StdEncoding.EncodeToString(encDat),
}
jsonStr, _ := json.Marshal(a)
req, _ := http.NewRequest("POST", ts.URL+"/api/v1/auth/image", 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)
return
}
u, err := Database.GetUserByUsername("test")
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
if u.AttachmentID.IsNil() {
t.Errorf("Attachment not assigned to user")
}
err = os.Remove("/app/attachments/" + u.Attachment.FilePath)
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
}

+ 8
- 4
Backend/Api/Auth/ChangeMessageExpiry.go View File

@ -5,8 +5,8 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
) )
type rawChangeMessageExpiry struct { type rawChangeMessageExpiry struct {
@ -37,7 +37,11 @@ func ChangeMessageExpiry(w http.ResponseWriter, r *http.Request) {
return return
} }
user.MessageExpiryDefault.Scan(changeMessageExpiry.MessageExpiry)
err = user.MessageExpiryDefault.Scan(changeMessageExpiry.MessageExpiry)
if err != nil {
http.Error(w, "Error", http.StatusUnprocessableEntity)
return
}
err = Database.UpdateUser( err = Database.UpdateUser(
user.ID.String(), user.ID.String(),
@ -48,5 +52,5 @@ func ChangeMessageExpiry(w http.ResponseWriter, r *http.Request) {
return return
} }
w.WriteHeader(http.StatusOK)
w.WriteHeader(http.StatusNoContent)
} }

+ 89
- 0
Backend/Api/Auth/ChangeMessageExpiry_test.go View File

@ -0,0 +1,89 @@
package Auth_test
import (
"bytes"
"encoding/json"
"net/http"
"testing"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Tests"
)
func Test_ChangeMessageExpiry(t *testing.T) {
client, ts, err := Tests.InitTestEnv()
defer ts.Close()
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
d := struct {
MessageExpiry string `json:"message_expiry"`
}{
MessageExpiry: "fifteen_min",
}
jsonStr, _ := json.Marshal(d)
req, _ := http.NewRequest("POST", ts.URL+"/api/v1/auth/message_expiry", 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)
}
u, err := Database.GetUserByUsername("test")
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
if u.MessageExpiryDefault.String() != "fifteen_min" {
t.Errorf("Failed to verify the MessageExpiryDefault has been changed")
}
}
func Test_ChangeMessageExpiryInvalidData(t *testing.T) {
client, ts, err := Tests.InitTestEnv()
defer ts.Close()
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
d := struct {
MessageExpiry string `json:"message_expiry"`
}{
MessageExpiry: "invalid_message_expiry",
}
jsonStr, _ := json.Marshal(d)
req, _ := http.NewRequest("POST", ts.URL+"/api/v1/auth/message_expiry", 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.StatusUnprocessableEntity {
t.Errorf("Expected %d, recieved %d", http.StatusUnprocessableEntity, resp.StatusCode)
}
u, err := Database.GetUserByUsername("test")
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
if u.MessageExpiryDefault.String() != "no_expiry" {
t.Errorf("Failed to verify the MessageExpiryDefault has not been changed")
}
}

+ 3
- 3
Backend/Api/Auth/ChangePassword.go View File

@ -5,8 +5,8 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
) )
type rawChangePassword struct { type rawChangePassword struct {
@ -72,5 +72,5 @@ func ChangePassword(w http.ResponseWriter, r *http.Request) {
return return
} }
w.WriteHeader(http.StatusOK)
w.WriteHeader(http.StatusNoContent)
} }

+ 128
- 0
Backend/Api/Auth/ChangePassword_test.go View File

@ -0,0 +1,128 @@
package Auth_test
import (
"bytes"
"encoding/json"
"net/http"
"testing"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Api/Auth"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Tests"
)
func Test_ChangePassword(t *testing.T) {
client, ts, err := Tests.InitTestEnv()
defer ts.Close()
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
d := struct {
OldPassword string `json:"old_password"`
NewPassword string `json:"new_password"`
NewPasswordConfirm string `json:"new_password_confirm"`
PrivateKey string `json:"private_key"`
}{
OldPassword: "password",
NewPassword: "password1",
NewPasswordConfirm: "password1",
PrivateKey: "",
}
jsonStr, _ := json.Marshal(d)
req, _ := http.NewRequest("POST", ts.URL+"/api/v1/auth/change_password", 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)
return
}
u, err := Database.GetUserByUsername("test")
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
if !Auth.CheckPasswordHash("password1", u.Password) {
t.Errorf("Failed to verify the password has been changed")
}
}
func Test_ChangePasswordMismatchConfirmFails(t *testing.T) {
client, ts, err := Tests.InitTestEnv()
defer ts.Close()
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
d := struct {
OldPassword string `json:"old_password"`
NewPassword string `json:"new_password"`
NewPasswordConfirm string `json:"new_password_confirm"`
PrivateKey string `json:"private_key"`
}{
OldPassword: "password",
NewPassword: "password1",
NewPasswordConfirm: "password2",
PrivateKey: "",
}
jsonStr, _ := json.Marshal(d)
req, _ := http.NewRequest("POST", ts.URL+"/api/v1/auth/change_password", 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.StatusUnprocessableEntity {
t.Errorf("Expected %d, recieved %d", http.StatusUnprocessableEntity, resp.StatusCode)
}
}
func Test_ChangePasswordInvalidCurrentPasswordFails(t *testing.T) {
client, ts, err := Tests.InitTestEnv()
defer ts.Close()
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
d := struct {
OldPassword string `json:"old_password"`
NewPassword string `json:"new_password"`
NewPasswordConfirm string `json:"new_password_confirm"`
PrivateKey string `json:"private_key"`
}{
OldPassword: "password2",
NewPassword: "password1",
NewPasswordConfirm: "password1",
PrivateKey: "",
}
jsonStr, _ := json.Marshal(d)
req, _ := http.NewRequest("POST", ts.URL+"/api/v1/auth/change_password", 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.StatusForbidden {
t.Errorf("Expected %d, recieved %d", http.StatusForbidden, resp.StatusCode)
}
}

+ 2
- 2
Backend/Api/Auth/Login.go View File

@ -6,8 +6,8 @@ import (
"net/http" "net/http"
"time" "time"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
) )
type credentials struct { type credentials struct {


+ 95
- 0
Backend/Api/Auth/Login_test.go View File

@ -0,0 +1,95 @@
package Auth_test
import (
"bytes"
"encoding/json"
"net/http"
"testing"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Tests"
)
func Test_Login(t *testing.T) {
_, ts, err := Tests.InitTestEnv()
defer ts.Close()
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
d := struct {
Username string `json:"username"`
Password string `json:"password"`
}{
Username: "test",
Password: "password",
}
jsonStr, _ := json.Marshal(d)
req, _ := http.NewRequest("POST", ts.URL+"/api/v1/login", bytes.NewBuffer(jsonStr))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
if resp.StatusCode != http.StatusOK {
t.Errorf("Expected %d, recieved %d", http.StatusOK, resp.StatusCode)
return
}
u, err := Database.GetUserByUsername("test")
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
var session Models.Session
err = Database.DB.First(&session, "user_id = ?", u.ID.String()).Error
if err != nil {
t.Errorf("Expected user record, recieved %s", err.Error())
return
}
}
func Test_Login_PasswordFails(t *testing.T) {
_, ts, err := Tests.InitTestEnv()
defer ts.Close()
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
d := struct {
Username string `json:"username"`
Password string `json:"password"`
}{
Username: "test",
Password: "password1",
}
jsonStr, _ := json.Marshal(d)
req, _ := http.NewRequest("POST", ts.URL+"/api/v1/login", bytes.NewBuffer(jsonStr))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("Expected %d, recieved %d", http.StatusUnauthorized, resp.StatusCode)
return
}
}

+ 5
- 2
Backend/Api/Auth/Logout.go View File

@ -5,9 +5,10 @@ import (
"net/http" "net/http"
"time" "time"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
) )
// Logout logs out from system
func Logout(w http.ResponseWriter, r *http.Request) { func Logout(w http.ResponseWriter, r *http.Request) {
var ( var (
c *http.Cookie c *http.Cookie
@ -27,7 +28,7 @@ func Logout(w http.ResponseWriter, r *http.Request) {
sessionToken = c.Value sessionToken = c.Value
err = Database.DeleteSessionById(sessionToken)
err = Database.DeleteSessionByID(sessionToken)
if err != nil { if err != nil {
log.Println("Could not delete session cookie") log.Println("Could not delete session cookie")
} }
@ -37,4 +38,6 @@ func Logout(w http.ResponseWriter, r *http.Request) {
Value: "", Value: "",
Expires: time.Now(), Expires: time.Now(),
}) })
w.WriteHeader(http.StatusOK)
} }

+ 44
- 0
Backend/Api/Auth/Logout_test.go View File

@ -0,0 +1,44 @@
package Auth_test
import (
"net/http"
"testing"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Tests"
)
func Test_Logout(t *testing.T) {
client, ts, err := Tests.InitTestEnv()
defer ts.Close()
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
resp, err := client.Get(ts.URL + "/api/v1/logout")
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
}
var session Models.Session
u, err := Database.GetUserByUsername("test")
if err != nil {
t.Errorf("Expected user record, recieved %s", err.Error())
return
}
err = Database.DB.First(&session, "user_id = ?", u.ID.String()).Error
if err == nil {
t.Errorf("Expected no session record, recieved %s", session.UserID)
return
}
}

+ 3
- 3
Backend/Api/Auth/Session.go View File

@ -4,8 +4,8 @@ import (
"errors" "errors"
"net/http" "net/http"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
) )
func CheckCookie(r *http.Request) (Models.Session, error) { func CheckCookie(r *http.Request) (Models.Session, error) {
@ -23,7 +23,7 @@ func CheckCookie(r *http.Request) (Models.Session, error) {
sessionToken = c.Value sessionToken = c.Value
// We then get the session from our session map // We then get the session from our session map
userSession, err = Database.GetSessionById(sessionToken)
userSession, err = Database.GetSessionByID(sessionToken)
if err != nil { if err != nil {
return userSession, errors.New("Cookie not found") return userSession, errors.New("Cookie not found")
} }


+ 29
- 57
Backend/Api/Auth/Signup.go View File

@ -2,95 +2,67 @@ package Auth
import ( import (
"encoding/json" "encoding/json"
"io/ioutil"
"log"
"net/http" "net/http"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Api/JsonSerialization"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
) )
type signupResponse struct {
Status string `json:"status"`
Message string `json:"message"`
}
func makeSignupResponse(w http.ResponseWriter, code int, message string) {
var (
status = "error"
returnJSON []byte
err error
)
if code > 200 && code < 300 {
status = "success"
}
returnJSON, err = json.MarshalIndent(signupResponse{
Status: status,
Message: message,
}, "", " ")
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
// Return updated json
w.WriteHeader(code)
w.Write(returnJSON)
type signup struct {
Username string `json:"username"`
Password string `json:"password"`
ConfirmPassword string `json:"confirm_password"`
PublicKey string `json:"asymmetric_public_key"`
PrivateKey string `json:"asymmetric_private_key"`
SymmetricKey string `json:"symmetric_key"`
} }
// Signup to the platform // Signup to the platform
func Signup(w http.ResponseWriter, r *http.Request) { func Signup(w http.ResponseWriter, r *http.Request) {
var ( var (
userData Models.User
requestBody []byte
user Models.User
err error err error
) )
requestBody, err = ioutil.ReadAll(r.Body)
err = json.NewDecoder(r.Body).Decode(&user)
if err != nil { if err != nil {
log.Printf("Error encountered reading POST body: %s\n", err.Error())
makeSignupResponse(w, http.StatusInternalServerError, "An error occurred")
http.Error(w, "Invalid Data", http.StatusUnprocessableEntity)
return return
} }
userData, err = JsonSerialization.DeserializeUser(requestBody, []string{
"id",
}, false)
if err != nil {
log.Printf("Invalid data provided to Signup: %s\n", err.Error())
makeSignupResponse(w, http.StatusUnprocessableEntity, "Invalid data provided")
if user.Username == "" ||
user.Password == "" ||
user.ConfirmPassword == "" ||
len(user.AsymmetricPrivateKey) == 0 ||
len(user.AsymmetricPublicKey) == 0 ||
len(user.SymmetricKey) == 0 {
http.Error(w, "Invalid Data", http.StatusUnprocessableEntity)
return return
} }
if userData.Username == "" ||
userData.Password == "" ||
userData.ConfirmPassword == "" ||
len(userData.AsymmetricPrivateKey) == 0 ||
len(userData.AsymmetricPublicKey) == 0 {
makeSignupResponse(w, http.StatusUnprocessableEntity, "Invalid data provided")
if user.Password != user.ConfirmPassword {
http.Error(w, "Invalid Data", http.StatusUnprocessableEntity)
return return
} }
err = Database.CheckUniqueUsername(userData.Username)
err = Database.CheckUniqueUsername(user.Username)
if err != nil { if err != nil {
makeSignupResponse(w, http.StatusUnprocessableEntity, "Invalid data provided")
http.Error(w, "Invalid Data", http.StatusUnprocessableEntity)
return return
} }
userData.Password, err = HashPassword(userData.Password)
user.Password, err = HashPassword(user.Password)
if err != nil { if err != nil {
makeSignupResponse(w, http.StatusInternalServerError, "An error occurred")
http.Error(w, "Error", http.StatusInternalServerError)
return return
} }
err = Database.CreateUser(&userData)
err = Database.CreateUser(&user)
if err != nil { if err != nil {
makeSignupResponse(w, http.StatusInternalServerError, "An error occurred")
http.Error(w, "Error", http.StatusInternalServerError)
return return
} }
makeSignupResponse(w, http.StatusCreated, "Successfully signed up")
w.WriteHeader(http.StatusNoContent)
} }

+ 168
- 0
Backend/Api/Auth/Signup_test.go View File

@ -0,0 +1,168 @@
package Auth_test
import (
"bytes"
"encoding/base64"
"encoding/json"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"testing"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Api"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database/Seeder"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"github.com/gorilla/mux"
)
func Test_Signup(t *testing.T) {
log.SetOutput(ioutil.Discard)
Database.InitTest()
r := mux.NewRouter()
Api.InitAPIEndpoints(r)
ts := httptest.NewServer(r)
defer ts.Close()
userKey, _ := Seeder.GenerateAesKey()
pubKey := Seeder.GetPubKey()
d := struct {
Username string `json:"username"`
Password string `json:"password"`
ConfirmPassword string `json:"confirm_password"`
PubKey string `json:"asymmetric_public_key"`
PrivKey string `json:"asymmetric_private_key"`
SymKey string `json:"symmetric_key"`
}{
Username: "test",
Password: "password",
ConfirmPassword: "password",
PubKey: Seeder.PublicKey,
PrivKey: Seeder.EncryptedPrivateKey,
SymKey: base64.StdEncoding.EncodeToString(
Seeder.EncryptWithPublicKey(userKey.Key, pubKey),
),
}
jsonStr, _ := json.Marshal(d)
req, _ := http.NewRequest("POST", ts.URL+"/api/v1/signup", bytes.NewBuffer(jsonStr))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
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)
return
}
var user Models.User
err = Database.DB.First(&user, "username = ?", "test").Error
if err != nil {
t.Errorf("Expected user record, recieved %s", err.Error())
return
}
}
func Test_Signup_PasswordMismatchFails(t *testing.T) {
log.SetOutput(ioutil.Discard)
Database.InitTest()
r := mux.NewRouter()
Api.InitAPIEndpoints(r)
ts := httptest.NewServer(r)
defer ts.Close()
userKey, _ := Seeder.GenerateAesKey()
pubKey := Seeder.GetPubKey()
d := struct {
Username string `json:"username"`
Password string `json:"password"`
ConfirmPassword string `json:"confirm_password"`
PubKey string `json:"asymmetric_public_key"`
PrivKey string `json:"asymmetric_private_key"`
SymKey string `json:"symmetric_key"`
}{
Username: "test",
Password: "password",
ConfirmPassword: "password1",
PubKey: Seeder.PublicKey,
PrivKey: Seeder.EncryptedPrivateKey,
SymKey: base64.StdEncoding.EncodeToString(
Seeder.EncryptWithPublicKey(userKey.Key, pubKey),
),
}
jsonStr, _ := json.Marshal(d)
req, _ := http.NewRequest("POST", ts.URL+"/api/v1/signup", bytes.NewBuffer(jsonStr))
req.Header.Set("X-Custom-Header", "myvalue")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
if resp.StatusCode != http.StatusUnprocessableEntity {
t.Errorf("Expected %d, recieved %d", http.StatusUnprocessableEntity, resp.StatusCode)
return
}
}
func Test_Signup_MissingDataFails(t *testing.T) {
log.SetOutput(ioutil.Discard)
Database.InitTest()
r := mux.NewRouter()
Api.InitAPIEndpoints(r)
ts := httptest.NewServer(r)
defer ts.Close()
d := struct {
Username string `json:"username"`
Password string `json:"password"`
ConfirmPassword string `json:"confirm_password"`
PubKey string `json:"asymmetric_public_key"`
PrivKey string `json:"asymmetric_private_key"`
SymKey string `json:"symmetric_key"`
}{
Username: "test",
Password: "password",
ConfirmPassword: "password",
PubKey: "",
PrivKey: "",
SymKey: "",
}
jsonStr, _ := json.Marshal(d)
req, _ := http.NewRequest("POST", ts.URL+"/api/v1/signup", bytes.NewBuffer(jsonStr))
req.Header.Set("X-Custom-Header", "myvalue")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
}
if resp.StatusCode != http.StatusUnprocessableEntity {
t.Errorf("Expected %d, recieved %d", http.StatusUnprocessableEntity, resp.StatusCode)
}
}

BIN
Backend/Api/Auth/profile_picture_test.png View File

Before After
Width: 487  |  Height: 530  |  Size: 136 KiB

+ 2
- 2
Backend/Api/Friends/AcceptFriendRequest.go View File

@ -6,8 +6,8 @@ import (
"net/http" "net/http"
"time" "time"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )


+ 87
- 0
Backend/Api/Friends/CreateFriendRequest.go View File

@ -0,0 +1,87 @@
package Friends
import (
"encoding/json"
"io/ioutil"
"net/http"
"time"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
)
// CreateFriendRequest creates a FriendRequest from post data
func CreateFriendRequest(w http.ResponseWriter, r *http.Request) {
var (
friendRequest Models.FriendRequest
requestBody []byte
returnJSON []byte
err error
)
requestBody, err = ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
err = json.Unmarshal(requestBody, &friendRequest)
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
friendRequest.AcceptedAt.Scan(nil)
err = Database.CreateFriendRequest(&friendRequest)
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
returnJSON, err = json.MarshalIndent(friendRequest, "", " ")
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
// Return updated json
w.WriteHeader(http.StatusOK)
w.Write(returnJSON)
}
// CreateFriendRequestQrCode creates a FriendRequest from post data from qr code scan
func CreateFriendRequestQrCode(w http.ResponseWriter, r *http.Request) {
var (
friendRequests []Models.FriendRequest
requestBody []byte
i int
err error
)
requestBody, err = ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
err = json.Unmarshal(requestBody, &friendRequests)
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
for i = range friendRequests {
friendRequests[i].AcceptedAt.Time = time.Now()
friendRequests[i].AcceptedAt.Valid = true
}
err = Database.CreateFriendRequests(&friendRequests)
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
// Return updated json
w.WriteHeader(http.StatusOK)
}

+ 0
- 41
Backend/Api/Friends/EncryptedFriendsList.go View File

@ -1,41 +0,0 @@
package Friends
import (
"encoding/json"
"net/http"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Api/Auth"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
)
// EncryptedFriendRequestList gets friend request list
func EncryptedFriendRequestList(w http.ResponseWriter, r *http.Request) {
var (
userSession Models.Session
friends []Models.FriendRequest
returnJSON []byte
err error
)
userSession, err = Auth.CheckCookie(r)
if err != nil {
http.Error(w, "Forbidden", http.StatusUnauthorized)
return
}
friends, err = Database.GetFriendRequestsByUserID(userSession.UserID.String())
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
returnJSON, err = json.MarshalIndent(friends, "", " ")
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
w.Write(returnJSON)
}

+ 3
- 3
Backend/Api/Friends/FriendRequest.go View File

@ -5,9 +5,9 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Util"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Util"
) )
func FriendRequest(w http.ResponseWriter, r *http.Request) { func FriendRequest(w http.ResponseWriter, r *http.Request) {


+ 17
- 56
Backend/Api/Friends/Friends.go View File

@ -2,86 +2,47 @@ package Friends
import ( import (
"encoding/json" "encoding/json"
"io/ioutil"
"net/http" "net/http"
"time"
"net/url"
"strconv"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Api/Auth"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
) )
// CreateFriendRequest creates a FriendRequest from post data
func CreateFriendRequest(w http.ResponseWriter, r *http.Request) {
// FriendRequestList gets friend request list
func FriendRequestList(w http.ResponseWriter, r *http.Request) {
var ( var (
friendRequest Models.FriendRequest
requestBody []byte
userSession Models.Session
friends []Models.FriendRequest
values url.Values
returnJSON []byte returnJSON []byte
page int
err error err error
) )
requestBody, err = ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
values = r.URL.Query()
err = json.Unmarshal(requestBody, &friendRequest)
page, err = strconv.Atoi(values.Get("page"))
if err != nil { if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
page = 0
} }
friendRequest.AcceptedAt.Scan(nil)
userSession, _ = Auth.CheckCookie(r)
err = Database.CreateFriendRequest(&friendRequest)
friends, err = Database.GetFriendRequestsByUserID(userSession.UserID.String(), page)
if err != nil { if err != nil {
http.Error(w, "Error", http.StatusInternalServerError) http.Error(w, "Error", http.StatusInternalServerError)
return return
} }
returnJSON, err = json.MarshalIndent(friendRequest, "", " ")
returnJSON, err = json.MarshalIndent(friends, "", " ")
if err != nil { if err != nil {
http.Error(w, "Error", http.StatusInternalServerError) http.Error(w, "Error", http.StatusInternalServerError)
return return
} }
// Return updated json
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write(returnJSON) w.Write(returnJSON)
} }
// CreateFriendRequestQrCode creates a FriendRequest from post data from qr code scan
func CreateFriendRequestQrCode(w http.ResponseWriter, r *http.Request) {
var (
friendRequests []Models.FriendRequest
requestBody []byte
i int
err error
)
requestBody, err = ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
err = json.Unmarshal(requestBody, &friendRequests)
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
for i = range friendRequests {
friendRequests[i].AcceptedAt.Time = time.Now()
friendRequests[i].AcceptedAt.Valid = true
}
err = Database.CreateFriendRequests(&friendRequests)
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
// Return updated json
w.WriteHeader(http.StatusOK)
}

+ 124
- 0
Backend/Api/Friends/Friends_test.go View File

@ -0,0 +1,124 @@
package Friends_test
import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"testing"
"time"
"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_FriendRequestList(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")
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
for i := 0; i < 30; i++ {
u2, err := Tests.InitTestCreateUser(fmt.Sprintf("test%d", i))
decodedPublicKey := Seeder.GetPubKey()
key, err := Seeder.GenerateAesKey()
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
encPublicKey, err := key.AesEncrypt([]byte(Seeder.PublicKey))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
friendReq := Models.FriendRequest{
UserID: u.ID,
FriendID: base64.StdEncoding.EncodeToString(
Seeder.EncryptWithPublicKey(
[]byte(u2.ID.String()),
decodedPublicKey,
),
),
FriendUsername: base64.StdEncoding.EncodeToString(
Seeder.EncryptWithPublicKey(
[]byte(u2.Username),
decodedPublicKey,
),
),
FriendPublicAsymmetricKey: base64.StdEncoding.EncodeToString(
encPublicKey,
),
SymmetricKey: base64.StdEncoding.EncodeToString(
Seeder.EncryptWithPublicKey(key.Key, decodedPublicKey),
),
}
if i > 20 {
friendReq.AcceptedAt.Time = time.Now()
friendReq.AcceptedAt.Valid = true
}
err = Database.CreateFriendRequest(&friendReq)
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
}
req, _ := http.NewRequest("GET", ts.URL+"/api/v1/auth/friend_requests", nil)
resp, err := client.Do(req)
if err != nil {
t.Errorf("Expected nil, 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 %d, recieved %d", http.StatusOK, resp.StatusCode)
return
}
var users []Models.FriendRequest
json.Unmarshal(requestBody, &users)
if len(users) != 20 {
t.Errorf("Expected %d, recieved %d", 1, len(users))
return
}
for i := 0; i < 20; i++ {
eq := true
if i > 8 {
eq = false
}
if users[i].AcceptedAt.Valid != eq {
t.Errorf(
"Expected %v, recieved %v, on user %d",
eq, users[i].AcceptedAt.Valid,
i,
)
return
}
}
}

+ 2
- 2
Backend/Api/Friends/RejectFriendRequest.go View File

@ -3,8 +3,8 @@ package Friends
import ( import (
"net/http" "net/http"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )


+ 0
- 76
Backend/Api/JsonSerialization/DeserializeUserJson.go View File

@ -1,76 +0,0 @@
package JsonSerialization
import (
"encoding/json"
"errors"
"fmt"
"strings"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
schema "github.com/Kangaroux/go-map-schema"
)
func DeserializeUser(data []byte, allowMissing []string, allowAllMissing bool) (Models.User, error) {
var (
userData Models.User = Models.User{}
jsonStructureTest map[string]interface{} = make(map[string]interface{})
jsonStructureTestResults *schema.CompareResults
field schema.FieldMissing
allowed string
missingFields []string
i int
err error
)
// Verify the JSON has the correct structure
json.Unmarshal(data, &jsonStructureTest)
jsonStructureTestResults, err = schema.CompareMapToStruct(
&userData,
jsonStructureTest,
&schema.CompareOpts{
ConvertibleFunc: CanConvert,
TypeNameFunc: schema.DetailedTypeName,
})
if err != nil {
return userData, err
}
if len(jsonStructureTestResults.MismatchedFields) > 0 {
return userData, errors.New(fmt.Sprintf(
"MismatchedFields found when deserializing data: %s",
jsonStructureTestResults.Errors().Error(),
))
}
// Remove allowed missing fields from MissingFields
for _, allowed = range allowMissing {
for i, field = range jsonStructureTestResults.MissingFields {
if allowed == field.String() {
jsonStructureTestResults.MissingFields = append(
jsonStructureTestResults.MissingFields[:i],
jsonStructureTestResults.MissingFields[i+1:]...,
)
}
}
}
if !allowAllMissing && len(jsonStructureTestResults.MissingFields) > 0 {
for _, field = range jsonStructureTestResults.MissingFields {
missingFields = append(missingFields, field.String())
}
return userData, errors.New(fmt.Sprintf(
"MissingFields found when deserializing data: %s",
strings.Join(missingFields, ", "),
))
}
// Deserialize the JSON into the struct
err = json.Unmarshal(data, &userData)
if err != nil {
return userData, err
}
return userData, err
}

+ 0
- 109
Backend/Api/JsonSerialization/VerifyJson.go View File

@ -1,109 +0,0 @@
package JsonSerialization
import (
"math"
"reflect"
)
// isIntegerType returns whether the type is an integer and if it's unsigned.
// See: https://github.com/Kangaroux/go-map-schema/blob/master/schema.go#L328
func isIntegerType(t reflect.Type) (bool, bool) {
var (
yes bool
unsigned bool
)
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
yes = true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
yes = true
unsigned = true
}
return yes, unsigned
}
// isFloatType returns true if the type is a floating point. Note that this doesn't
// care about the value -- unmarshaling the number "0" gives a float, not an int.
// See: https://github.com/Kangaroux/go-map-schema/blob/master/schema.go#L319
func isFloatType(t reflect.Type) bool {
var (
yes bool
)
switch t.Kind() {
case reflect.Float32, reflect.Float64:
yes = true
}
return yes
}
// CanConvert returns whether value v is convertible to type t.
//
// If t is a pointer and v is not nil, it checks if v is convertible to the type that
// t points to.
// Modified due to not handling slices (DefaultCanConvert fails on PhotoUrls and Tags)
// See: https://github.com/Kangaroux/go-map-schema/blob/master/schema.go#L191
func CanConvert(t reflect.Type, v reflect.Value) bool {
var (
isPtr bool
isStruct bool
isArray bool
dstType reflect.Type
dstInt bool
unsigned bool
f float64
srcInt bool
)
isPtr = t.Kind() == reflect.Ptr
isStruct = t.Kind() == reflect.Struct
isArray = t.Kind() == reflect.Array
dstType = t
// Check if v is a nil value.
if !v.IsValid() || (v.CanAddr() && v.IsNil()) {
return isPtr
}
// If the dst is a pointer, check if we can convert to the type it's pointing to.
if isPtr {
dstType = t.Elem()
isStruct = t.Elem().Kind() == reflect.Struct
}
// If the dst is a struct, we should check its nested fields.
if isStruct {
return v.Kind() == reflect.Map
}
if isArray {
return v.Kind() == reflect.String
}
if t.Kind() == reflect.Slice {
return v.Kind() == reflect.Slice
}
if !v.Type().ConvertibleTo(dstType) {
return false
}
// Handle converting to an integer type.
dstInt, unsigned = isIntegerType(dstType)
if dstInt {
if isFloatType(v.Type()) {
f = v.Float()
if math.Trunc(f) != f || unsigned && f < 0 {
return false
}
}
srcInt, _ = isIntegerType(v.Type())
if srcInt && unsigned && v.Int() < 0 {
return false
}
}
return true
}

+ 3
- 3
Backend/Api/Messages/AddConversationImage.go View File

@ -5,9 +5,9 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Util"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Util"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )


+ 17
- 10
Backend/Api/Messages/Conversations.go View File

@ -4,30 +4,37 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"net/url" "net/url"
"strconv"
"strings" "strings"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Api/Auth"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Api/Auth"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
) )
// EncryptedConversationList returns an encrypted list of all Conversations
func EncryptedConversationList(w http.ResponseWriter, r *http.Request) {
// ConversationList returns an encrypted list of all Conversations
func ConversationList(w http.ResponseWriter, r *http.Request) {
var ( var (
conversationDetails []Models.UserConversation conversationDetails []Models.UserConversation
userSession Models.Session userSession Models.Session
returnJSON []byte returnJSON []byte
values url.Values
page int
err error err error
) )
userSession, err = Auth.CheckCookie(r)
values = r.URL.Query()
page, err = strconv.Atoi(values.Get("page"))
if err != nil { if err != nil {
http.Error(w, "Forbidden", http.StatusUnauthorized)
return
page = 0
} }
userSession, _ = Auth.CheckCookie(r)
conversationDetails, err = Database.GetUserConversationsByUserId( conversationDetails, err = Database.GetUserConversationsByUserId(
userSession.UserID.String(), userSession.UserID.String(),
page,
) )
if err != nil { if err != nil {
http.Error(w, "Error", http.StatusInternalServerError) http.Error(w, "Error", http.StatusInternalServerError)
@ -44,8 +51,8 @@ func EncryptedConversationList(w http.ResponseWriter, r *http.Request) {
w.Write(returnJSON) w.Write(returnJSON)
} }
// EncryptedConversationDetailsList returns an encrypted list of all ConversationDetails
func EncryptedConversationDetailsList(w http.ResponseWriter, r *http.Request) {
// ConversationDetailsList returns an encrypted list of all ConversationDetails
func ConversationDetailsList(w http.ResponseWriter, r *http.Request) {
var ( var (
conversationDetails []Models.ConversationDetail conversationDetails []Models.ConversationDetail
detail Models.ConversationDetail detail Models.ConversationDetail


+ 256
- 0
Backend/Api/Messages/Conversations_test.go View File

@ -0,0 +1,256 @@
package Messages_test
import (
"encoding/base64"
"encoding/json"
"fmt"
"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_ConversationsList(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
}
nameCiphertext, err := key.AesEncrypt([]byte("Test conversation"))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
twoUserCiphertext, err := key.AesEncrypt([]byte("false"))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
messageThread := Models.ConversationDetail{
Name: base64.StdEncoding.EncodeToString(nameCiphertext),
TwoUser: base64.StdEncoding.EncodeToString(twoUserCiphertext),
}
err = Database.CreateConversationDetail(&messageThread)
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
conversationDetailIDCiphertext, err := key.AesEncrypt([]byte(messageThread.ID.String()))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
adminCiphertext, err := key.AesEncrypt([]byte("true"))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
pubKey := Seeder.GetPubKey()
messageThreadUser := 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(&messageThreadUser)
req, _ := http.NewRequest("GET", ts.URL+"/api/v1/auth/conversations", nil)
resp, err := client.Do(req)
if err != nil {
t.Errorf("Expected nil, 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 %d, recieved %d", http.StatusOK, resp.StatusCode)
return
}
var conversations []Models.UserConversation
json.Unmarshal(requestBody, &conversations)
if len(conversations) != 1 {
t.Errorf("Expected %d, recieved %d", 1, len(conversations))
return
}
conv := conversations[0]
decodedId, err := base64.StdEncoding.DecodeString(conv.ConversationDetailID)
if err != nil {
t.Errorf("Expected %d, recieved %d", http.StatusOK, resp.StatusCode)
return
}
decrypedId, err := key.AesDecrypt(decodedId)
if err != nil {
t.Errorf("Expected %d, recieved %d", http.StatusOK, resp.StatusCode)
return
}
req, _ = http.NewRequest(
"GET",
ts.URL+"/api/v1/auth/conversation_details?conversation_detail_ids="+string(decrypedId),
nil,
)
resp, err = client.Do(req)
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
var conversationDetails []Models.ConversationDetail
requestBody, err = ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("Expected %d, recieved %d", http.StatusOK, resp.StatusCode)
return
}
json.Unmarshal(requestBody, &conversationDetails)
if len(conversationDetails) != 1 {
t.Errorf("Expected %d, recieved %d", 1, len(conversations))
}
decodedName, err := base64.StdEncoding.DecodeString(conversationDetails[0].Name)
if err != nil {
t.Errorf("Expected %d, recieved %d", http.StatusOK, resp.StatusCode)
return
}
decrypedName, err := key.AesDecrypt(decodedName)
if err != nil {
t.Errorf("Expected %d, recieved %d", http.StatusOK, resp.StatusCode)
return
}
if string(decrypedName) != "Test conversation" {
t.Errorf("Expected %s, recieved %s", "Test converation", string(decrypedName))
}
}
func Test_ConversationsListPagination(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
}
for i := 0; i < 40; i++ {
nameCiphertext, err := key.AesEncrypt([]byte(
fmt.Sprintf("Test conversation %d", i),
))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
twoUserCiphertext, err := key.AesEncrypt([]byte("false"))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
messageThread := Models.ConversationDetail{
Name: base64.StdEncoding.EncodeToString(nameCiphertext),
TwoUser: base64.StdEncoding.EncodeToString(twoUserCiphertext),
}
err = Database.CreateConversationDetail(&messageThread)
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
conversationDetailIDCiphertext, err := key.AesEncrypt([]byte(messageThread.ID.String()))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
adminCiphertext, err := key.AesEncrypt([]byte("true"))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
pubKey := Seeder.GetPubKey()
messageThreadUser := 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(&messageThreadUser)
}
req, _ := http.NewRequest("GET", ts.URL+"/api/v1/auth/conversations?page=0", nil)
resp, err := client.Do(req)
if err != nil {
t.Errorf("Expected nil, 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 %d, recieved %d", http.StatusOK, resp.StatusCode)
return
}
var conversations []Models.UserConversation
json.Unmarshal(requestBody, &conversations)
if len(conversations) != 20 {
t.Errorf("Expected %d, recieved %d", 1, len(conversations))
}
}

+ 3
- 3
Backend/Api/Messages/CreateConversation.go View File

@ -6,8 +6,8 @@ import (
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
) )
// RawCreateConversationData for holding POST payload // RawCreateConversationData for holding POST payload
@ -54,5 +54,5 @@ func CreateConversation(w http.ResponseWriter, r *http.Request) {
return return
} }
w.WriteHeader(http.StatusOK)
w.WriteHeader(http.StatusNoContent)
} }

+ 128
- 0
Backend/Api/Messages/CreateConversation_test.go View File

@ -0,0 +1,128 @@
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"
"github.com/gofrs/uuid"
)
func Test_CreateConversation(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
}
nameCiphertext, err := key.AesEncrypt([]byte("Test conversation"))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
twoUserCiphertext, err := key.AesEncrypt([]byte("false"))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
id, err := uuid.NewV4()
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
conversationDetailIDCiphertext, err := key.AesEncrypt([]byte(id.String()))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
adminCiphertext, err := key.AesEncrypt([]byte("true"))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
userIDCiphertext, err := key.AesEncrypt([]byte(u.ID.String()))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
usernameCiphertext, err := key.AesEncrypt([]byte(u.Username))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
pubKey := Seeder.GetPubKey()
d := struct {
ID string `json:"id"`
Name string `json:"name"`
TwoUser string `json:"two_user"`
Users []Models.ConversationDetailUser `json:"users"`
UserConversations []Models.UserConversation `json:"user_conversations"`
}{
ID: id.String(),
Name: base64.StdEncoding.EncodeToString(nameCiphertext),
TwoUser: base64.StdEncoding.EncodeToString(twoUserCiphertext),
Users: []Models.ConversationDetailUser{
{
ConversationDetailID: id,
UserID: base64.StdEncoding.EncodeToString(userIDCiphertext),
Username: base64.StdEncoding.EncodeToString(usernameCiphertext),
AssociationKey: "",
PublicKey: "",
Admin: base64.StdEncoding.EncodeToString(adminCiphertext),
},
},
UserConversations: []Models.UserConversation{
{
UserID: u.ID,
ConversationDetailID: base64.StdEncoding.EncodeToString(conversationDetailIDCiphertext),
Admin: base64.StdEncoding.EncodeToString(adminCiphertext),
SymmetricKey: base64.StdEncoding.EncodeToString(
Seeder.EncryptWithPublicKey(key.Key, pubKey),
),
},
},
}
jsonStr, _ := json.Marshal(d)
req, _ := http.NewRequest("POST", 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 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
}
}

+ 21
- 4
Backend/Api/Messages/CreateMessage.go View File

@ -4,10 +4,11 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"net/http" "net/http"
"time"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Util"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Util"
) )
type rawMessageData struct { type rawMessageData struct {
@ -20,8 +21,11 @@ func CreateMessage(w http.ResponseWriter, r *http.Request) {
var ( var (
messagesData []rawMessageData messagesData []rawMessageData
messageData rawMessageData messageData rawMessageData
message Models.Message
t time.Time
decodedFile []byte decodedFile []byte
fileName string fileName string
i int
err error err error
) )
@ -38,6 +42,19 @@ func CreateMessage(w http.ResponseWriter, r *http.Request) {
messageData.MessageData.Attachment.FilePath = fileName messageData.MessageData.Attachment.FilePath = fileName
} }
for i, message = range messageData.Messages {
t, err = time.Parse("2006-01-02T15:04:05Z", message.ExpiryRaw)
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
err = messageData.Messages[i].Expiry.Scan(t)
if err != nil {
http.Error(w, "Error", http.StatusInternalServerError)
return
}
}
err = Database.CreateMessageData(&messageData.MessageData) err = Database.CreateMessageData(&messageData.MessageData)
if err != nil { if err != nil {
http.Error(w, "Error", http.StatusInternalServerError) http.Error(w, "Error", http.StatusInternalServerError)
@ -51,5 +68,5 @@ func CreateMessage(w http.ResponseWriter, r *http.Request) {
} }
} }
w.WriteHeader(http.StatusOK)
w.WriteHeader(http.StatusNoContent)
} }

+ 133
- 0
Backend/Api/Messages/CreateMessage_test.go View File

@ -0,0 +1,133 @@
package Messages_test
import (
"bytes"
"encoding/base64"
"encoding/json"
"net/http"
"testing"
"time"
"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"
"github.com/gofrs/uuid"
)
// TODO: Write test for message expiry
func Test_CreateMessage(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
}
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
}
id, err := uuid.NewV4()
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
id2, err := uuid.NewV4()
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
d := []struct {
MessageData struct {
ID uuid.UUID `json:"id"`
Data string `json:"data"`
SenderID string `json:"sender_id"`
SymmetricKey string `json:"symmetric_key"`
} `json:"message_data"`
Messages []struct {
ID uuid.UUID `json:"id"`
MessageDataID uuid.UUID `json:"message_data_id"`
SymmetricKey string `json:"symmetric_key"`
AssociationKey string `json:"association_key"`
Expiry time.Time `json:"expiry"`
} `json:"message"`
}{
{
MessageData: struct {
ID uuid.UUID `json:"id"`
Data string `json:"data"`
SenderID string `json:"sender_id"`
SymmetricKey string `json:"symmetric_key"`
}{
ID: id,
Data: base64.StdEncoding.EncodeToString(dataCiphertext),
SenderID: base64.StdEncoding.EncodeToString(senderIDCiphertext),
SymmetricKey: "",
},
Messages: []struct {
ID uuid.UUID `json:"id"`
MessageDataID uuid.UUID `json:"message_data_id"`
SymmetricKey string `json:"symmetric_key"`
AssociationKey string `json:"association_key"`
Expiry time.Time `json:"expiry"`
}{
{
ID: id2,
MessageDataID: id,
SymmetricKey: "",
AssociationKey: "",
Expiry: time.Now(),
},
},
},
}
jsonStr, _ := json.Marshal(d)
req, _ := http.NewRequest("POST", ts.URL+"/api/v1/auth/message", 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)
return
}
var m Models.Message
err = Database.DB.First(&m).Error
if err != nil {
t.Errorf("Expected conversation detail record, received %s", err.Error())
return
}
var md Models.MessageData
err = Database.DB.First(&md).Error
if err != nil {
t.Errorf("Expected conversation detail record, received %s", err.Error())
return
}
}

+ 14
- 3
Backend/Api/Messages/MessageThread.go View File

@ -3,9 +3,11 @@ package Messages
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"net/url"
"strconv"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
@ -17,7 +19,9 @@ func Messages(w http.ResponseWriter, r *http.Request) {
message Models.Message message Models.Message
urlVars map[string]string urlVars map[string]string
associationKey string associationKey string
values url.Values
returnJSON []byte returnJSON []byte
page int
i int i int
ok bool ok bool
err error err error
@ -30,7 +34,14 @@ func Messages(w http.ResponseWriter, r *http.Request) {
return return
} }
messages, err = Database.GetMessagesByAssociationKey(associationKey)
values = r.URL.Query()
page, err = strconv.Atoi(values.Get("page"))
if err != nil {
page = 0
}
messages, err = Database.GetMessagesByAssociationKey(associationKey, page)
if !ok { if !ok {
http.Error(w, "Not Found", http.StatusNotFound) http.Error(w, "Not Found", http.StatusNotFound)
return return


+ 205
- 0
Backend/Api/Messages/MessageThread_test.go View File

@ -0,0 +1,205 @@
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))
}
}
func Test_MessagesPagination(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()
for i := 0; i < 50; i++ {
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) != 20 {
t.Errorf("Expected %d, recieved %d", 20, len(m))
}
}

+ 2
- 2
Backend/Api/Messages/UpdateConversation.go View File

@ -6,8 +6,8 @@ import (
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
) )
type rawUpdateConversationData struct { type rawUpdateConversationData struct {


+ 183
- 0
Backend/Api/Messages/UpdateConversation_test.go View File

@ -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))
}
}

+ 7
- 7
Backend/Api/Routes.go View File

@ -4,10 +4,10 @@ import (
"log" "log"
"net/http" "net/http"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Api/Auth"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Api/Friends"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Api/Messages"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Api/Users"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Api/Auth"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Api/Friends"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Api/Messages"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Api/Users"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
@ -68,14 +68,14 @@ func InitAPIEndpoints(router *mux.Router) {
authAPI.HandleFunc("/users", Users.SearchUsers).Methods("GET") authAPI.HandleFunc("/users", Users.SearchUsers).Methods("GET")
authAPI.HandleFunc("/friend_requests", Friends.EncryptedFriendRequestList).Methods("GET")
authAPI.HandleFunc("/friend_requests", Friends.FriendRequestList).Methods("GET")
authAPI.HandleFunc("/friend_request", Friends.CreateFriendRequest).Methods("POST") authAPI.HandleFunc("/friend_request", Friends.CreateFriendRequest).Methods("POST")
authAPI.HandleFunc("/friend_request/qr_code", Friends.CreateFriendRequestQrCode).Methods("POST") authAPI.HandleFunc("/friend_request/qr_code", Friends.CreateFriendRequestQrCode).Methods("POST")
authAPI.HandleFunc("/friend_request/{requestID}", Friends.AcceptFriendRequest).Methods("POST") authAPI.HandleFunc("/friend_request/{requestID}", Friends.AcceptFriendRequest).Methods("POST")
authAPI.HandleFunc("/friend_request/{requestID}", Friends.RejectFriendRequest).Methods("DELETE") authAPI.HandleFunc("/friend_request/{requestID}", Friends.RejectFriendRequest).Methods("DELETE")
authAPI.HandleFunc("/conversations", Messages.EncryptedConversationList).Methods("GET")
authAPI.HandleFunc("/conversation_details", Messages.EncryptedConversationDetailsList).Methods("GET")
authAPI.HandleFunc("/conversations", Messages.ConversationList).Methods("GET")
authAPI.HandleFunc("/conversation_details", Messages.ConversationDetailsList).Methods("GET")
authAPI.HandleFunc("/conversations", Messages.CreateConversation).Methods("POST") authAPI.HandleFunc("/conversations", Messages.CreateConversation).Methods("POST")
authAPI.HandleFunc("/conversations", Messages.UpdateConversation).Methods("PUT") authAPI.HandleFunc("/conversations", Messages.UpdateConversation).Methods("PUT")
authAPI.HandleFunc("/conversations/{detailID}/image", Messages.AddConversationImage).Methods("POST") authAPI.HandleFunc("/conversations/{detailID}/image", Messages.AddConversationImage).Methods("POST")


+ 2
- 3
Backend/Api/Users/SearchUsers.go View File

@ -5,8 +5,8 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
) )
// SearchUsers searches a for a user by username // SearchUsers searches a for a user by username
@ -46,7 +46,6 @@ func SearchUsers(w http.ResponseWriter, r *http.Request) {
returnJSON, err = json.MarshalIndent(user, "", " ") returnJSON, err = json.MarshalIndent(user, "", " ")
if err != nil { if err != nil {
panic(err)
http.Error(w, "Not Found", http.StatusNotFound) http.Error(w, "Not Found", http.StatusNotFound)
return return
} }


+ 106
- 0
Backend/Api/Users/SearchUsers_test.go View File

@ -0,0 +1,106 @@
package Users_test
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"testing"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Tests"
)
func Test_SearchUsers(t *testing.T) {
client, ts, err := Tests.InitTestEnv()
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
u2, err := Tests.InitTestCreateUser("abcd")
req, _ := http.NewRequest(
"GET",
fmt.Sprintf("%s/api/v1/auth/users?username=%s", ts.URL, u2.Username),
nil,
)
resp, err := client.Do(req)
if err != nil {
t.Errorf("Expected nil, 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 %d, recieved %d", http.StatusOK, resp.StatusCode)
return
}
var user Models.User
json.Unmarshal(requestBody, &user)
if user.Username != "abcd" {
t.Errorf("Expected abcd, recieved %s", user.Username)
return
}
if user.Password != "" {
t.Errorf("Expected \"\", recieved %s", user.Password)
return
}
if user.AsymmetricPrivateKey != "" {
t.Errorf("Expected \"\", recieved %s", user.AsymmetricPrivateKey)
return
}
}
func Test_SearchUsersPartialMatchFails(t *testing.T) {
client, ts, err := Tests.InitTestEnv()
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
_, err = Tests.InitTestCreateUser("abcd")
req, _ := http.NewRequest(
"GET",
fmt.Sprintf("%s/api/v1/auth/users?username=%s", ts.URL, "abc"),
nil,
)
resp, err := client.Do(req)
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
if resp.StatusCode != http.StatusNotFound {
t.Errorf("Expected %d, recieved %d", http.StatusOK, resp.StatusCode)
return
}
requestBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("Expected %d, recieved %d", http.StatusOK, resp.StatusCode)
return
}
var user interface{}
json.Unmarshal(requestBody, &user)
if user != nil {
t.Errorf("Expected nil, recieved %+v", user)
return
}
}

+ 1
- 1
Backend/Database/Attachments.go View File

@ -1,7 +1,7 @@
package Database package Database
import ( import (
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"


+ 1
- 1
Backend/Database/ConversationDetailUsers.go View File

@ -1,7 +1,7 @@
package Database package Database
import ( import (
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"


+ 1
- 1
Backend/Database/ConversationDetails.go View File

@ -1,7 +1,7 @@
package Database package Database
import ( import (
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"


+ 8
- 2
Backend/Database/FriendRequests.go View File

@ -1,7 +1,7 @@
package Database package Database
import ( import (
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
@ -22,14 +22,20 @@ func GetFriendRequestByID(id string) (Models.FriendRequest, error) {
} }
// GetFriendRequestsByUserID gets friend request by user id // GetFriendRequestsByUserID gets friend request by user id
func GetFriendRequestsByUserID(userID string) ([]Models.FriendRequest, error) {
func GetFriendRequestsByUserID(userID string, page int) ([]Models.FriendRequest, error) {
var ( var (
friends []Models.FriendRequest friends []Models.FriendRequest
offset int
err error err error
) )
offset = page * PageSize
err = DB.Model(Models.FriendRequest{}). err = DB.Model(Models.FriendRequest{}).
Where("user_id = ?", userID). Where("user_id = ?", userID).
Offset(offset).
Limit(PageSize).
Order("created_at DESC").
Find(&friends). Find(&friends).
Error Error


+ 14
- 14
Backend/Database/Init.go View File

@ -3,22 +3,23 @@ package Database
import ( import (
"log" "log"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
"gorm.io/gorm" "gorm.io/gorm"
) )
const ( const (
dbURL = "postgres://postgres:@localhost:5432/envelope"
dbTestURL = "postgres://postgres:@localhost:5432/envelope_test"
dbURL = "postgres://postgres:password@postgres:5432/capsule"
dbTestURL = "postgres://postgres:password@postgres-testing:5432/capsule-testing"
PageSize = 20
) )
// DB db // DB db
var DB *gorm.DB var DB *gorm.DB
func getModels() []interface{} {
return []interface{}{
var models = []interface{}{
&Models.Session{}, &Models.Session{},
&Models.Attachment{}, &Models.Attachment{},
&Models.User{}, &Models.User{},
@ -28,13 +29,11 @@ func getModels() []interface{} {
&Models.ConversationDetail{}, &Models.ConversationDetail{},
&Models.ConversationDetailUser{}, &Models.ConversationDetailUser{},
&Models.UserConversation{}, &Models.UserConversation{},
}
} }
// Init initializes the database connection // Init initializes the database connection
func Init() { func Init() {
var ( var (
model interface{}
err error err error
) )
@ -48,18 +47,15 @@ func Init() {
log.Println("Running AutoMigrate...") log.Println("Running AutoMigrate...")
for _, model = range getModels() {
err = DB.AutoMigrate(model)
err = DB.AutoMigrate(models...)
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
}
} }
// InitTest initializes the test datbase // InitTest initializes the test datbase
func InitTest() { func InitTest() {
var ( var (
model interface{}
err error err error
) )
@ -69,8 +65,12 @@ func InitTest() {
log.Fatalln(err) log.Fatalln(err)
} }
for _, model = range getModels() {
DB.Migrator().DropTable(model)
DB.AutoMigrate(model)
err = DB.Migrator().DropTable(models...)
if err != nil {
panic(err)
}
err = DB.AutoMigrate(models...)
if err != nil {
panic(err)
} }
} }

+ 1
- 1
Backend/Database/MessageData.go View File

@ -1,7 +1,7 @@
package Database package Database
import ( import (
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"


+ 8
- 3
Backend/Database/Messages.go View File

@ -1,7 +1,7 @@
package Database package Database
import ( import (
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
@ -22,15 +22,20 @@ func GetMessageByID(id string) (Models.Message, error) {
} }
// GetMessagesByAssociationKey for getting whole thread // GetMessagesByAssociationKey for getting whole thread
// TODO: Add pagination
func GetMessagesByAssociationKey(associationKey string) ([]Models.Message, error) {
func GetMessagesByAssociationKey(associationKey string, page int) ([]Models.Message, error) {
var ( var (
messages []Models.Message messages []Models.Message
offset int
err error err error
) )
offset = page * PageSize
err = DB.Preload("MessageData"). err = DB.Preload("MessageData").
Preload("MessageData.Attachment"). Preload("MessageData.Attachment").
Offset(offset).
Limit(PageSize).
Order("created_at DESC").
Find(&messages, "association_key = ?", associationKey). Find(&messages, "association_key = ?", associationKey).
Error Error


+ 8
- 8
Backend/Database/Seeder/FriendSeeder.go View File

@ -6,24 +6,24 @@ import (
"os" "os"
"time" "time"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
) )
func seedFriend(userRequestTo, userRequestFrom Models.User, accepted bool) error { func seedFriend(userRequestTo, userRequestFrom Models.User, accepted bool) error {
var ( var (
friendRequest Models.FriendRequest friendRequest Models.FriendRequest
symKey aesKey
symKey AesKey
encPublicKey []byte encPublicKey []byte
err error err error
) )
symKey, err = generateAesKey()
symKey, err = GenerateAesKey()
if err != nil { if err != nil {
return err return err
} }
encPublicKey, err = symKey.aesEncrypt([]byte(publicKey))
encPublicKey, err = symKey.AesEncrypt([]byte(PublicKey))
if err != nil { if err != nil {
return err return err
} }
@ -31,13 +31,13 @@ func seedFriend(userRequestTo, userRequestFrom Models.User, accepted bool) error
friendRequest = Models.FriendRequest{ friendRequest = Models.FriendRequest{
UserID: userRequestTo.ID, UserID: userRequestTo.ID,
FriendID: base64.StdEncoding.EncodeToString( FriendID: base64.StdEncoding.EncodeToString(
encryptWithPublicKey(
EncryptWithPublicKey(
[]byte(userRequestFrom.ID.String()), []byte(userRequestFrom.ID.String()),
decodedPublicKey, decodedPublicKey,
), ),
), ),
FriendUsername: base64.StdEncoding.EncodeToString( FriendUsername: base64.StdEncoding.EncodeToString(
encryptWithPublicKey(
EncryptWithPublicKey(
[]byte(userRequestFrom.Username), []byte(userRequestFrom.Username),
decodedPublicKey, decodedPublicKey,
), ),
@ -46,7 +46,7 @@ func seedFriend(userRequestTo, userRequestFrom Models.User, accepted bool) error
encPublicKey, encPublicKey,
), ),
SymmetricKey: base64.StdEncoding.EncodeToString( SymmetricKey: base64.StdEncoding.EncodeToString(
encryptWithPublicKey(symKey.Key, decodedPublicKey),
EncryptWithPublicKey(symKey.Key, decodedPublicKey),
), ),
} }


+ 26
- 26
Backend/Database/Seeder/MessageSeeder.go View File

@ -3,8 +3,8 @@ package Seeder
import ( import (
"encoding/base64" "encoding/base64"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
) )
@ -17,7 +17,7 @@ func seedMessage(
var ( var (
message Models.Message message Models.Message
messageData Models.MessageData messageData Models.MessageData
key, userKey aesKey
key, userKey AesKey
keyCiphertext []byte keyCiphertext []byte
plaintext string plaintext string
dataCiphertext []byte dataCiphertext []byte
@ -27,34 +27,34 @@ func seedMessage(
plaintext = "Test Message" plaintext = "Test Message"
userKey, err = generateAesKey()
userKey, err = GenerateAesKey()
if err != nil { if err != nil {
panic(err) panic(err)
} }
key, err = generateAesKey()
key, err = GenerateAesKey()
if err != nil { if err != nil {
panic(err) panic(err)
} }
dataCiphertext, err = key.aesEncrypt([]byte(plaintext))
dataCiphertext, err = key.AesEncrypt([]byte(plaintext))
if err != nil { if err != nil {
panic(err) panic(err)
} }
senderIDCiphertext, err = key.aesEncrypt([]byte(primaryUser.ID.String()))
senderIDCiphertext, err = key.AesEncrypt([]byte(primaryUser.ID.String()))
if err != nil { if err != nil {
panic(err) panic(err)
} }
if i%2 == 0 { if i%2 == 0 {
senderIDCiphertext, err = key.aesEncrypt([]byte(secondaryUser.ID.String()))
senderIDCiphertext, err = key.AesEncrypt([]byte(secondaryUser.ID.String()))
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }
keyCiphertext, err = userKey.aesEncrypt(
keyCiphertext, err = userKey.AesEncrypt(
[]byte(base64.StdEncoding.EncodeToString(key.Key)), []byte(base64.StdEncoding.EncodeToString(key.Key)),
) )
if err != nil { if err != nil {
@ -70,7 +70,7 @@ func seedMessage(
message = Models.Message{ message = Models.Message{
MessageData: messageData, MessageData: messageData,
SymmetricKey: base64.StdEncoding.EncodeToString( SymmetricKey: base64.StdEncoding.EncodeToString(
encryptWithPublicKey(userKey.Key, decodedPublicKey),
EncryptWithPublicKey(userKey.Key, decodedPublicKey),
), ),
AssociationKey: primaryUserAssociationKey, AssociationKey: primaryUserAssociationKey,
} }
@ -83,7 +83,7 @@ func seedMessage(
message = Models.Message{ message = Models.Message{
MessageData: messageData, MessageData: messageData,
SymmetricKey: base64.StdEncoding.EncodeToString( SymmetricKey: base64.StdEncoding.EncodeToString(
encryptWithPublicKey(userKey.Key, decodedPublicKey),
EncryptWithPublicKey(userKey.Key, decodedPublicKey),
), ),
AssociationKey: secondaryUserAssociationKey, AssociationKey: secondaryUserAssociationKey,
} }
@ -91,7 +91,7 @@ func seedMessage(
return Database.CreateMessage(&message) return Database.CreateMessage(&message)
} }
func seedConversationDetail(key aesKey) (Models.ConversationDetail, error) {
func seedConversationDetail(key AesKey) (Models.ConversationDetail, error) {
var ( var (
messageThread Models.ConversationDetail messageThread Models.ConversationDetail
name string name string
@ -102,12 +102,12 @@ func seedConversationDetail(key aesKey) (Models.ConversationDetail, error) {
name = "Test Conversation" name = "Test Conversation"
nameCiphertext, err = key.aesEncrypt([]byte(name))
nameCiphertext, err = key.AesEncrypt([]byte(name))
if err != nil { if err != nil {
panic(err) panic(err)
} }
twoUserCiphertext, err = key.aesEncrypt([]byte("false"))
twoUserCiphertext, err = key.AesEncrypt([]byte("false"))
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -124,7 +124,7 @@ func seedConversationDetail(key aesKey) (Models.ConversationDetail, error) {
func seedUserConversation( func seedUserConversation(
user Models.User, user Models.User,
threadID uuid.UUID, threadID uuid.UUID,
key aesKey,
key AesKey,
) (Models.UserConversation, error) { ) (Models.UserConversation, error) {
var ( var (
messageThreadUser Models.UserConversation messageThreadUser Models.UserConversation
@ -133,12 +133,12 @@ func seedUserConversation(
err error err error
) )
conversationDetailIDCiphertext, err = key.aesEncrypt([]byte(threadID.String()))
conversationDetailIDCiphertext, err = key.AesEncrypt([]byte(threadID.String()))
if err != nil { if err != nil {
return messageThreadUser, err return messageThreadUser, err
} }
adminCiphertext, err = key.aesEncrypt([]byte("true"))
adminCiphertext, err = key.AesEncrypt([]byte("true"))
if err != nil { if err != nil {
return messageThreadUser, err return messageThreadUser, err
} }
@ -148,7 +148,7 @@ func seedUserConversation(
ConversationDetailID: base64.StdEncoding.EncodeToString(conversationDetailIDCiphertext), ConversationDetailID: base64.StdEncoding.EncodeToString(conversationDetailIDCiphertext),
Admin: base64.StdEncoding.EncodeToString(adminCiphertext), Admin: base64.StdEncoding.EncodeToString(adminCiphertext),
SymmetricKey: base64.StdEncoding.EncodeToString( SymmetricKey: base64.StdEncoding.EncodeToString(
encryptWithPublicKey(key.Key, decodedPublicKey),
EncryptWithPublicKey(key.Key, decodedPublicKey),
), ),
} }
@ -161,7 +161,7 @@ func seedConversationDetailUser(
conversationDetail Models.ConversationDetail, conversationDetail Models.ConversationDetail,
associationKey uuid.UUID, associationKey uuid.UUID,
admin bool, admin bool,
key aesKey,
key AesKey,
) (Models.ConversationDetailUser, error) { ) (Models.ConversationDetailUser, error) {
var ( var (
conversationDetailUser Models.ConversationDetailUser conversationDetailUser Models.ConversationDetailUser
@ -181,27 +181,27 @@ func seedConversationDetailUser(
adminString = "true" adminString = "true"
} }
userIDCiphertext, err = key.aesEncrypt([]byte(user.ID.String()))
userIDCiphertext, err = key.AesEncrypt([]byte(user.ID.String()))
if err != nil { if err != nil {
return conversationDetailUser, err return conversationDetailUser, err
} }
usernameCiphertext, err = key.aesEncrypt([]byte(user.Username))
usernameCiphertext, err = key.AesEncrypt([]byte(user.Username))
if err != nil { if err != nil {
return conversationDetailUser, err return conversationDetailUser, err
} }
adminCiphertext, err = key.aesEncrypt([]byte(adminString))
adminCiphertext, err = key.AesEncrypt([]byte(adminString))
if err != nil { if err != nil {
return conversationDetailUser, err return conversationDetailUser, err
} }
associationKeyCiphertext, err = key.aesEncrypt([]byte(associationKey.String()))
associationKeyCiphertext, err = key.AesEncrypt([]byte(associationKey.String()))
if err != nil { if err != nil {
return conversationDetailUser, err return conversationDetailUser, err
} }
publicKeyCiphertext, err = key.aesEncrypt([]byte(user.AsymmetricPublicKey))
publicKeyCiphertext, err = key.AesEncrypt([]byte(user.AsymmetricPublicKey))
if err != nil { if err != nil {
return conversationDetailUser, err return conversationDetailUser, err
} }
@ -224,7 +224,7 @@ func seedConversationDetailUser(
func SeedMessages() { func SeedMessages() {
var ( var (
conversationDetail Models.ConversationDetail conversationDetail Models.ConversationDetail
key aesKey
key AesKey
primaryUser Models.User primaryUser Models.User
primaryUserAssociationKey uuid.UUID primaryUserAssociationKey uuid.UUID
secondaryUser Models.User secondaryUser Models.User
@ -233,7 +233,7 @@ func SeedMessages() {
err error err error
) )
key, err = generateAesKey()
key, err = GenerateAesKey()
if err != nil { if err != nil {
panic(err) panic(err)
} }


+ 35
- 12
Backend/Database/Seeder/Seed.go View File

@ -9,11 +9,11 @@ import (
) )
const ( const (
// Encrypted with "password"
encryptedPrivateKey string = `sPhQsHpXYFqPb7qdmTY7APFwBb4m7meCITujDeKMQFnIjplOVm9ijjXU+YAmGvrX13ukBj8zo9MTVhjJUjJ917pyLhl4w8uyg1jCvplUYtJVXhGA9Wy3NqHMuq3SU3fKdlEM+oR4zYkbAYWp42XvulbcuVBEWiWkvHOrbdKPFpMmd54SL2c/vcWrmjgC7rTlJf2TYICZwRK+6Y0XZi5fSWeU0vg7+rHWKHc5MHHtAdAiL+HCa90c5gfh+hXkT5ojGHOkhT9kdLy3PTPN19EGpdXgZ3WFq1z9CZ6zX7uM091uR0IvgzfwaLx8HJCx7ViWQhioH9LJZgC73RMf/dwzejg2COy4QT/E59RPOczgd779rxiRmphMoR8xJYBFRlkTVmcUO4NcUE50Cc39hXezcekHuV1YQK4BXTrxGX1ceiCXYlKAWS9wHZpog9OldTCPBpw5XAWExh3kRzqdvsdHxHVE+TpAEIjDljAlc3r+FPHYH1zWWk41eQ/zz3Vkx5Zl4dMF9x+uUOspQXVb/4K42e9fMKychNUN5o/JzIwy7xOzgXa6iwf223On/mXKV6FK6Q8lojK7Wc8g7AwfqnN9//HjI14pVqGBJtn5ggL/g4qt0JFl3pV/6n/ZLMG6k8wpsaApLGvsTPqZHcv+C69Z33rZQ4TagXVxpmnWMpPCaR0+Dawn4iAce2UvUtIN2KbJNcTtRQo4z30+BbgmVKHgkR0EHMu4cYjJPYwJ5H8IYcQuFKb7+Cp33FD2Lv54I9uvtVHH9bWcid9K82y68PufJi/0icZ3EyEqZygez9mgJzxXO1b7xZMiosGs82QRv7IIOSzqBPRYv1Lxi3fWkgnOvw4dWFxJnKEI2+KD9K0z+XsgVlm26fdRklQAAf6xOJ1nJXBScbm12FBTWLMjLzHWz/iI9mQ+eGV9AREqrgQjUayXdnCsa0Q9bTTktxBkrJND4NUEDSGklhj9SY+VM0mhgAbkCvSE59vKtcNmCHx2Y+JnbZyKzJ71EaErX9vOpYCneKOjn8phVBJHQHM16QRLGyW4DUfn2CtAvb7Kks56kf/mn9YZDU68zSoLzm9rz7fjS2OUsxwmuv2IRCv/UTGgtfEfCs34qzagADfTNKTou7qkedhoygvuHiN4PzgGnjw1DQMks9PWr44z1gvIV4pEGiqgIuNHDjxKsfgQy0Cp2AV1+FNLWd1zd5t/K2pXR+knDoeHIZ2m6txQMl9I4GIyQ1bQFJWrYXPS8oMjvoH0YYVsHyShBsU2SKlG7nGbuUyoCR1EtRIzHMgP1Dq+Whqdbv67pRvhGVmydkCh0wbD+LJBcp2KJK+EQT9vv6GT5JW0oVHnE5TEXCnEJOW/rMhNMTMSccRmnVdguIE4HZsXx+cmV36jHgEt9bzcsvyWvFFoG4xL+t2UUnztX870vu//XaeVuOEAgehY/KLncrY7lhsQA4puCFIWpPteiCNhU1D8DTKc8V0ZtLT9a31SL1NLhZ+YHiD8Hs5SYdj6FW50E5yYUqPRPkg5mpbh88cRcPdsngCxU8iusNN3MSP07lO0h8zULDqtQsAq9p5o7IFTvWlAjekMy1sKTj3CuH7FuAkMHvwU0odMFeaS9T+8+4OGeprHwogWTzTbPnoOqOP/RC6vGfBvpju5s264hYguT24iXzhDFYk/8JQQe+USIbkQ7wXRw+/9cK8h5cs4LyaxMOx0pXHooxJ01bF8BYgYG4s0RB2gItzMk/L5/XhrOdWxEAdYR27s0dCN58gyvoU6phgQbTqvNTFYAObRcjfKfHu3PrFCYBBAKJ7Nm58C3rz832+ZTGVdQ3490TvO+sCLYKzpgtsqr8KyedG9LKa8wn/wlRD7kYn+J2SrMPY2Q0e4evyJaCAsolp/BQfy9JFtyRDPWTHn+jOHjW8ZN7vswGkRwYlSJSl0UC8mmJyS4lwnO/Vv4wBnDHQEzIycjn3JZAlV5ing0HKqUfW6G07453JXd8oZiMC/kIQjgWkdg34zxBYarVVrHFG5FIH9w7QWY8PCDU/kkcLniT0yD1/gkqAG2HpwaXEcSqX8Ofrbpd/IA7R7iCXYE5Q1mAvSvICpPg9Cf3CHjLyAEDz9cwKnZHkocXC8evdsTf2e7Wz8FFPAI3onFvym0MfZuRrIZitX1V8NOLedd3y74CwuErfzrr60DjyPRxGbJ4llMbm+ojeENe0HBedNm71jf+McSihKbSo5GDBxfVYVreYZ8A4iP0LsxtzQFxuzdeDL5KA9uNNw+LN9FN9vKhdALhQSnSfLPfMBsM/ey7dbxb4eRT0fpApX`
// EncryptedPrivateKey with "password"
EncryptedPrivateKey string = `sPhQsHpXYFqPb7qdmTY7APFwBb4m7meCITujDeKMQFnIjplOVm9ijjXU+YAmGvrX13ukBj8zo9MTVhjJUjJ917pyLhl4w8uyg1jCvplUYtJVXhGA9Wy3NqHMuq3SU3fKdlEM+oR4zYkbAYWp42XvulbcuVBEWiWkvHOrbdKPFpMmd54SL2c/vcWrmjgC7rTlJf2TYICZwRK+6Y0XZi5fSWeU0vg7+rHWKHc5MHHtAdAiL+HCa90c5gfh+hXkT5ojGHOkhT9kdLy3PTPN19EGpdXgZ3WFq1z9CZ6zX7uM091uR0IvgzfwaLx8HJCx7ViWQhioH9LJZgC73RMf/dwzejg2COy4QT/E59RPOczgd779rxiRmphMoR8xJYBFRlkTVmcUO4NcUE50Cc39hXezcekHuV1YQK4BXTrxGX1ceiCXYlKAWS9wHZpog9OldTCPBpw5XAWExh3kRzqdvsdHxHVE+TpAEIjDljAlc3r+FPHYH1zWWk41eQ/zz3Vkx5Zl4dMF9x+uUOspQXVb/4K42e9fMKychNUN5o/JzIwy7xOzgXa6iwf223On/mXKV6FK6Q8lojK7Wc8g7AwfqnN9//HjI14pVqGBJtn5ggL/g4qt0JFl3pV/6n/ZLMG6k8wpsaApLGvsTPqZHcv+C69Z33rZQ4TagXVxpmnWMpPCaR0+Dawn4iAce2UvUtIN2KbJNcTtRQo4z30+BbgmVKHgkR0EHMu4cYjJPYwJ5H8IYcQuFKb7+Cp33FD2Lv54I9uvtVHH9bWcid9K82y68PufJi/0icZ3EyEqZygez9mgJzxXO1b7xZMiosGs82QRv7IIOSzqBPRYv1Lxi3fWkgnOvw4dWFxJnKEI2+KD9K0z+XsgVlm26fdRklQAAf6xOJ1nJXBScbm12FBTWLMjLzHWz/iI9mQ+eGV9AREqrgQjUayXdnCsa0Q9bTTktxBkrJND4NUEDSGklhj9SY+VM0mhgAbkCvSE59vKtcNmCHx2Y+JnbZyKzJ71EaErX9vOpYCneKOjn8phVBJHQHM16QRLGyW4DUfn2CtAvb7Kks56kf/mn9YZDU68zSoLzm9rz7fjS2OUsxwmuv2IRCv/UTGgtfEfCs34qzagADfTNKTou7qkedhoygvuHiN4PzgGnjw1DQMks9PWr44z1gvIV4pEGiqgIuNHDjxKsfgQy0Cp2AV1+FNLWd1zd5t/K2pXR+knDoeHIZ2m6txQMl9I4GIyQ1bQFJWrYXPS8oMjvoH0YYVsHyShBsU2SKlG7nGbuUyoCR1EtRIzHMgP1Dq+Whqdbv67pRvhGVmydkCh0wbD+LJBcp2KJK+EQT9vv6GT5JW0oVHnE5TEXCnEJOW/rMhNMTMSccRmnVdguIE4HZsXx+cmV36jHgEt9bzcsvyWvFFoG4xL+t2UUnztX870vu//XaeVuOEAgehY/KLncrY7lhsQA4puCFIWpPteiCNhU1D8DTKc8V0ZtLT9a31SL1NLhZ+YHiD8Hs5SYdj6FW50E5yYUqPRPkg5mpbh88cRcPdsngCxU8iusNN3MSP07lO0h8zULDqtQsAq9p5o7IFTvWlAjekMy1sKTj3CuH7FuAkMHvwU0odMFeaS9T+8+4OGeprHwogWTzTbPnoOqOP/RC6vGfBvpju5s264hYguT24iXzhDFYk/8JQQe+USIbkQ7wXRw+/9cK8h5cs4LyaxMOx0pXHooxJ01bF8BYgYG4s0RB2gItzMk/L5/XhrOdWxEAdYR27s0dCN58gyvoU6phgQbTqvNTFYAObRcjfKfHu3PrFCYBBAKJ7Nm58C3rz832+ZTGVdQ3490TvO+sCLYKzpgtsqr8KyedG9LKa8wn/wlRD7kYn+J2SrMPY2Q0e4evyJaCAsolp/BQfy9JFtyRDPWTHn+jOHjW8ZN7vswGkRwYlSJSl0UC8mmJyS4lwnO/Vv4wBnDHQEzIycjn3JZAlV5ing0HKqUfW6G07453JXd8oZiMC/kIQjgWkdg34zxBYarVVrHFG5FIH9w7QWY8PCDU/kkcLniT0yD1/gkqAG2HpwaXEcSqX8Ofrbpd/IA7R7iCXYE5Q1mAvSvICpPg9Cf3CHjLyAEDz9cwKnZHkocXC8evdsTf2e7Wz8FFPAI3onFvym0MfZuRrIZitX1V8NOLedd3y74CwuErfzrr60DjyPRxGbJ4llMbm+ojeENe0HBedNm71jf+McSihKbSo5GDBxfVYVreYZ8A4iP0LsxtzQFxuzdeDL5KA9uNNw+LN9FN9vKhdALhQSnSfLPfMBsM/ey7dbxb4eRT0fpApX`
// Private key for testing server side
privateKey string = `-----BEGIN PRIVATE KEY-----
// PrivateKey for testing server side
PrivateKey string = `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJScQQJxWxKwqf MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJScQQJxWxKwqf
FmXH64QnRBVyW7cU25F+O9Zy96dqTjbV4ruWrzb4+txmK20ZPQvMxDLefhEzTXWb FmXH64QnRBVyW7cU25F+O9Zy96dqTjbV4ruWrzb4+txmK20ZPQvMxDLefhEzTXWb
HZV1P/XxgmEpaBVHwHnkhaPzzChOa/G18CDoCNrgyVzh5a31OotTCuGlS1bSkR53 HZV1P/XxgmEpaBVHwHnkhaPzzChOa/G18CDoCNrgyVzh5a31OotTCuGlS1bSkR53
@ -42,7 +42,8 @@ b0XvaLzh1iKG7HZ9tvPt/VhHlKKosNBK/j4fvgMZg7/bhRfHmaDQKoqlGbtyWjEQ
mj1b2/Gnbk3VYDR16BFfj7m2 mj1b2/Gnbk3VYDR16BFfj7m2
-----END PRIVATE KEY-----` -----END PRIVATE KEY-----`
publicKey string = `-----BEGIN PUBLIC KEY-----
// PublicKey for encryption
PublicKey string = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyUnEECcVsSsKnxZlx+uE MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyUnEECcVsSsKnxZlx+uE
J0QVclu3FNuRfjvWcvenak421eK7lq82+PrcZittGT0LzMQy3n4RM011mx2VdT/1 J0QVclu3FNuRfjvWcvenak421eK7lq82+PrcZittGT0LzMQy3n4RM011mx2VdT/1
8YJhKWgVR8B55IWj88woTmvxtfAg6Aja4Mlc4eWt9TqLUwrhpUtW0pEedxMT10Kv 8YJhKWgVR8B55IWj88woTmvxtfAg6Aja4Mlc4eWt9TqLUwrhpUtW0pEedxMT10Kv
@ -58,35 +59,57 @@ var (
decodedPrivateKey *rsa.PrivateKey decodedPrivateKey *rsa.PrivateKey
) )
// Seed seeds semi random data for use in testing & development
func Seed() {
// GetPubKey for seeding & tests
func GetPubKey() *rsa.PublicKey {
var ( var (
block *pem.Block block *pem.Block
decKey any decKey any
decPubKey *rsa.PublicKey
ok bool ok bool
err error err error
) )
block, _ = pem.Decode([]byte(publicKey))
block, _ = pem.Decode([]byte(PublicKey))
decKey, err = x509.ParsePKIXPublicKey(block.Bytes) decKey, err = x509.ParsePKIXPublicKey(block.Bytes)
if err != nil { if err != nil {
panic(err) panic(err)
} }
decodedPublicKey, ok = decKey.(*rsa.PublicKey)
decPubKey, ok = decKey.(*rsa.PublicKey)
if !ok { if !ok {
panic(errors.New("Invalid decodedPublicKey")) panic(errors.New("Invalid decodedPublicKey"))
} }
block, _ = pem.Decode([]byte(privateKey))
return decPubKey
}
// GetPrivKey for seeding & tests
func GetPrivKey() *rsa.PrivateKey {
var (
block *pem.Block
decKey any
decPrivKey *rsa.PrivateKey
ok bool
err error
)
block, _ = pem.Decode([]byte(PrivateKey))
decKey, err = x509.ParsePKCS8PrivateKey(block.Bytes) decKey, err = x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil { if err != nil {
panic(err) panic(err)
} }
decodedPrivateKey, ok = decKey.(*rsa.PrivateKey)
decPrivKey, ok = decKey.(*rsa.PrivateKey)
if !ok { if !ok {
panic(errors.New("Invalid decodedPrivateKey")) panic(errors.New("Invalid decodedPrivateKey"))
} }
return decPrivKey
}
// Seed seeds semi random data for use in testing & development
func Seed() {
decodedPublicKey = GetPubKey()
decodedPrivateKey = GetPrivKey()
log.Println("Seeding users...") log.Println("Seeding users...")
SeedUsers() SeedUsers()


+ 8
- 8
Backend/Database/Seeder/UserSeeder.go View File

@ -3,9 +3,9 @@ package Seeder
import ( import (
"encoding/base64" "encoding/base64"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Api/Auth"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Api/Auth"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
) )
var userNames = []string{ var userNames = []string{
@ -25,12 +25,12 @@ var userNames = []string{
func createUser(username string) (Models.User, error) { func createUser(username string) (Models.User, error) {
var ( var (
userData Models.User userData Models.User
userKey aesKey
userKey AesKey
password string password string
err error err error
) )
userKey, err = generateAesKey()
userKey, err = GenerateAesKey()
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -43,10 +43,10 @@ func createUser(username string) (Models.User, error) {
userData = Models.User{ userData = Models.User{
Username: username, Username: username,
Password: password, Password: password,
AsymmetricPrivateKey: encryptedPrivateKey,
AsymmetricPublicKey: publicKey,
AsymmetricPrivateKey: EncryptedPrivateKey,
AsymmetricPublicKey: PublicKey,
SymmetricKey: base64.StdEncoding.EncodeToString( SymmetricKey: base64.StdEncoding.EncodeToString(
encryptWithPublicKey(userKey.Key, decodedPublicKey),
EncryptWithPublicKey(userKey.Key, decodedPublicKey),
), ),
} }


+ 15
- 10
Backend/Database/Seeder/encryption.go View File

@ -17,12 +17,12 @@ import (
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
) )
type aesKey struct {
type AesKey struct {
Key []byte Key []byte
Iv []byte Iv []byte
} }
func (key aesKey) encode() string {
func (key AesKey) encode() string {
return base64.StdEncoding.EncodeToString(key.Key) return base64.StdEncoding.EncodeToString(key.Key)
} }
@ -71,7 +71,7 @@ func pkcs7strip(data []byte, blockSize int) ([]byte, error) {
return data[:length-padLen], nil return data[:length-padLen], nil
} }
func generateAesKey() (aesKey, error) {
func GenerateAesKey() (AesKey, error) {
var ( var (
saltBytes []byte = []byte{} saltBytes []byte = []byte{}
password []byte password []byte
@ -83,22 +83,22 @@ func generateAesKey() (aesKey, error) {
password = make([]byte, 64) password = make([]byte, 64)
_, err = rand.Read(password) _, err = rand.Read(password)
if err != nil { if err != nil {
return aesKey{}, err
return AesKey{}, err
} }
seed = make([]byte, 64) seed = make([]byte, 64)
_, err = rand.Read(seed) _, err = rand.Read(seed)
if err != nil { if err != nil {
return aesKey{}, err
return AesKey{}, err
} }
iv = make([]byte, 16) iv = make([]byte, 16)
_, err = rand.Read(iv) _, err = rand.Read(iv)
if err != nil { if err != nil {
return aesKey{}, err
return AesKey{}, err
} }
return aesKey{
return AesKey{
Key: pbkdf2.Key( Key: pbkdf2.Key(
password, password,
saltBytes, saltBytes,
@ -110,7 +110,7 @@ func generateAesKey() (aesKey, error) {
}, nil }, nil
} }
func (key aesKey) aesEncrypt(plaintext []byte) ([]byte, error) {
func (key AesKey) AesEncrypt(plaintext []byte) ([]byte, error) {
var ( var (
bPlaintext []byte bPlaintext []byte
ciphertext []byte ciphertext []byte
@ -134,7 +134,7 @@ func (key aesKey) aesEncrypt(plaintext []byte) ([]byte, error) {
return ciphertext, nil return ciphertext, nil
} }
func (key aesKey) aesDecrypt(ciphertext []byte) ([]byte, error) {
func (key AesKey) AesDecrypt(ciphertext []byte) ([]byte, error) {
var ( var (
plaintext []byte plaintext []byte
iv []byte iv []byte
@ -153,11 +153,16 @@ func (key aesKey) aesDecrypt(ciphertext []byte) ([]byte, error) {
decMode := cipher.NewCBCDecrypter(block, iv) decMode := cipher.NewCBCDecrypter(block, iv)
decMode.CryptBlocks(plaintext, plaintext) decMode.CryptBlocks(plaintext, plaintext)
plaintext, err = pkcs7strip(plaintext, 16)
if err != nil {
return []byte{}, err
}
return plaintext, nil return plaintext, nil
} }
// EncryptWithPublicKey encrypts data with public key // EncryptWithPublicKey encrypts data with public key
func encryptWithPublicKey(msg []byte, pub *rsa.PublicKey) []byte {
func EncryptWithPublicKey(msg []byte, pub *rsa.PublicKey) []byte {
var ( var (
hash hash.Hash hash hash.Hash
) )


+ 12
- 4
Backend/Database/Sessions.go View File

@ -1,12 +1,13 @@
package Database package Database
import ( import (
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
) )
func GetSessionById(id string) (Models.Session, error) {
// GetSessionByID Gets session
func GetSessionByID(id string) (Models.Session, error) {
var ( var (
session Models.Session session Models.Session
err error err error
@ -19,6 +20,7 @@ func GetSessionById(id string) (Models.Session, error) {
return session, err return session, err
} }
// CreateSession creates session
func CreateSession(session *Models.Session) error { func CreateSession(session *Models.Session) error {
var ( var (
err error err error
@ -29,10 +31,16 @@ func CreateSession(session *Models.Session) error {
return err return err
} }
// DeleteSession deletes session
func DeleteSession(session *Models.Session) error { func DeleteSession(session *Models.Session) error {
return DB.Delete(session).Error return DB.Delete(session).Error
} }
func DeleteSessionById(id string) error {
return DB.Delete(&Models.Session{}, id).Error
// DeleteSessionByID deletes session
func DeleteSessionByID(id string) error {
return DB.Delete(
&Models.Session{},
"id = ?",
id,
).Error
} }

+ 9
- 3
Backend/Database/UserConversations.go View File

@ -1,7 +1,7 @@
package Database package Database
import ( import (
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
@ -19,13 +19,19 @@ func GetUserConversationById(id string) (Models.UserConversation, error) {
return message, err return message, err
} }
func GetUserConversationsByUserId(id string) ([]Models.UserConversation, error) {
func GetUserConversationsByUserId(id string, page int) ([]Models.UserConversation, error) {
var ( var (
conversations []Models.UserConversation conversations []Models.UserConversation
offset int
err error err error
) )
err = DB.Find(&conversations, "user_id = ?", id).
offset = page * PageSize
err = DB.Offset(offset).
Limit(PageSize).
Order("created_at DESC").
Find(&conversations, "user_id = ?", id).
Error Error
return conversations, err return conversations, err


+ 1
- 1
Backend/Database/Users.go View File

@ -3,7 +3,7 @@ package Database
import ( import (
"errors" "errors"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"


+ 16
- 0
Backend/Dockerfile View File

@ -0,0 +1,16 @@
FROM golang:1.19-alpine
RUN mkdir -p /go/src/git.tovijaeschke.xyz/Capsule/Backend
COPY ./ /go/src/git.tovijaeschke.xyz/Capsule/Backend
WORKDIR /go/src/git.tovijaeschke.xyz/Capsule/Backend
# For "go test"
RUN apk add gcc libc-dev
RUN go mod download
RUN go build -o /go/bin/capsule-server main.go
CMD [ "/go/bin/capsule-server" ]

+ 3
- 0
Backend/Models/Conversations.go View File

@ -1,6 +1,8 @@
package Models package Models
import ( import (
"time"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
) )
@ -34,4 +36,5 @@ type UserConversation struct {
ConversationDetailID string `gorm:"not null" json:"conversation_detail_id"` // Stored encrypted ConversationDetailID string `gorm:"not null" json:"conversation_detail_id"` // Stored encrypted
Admin string `gorm:"not null" json:"admin"` // Bool if user is admin of thread, stored encrypted Admin string `gorm:"not null" json:"admin"` // Bool if user is admin of thread, stored encrypted
SymmetricKey string `gorm:"not null" json:"symmetric_key"` // Stored encrypted SymmetricKey string `gorm:"not null" json:"symmetric_key"` // Stored encrypted
CreatedAt time.Time `gorm:"not null" json:"created_at"`
} }

+ 2
- 0
Backend/Models/Friends.go View File

@ -2,6 +2,7 @@ package Models
import ( import (
"database/sql" "database/sql"
"time"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
) )
@ -17,4 +18,5 @@ type FriendRequest struct {
FriendPublicAsymmetricKey string ` json:"asymmetric_public_key"` // Stored encrypted FriendPublicAsymmetricKey string ` json:"asymmetric_public_key"` // Stored encrypted
SymmetricKey string `gorm:"not null" json:"symmetric_key"` // Stored encrypted SymmetricKey string `gorm:"not null" json:"symmetric_key"` // Stored encrypted
AcceptedAt sql.NullTime ` json:"accepted_at"` AcceptedAt sql.NullTime ` json:"accepted_at"`
CreatedAt time.Time `gorm:"not null" json:"created_at"`
} }

+ 2
- 1
Backend/Models/Messages.go View File

@ -25,6 +25,7 @@ type Message struct {
MessageData MessageData ` json:"message_data"` MessageData MessageData ` json:"message_data"`
SymmetricKey string `gorm:"not null" json:"symmetric_key"` // Stored encrypted SymmetricKey string `gorm:"not null" json:"symmetric_key"` // Stored encrypted
AssociationKey string `gorm:"not null" json:"association_key"` // Stored encrypted AssociationKey string `gorm:"not null" json:"association_key"` // Stored encrypted
Expiry sql.NullTime ` json:"expiry"`
ExpiryRaw string ` json:"expiry"`
Expiry sql.NullTime ` json:"-"`
CreatedAt time.Time `gorm:"not null" json:"created_at"` CreatedAt time.Time `gorm:"not null" json:"created_at"`
} }

+ 31
- 1
Backend/Models/Users.go View File

@ -2,6 +2,7 @@ package Models
import ( import (
"database/sql/driver" "database/sql/driver"
"errors"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
"gorm.io/gorm" "gorm.io/gorm"
@ -40,10 +41,35 @@ const (
MessageExpiryNoExpiry = "no_expiry" MessageExpiryNoExpiry = "no_expiry"
) )
// MessageExpiryValues list of all expiry values for validation
var MessageExpiryValues = []string{
MessageExpiryFifteenMin,
MessageExpiryThirtyMin,
MessageExpiryOneHour,
MessageExpiryThreeHour,
MessageExpirySixHour,
MessageExpiryTwelveHour,
MessageExpiryOneDay,
MessageExpiryThreeDay,
MessageExpiryNoExpiry,
}
// Scan new value into MessageExpiry // Scan new value into MessageExpiry
func (e *MessageExpiry) Scan(value interface{}) error { func (e *MessageExpiry) Scan(value interface{}) error {
*e = MessageExpiry(value.(string))
var (
strValue = value.(string)
m string
)
for _, m = range MessageExpiryValues {
if strValue != m {
continue
}
*e = MessageExpiry(strValue)
return nil return nil
}
return errors.New("Invalid MessageExpiry value")
} }
// Value gets value out of MessageExpiry column // Value gets value out of MessageExpiry column
@ -51,6 +77,10 @@ func (e MessageExpiry) Value() (driver.Value, error) {
return string(e), nil return string(e), nil
} }
func (e MessageExpiry) String() string {
return string(e)
}
// User holds user data // User holds user data
type User struct { type User struct {
Base Base


+ 92
- 0
Backend/Tests/Init.go View File

@ -0,0 +1,92 @@
package Tests
import (
"encoding/base64"
"io/ioutil"
"log"
"net/http"
"net/http/cookiejar"
"net/http/httptest"
"net/url"
"time"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Api"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Api/Auth"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database/Seeder"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"github.com/gorilla/mux"
)
func InitTestCreateUser(username string) (Models.User, error) {
userKey, err := Seeder.GenerateAesKey()
if err != nil {
return Models.User{}, err
}
pubKey := Seeder.GetPubKey()
p, _ := Auth.HashPassword("password")
u := Models.User{
Username: username,
Password: p,
AsymmetricPublicKey: Seeder.PublicKey,
AsymmetricPrivateKey: Seeder.EncryptedPrivateKey,
SymmetricKey: base64.StdEncoding.EncodeToString(
Seeder.EncryptWithPublicKey(userKey.Key, pubKey),
),
}
err = Database.CreateUser(&u)
return u, err
}
// InitTestEnv initializes the test environment
// client is used for making authenticated requests
// ts is the testing server
// err, in case it fails ¯\_(ツ)_/¯
func InitTestEnv() (*http.Client, *httptest.Server, error) {
log.SetOutput(ioutil.Discard)
Database.InitTest()
r := mux.NewRouter()
Api.InitAPIEndpoints(r)
ts := httptest.NewServer(r)
u, err := InitTestCreateUser("test")
if err != nil {
return http.DefaultClient, ts, err
}
session := Models.Session{
UserID: u.ID,
Expiry: time.Now().Add(12 * time.Hour),
}
err = Database.CreateSession(&session)
if err != nil {
return http.DefaultClient, ts, err
}
jar, err := cookiejar.New(nil)
url, _ := url.Parse(ts.URL)
jar.SetCookies(
url,
[]*http.Cookie{
{
Name: "session_token",
Value: session.ID.String(),
MaxAge: 300,
},
},
)
client := &http.Client{
Jar: jar,
}
return client, ts, err
}

+ 1
- 8
Backend/Util/Files.go View File

@ -10,21 +10,14 @@ func WriteFile(contents []byte) (string, error) {
var ( var (
fileName string fileName string
filePath string filePath string
cwd string
f *os.File f *os.File
err error err error
) )
cwd, err = os.Getwd()
if err != nil {
return fileName, err
}
fileName = RandomString(32) fileName = RandomString(32)
filePath = fmt.Sprintf( filePath = fmt.Sprintf(
"%s/attachments/%s",
cwd,
"/app/attachments/%s",
fileName, fileName,
) )


+ 6
- 0
Backend/Util/Strings.go View File

@ -2,12 +2,18 @@ package Util
import ( import (
"math/rand" "math/rand"
"time"
) )
var ( var (
letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
) )
func init() {
rand.Seed(time.Now().UnixNano())
}
// RandomString generates a random string
func RandomString(n int) string { func RandomString(n int) string {
var ( var (
b []rune b []rune


+ 2
- 2
Backend/Util/UserHelper.go View File

@ -5,8 +5,8 @@ import (
"log" "log"
"net/http" "net/http"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )


+ 1
- 2
Backend/go.mod View File

@ -1,9 +1,8 @@
module git.tovijaeschke.xyz/tovi/Envelope/Backend
module git.tovijaeschke.xyz/tovi/Capsule/Backend
go 1.18 go 1.18
require ( require (
github.com/Kangaroux/go-map-schema v0.6.1
github.com/gofrs/uuid v4.2.0+incompatible github.com/gofrs/uuid v4.2.0+incompatible
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519


+ 3
- 3
Backend/main.go View File

@ -5,9 +5,9 @@ import (
"log" "log"
"net/http" "net/http"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Api"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database"
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database/Seeder"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Api"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database/Seeder"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )


+ 1
- 1
README.md View File

@ -1,4 +1,4 @@
# Envelope
# Capsule
Encrypted messaging app Encrypted messaging app


+ 49
- 0
docker-compose.yml View File

@ -0,0 +1,49 @@
version: "3"
services:
server:
build:
context: ./Backend
ports:
- "8080:8080"
volumes:
- "./Backend:/app"
links:
- postgres
- postgres-testing
depends_on:
postgres:
condition: service_healthy
depends_on:
postgres-testing:
condition: service_healthy
postgres:
image: postgres:14.5
ports:
- "54321:5432"
environment:
POSTGRES_DB: capsule
POSTGRES_PASSWORD: password
volumes:
- /var/lib/postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
postgres-testing:
image: postgres:14.5
ports:
- "54322:5432"
environment:
POSTGRES_DB: capsule-testing
POSTGRES_PASSWORD: password
tmpfs:
- /var/lib/mysql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5

+ 1
- 1
mobile/android/app/src/main/AndroidManifest.xml View File

@ -2,7 +2,7 @@
package="com.example.mobile"> package="com.example.mobile">
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<application <application
android:label="Envelope"
android:label="Capsule"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity


+ 2
- 2
mobile/lib/components/view_image.dart View File

@ -1,5 +1,5 @@
import 'package:Envelope/components/custom_title_bar.dart';
import 'package:Envelope/models/image_message.dart';
import 'package:Capsule/components/custom_title_bar.dart';
import 'package:Capsule/models/image_message.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ViewImage extends StatelessWidget { class ViewImage extends StatelessWidget {


+ 1
- 1
mobile/lib/main.dart View File

@ -13,7 +13,7 @@ void main() async {
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({Key? key}) : super(key: key);
static const String _title = 'Envelope';
static const String _title = 'Capsule';
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {


+ 2
- 2
mobile/lib/models/conversation_users.dart View File

@ -1,8 +1,8 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:Envelope/utils/encryption/aes_helper.dart';
import 'package:Envelope/utils/encryption/crypto_utils.dart';
import 'package:Capsule/utils/encryption/aes_helper.dart';
import 'package:Capsule/utils/encryption/crypto_utils.dart';
import 'package:pointycastle/impl.dart'; import 'package:pointycastle/impl.dart';
import '/models/conversations.dart'; import '/models/conversations.dart';


+ 2
- 2
mobile/lib/models/conversations.dart View File

@ -2,8 +2,8 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:Envelope/models/messages.dart';
import 'package:Envelope/models/text_messages.dart';
import 'package:Capsule/models/messages.dart';
import 'package:Capsule/models/text_messages.dart';
import 'package:mime/mime.dart'; import 'package:mime/mime.dart';
import 'package:pointycastle/export.dart'; import 'package:pointycastle/export.dart';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';


+ 3
- 3
mobile/lib/models/image_message.dart View File

@ -2,9 +2,9 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:Envelope/models/my_profile.dart';
import 'package:Envelope/utils/storage/get_file.dart';
import 'package:Envelope/utils/storage/write_file.dart';
import 'package:Capsule/models/my_profile.dart';
import 'package:Capsule/utils/storage/get_file.dart';
import 'package:Capsule/utils/storage/write_file.dart';
import 'package:mime/mime.dart'; import 'package:mime/mime.dart';
import 'package:pointycastle/pointycastle.dart'; import 'package:pointycastle/pointycastle.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';


+ 1
- 1
mobile/lib/models/my_profile.dart View File

@ -1,7 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:Envelope/utils/storage/get_file.dart';
import 'package:Capsule/utils/storage/get_file.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:pointycastle/impl.dart'; import 'package:pointycastle/impl.dart';


+ 2
- 2
mobile/lib/utils/storage/conversations.dart View File

@ -1,7 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:Envelope/exceptions/update_data_exception.dart';
import 'package:Envelope/utils/storage/get_file.dart';
import 'package:Capsule/exceptions/update_data_exception.dart';
import 'package:Capsule/utils/storage/get_file.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;


+ 2
- 2
mobile/lib/utils/storage/database.dart View File

@ -4,14 +4,14 @@ import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
Future<void> deleteDb() async { Future<void> deleteDb() async {
final path = join(await getDatabasesPath(), 'envelope.db');
final path = join(await getDatabasesPath(), 'capsule.db');
deleteDatabase(path); deleteDatabase(path);
} }
Future<Database> getDatabaseConnection() async { Future<Database> getDatabaseConnection() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
final path = join(await getDatabasesPath(), 'envelope.db');
final path = join(await getDatabasesPath(), 'capsule.db');
final database = openDatabase( final database = openDatabase(
path, path,


+ 2
- 2
mobile/lib/utils/storage/messages.dart View File

@ -1,8 +1,8 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:Envelope/models/messages.dart';
import 'package:Envelope/utils/storage/write_file.dart';
import 'package:Capsule/models/messages.dart';
import 'package:Capsule/utils/storage/write_file.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';


+ 1
- 1
mobile/lib/views/authentication/login.dart View File

@ -177,7 +177,7 @@ class _LoginWidgetState extends State<LoginWidget> {
}).catchError((error) { }).catchError((error) {
print(error); print(error);
showMessage( showMessage(
'Could not login to Envelope, please try again later.',
'Could not login to Capsule, please try again later.',
context, context,
); );
}); });


+ 3
- 3
mobile/lib/views/authentication/signup.dart View File

@ -1,8 +1,8 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:Envelope/components/flash_message.dart';
import 'package:Envelope/models/my_profile.dart';
import 'package:Capsule/components/flash_message.dart';
import 'package:Capsule/models/my_profile.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
@ -176,7 +176,7 @@ class _SignupWidgetState extends State<SignupWidget> {
.then((dynamic) { .then((dynamic) {
Navigator.of(context).popUntil((route) => route.isFirst); Navigator.of(context).popUntil((route) => route.isFirst);
}).catchError((error) { }).catchError((error) {
showMessage('Failed to signup to Envelope, please try again later', context);
showMessage('Failed to signup to Capsule, please try again later', context);
}); });
}, },
child: const Text('Submit'), child: const Text('Submit'),


+ 1
- 1
mobile/lib/views/authentication/unauthenticated_landing.dart View File

@ -46,7 +46,7 @@ class _UnauthenticatedLandingWidgetState extends State<UnauthenticatedLandingWid
), ),
const SizedBox(width: 15), const SizedBox(width: 15),
Text( Text(
'Envelope',
'Capsule',
style: TextStyle( style: TextStyle(
fontSize: 40, fontSize: 40,
color: Theme.of(context).colorScheme.onBackground, color: Theme.of(context).colorScheme.onBackground,


+ 2
- 2
mobile/lib/views/main/conversation/create_add_users_list.dart View File

@ -1,5 +1,5 @@
import 'package:Envelope/components/custom_circle_avatar.dart';
import 'package:Envelope/models/friends.dart';
import 'package:Capsule/components/custom_circle_avatar.dart';
import 'package:Capsule/models/friends.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ConversationAddFriendItem extends StatefulWidget{ class ConversationAddFriendItem extends StatefulWidget{


+ 1
- 1
mobile/lib/views/main/conversation/detail.dart View File

@ -1,6 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:Envelope/views/main/conversation/message.dart';
import 'package:Capsule/views/main/conversation/message.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';


+ 1
- 1
mobile/lib/views/main/conversation/edit_details.dart View File

@ -1,6 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:Envelope/components/file_picker.dart';
import 'package:Capsule/components/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';


+ 3
- 3
mobile/lib/views/main/conversation/list.dart View File

@ -1,8 +1,8 @@
import 'dart:io'; import 'dart:io';
import 'package:Envelope/components/custom_title_bar.dart';
import 'package:Envelope/models/friends.dart';
import 'package:Envelope/utils/storage/conversations.dart';
import 'package:Capsule/components/custom_title_bar.dart';
import 'package:Capsule/models/friends.dart';
import 'package:Capsule/utils/storage/conversations.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '/models/conversations.dart'; import '/models/conversations.dart';


+ 4
- 4
mobile/lib/views/main/conversation/message.dart View File

@ -1,7 +1,7 @@
import 'package:Envelope/components/view_image.dart';
import 'package:Envelope/models/image_message.dart';
import 'package:Envelope/models/my_profile.dart';
import 'package:Envelope/utils/time.dart';
import 'package:Capsule/components/view_image.dart';
import 'package:Capsule/models/image_message.dart';
import 'package:Capsule/models/my_profile.dart';
import 'package:Capsule/utils/time.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '/models/messages.dart'; import '/models/messages.dart';


+ 7
- 7
mobile/lib/views/main/conversation/settings.dart View File

@ -1,12 +1,12 @@
import 'dart:io'; import 'dart:io';
import 'package:Envelope/components/custom_title_bar.dart';
import 'package:Envelope/components/flash_message.dart';
import 'package:Envelope/exceptions/update_data_exception.dart';
import 'package:Envelope/models/friends.dart';
import 'package:Envelope/utils/encryption/crypto_utils.dart';
import 'package:Envelope/utils/storage/write_file.dart';
import 'package:Envelope/views/main/conversation/create_add_users.dart';
import 'package:Capsule/components/custom_title_bar.dart';
import 'package:Capsule/components/flash_message.dart';
import 'package:Capsule/exceptions/update_data_exception.dart';
import 'package:Capsule/models/friends.dart';
import 'package:Capsule/utils/encryption/crypto_utils.dart';
import 'package:Capsule/utils/storage/write_file.dart';
import 'package:Capsule/views/main/conversation/create_add_users.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '/models/conversation_users.dart'; import '/models/conversation_users.dart';


+ 4
- 4
mobile/lib/views/main/friend/list.dart View File

@ -1,7 +1,7 @@
import 'package:Envelope/components/custom_title_bar.dart';
import 'package:Envelope/components/qr_reader.dart';
import 'package:Envelope/views/main/friend/add_search.dart';
import 'package:Envelope/views/main/friend/request_list_item.dart';
import 'package:Capsule/components/custom_title_bar.dart';
import 'package:Capsule/components/qr_reader.dart';
import 'package:Capsule/views/main/friend/add_search.dart';
import 'package:Capsule/views/main/friend/request_list_item.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '/models/friends.dart'; import '/models/friends.dart';


+ 6
- 6
mobile/lib/views/main/friend/list_item.dart View File

@ -1,9 +1,9 @@
import 'package:Envelope/components/custom_circle_avatar.dart';
import 'package:Envelope/models/conversations.dart';
import 'package:Envelope/models/friends.dart';
import 'package:Envelope/utils/storage/conversations.dart';
import 'package:Envelope/utils/strings.dart';
import 'package:Envelope/views/main/conversation/detail.dart';
import 'package:Capsule/components/custom_circle_avatar.dart';
import 'package:Capsule/models/conversations.dart';
import 'package:Capsule/models/friends.dart';
import 'package:Capsule/utils/storage/conversations.dart';
import 'package:Capsule/utils/strings.dart';
import 'package:Capsule/views/main/conversation/detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class FriendListItem extends StatefulWidget{ class FriendListItem extends StatefulWidget{


+ 1
- 1
mobile/lib/views/main/profile/change_server_url.dart View File

@ -111,7 +111,7 @@ WARNING: Do not use this feature unless you know what you\'re doing!
'''; ''';
String warning2 = ''' String warning2 = '''
Changing the server url will disconnect you from all friends and conversations on this server, and connect you to a fresh environment. This feature is intended to be used by people that are willing to host their own Envelope server, which you can find by going to \nhttps://github.com/SomeUsername/SomeRepo.\n\n
Changing the server url will disconnect you from all friends and conversations on this server, and connect you to a fresh environment. This feature is intended to be used by people that are willing to host their own Capsule server, which you can find by going to \nhttps://github.com/SomeUsername/SomeRepo.\n\n
You can revert this by entering \nhttps://envelope-messenger.com\n on the login screen. You can revert this by entering \nhttps://envelope-messenger.com\n on the login screen.
'''; ''';


+ 5
- 5
mobile/lib/views/main/profile/profile.dart View File

@ -2,11 +2,11 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:Envelope/components/file_picker.dart';
import 'package:Envelope/components/flash_message.dart';
import 'package:Envelope/utils/encryption/aes_helper.dart';
import 'package:Envelope/utils/storage/session_cookie.dart';
import 'package:Envelope/utils/storage/write_file.dart';
import 'package:Capsule/components/file_picker.dart';
import 'package:Capsule/components/flash_message.dart';
import 'package:Capsule/utils/encryption/aes_helper.dart';
import 'package:Capsule/utils/storage/session_cookie.dart';
import 'package:Capsule/utils/storage/write_file.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';


+ 2
- 2
mobile/pubspec.yaml View File

@ -1,5 +1,5 @@
name: Envelope
description: A new Flutter project.
name: Capsule
description: An encrypted messaging app
publish_to: 'none' # Remove this line if you wish to publish to pub.dev publish_to: 'none' # Remove this line if you wish to publish to pub.dev


+ 3
- 0
test.sh View File

@ -0,0 +1,3 @@
#!/bin/sh
docker-compose exec server sh -c "cd /app && go test -p 1 -v ./..."

Loading…
Cancel
Save