From 3556ae4e651d9443dc3bb8a170dd3cc726517a53 Mon Sep 17 00:00:00 2001 From: boojack Date: Wed, 28 Dec 2022 20:22:52 +0800 Subject: [PATCH] fix: access control (#870) --- api/memo.go | 8 +- api/memo_organizer.go | 10 +- api/memo_resource.go | 2 +- api/resource.go | 2 +- api/shortcut.go | 2 +- api/tag.go | 2 +- api/user_setting.go | 2 +- server/auth.go | 6 +- server/memo.go | 325 ++++++++++++++++++++++-------------------- server/resource.go | 21 +-- server/shortcut.go | 56 +++++--- server/system.go | 29 ++-- server/tag.go | 28 +--- server/user.go | 18 ++- 14 files changed, 276 insertions(+), 235 deletions(-) diff --git a/api/memo.go b/api/memo.go index 01aa42ff003ca..32e53835ff5a2 100644 --- a/api/memo.go +++ b/api/memo.go @@ -46,7 +46,7 @@ type Memo struct { type MemoCreate struct { // Standard fields - CreatorID int + CreatorID int `json:"-"` // Domain specific fields Visibility Visibility `json:"visibility"` @@ -73,11 +73,11 @@ type MemoPatch struct { } type MemoFind struct { - ID *int `json:"id"` + ID *int // Standard fields - RowStatus *RowStatus `json:"rowStatus"` - CreatorID *int `json:"creatorId"` + RowStatus *RowStatus + CreatorID *int // Domain specific fields Pinned *bool diff --git a/api/memo_organizer.go b/api/memo_organizer.go index 7c99280c96cc4..10cc83bcbddd3 100644 --- a/api/memo_organizer.go +++ b/api/memo_organizer.go @@ -9,15 +9,15 @@ type MemoOrganizer struct { Pinned bool } -type MemoOrganizerFind struct { - MemoID int - UserID int +type MemoOrganizerUpsert struct { + MemoID int `json:"-"` + UserID int `json:"-"` + Pinned bool `json:"pinned"` } -type MemoOrganizerUpsert struct { +type MemoOrganizerFind struct { MemoID int UserID int - Pinned bool `json:"pinned"` } type MemoOrganizerDelete struct { diff --git a/api/memo_resource.go b/api/memo_resource.go index f2ceacf63a625..3c0b82cea60bc 100644 --- a/api/memo_resource.go +++ b/api/memo_resource.go @@ -8,7 +8,7 @@ type MemoResource struct { } type MemoResourceUpsert struct { - MemoID int + MemoID int `json:"-"` ResourceID int UpdatedTs *int64 } diff --git a/api/resource.go b/api/resource.go index 90e47c7f789ee..d08ca072f883b 100644 --- a/api/resource.go +++ b/api/resource.go @@ -20,7 +20,7 @@ type Resource struct { type ResourceCreate struct { // Standard fields - CreatorID int + CreatorID int `json:"-"` // Domain specific fields Filename string `json:"filename"` diff --git a/api/shortcut.go b/api/shortcut.go index 5eaedd3ccc572..ce5dffffb574d 100644 --- a/api/shortcut.go +++ b/api/shortcut.go @@ -16,7 +16,7 @@ type Shortcut struct { type ShortcutCreate struct { // Standard fields - CreatorID int + CreatorID int `json:"-"` // Domain specific fields Title string `json:"title"` diff --git a/api/tag.go b/api/tag.go index 77d6403c307a5..50e6b5ed1945d 100644 --- a/api/tag.go +++ b/api/tag.go @@ -7,7 +7,7 @@ type Tag struct { type TagUpsert struct { Name string - CreatorID int + CreatorID int `json:"-"` } type TagFind struct { diff --git a/api/user_setting.go b/api/user_setting.go index 6c7ff6987041e..f3b272f4702c4 100644 --- a/api/user_setting.go +++ b/api/user_setting.go @@ -50,7 +50,7 @@ type UserSetting struct { } type UserSettingUpsert struct { - UserID int + UserID int `json:"-"` Key UserSettingKey `json:"key"` Value string `json:"value"` } diff --git a/server/auth.go b/server/auth.go index 15ea38535fe1b..3224e5f8fdf1b 100644 --- a/server/auth.go +++ b/server/auth.go @@ -84,7 +84,7 @@ func (s *Server) registerAuthRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find host user").SetInternal(err) } if signup.Role == api.Host && hostUser != nil { - return echo.NewHTTPError(http.StatusUnauthorized, "Site Host existed, please contact the site host to signin account firstly.").SetInternal(err) + return echo.NewHTTPError(http.StatusUnauthorized, "Site Host existed, please contact the site host to signin account firstly").SetInternal(err) } systemSettingAllowSignUpName := api.SystemSettingAllowSignUpName @@ -103,7 +103,7 @@ func (s *Server) registerAuthRoutes(g *echo.Group) { } } if !allowSignUpSettingValue && hostUser != nil { - return echo.NewHTTPError(http.StatusUnauthorized, "Site Host existed, please contact the site host to signin account firstly.").SetInternal(err) + return echo.NewHTTPError(http.StatusUnauthorized, "Site Host existed, please contact the site host to signin account firstly").SetInternal(err) } userCreate := &api.UserCreate{ @@ -114,7 +114,7 @@ func (s *Server) registerAuthRoutes(g *echo.Group) { OpenID: common.GenUUID(), } if err := userCreate.Validate(); err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Invalid user create format.").SetInternal(err) + return echo.NewHTTPError(http.StatusBadRequest, "Invalid user create format").SetInternal(err) } passwordHash, err := bcrypt.GenerateFromPassword([]byte(signup.Password), bcrypt.DefaultCost) diff --git a/server/memo.go b/server/memo.go index a435d6549ea47..d22f398a431fe 100644 --- a/server/memo.go +++ b/server/memo.go @@ -24,9 +24,7 @@ func (s *Server) registerMemoRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session") } - memoCreate := &api.MemoCreate{ - CreatorID: userID, - } + memoCreate := &api.MemoCreate{} if err := json.NewDecoder(c.Request().Body).Decode(memoCreate); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post memo request").SetInternal(err) } @@ -57,6 +55,7 @@ func (s *Server) registerMemoRoutes(g *echo.Group) { } } + memoCreate.CreatorID = userID memo, err := s.Store.CreateMemo(ctx, memoCreate) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create memo").SetInternal(err) @@ -98,13 +97,15 @@ func (s *Server) registerMemoRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("memoId"))).SetInternal(err) } - memoFind := &api.MemoFind{ - ID: &memoID, - CreatorID: &userID, - } - if _, err := s.Store.FindMemo(ctx, memoFind); err != nil { + memo, err := s.Store.FindMemo(ctx, &api.MemoFind{ + ID: &memoID, + }) + if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find memo").SetInternal(err) } + if memo.CreatorID != userID { + return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized") + } currentTs := time.Now().Unix() memoPatch := &api.MemoPatch{ @@ -115,7 +116,7 @@ func (s *Server) registerMemoRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusBadRequest, "Malformatted patch memo request").SetInternal(err) } - memo, err := s.Store.PatchMemo(ctx, memoPatch) + memo, err = s.Store.PatchMemo(ctx, memoPatch) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch memo").SetInternal(err) } @@ -173,7 +174,7 @@ func (s *Server) registerMemoRoutes(g *echo.Group) { } tag := c.QueryParam("tag") if tag != "" { - contentSearch := "#" + tag + " " + contentSearch := "#" + tag memoFind.ContentSearch = &contentSearch } visibilityListStr := c.QueryParam("visibility") @@ -229,129 +230,6 @@ func (s *Server) registerMemoRoutes(g *echo.Group) { return nil }) - g.GET("/memo/amount", func(c echo.Context) error { - ctx := c.Request().Context() - normalRowStatus := api.Normal - memoFind := &api.MemoFind{ - RowStatus: &normalRowStatus, - } - if userID, err := strconv.Atoi(c.QueryParam("userId")); err == nil { - memoFind.CreatorID = &userID - } - - memoList, err := s.Store.FindMemoList(ctx, memoFind) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find memo list").SetInternal(err) - } - - c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) - if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(len(memoList))); err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode memo amount").SetInternal(err) - } - return nil - }) - - g.GET("/memo/stats", func(c echo.Context) error { - ctx := c.Request().Context() - normalStatus := api.Normal - memoFind := &api.MemoFind{ - RowStatus: &normalStatus, - } - if creatorID, err := strconv.Atoi(c.QueryParam("creatorId")); err == nil { - memoFind.CreatorID = &creatorID - } - if memoFind.CreatorID == nil { - return echo.NewHTTPError(http.StatusBadRequest, "Missing user id to find memo") - } - - currentUserID, ok := c.Get(getUserIDContextKey()).(int) - if !ok { - memoFind.VisibilityList = []api.Visibility{api.Public} - } else { - if *memoFind.CreatorID != currentUserID { - memoFind.VisibilityList = []api.Visibility{api.Public, api.Protected} - } else { - memoFind.VisibilityList = []api.Visibility{api.Public, api.Protected, api.Private} - } - } - - list, err := s.Store.FindMemoList(ctx, memoFind) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch memo list").SetInternal(err) - } - - displayTsList := []int64{} - for _, memo := range list { - displayTsList = append(displayTsList, memo.DisplayTs) - } - - c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) - if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(displayTsList)); err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode memo stats response").SetInternal(err) - } - return nil - }) - - g.GET("/memo/all", func(c echo.Context) error { - ctx := c.Request().Context() - memoFind := &api.MemoFind{} - - _, ok := c.Get(getUserIDContextKey()).(int) - if !ok { - memoFind.VisibilityList = []api.Visibility{api.Public} - } else { - memoFind.VisibilityList = []api.Visibility{api.Public, api.Protected} - } - - pinnedStr := c.QueryParam("pinned") - if pinnedStr != "" { - pinned := pinnedStr == "true" - memoFind.Pinned = &pinned - } - tag := c.QueryParam("tag") - if tag != "" { - contentSearch := "#" + tag + " " - memoFind.ContentSearch = &contentSearch - } - visibilityListStr := c.QueryParam("visibility") - if visibilityListStr != "" { - visibilityList := []api.Visibility{} - for _, visibility := range strings.Split(visibilityListStr, ",") { - visibilityList = append(visibilityList, api.Visibility(visibility)) - } - memoFind.VisibilityList = visibilityList - } - if limit, err := strconv.Atoi(c.QueryParam("limit")); err == nil { - memoFind.Limit = limit - } - if offset, err := strconv.Atoi(c.QueryParam("offset")); err == nil { - memoFind.Offset = offset - } - - // Only fetch normal status memos. - normalStatus := api.Normal - memoFind.RowStatus = &normalStatus - - list, err := s.Store.FindMemoList(ctx, memoFind) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch all memo list").SetInternal(err) - } - - sort.Slice(list, func(i, j int) bool { - return list[i].DisplayTs > list[j].DisplayTs - }) - - if memoFind.Limit != 0 { - list = list[memoFind.Offset:common.Min(len(list), memoFind.Offset+memoFind.Limit)] - } - - c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) - if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(list)); err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode all memo list response").SetInternal(err) - } - return nil - }) - g.GET("/memo/:memoId", func(c echo.Context) error { ctx := c.Request().Context() memoID, err := strconv.Atoi(c.Param("memoId")) @@ -400,13 +278,12 @@ func (s *Server) registerMemoRoutes(g *echo.Group) { if !ok { return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session") } - memoOrganizerUpsert := &api.MemoOrganizerUpsert{ - MemoID: memoID, - UserID: userID, - } + memoOrganizerUpsert := &api.MemoOrganizerUpsert{} if err := json.NewDecoder(c.Request().Body).Decode(memoOrganizerUpsert); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post memo organizer request").SetInternal(err) } + memoOrganizerUpsert.MemoID = memoID + memoOrganizerUpsert.UserID = userID err = s.Store.UpsertMemoOrganizer(ctx, memoOrganizerUpsert) if err != nil { @@ -440,12 +317,12 @@ func (s *Server) registerMemoRoutes(g *echo.Group) { currentTs := time.Now().Unix() memoResourceUpsert := &api.MemoResourceUpsert{ - MemoID: memoID, UpdatedTs: ¤tTs, } if err := json.NewDecoder(c.Request().Body).Decode(memoResourceUpsert); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post memo resource request").SetInternal(err) } + memoResourceUpsert.MemoID = memoID if _, err := s.Store.UpsertMemoResource(ctx, memoResourceUpsert); err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upsert memo resource").SetInternal(err) @@ -488,26 +365,127 @@ func (s *Server) registerMemoRoutes(g *echo.Group) { return nil }) - g.DELETE("/memo/:memoId/resource/:resourceId", func(c echo.Context) error { + g.GET("/memo/amount", func(c echo.Context) error { ctx := c.Request().Context() - memoID, err := strconv.Atoi(c.Param("memoId")) + normalRowStatus := api.Normal + memoFind := &api.MemoFind{ + RowStatus: &normalRowStatus, + } + if userID, err := strconv.Atoi(c.QueryParam("userId")); err == nil { + memoFind.CreatorID = &userID + } + + memoList, err := s.Store.FindMemoList(ctx, memoFind) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Memo ID is not a number: %s", c.Param("memoId"))).SetInternal(err) + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find memo list").SetInternal(err) } - resourceID, err := strconv.Atoi(c.Param("resourceId")) + + c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) + if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(len(memoList))); err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode memo amount").SetInternal(err) + } + return nil + }) + + g.GET("/memo/stats", func(c echo.Context) error { + ctx := c.Request().Context() + normalStatus := api.Normal + memoFind := &api.MemoFind{ + RowStatus: &normalStatus, + } + if creatorID, err := strconv.Atoi(c.QueryParam("creatorId")); err == nil { + memoFind.CreatorID = &creatorID + } + if memoFind.CreatorID == nil { + return echo.NewHTTPError(http.StatusBadRequest, "Missing user id to find memo") + } + + currentUserID, ok := c.Get(getUserIDContextKey()).(int) + if !ok { + memoFind.VisibilityList = []api.Visibility{api.Public} + } else { + if *memoFind.CreatorID != currentUserID { + memoFind.VisibilityList = []api.Visibility{api.Public, api.Protected} + } else { + memoFind.VisibilityList = []api.Visibility{api.Public, api.Protected, api.Private} + } + } + + list, err := s.Store.FindMemoList(ctx, memoFind) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Resource ID is not a number: %s", c.Param("resourceId"))).SetInternal(err) + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch memo list").SetInternal(err) } - memoResourceDelete := &api.MemoResourceDelete{ - MemoID: &memoID, - ResourceID: &resourceID, + displayTsList := []int64{} + for _, memo := range list { + displayTsList = append(displayTsList, memo.DisplayTs) } - if err := s.Store.DeleteMemoResource(ctx, memoResourceDelete); err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch resource list").SetInternal(err) + + c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) + if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(displayTsList)); err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode memo stats response").SetInternal(err) } + return nil + }) - return c.JSON(http.StatusOK, true) + g.GET("/memo/all", func(c echo.Context) error { + ctx := c.Request().Context() + memoFind := &api.MemoFind{} + + _, ok := c.Get(getUserIDContextKey()).(int) + if !ok { + memoFind.VisibilityList = []api.Visibility{api.Public} + } else { + memoFind.VisibilityList = []api.Visibility{api.Public, api.Protected} + } + + pinnedStr := c.QueryParam("pinned") + if pinnedStr != "" { + pinned := pinnedStr == "true" + memoFind.Pinned = &pinned + } + tag := c.QueryParam("tag") + if tag != "" { + contentSearch := "#" + tag + " " + memoFind.ContentSearch = &contentSearch + } + visibilityListStr := c.QueryParam("visibility") + if visibilityListStr != "" { + visibilityList := []api.Visibility{} + for _, visibility := range strings.Split(visibilityListStr, ",") { + visibilityList = append(visibilityList, api.Visibility(visibility)) + } + memoFind.VisibilityList = visibilityList + } + if limit, err := strconv.Atoi(c.QueryParam("limit")); err == nil { + memoFind.Limit = limit + } + if offset, err := strconv.Atoi(c.QueryParam("offset")); err == nil { + memoFind.Offset = offset + } + + // Only fetch normal status memos. + normalStatus := api.Normal + memoFind.RowStatus = &normalStatus + + list, err := s.Store.FindMemoList(ctx, memoFind) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch all memo list").SetInternal(err) + } + + sort.Slice(list, func(i, j int) bool { + return list[i].DisplayTs > list[j].DisplayTs + }) + + if memoFind.Limit != 0 { + list = list[memoFind.Offset:common.Min(len(list), memoFind.Offset+memoFind.Limit)] + } + + c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) + if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(list)); err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode all memo list response").SetInternal(err) + } + return nil }) g.DELETE("/memo/:memoId", func(c echo.Context) error { @@ -516,19 +494,20 @@ func (s *Server) registerMemoRoutes(g *echo.Group) { if !ok { return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session") } - memoID, err := strconv.Atoi(c.Param("memoId")) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("memoId"))).SetInternal(err) } - memoFind := &api.MemoFind{ - ID: &memoID, - CreatorID: &userID, - } - if _, err := s.Store.FindMemo(ctx, memoFind); err != nil { + memo, err := s.Store.FindMemo(ctx, &api.MemoFind{ + ID: &memoID, + }) + if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find memo").SetInternal(err) } + if memo.CreatorID != userID { + return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized") + } memoDelete := &api.MemoDelete{ ID: memoID, @@ -542,4 +521,40 @@ func (s *Server) registerMemoRoutes(g *echo.Group) { return c.JSON(http.StatusOK, true) }) + + g.DELETE("/memo/:memoId/resource/:resourceId", func(c echo.Context) error { + ctx := c.Request().Context() + userID, ok := c.Get(getUserIDContextKey()).(int) + if !ok { + return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session") + } + memoID, err := strconv.Atoi(c.Param("memoId")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Memo ID is not a number: %s", c.Param("memoId"))).SetInternal(err) + } + resourceID, err := strconv.Atoi(c.Param("resourceId")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Resource ID is not a number: %s", c.Param("resourceId"))).SetInternal(err) + } + + memo, err := s.Store.FindMemo(ctx, &api.MemoFind{ + ID: &memoID, + }) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find memo").SetInternal(err) + } + if memo.CreatorID != userID { + return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized") + } + + memoResourceDelete := &api.MemoResourceDelete{ + MemoID: &memoID, + ResourceID: &resourceID, + } + if err := s.Store.DeleteMemoResource(ctx, memoResourceDelete); err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch resource list").SetInternal(err) + } + + return c.JSON(http.StatusOK, true) + }) } diff --git a/server/resource.go b/server/resource.go index 9958e5fce6b45..09e3124520e03 100644 --- a/server/resource.go +++ b/server/resource.go @@ -56,13 +56,12 @@ func (s *Server) registerResourceRoutes(g *echo.Group) { } resourceCreate := &api.ResourceCreate{ + CreatorID: userID, Filename: filename, Type: filetype, Size: size, Blob: fileBytes, - CreatorID: userID, } - resource, err := s.Store.CreateResource(ctx, resourceCreate) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create resource").SetInternal(err) @@ -158,6 +157,7 @@ func (s *Server) registerResourceRoutes(g *echo.Group) { c.Response().Writer.WriteHeader(http.StatusOK) c.Response().Writer.Header().Set("Content-Type", resource.Type) + c.Response().Writer.Header().Set(echo.HeaderContentSecurityPolicy, "default-src 'self'") if _, err := c.Response().Writer.Write(resource.Blob); err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to write resource blob").SetInternal(err) } @@ -177,23 +177,26 @@ func (s *Server) registerResourceRoutes(g *echo.Group) { } resourceFind := &api.ResourceFind{ - ID: &resourceID, - CreatorID: &userID, + ID: &resourceID, } - if _, err := s.Store.FindResource(ctx, resourceFind); err != nil { + resource, err := s.Store.FindResource(ctx, resourceFind) + if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find resource").SetInternal(err) } + if resource.CreatorID != userID { + return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized") + } currentTs := time.Now().Unix() resourcePatch := &api.ResourcePatch{ - ID: resourceID, UpdatedTs: ¤tTs, } if err := json.NewDecoder(c.Request().Body).Decode(resourcePatch); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Malformatted patch resource request").SetInternal(err) } - resource, err := s.Store.PatchResource(ctx, resourcePatch) + resource.ID = resourceID + resource, err = s.Store.PatchResource(ctx, resourcePatch) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch resource").SetInternal(err) } @@ -224,8 +227,8 @@ func (s *Server) registerResourceRoutes(g *echo.Group) { if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find resource").SetInternal(err) } - if resource == nil { - return echo.NewHTTPError(http.StatusNotFound, "Not find resource").SetInternal(err) + if resource.CreatorID != userID { + return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized") } resourceDelete := &api.ResourceDelete{ diff --git a/server/shortcut.go b/server/shortcut.go index 11f4949b8f21c..23b875a144912 100644 --- a/server/shortcut.go +++ b/server/shortcut.go @@ -21,13 +21,12 @@ func (s *Server) registerShortcutRoutes(g *echo.Group) { if !ok { return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session") } - shortcutCreate := &api.ShortcutCreate{ - CreatorID: userID, - } + shortcutCreate := &api.ShortcutCreate{} if err := json.NewDecoder(c.Request().Body).Decode(shortcutCreate); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post shortcut request").SetInternal(err) } + shortcutCreate.CreatorID = userID shortcut, err := s.Store.CreateShortcut(ctx, shortcutCreate) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create shortcut").SetInternal(err) @@ -45,21 +44,36 @@ func (s *Server) registerShortcutRoutes(g *echo.Group) { g.PATCH("/shortcut/:shortcutId", func(c echo.Context) error { ctx := c.Request().Context() + userID, ok := c.Get(getUserIDContextKey()).(int) + if !ok { + return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session") + } shortcutID, err := strconv.Atoi(c.Param("shortcutId")) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("shortcutId"))).SetInternal(err) } + shortcutFind := &api.ShortcutFind{ + ID: &shortcutID, + } + shortcut, err := s.Store.FindShortcut(ctx, shortcutFind) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find shortcut").SetInternal(err) + } + if shortcut.CreatorID != userID { + return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized") + } + currentTs := time.Now().Unix() shortcutPatch := &api.ShortcutPatch{ - ID: shortcutID, UpdatedTs: ¤tTs, } if err := json.NewDecoder(c.Request().Body).Decode(shortcutPatch); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Malformatted patch shortcut request").SetInternal(err) } - shortcut, err := s.Store.PatchShortcut(ctx, shortcutPatch) + shortcutPatch.ID = shortcutID + shortcut, err = s.Store.PatchShortcut(ctx, shortcutPatch) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch shortcut").SetInternal(err) } @@ -73,17 +87,12 @@ func (s *Server) registerShortcutRoutes(g *echo.Group) { g.GET("/shortcut", func(c echo.Context) error { ctx := c.Request().Context() - shortcutFind := &api.ShortcutFind{} - - if userID, err := strconv.Atoi(c.QueryParam("creatorId")); err == nil { - shortcutFind.CreatorID = &userID - } else { - userID, ok := c.Get(getUserIDContextKey()).(int) - if !ok { - return echo.NewHTTPError(http.StatusBadRequest, "Missing user id to find shortcut") - } - - shortcutFind.CreatorID = &userID + userID, ok := c.Get(getUserIDContextKey()).(int) + if !ok { + return echo.NewHTTPError(http.StatusBadRequest, "Missing user id to find shortcut") + } + shortcutFind := &api.ShortcutFind{ + CreatorID: &userID, } list, err := s.Store.FindShortcutList(ctx, shortcutFind) @@ -122,11 +131,26 @@ func (s *Server) registerShortcutRoutes(g *echo.Group) { g.DELETE("/shortcut/:shortcutId", func(c echo.Context) error { ctx := c.Request().Context() + userID, ok := c.Get(getUserIDContextKey()).(int) + if !ok { + return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session") + } shortcutID, err := strconv.Atoi(c.Param("shortcutId")) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("shortcutId"))).SetInternal(err) } + shortcutFind := &api.ShortcutFind{ + ID: &shortcutID, + } + shortcut, err := s.Store.FindShortcut(ctx, shortcutFind) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find shortcut").SetInternal(err) + } + if shortcut.CreatorID != userID { + return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized") + } + shortcutDelete := &api.ShortcutDelete{ ID: &shortcutID, } diff --git a/server/system.go b/server/system.go index 90d522abb28d0..74a37685c9bfe 100644 --- a/server/system.go +++ b/server/system.go @@ -76,13 +76,24 @@ func (s *Server) registerSystemRoutes(g *echo.Group) { systemStatus.AdditionalScript = value.(string) } else if systemSetting.Name == api.SystemSettingCustomizedProfileName { valueMap := value.(map[string]interface{}) - systemStatus.CustomizedProfile = api.CustomizedProfile{ - Name: valueMap["name"].(string), - LogoURL: valueMap["logoUrl"].(string), - Description: valueMap["description"].(string), - Locale: valueMap["locale"].(string), - Appearance: valueMap["appearance"].(string), - ExternalURL: valueMap["externalUrl"].(string), + systemStatus.CustomizedProfile = api.CustomizedProfile{} + if v := valueMap["name"]; v != nil { + systemStatus.CustomizedProfile.Name = v.(string) + } + if v := valueMap["logoUrl"]; v != nil { + systemStatus.CustomizedProfile.LogoURL = v.(string) + } + if v := valueMap["description"]; v != nil { + systemStatus.CustomizedProfile.Description = v.(string) + } + if v := valueMap["locale"]; v != nil { + systemStatus.CustomizedProfile.Locale = v.(string) + } + if v := valueMap["appearance"]; v != nil { + systemStatus.CustomizedProfile.Appearance = v.(string) + } + if v := valueMap["externalUrl"]; v != nil { + systemStatus.CustomizedProfile.ExternalURL = v.(string) } } } @@ -125,9 +136,7 @@ func (s *Server) registerSystemRoutes(g *echo.Group) { if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user").SetInternal(err) } - if user == nil { - return echo.NewHTTPError(http.StatusNotFound, "Current signin user not found") - } else if user.Role != api.Host { + if user == nil || user.Role != api.Host { return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized") } diff --git a/server/tag.go b/server/tag.go index 5b139db01ff57..fc47e7ad1c585 100644 --- a/server/tag.go +++ b/server/tag.go @@ -23,9 +23,7 @@ func (s *Server) registerTagRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session") } - tagUpsert := &api.TagUpsert{ - CreatorID: userID, - } + tagUpsert := &api.TagUpsert{} if err := json.NewDecoder(c.Request().Body).Decode(tagUpsert); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post tag request").SetInternal(err) } @@ -33,6 +31,7 @@ func (s *Server) registerTagRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusBadRequest, "Tag name shouldn't be empty") } + tagUpsert.CreatorID = userID tag, err := s.Store.UpsertTag(ctx, tagUpsert) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upsert tag").SetInternal(err) @@ -82,31 +81,18 @@ func (s *Server) registerTagRoutes(g *echo.Group) { g.GET("/tag/suggestion", func(c echo.Context) error { ctx := c.Request().Context() + userID, ok := c.Get(getUserIDContextKey()).(int) + if !ok { + return echo.NewHTTPError(http.StatusBadRequest, "Missing user session") + } contentSearch := "#" normalRowStatus := api.Normal memoFind := api.MemoFind{ + CreatorID: &userID, ContentSearch: &contentSearch, RowStatus: &normalRowStatus, } - if userID, err := strconv.Atoi(c.QueryParam("creatorId")); err == nil { - memoFind.CreatorID = &userID - } - - currentUserID, ok := c.Get(getUserIDContextKey()).(int) - if !ok { - if memoFind.CreatorID == nil { - return echo.NewHTTPError(http.StatusBadRequest, "Missing user id to find memo") - } - memoFind.VisibilityList = []api.Visibility{api.Public} - } else { - if memoFind.CreatorID == nil { - memoFind.CreatorID = ¤tUserID - } else { - memoFind.VisibilityList = []api.Visibility{api.Public, api.Protected} - } - } - memoList, err := s.Store.FindMemoList(ctx, &memoFind) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find memo list").SetInternal(err) diff --git a/server/user.go b/server/user.go index 76d765871093b..8b608b8bbba23 100644 --- a/server/user.go +++ b/server/user.go @@ -29,18 +29,20 @@ func (s *Server) registerUserRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user by id").SetInternal(err) } if currentUser.Role != api.Host { - return echo.NewHTTPError(http.StatusUnauthorized, "Only Host user can create member.") + return echo.NewHTTPError(http.StatusUnauthorized, "Only Host user can create member") } - userCreate := &api.UserCreate{ - OpenID: common.GenUUID(), - } + userCreate := &api.UserCreate{} if err := json.NewDecoder(c.Request().Body).Decode(userCreate); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post user request").SetInternal(err) } + if userCreate.Role == api.Host { + return echo.NewHTTPError(http.StatusForbidden, "Could not create host user") + } + userCreate.OpenID = common.GenUUID() if err := userCreate.Validate(); err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Invalid user create format.").SetInternal(err) + return echo.NewHTTPError(http.StatusBadRequest, "Invalid user create format").SetInternal(err) } passwordHash, err := bcrypt.GenerateFromPassword([]byte(userCreate.Password), bcrypt.DefaultCost) @@ -74,6 +76,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) { for _, user := range userList { // data desensitize user.OpenID = "" + user.Email = "" } c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) @@ -159,6 +162,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) { if user != nil { // data desensitize user.OpenID = "" + user.Email = "" } c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) @@ -192,14 +196,14 @@ func (s *Server) registerUserRoutes(g *echo.Group) { currentTs := time.Now().Unix() userPatch := &api.UserPatch{ - ID: userID, UpdatedTs: ¤tTs, } if err := json.NewDecoder(c.Request().Body).Decode(userPatch); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Malformatted patch user request").SetInternal(err) } + userPatch.ID = userID if err := userPatch.Validate(); err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Invalid user patch format.").SetInternal(err) + return echo.NewHTTPError(http.StatusBadRequest, "Invalid user patch format").SetInternal(err) } if userPatch.Password != nil && *userPatch.Password != "" {