diff --git a/Backend/Api/Auth/AddProfileImage.go b/Backend/Api/Auth/AddProfileImage.go index deaea1c..af88ced 100644 --- a/Backend/Api/Auth/AddProfileImage.go +++ b/Backend/Api/Auth/AddProfileImage.go @@ -35,7 +35,17 @@ func AddProfileImage(w http.ResponseWriter, r *http.Request) { } decodedFile, err = base64.StdEncoding.DecodeString(attachment.Data) + if err != nil { + http.Error(w, "Error", http.StatusInternalServerError) + return + } + fileName, err = Util.WriteFile(decodedFile) + if err != nil { + http.Error(w, "Error", http.StatusInternalServerError) + return + } + attachment.FilePath = fileName user.Attachment = attachment diff --git a/Backend/Api/Auth/AddProfileImage_test.go b/Backend/Api/Auth/AddProfileImage_test.go new file mode 100644 index 0000000..eb0864f --- /dev/null +++ b/Backend/Api/Auth/AddProfileImage_test.go @@ -0,0 +1,143 @@ +package Auth_test + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "io/ioutil" + "log" + "net/http" + "net/http/cookiejar" + "net/http/httptest" + "net/url" + "os" + "testing" + "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 Test_AddProfileImage(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() + + p, _ := Auth.HashPassword("password") + + u := Models.User{ + Username: "test", + Password: p, + AsymmetricPublicKey: Seeder.PublicKey, + AsymmetricPrivateKey: Seeder.EncryptedPrivateKey, + SymmetricKey: base64.StdEncoding.EncodeToString( + Seeder.EncryptWithPublicKey(userKey.Key, pubKey), + ), + } + + err := Database.CreateUser(&u) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + session := Models.Session{ + UserID: u.ID, + Expiry: time.Now().Add(12 * time.Hour), + } + + err = Database.CreateSession(&session) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + jar, err := cookiejar.New(nil) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + url, _ := url.Parse(ts.URL) + + jar.SetCookies( + url, + []*http.Cookie{ + { + Name: "session_token", + Value: session.ID.String(), + MaxAge: 300, + }, + }, + ) + + 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") + + client := &http.Client{ + Jar: jar, + } + + 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.GetUserById(u.ID.String()) + 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 + } +} diff --git a/Backend/Api/Auth/ChangeMessageExpiry.go b/Backend/Api/Auth/ChangeMessageExpiry.go index 883f7e7..aa2fd5e 100644 --- a/Backend/Api/Auth/ChangeMessageExpiry.go +++ b/Backend/Api/Auth/ChangeMessageExpiry.go @@ -37,7 +37,11 @@ func ChangeMessageExpiry(w http.ResponseWriter, r *http.Request) { 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( user.ID.String(), @@ -48,5 +52,5 @@ func ChangeMessageExpiry(w http.ResponseWriter, r *http.Request) { return } - w.WriteHeader(http.StatusOK) + w.WriteHeader(http.StatusNoContent) } diff --git a/Backend/Api/Auth/ChangeMessageExpiry_test.go b/Backend/Api/Auth/ChangeMessageExpiry_test.go new file mode 100644 index 0000000..03012dc --- /dev/null +++ b/Backend/Api/Auth/ChangeMessageExpiry_test.go @@ -0,0 +1,212 @@ +package Auth_test + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "io/ioutil" + "log" + "net/http" + "net/http/cookiejar" + "net/http/httptest" + "net/url" + "testing" + "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 Test_ChangeMessageExpiry(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() + + p, _ := Auth.HashPassword("password") + + u := Models.User{ + Username: "test", + Password: p, + AsymmetricPublicKey: Seeder.PublicKey, + AsymmetricPrivateKey: Seeder.EncryptedPrivateKey, + SymmetricKey: base64.StdEncoding.EncodeToString( + Seeder.EncryptWithPublicKey(userKey.Key, pubKey), + ), + } + + err := Database.CreateUser(&u) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + session := Models.Session{ + UserID: u.ID, + Expiry: time.Now().Add(12 * time.Hour), + } + + err = Database.CreateSession(&session) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + jar, err := cookiejar.New(nil) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + url, _ := url.Parse(ts.URL) + + jar.SetCookies( + url, + []*http.Cookie{ + { + Name: "session_token", + Value: session.ID.String(), + MaxAge: 300, + }, + }, + ) + + 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") + + client := &http.Client{ + Jar: jar, + } + + 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.GetUserById(u.ID.String()) + 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) { + 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() + + p, _ := Auth.HashPassword("password") + + u := Models.User{ + Username: "test", + Password: p, + AsymmetricPublicKey: Seeder.PublicKey, + AsymmetricPrivateKey: Seeder.EncryptedPrivateKey, + SymmetricKey: base64.StdEncoding.EncodeToString( + Seeder.EncryptWithPublicKey(userKey.Key, pubKey), + ), + } + + err := Database.CreateUser(&u) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + session := Models.Session{ + UserID: u.ID, + Expiry: time.Now().Add(12 * time.Hour), + } + + err = Database.CreateSession(&session) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + jar, err := cookiejar.New(nil) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + url, _ := url.Parse(ts.URL) + + jar.SetCookies( + url, + []*http.Cookie{ + { + Name: "session_token", + Value: session.ID.String(), + MaxAge: 300, + }, + }, + ) + + 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") + + client := &http.Client{ + Jar: jar, + } + + 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.GetUserById(u.ID.String()) + 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") + } +} diff --git a/Backend/Api/Auth/ChangePassword.go b/Backend/Api/Auth/ChangePassword.go index 2688251..6e04bb5 100644 --- a/Backend/Api/Auth/ChangePassword.go +++ b/Backend/Api/Auth/ChangePassword.go @@ -72,5 +72,5 @@ func ChangePassword(w http.ResponseWriter, r *http.Request) { return } - w.WriteHeader(http.StatusOK) + w.WriteHeader(http.StatusNoContent) } diff --git a/Backend/Api/Auth/ChangePassword_test.go b/Backend/Api/Auth/ChangePassword_test.go new file mode 100644 index 0000000..53d9491 --- /dev/null +++ b/Backend/Api/Auth/ChangePassword_test.go @@ -0,0 +1,306 @@ +package Auth_test + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "io/ioutil" + "log" + "net/http" + "net/http/cookiejar" + "net/http/httptest" + "net/url" + "testing" + "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 Test_ChangePassword(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() + + p, _ := Auth.HashPassword("password") + + u := Models.User{ + Username: "test", + Password: p, + AsymmetricPublicKey: Seeder.PublicKey, + AsymmetricPrivateKey: Seeder.EncryptedPrivateKey, + SymmetricKey: base64.StdEncoding.EncodeToString( + Seeder.EncryptWithPublicKey(userKey.Key, pubKey), + ), + } + + err := Database.CreateUser(&u) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + session := Models.Session{ + UserID: u.ID, + Expiry: time.Now().Add(12 * time.Hour), + } + + err = Database.CreateSession(&session) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + jar, err := cookiejar.New(nil) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + url, _ := url.Parse(ts.URL) + + jar.SetCookies( + url, + []*http.Cookie{ + { + Name: "session_token", + Value: session.ID.String(), + MaxAge: 300, + }, + }, + ) + + 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") + + client := &http.Client{ + Jar: jar, + } + + 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.GetUserById(u.ID.String()) + 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) { + 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() + + p, _ := Auth.HashPassword("password") + + u := Models.User{ + Username: "test", + Password: p, + AsymmetricPublicKey: Seeder.PublicKey, + AsymmetricPrivateKey: Seeder.EncryptedPrivateKey, + SymmetricKey: base64.StdEncoding.EncodeToString( + Seeder.EncryptWithPublicKey(userKey.Key, pubKey), + ), + } + + err := Database.CreateUser(&u) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + session := Models.Session{ + UserID: u.ID, + Expiry: time.Now().Add(12 * time.Hour), + } + + err = Database.CreateSession(&session) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + jar, err := cookiejar.New(nil) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + url, _ := url.Parse(ts.URL) + + jar.SetCookies( + url, + []*http.Cookie{ + { + Name: "session_token", + Value: session.ID.String(), + MaxAge: 300, + }, + }, + ) + + 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") + + client := &http.Client{ + Jar: jar, + } + + 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) { + 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() + + p, _ := Auth.HashPassword("password") + + u := Models.User{ + Username: "test", + Password: p, + AsymmetricPublicKey: Seeder.PublicKey, + AsymmetricPrivateKey: Seeder.EncryptedPrivateKey, + SymmetricKey: base64.StdEncoding.EncodeToString( + Seeder.EncryptWithPublicKey(userKey.Key, pubKey), + ), + } + + err := Database.CreateUser(&u) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + session := Models.Session{ + UserID: u.ID, + Expiry: time.Now().Add(12 * time.Hour), + } + + err = Database.CreateSession(&session) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + jar, err := cookiejar.New(nil) + if err != nil { + t.Errorf("Expected nil, recieved %s", err.Error()) + return + } + + url, _ := url.Parse(ts.URL) + + jar.SetCookies( + url, + []*http.Cookie{ + { + Name: "session_token", + Value: session.ID.String(), + MaxAge: 300, + }, + }, + ) + + 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") + + client := &http.Client{ + Jar: jar, + } + + 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) + } +} diff --git a/Backend/Api/Auth/Logout_test.go b/Backend/Api/Auth/Logout_test.go index 89e2262..85c2516 100644 --- a/Backend/Api/Auth/Logout_test.go +++ b/Backend/Api/Auth/Logout_test.go @@ -10,7 +10,6 @@ import ( "net/http/cookiejar" "net/http/httptest" "net/url" - "sync" "testing" "git.tovijaeschke.xyz/tovi/Capsule/Backend/Api" @@ -21,11 +20,6 @@ import ( "github.com/gorilla/mux" ) -type Jar struct { - lk sync.Mutex - cookies map[string][]*http.Cookie -} - func Test_Logout(t *testing.T) { log.SetOutput(ioutil.Discard) Database.InitTest() diff --git a/Backend/Api/Auth/profile_picture_test.png b/Backend/Api/Auth/profile_picture_test.png new file mode 100644 index 0000000..bec7dbc Binary files /dev/null and b/Backend/Api/Auth/profile_picture_test.png differ diff --git a/Backend/Api/JsonSerialization/DeserializeUserJson.go b/Backend/Api/JsonSerialization/DeserializeUserJson.go deleted file mode 100644 index 4d5af16..0000000 --- a/Backend/Api/JsonSerialization/DeserializeUserJson.go +++ /dev/null @@ -1,76 +0,0 @@ -package JsonSerialization - -import ( - "encoding/json" - "errors" - "fmt" - "strings" - - "git.tovijaeschke.xyz/tovi/Capsule/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 -} diff --git a/Backend/Api/JsonSerialization/VerifyJson.go b/Backend/Api/JsonSerialization/VerifyJson.go deleted file mode 100644 index 3a3ae78..0000000 --- a/Backend/Api/JsonSerialization/VerifyJson.go +++ /dev/null @@ -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 -} diff --git a/Backend/Database/Seeder/FriendSeeder.go b/Backend/Database/Seeder/FriendSeeder.go index a527004..b4cacd5 100644 --- a/Backend/Database/Seeder/FriendSeeder.go +++ b/Backend/Database/Seeder/FriendSeeder.go @@ -23,7 +23,7 @@ func seedFriend(userRequestTo, userRequestFrom Models.User, accepted bool) error return err } - encPublicKey, err = symKey.aesEncrypt([]byte(PublicKey)) + encPublicKey, err = symKey.AesEncrypt([]byte(PublicKey)) if err != nil { return err } diff --git a/Backend/Database/Seeder/MessageSeeder.go b/Backend/Database/Seeder/MessageSeeder.go index efe4646..1b4b0a8 100644 --- a/Backend/Database/Seeder/MessageSeeder.go +++ b/Backend/Database/Seeder/MessageSeeder.go @@ -37,24 +37,24 @@ func seedMessage( panic(err) } - dataCiphertext, err = key.aesEncrypt([]byte(plaintext)) + dataCiphertext, err = key.AesEncrypt([]byte(plaintext)) if err != nil { panic(err) } - senderIDCiphertext, err = key.aesEncrypt([]byte(primaryUser.ID.String())) + senderIDCiphertext, err = key.AesEncrypt([]byte(primaryUser.ID.String())) if err != nil { panic(err) } if i%2 == 0 { - senderIDCiphertext, err = key.aesEncrypt([]byte(secondaryUser.ID.String())) + senderIDCiphertext, err = key.AesEncrypt([]byte(secondaryUser.ID.String())) if err != nil { panic(err) } } - keyCiphertext, err = userKey.aesEncrypt( + keyCiphertext, err = userKey.AesEncrypt( []byte(base64.StdEncoding.EncodeToString(key.Key)), ) if err != nil { @@ -102,12 +102,12 @@ func seedConversationDetail(key aesKey) (Models.ConversationDetail, error) { name = "Test Conversation" - nameCiphertext, err = key.aesEncrypt([]byte(name)) + nameCiphertext, err = key.AesEncrypt([]byte(name)) if err != nil { panic(err) } - twoUserCiphertext, err = key.aesEncrypt([]byte("false")) + twoUserCiphertext, err = key.AesEncrypt([]byte("false")) if err != nil { panic(err) } @@ -133,12 +133,12 @@ func seedUserConversation( err error ) - conversationDetailIDCiphertext, err = key.aesEncrypt([]byte(threadID.String())) + conversationDetailIDCiphertext, err = key.AesEncrypt([]byte(threadID.String())) if err != nil { return messageThreadUser, err } - adminCiphertext, err = key.aesEncrypt([]byte("true")) + adminCiphertext, err = key.AesEncrypt([]byte("true")) if err != nil { return messageThreadUser, err } @@ -181,27 +181,27 @@ func seedConversationDetailUser( adminString = "true" } - userIDCiphertext, err = key.aesEncrypt([]byte(user.ID.String())) + userIDCiphertext, err = key.AesEncrypt([]byte(user.ID.String())) if err != nil { return conversationDetailUser, err } - usernameCiphertext, err = key.aesEncrypt([]byte(user.Username)) + usernameCiphertext, err = key.AesEncrypt([]byte(user.Username)) if err != nil { return conversationDetailUser, err } - adminCiphertext, err = key.aesEncrypt([]byte(adminString)) + adminCiphertext, err = key.AesEncrypt([]byte(adminString)) if err != nil { return conversationDetailUser, err } - associationKeyCiphertext, err = key.aesEncrypt([]byte(associationKey.String())) + associationKeyCiphertext, err = key.AesEncrypt([]byte(associationKey.String())) if err != nil { return conversationDetailUser, err } - publicKeyCiphertext, err = key.aesEncrypt([]byte(user.AsymmetricPublicKey)) + publicKeyCiphertext, err = key.AesEncrypt([]byte(user.AsymmetricPublicKey)) if err != nil { return conversationDetailUser, err } diff --git a/Backend/Database/Seeder/encryption.go b/Backend/Database/Seeder/encryption.go index 61f9013..e0f5c74 100644 --- a/Backend/Database/Seeder/encryption.go +++ b/Backend/Database/Seeder/encryption.go @@ -110,7 +110,7 @@ func GenerateAesKey() (aesKey, error) { }, nil } -func (key aesKey) aesEncrypt(plaintext []byte) ([]byte, error) { +func (key aesKey) AesEncrypt(plaintext []byte) ([]byte, error) { var ( bPlaintext []byte ciphertext []byte @@ -134,7 +134,7 @@ func (key aesKey) aesEncrypt(plaintext []byte) ([]byte, error) { return ciphertext, nil } -func (key aesKey) aesDecrypt(ciphertext []byte) ([]byte, error) { +func (key aesKey) AesDecrypt(ciphertext []byte) ([]byte, error) { var ( plaintext []byte iv []byte diff --git a/Backend/Models/Users.go b/Backend/Models/Users.go index 811c3ab..736289e 100644 --- a/Backend/Models/Users.go +++ b/Backend/Models/Users.go @@ -2,6 +2,7 @@ package Models import ( "database/sql/driver" + "errors" "github.com/gofrs/uuid" "gorm.io/gorm" @@ -40,10 +41,35 @@ const ( 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 func (e *MessageExpiry) Scan(value interface{}) error { - *e = MessageExpiry(value.(string)) - return nil + var ( + strValue = value.(string) + m string + ) + + for _, m = range MessageExpiryValues { + if strValue != m { + continue + } + *e = MessageExpiry(strValue) + return nil + } + + return errors.New("Invalid MessageExpiry value") } // Value gets value out of MessageExpiry column @@ -51,6 +77,10 @@ func (e MessageExpiry) Value() (driver.Value, error) { return string(e), nil } +func (e MessageExpiry) String() string { + return string(e) +} + // User holds user data type User struct { Base diff --git a/Backend/Util/Files.go b/Backend/Util/Files.go index 4ee8b81..154b1ef 100644 --- a/Backend/Util/Files.go +++ b/Backend/Util/Files.go @@ -10,21 +10,14 @@ func WriteFile(contents []byte) (string, error) { var ( fileName string filePath string - cwd string f *os.File err error ) - cwd, err = os.Getwd() - if err != nil { - return fileName, err - } - fileName = RandomString(32) filePath = fmt.Sprintf( - "%s/attachments/%s", - cwd, + "/app/attachments/%s", fileName, ) diff --git a/test.sh b/test.sh index 153de7f..66de839 100644 --- a/test.sh +++ b/test.sh @@ -1,3 +1,3 @@ #!/bin/sh -docker-compose exec server sh -c "cd /app && go test -v ./Api/Auth" +docker-compose exec server sh -c "cd /app && go test -v ./..."