diff --git a/cmd/webapi/__debug_bin b/cmd/webapi/__debug_bin new file mode 100755 index 0000000..7110eaf Binary files /dev/null and b/cmd/webapi/__debug_bin differ diff --git a/cmd/webapi/wasaphoto.db b/cmd/webapi/wasaphoto.db index 99da9f3..965ceb7 100644 Binary files a/cmd/webapi/wasaphoto.db and b/cmd/webapi/wasaphoto.db differ diff --git a/service/api/api-handler.go b/service/api/api-handler.go index ebcd0b6..bc81e3e 100644 --- a/service/api/api-handler.go +++ b/service/api/api-handler.go @@ -22,6 +22,7 @@ func (rt *_router) Handler() http.Handler { rt.router.PUT("/users/:user_id/bans/:ban_uid", rt.wrap(rt.PutBan)) rt.router.DELETE("/users/:user_id/bans/:ban_uid", rt.wrap(rt.DeleteBan)) + rt.router.GET("/users/:user_id/photos", rt.wrap(rt.GetUserPhotos)) rt.router.POST("/users/:user_id/photos", rt.wrap(rt.PostPhoto)) rt.router.GET("/users/:user_id/photos/:photo_id", rt.wrap(rt.GetPhoto)) rt.router.DELETE("/users/:user_id/photos/:photo_id", rt.wrap(rt.DeletePhoto)) @@ -36,7 +37,7 @@ func (rt *_router) Handler() http.Handler { rt.router.GET("/users/:user_id", rt.wrap(rt.GetUserProfile)) - rt.router.GET("/stream", rt.wrap(rt.GetUserStream)) //todo: why not "/users/:user_id/stream"? + rt.router.GET("/stream", rt.wrap(rt.GetUserStream)) rt.router.GET("/", rt.getHelloWorld) rt.router.GET("/context", rt.wrap(rt.getContextReply)) diff --git a/service/api/authorization/auth-manager.go b/service/api/authorization/auth-manager.go index 325ba18..41d6da4 100644 --- a/service/api/authorization/auth-manager.go +++ b/service/api/authorization/auth-manager.go @@ -37,7 +37,7 @@ func SendAuthorizationError(f func(db database.AppDatabase, uid string) (reqcont } // requested user is not found -> 404 as the resource is not found if auth == reqcontext.USER_NOT_FOUND { - helpers.SendStatus(notFoundStatus, w, "Resource not found", l) + helpers.SendStatus(notFoundStatus, w, "User not found", l) return false } return true diff --git a/service/api/followers.go b/service/api/followers.go index be19e35..852fbc1 100644 --- a/service/api/followers.go +++ b/service/api/followers.go @@ -15,6 +15,10 @@ import ( func (rt *_router) GetFollowersFollowing(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) { + if !authorization.SendErrorIfNotLoggedIn(ctx.Auth.Authorized, rt.db, w, rt.baseLogger) { + return + } + uid := ps.ByName("user_id") if !helpers.VerifyUserOrNotFound(rt.db, uid, w, rt.baseLogger) { diff --git a/service/api/get-userprofile.go b/service/api/get-userprofile.go index 2ae5d75..feebf59 100644 --- a/service/api/get-userprofile.go +++ b/service/api/get-userprofile.go @@ -64,7 +64,7 @@ func (rt *_router) GetUserPhotos(w http.ResponseWriter, r *http.Request, ps http } // Get user photos - photos, err := rt.db.GetUserPhotos(uid, start_index, limit) + photos, err := rt.db.GetUserPhotos(uid, ctx.Auth.GetUserID(), start_index, limit) if err != nil { helpers.SendInternalError(err, "Database error: GetUserPhotos", w, rt.baseLogger) diff --git a/service/api/helpers/api-helpers.go b/service/api/helpers/api-helpers.go index 1105728..7d1bd81 100644 --- a/service/api/helpers/api-helpers.go +++ b/service/api/helpers/api-helpers.go @@ -41,7 +41,6 @@ func SendStatus(httpStatus int, w http.ResponseWriter, description string, l log err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description}) if err != nil { l.WithError(err).Error("Error encoding json") - //todo: empty response? } } @@ -50,7 +49,6 @@ func SendNotFound(w http.ResponseWriter, description string, l logrus.FieldLogge err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description}) if err != nil { l.WithError(err).Error("Error encoding json") - //todo: empty response? } } @@ -59,7 +57,6 @@ func SendBadRequest(w http.ResponseWriter, description string, l logrus.FieldLog err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description}) if err != nil { l.WithError(err).Error("Error encoding json") - //todo: empty response? } } @@ -69,7 +66,6 @@ func SendBadRequestError(err error, description string, w http.ResponseWriter, l err = json.NewEncoder(w).Encode(structures.GenericResponse{Status: description}) if err != nil { l.WithError(err).Error("Error encoding json") - //todo: empty response? } } @@ -79,7 +75,6 @@ func SendInternalError(err error, description string, w http.ResponseWriter, l l err = json.NewEncoder(w).Encode(structures.GenericResponse{Status: description}) if err != nil { l.WithError(err).Error("Error encoding json") - //todo: empty response? } } diff --git a/service/api/likes.go b/service/api/likes.go index f107dad..bd0db98 100644 --- a/service/api/likes.go +++ b/service/api/likes.go @@ -14,6 +14,10 @@ import ( func (rt *_router) GetLikes(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) { + if !authorization.SendErrorIfNotLoggedIn(ctx.Auth.Authorized, rt.db, w, rt.baseLogger) { + return + } + // get the user id from the url uid := ps.ByName("user_id") photo_id, err := strconv.ParseInt(ps.ByName("photo_id"), 10, 64) diff --git a/service/api/post-session.go b/service/api/post-session.go index 75dd230..673cc05 100644 --- a/service/api/post-session.go +++ b/service/api/post-session.go @@ -23,7 +23,7 @@ type _respbody struct { func (rt *_router) PostSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) { var request _reqbody - err := json.NewDecoder(r.Body).Decode(&request) //todo: capire se serve close + err := json.NewDecoder(r.Body).Decode(&request) var uid string if err == nil { // test if user exists diff --git a/service/api/put-updateusername.go b/service/api/put-updateusername.go index 4d267aa..0b2cb25 100644 --- a/service/api/put-updateusername.go +++ b/service/api/put-updateusername.go @@ -21,19 +21,12 @@ func (rt *_router) UpdateUsername(w http.ResponseWriter, r *http.Request, ps htt return } - //err := json.NewDecoder(r.Body).Decode(&req) //todo: capire se serve close - - //if err != nil { - // w.WriteHeader(http.StatusBadRequest) // todo: move to DecodeOrBadRequest helper - // return - //} - err := rt.db.UpdateUsername(uid, req.Name) if err != nil { - w.WriteHeader(http.StatusInternalServerError) // todo: is not ok, maybe let's use a helper + helpers.SendInternalError(err, "Database error: UpdateUsername", w, rt.baseLogger) return } - w.WriteHeader(http.StatusNoContent) // todo: change to 204 also in API spec + w.WriteHeader(http.StatusNoContent) } diff --git a/service/database/database.go b/service/database/database.go index 955c385..29ce0cb 100644 --- a/service/database/database.go +++ b/service/database/database.go @@ -42,6 +42,7 @@ import ( type AppDatabase interface { CreateUser(name string) (string, error) UserExists(uid string) (bool, error) + UserExistsNotBanned(uid string, requesting_uid string) (bool, error) GetUserID(name string) (string, error) SearchByName(name string, requesting_uid string, start_index int, limit int) (*[]structures.UIDName, error) @@ -67,7 +68,7 @@ type AppDatabase interface { UnlikePhoto(uid string, photo int64, liker_uid string) (QueryResult, error) GetUserProfile(uid string, requesting_uid string) (QueryResult, *structures.UserProfile, error) - GetUserPhotos(uid string, start_index int, limit int) (*[]structures.UserPhoto, error) + GetUserPhotos(uid string, requesting_uid string, start_index int, limit int) (*[]structures.UserPhoto, error) GetUserStream(uid string, start_index int, limit int) (*[]structures.Photo, error) GetComments(uid string, photo_id int64, requesting_uid string, start_index int, offset int) (QueryResult, *[]structures.Comment, error) diff --git a/service/database/db-comments.go b/service/database/db-comments.go index 1b8bc5d..4247b7e 100644 --- a/service/database/db-comments.go +++ b/service/database/db-comments.go @@ -23,8 +23,9 @@ func (db *appdbimpl) PostComment(uid string, photo_id int64, comment_user string _, err = db.c.Exec(`PRAGMA foreign_keys = ON; INSERT INTO "comments" ("user", "photo", "comment", "date") VALUES (?, ?, ?, ?)`, comment_user, photo_id, comment, time.Now().Format(time.RFC3339)) - // todo: we don't actually need it, it's already done before if db_errors.ForeignKeyViolation(err) { + // trying to post a comment on a photo that does not exist + // (actually this should never happen, as we checked if the photo exists before) return ERR_NOT_FOUND, nil } diff --git a/service/database/db-profile.go b/service/database/db-profile.go index c6beebe..7781793 100644 --- a/service/database/db-profile.go +++ b/service/database/db-profile.go @@ -64,7 +64,7 @@ func (db *appdbimpl) GetUserProfile(uid string, requesting_uid string) (QueryRes }, nil } -func (db *appdbimpl) GetUserPhotos(uid string, start_index int, limit int) (*[]structures.UserPhoto, error) { +func (db *appdbimpl) GetUserPhotos(uid string, requesting_uid string, start_index int, limit int) (*[]structures.UserPhoto, error) { // Get photos rows, err := db.c.Query(`SELECT "p"."id", "p"."date", @@ -83,8 +83,9 @@ func (db *appdbimpl) GetUserPhotos(uid string, start_index int, limit int) (*[]s ) FROM "photos" AS "p" WHERE "p"."user" = ? - OFFSET ? - LIMIT ?`, uid, uid, start_index, limit) + + LIMIT ? + OFFSET ?`, requesting_uid, uid, limit, start_index) if err != nil { // Return the error return nil, err diff --git a/service/database/db-users.go b/service/database/db-users.go index e528c3f..793f926 100644 --- a/service/database/db-users.go +++ b/service/database/db-users.go @@ -20,6 +20,24 @@ func (db *appdbimpl) UserExists(uid string) (bool, error) { return cnt > 0, nil } +// User exists and is not banned +func (db *appdbimpl) UserExistsNotBanned(uid string, requesting_uid string) (bool, error) { + + var cnt int + err := db.c.QueryRow(`SELECT COUNT(*) FROM "users" + WHERE "uid" = ? + AND NOT EXISTS ( + SELECT "bans"."user" FROM "bans" + WHERE "bans"."user" = "users"."uid" + AND "bans"."ban" = ? + )`, uid, requesting_uid).Scan(&cnt) + + if err != nil { + return false, err + } + return cnt > 0, nil +} + // Get user id by username func (db *appdbimpl) GetUserID(name string) (string, error) { var uid string @@ -47,7 +65,7 @@ func (db *appdbimpl) UpdateUsername(uid string, name string) error { func (db *appdbimpl) GetUserFollowers(uid string, requesting_uid string, start_index int, limit int) (QueryResult, *[]structures.UIDName, error) { // user may exist but have no followers - exists, err := db.UserExists(uid) + exists, err := db.UserExistsNotBanned(uid, requesting_uid) if err != nil { return ERR_INTERNAL, nil, err @@ -57,7 +75,7 @@ func (db *appdbimpl) GetUserFollowers(uid string, requesting_uid string, start_i return ERR_NOT_FOUND, nil, nil } - rows, err := db.c.Query(`SELECT "follower", "user"."name" FROM "follows", "users" + rows, err := db.c.Query(`SELECT "follower", "users"."name" FROM "follows", "users" WHERE "follows"."follower" = "users"."uid" AND "follows"."follower" NOT IN ( @@ -67,8 +85,8 @@ func (db *appdbimpl) GetUserFollowers(uid string, requesting_uid string, start_i ) AND "followed" = ? - OFFSET ? - LIMIT ?`, uid, requesting_uid, start_index, limit) + LIMIT ? + OFFSET ?`, uid, requesting_uid, limit, start_index) followers, err := db.uidNameQuery(rows, err) @@ -83,7 +101,7 @@ func (db *appdbimpl) GetUserFollowers(uid string, requesting_uid string, start_i func (db *appdbimpl) GetUserFollowing(uid string, requesting_uid string, start_index int, offset int) (QueryResult, *[]structures.UIDName, error) { // user may exist but have no followers - exists, err := db.UserExists(uid) + exists, err := db.UserExistsNotBanned(uid, requesting_uid) if err != nil { return ERR_INTERNAL, nil, err diff --git a/service/structures/api-structures.go b/service/structures/api-structures.go index 72d4e08..b1f08b4 100644 --- a/service/structures/api-structures.go +++ b/service/structures/api-structures.go @@ -21,7 +21,7 @@ type Comment struct { Date string `json:"date"` } -type Photo struct { //todo: move to structures +type Photo struct { UID string `json:"user_id"` ID int64 `json:"photo_id"` Likes int64 `json:"likes"` @@ -44,5 +44,5 @@ type UserProfile struct { Following int64 `json:"following"` Followers int64 `json:"followers"` Followed bool `json:"followed"` - Photos int64 `json:"photos"` //todo: check json names + Photos int64 `json:"photos"` }