fix: access control (#870)

This commit is contained in:
boojack 2022-12-28 20:22:52 +08:00 committed by GitHub
parent f888c62840
commit 3556ae4e65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 296 additions and 255 deletions

View File

@ -46,7 +46,7 @@ type Memo struct {
type MemoCreate struct { type MemoCreate struct {
// Standard fields // Standard fields
CreatorID int CreatorID int `json:"-"`
// Domain specific fields // Domain specific fields
Visibility Visibility `json:"visibility"` Visibility Visibility `json:"visibility"`
@ -73,11 +73,11 @@ type MemoPatch struct {
} }
type MemoFind struct { type MemoFind struct {
ID *int `json:"id"` ID *int
// Standard fields // Standard fields
RowStatus *RowStatus `json:"rowStatus"` RowStatus *RowStatus
CreatorID *int `json:"creatorId"` CreatorID *int
// Domain specific fields // Domain specific fields
Pinned *bool Pinned *bool

View File

@ -9,17 +9,17 @@ type MemoOrganizer struct {
Pinned bool Pinned bool
} }
type MemoOrganizerUpsert struct {
MemoID int `json:"-"`
UserID int `json:"-"`
Pinned bool `json:"pinned"`
}
type MemoOrganizerFind struct { type MemoOrganizerFind struct {
MemoID int MemoID int
UserID int UserID int
} }
type MemoOrganizerUpsert struct {
MemoID int
UserID int
Pinned bool `json:"pinned"`
}
type MemoOrganizerDelete struct { type MemoOrganizerDelete struct {
MemoID *int MemoID *int
UserID *int UserID *int

View File

@ -8,7 +8,7 @@ type MemoResource struct {
} }
type MemoResourceUpsert struct { type MemoResourceUpsert struct {
MemoID int MemoID int `json:"-"`
ResourceID int ResourceID int
UpdatedTs *int64 UpdatedTs *int64
} }

View File

@ -20,7 +20,7 @@ type Resource struct {
type ResourceCreate struct { type ResourceCreate struct {
// Standard fields // Standard fields
CreatorID int CreatorID int `json:"-"`
// Domain specific fields // Domain specific fields
Filename string `json:"filename"` Filename string `json:"filename"`

View File

@ -16,7 +16,7 @@ type Shortcut struct {
type ShortcutCreate struct { type ShortcutCreate struct {
// Standard fields // Standard fields
CreatorID int CreatorID int `json:"-"`
// Domain specific fields // Domain specific fields
Title string `json:"title"` Title string `json:"title"`

View File

@ -7,7 +7,7 @@ type Tag struct {
type TagUpsert struct { type TagUpsert struct {
Name string Name string
CreatorID int CreatorID int `json:"-"`
} }
type TagFind struct { type TagFind struct {

View File

@ -50,7 +50,7 @@ type UserSetting struct {
} }
type UserSettingUpsert struct { type UserSettingUpsert struct {
UserID int UserID int `json:"-"`
Key UserSettingKey `json:"key"` Key UserSettingKey `json:"key"`
Value string `json:"value"` Value string `json:"value"`
} }

View File

@ -84,7 +84,7 @@ func (s *Server) registerAuthRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find host user").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find host user").SetInternal(err)
} }
if signup.Role == api.Host && hostUser != nil { 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 systemSettingAllowSignUpName := api.SystemSettingAllowSignUpName
@ -103,7 +103,7 @@ func (s *Server) registerAuthRoutes(g *echo.Group) {
} }
} }
if !allowSignUpSettingValue && hostUser != nil { 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{ userCreate := &api.UserCreate{
@ -114,7 +114,7 @@ func (s *Server) registerAuthRoutes(g *echo.Group) {
OpenID: common.GenUUID(), OpenID: common.GenUUID(),
} }
if err := userCreate.Validate(); err != nil { 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) passwordHash, err := bcrypt.GenerateFromPassword([]byte(signup.Password), bcrypt.DefaultCost)

View File

@ -24,9 +24,7 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session") return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session")
} }
memoCreate := &api.MemoCreate{ memoCreate := &api.MemoCreate{}
CreatorID: userID,
}
if err := json.NewDecoder(c.Request().Body).Decode(memoCreate); err != nil { if err := json.NewDecoder(c.Request().Body).Decode(memoCreate); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post memo request").SetInternal(err) 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) memo, err := s.Store.CreateMemo(ctx, memoCreate)
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create memo").SetInternal(err) 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) return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("memoId"))).SetInternal(err)
} }
memoFind := &api.MemoFind{ memo, err := s.Store.FindMemo(ctx, &api.MemoFind{
ID: &memoID, ID: &memoID,
CreatorID: &userID, })
} if err != nil {
if _, err := s.Store.FindMemo(ctx, memoFind); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find memo").SetInternal(err) 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() currentTs := time.Now().Unix()
memoPatch := &api.MemoPatch{ 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) 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 { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch memo").SetInternal(err) 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") tag := c.QueryParam("tag")
if tag != "" { if tag != "" {
contentSearch := "#" + tag + " " contentSearch := "#" + tag
memoFind.ContentSearch = &contentSearch memoFind.ContentSearch = &contentSearch
} }
visibilityListStr := c.QueryParam("visibility") visibilityListStr := c.QueryParam("visibility")
@ -229,6 +230,141 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
return nil return nil
}) })
g.GET("/memo/:memoId", func(c echo.Context) error {
ctx := c.Request().Context()
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,
}
memo, err := s.Store.FindMemo(ctx, memoFind)
if err != nil {
if common.ErrorCode(err) == common.NotFound {
return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("Memo ID not found: %d", memoID)).SetInternal(err)
}
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to find memo by ID: %v", memoID)).SetInternal(err)
}
userID, ok := c.Get(getUserIDContextKey()).(int)
if memo.Visibility == api.Private {
if !ok || memo.CreatorID != userID {
return echo.NewHTTPError(http.StatusForbidden, "this memo is private only")
}
} else if memo.Visibility == api.Protected {
if !ok {
return echo.NewHTTPError(http.StatusForbidden, "this memo is protected, missing user in session")
}
}
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(memo)); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode memo response").SetInternal(err)
}
return nil
})
g.POST("/memo/:memoId/organizer", func(c echo.Context) error {
ctx := c.Request().Context()
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)
}
userID, ok := c.Get(getUserIDContextKey()).(int)
if !ok {
return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session")
}
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 {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upsert memo organizer").SetInternal(err)
}
memo, err := s.Store.FindMemo(ctx, &api.MemoFind{
ID: &memoID,
})
if err != nil {
if common.ErrorCode(err) == common.NotFound {
return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("Memo ID not found: %d", memoID)).SetInternal(err)
}
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to find memo by ID: %v", memoID)).SetInternal(err)
}
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(memo)); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode memo response").SetInternal(err)
}
return nil
})
g.POST("/memo/:memoId/resource", func(c echo.Context) error {
ctx := c.Request().Context()
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)
}
currentTs := time.Now().Unix()
memoResourceUpsert := &api.MemoResourceUpsert{
UpdatedTs: &currentTs,
}
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)
}
resourceFind := &api.ResourceFind{
ID: &memoResourceUpsert.ResourceID,
}
resource, err := s.Store.FindResource(ctx, resourceFind)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch resource").SetInternal(err)
}
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(resource)); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode resource response").SetInternal(err)
}
return nil
})
g.GET("/memo/:memoId/resource", func(c echo.Context) error {
ctx := c.Request().Context()
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)
}
resourceFind := &api.ResourceFind{
MemoID: &memoID,
}
resourceList, err := s.Store.FindResourceList(ctx, resourceFind)
if 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(resourceList)); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode resource list response").SetInternal(err)
}
return nil
})
g.GET("/memo/amount", func(c echo.Context) error { g.GET("/memo/amount", func(c echo.Context) error {
ctx := c.Request().Context() ctx := c.Request().Context()
normalRowStatus := api.Normal normalRowStatus := api.Normal
@ -352,183 +488,26 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
return nil return nil
}) })
g.GET("/memo/:memoId", func(c echo.Context) error {
ctx := c.Request().Context()
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,
}
memo, err := s.Store.FindMemo(ctx, memoFind)
if err != nil {
if common.ErrorCode(err) == common.NotFound {
return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("Memo ID not found: %d", memoID)).SetInternal(err)
}
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to find memo by ID: %v", memoID)).SetInternal(err)
}
userID, ok := c.Get(getUserIDContextKey()).(int)
if memo.Visibility == api.Private {
if !ok || memo.CreatorID != userID {
return echo.NewHTTPError(http.StatusForbidden, "this memo is private only")
}
} else if memo.Visibility == api.Protected {
if !ok {
return echo.NewHTTPError(http.StatusForbidden, "this memo is protected, missing user in session")
}
}
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(memo)); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode memo response").SetInternal(err)
}
return nil
})
g.POST("/memo/:memoId/organizer", func(c echo.Context) error {
ctx := c.Request().Context()
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)
}
userID, ok := c.Get(getUserIDContextKey()).(int)
if !ok {
return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session")
}
memoOrganizerUpsert := &api.MemoOrganizerUpsert{
MemoID: memoID,
UserID: userID,
}
if err := json.NewDecoder(c.Request().Body).Decode(memoOrganizerUpsert); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post memo organizer request").SetInternal(err)
}
err = s.Store.UpsertMemoOrganizer(ctx, memoOrganizerUpsert)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upsert memo organizer").SetInternal(err)
}
memo, err := s.Store.FindMemo(ctx, &api.MemoFind{
ID: &memoID,
})
if err != nil {
if common.ErrorCode(err) == common.NotFound {
return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("Memo ID not found: %d", memoID)).SetInternal(err)
}
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to find memo by ID: %v", memoID)).SetInternal(err)
}
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(memo)); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode memo response").SetInternal(err)
}
return nil
})
g.POST("/memo/:memoId/resource", func(c echo.Context) error {
ctx := c.Request().Context()
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)
}
currentTs := time.Now().Unix()
memoResourceUpsert := &api.MemoResourceUpsert{
MemoID: memoID,
UpdatedTs: &currentTs,
}
if err := json.NewDecoder(c.Request().Body).Decode(memoResourceUpsert); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post memo resource request").SetInternal(err)
}
if _, err := s.Store.UpsertMemoResource(ctx, memoResourceUpsert); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upsert memo resource").SetInternal(err)
}
resourceFind := &api.ResourceFind{
ID: &memoResourceUpsert.ResourceID,
}
resource, err := s.Store.FindResource(ctx, resourceFind)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch resource").SetInternal(err)
}
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(resource)); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode resource response").SetInternal(err)
}
return nil
})
g.GET("/memo/:memoId/resource", func(c echo.Context) error {
ctx := c.Request().Context()
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)
}
resourceFind := &api.ResourceFind{
MemoID: &memoID,
}
resourceList, err := s.Store.FindResourceList(ctx, resourceFind)
if 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(resourceList)); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode resource list response").SetInternal(err)
}
return nil
})
g.DELETE("/memo/:memoId/resource/:resourceId", func(c echo.Context) error {
ctx := c.Request().Context()
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)
}
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)
})
g.DELETE("/memo/:memoId", func(c echo.Context) error { g.DELETE("/memo/:memoId", func(c echo.Context) error {
ctx := c.Request().Context() ctx := c.Request().Context()
userID, ok := c.Get(getUserIDContextKey()).(int) userID, ok := c.Get(getUserIDContextKey()).(int)
if !ok { if !ok {
return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session") return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session")
} }
memoID, err := strconv.Atoi(c.Param("memoId")) memoID, err := strconv.Atoi(c.Param("memoId"))
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("memoId"))).SetInternal(err) return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("memoId"))).SetInternal(err)
} }
memoFind := &api.MemoFind{ memo, err := s.Store.FindMemo(ctx, &api.MemoFind{
ID: &memoID, ID: &memoID,
CreatorID: &userID, })
} if err != nil {
if _, err := s.Store.FindMemo(ctx, memoFind); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find memo").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find memo").SetInternal(err)
} }
if memo.CreatorID != userID {
return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized")
}
memoDelete := &api.MemoDelete{ memoDelete := &api.MemoDelete{
ID: memoID, ID: memoID,
@ -542,4 +521,40 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
return c.JSON(http.StatusOK, true) 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)
})
} }

View File

@ -56,13 +56,12 @@ func (s *Server) registerResourceRoutes(g *echo.Group) {
} }
resourceCreate := &api.ResourceCreate{ resourceCreate := &api.ResourceCreate{
CreatorID: userID,
Filename: filename, Filename: filename,
Type: filetype, Type: filetype,
Size: size, Size: size,
Blob: fileBytes, Blob: fileBytes,
CreatorID: userID,
} }
resource, err := s.Store.CreateResource(ctx, resourceCreate) resource, err := s.Store.CreateResource(ctx, resourceCreate)
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create resource").SetInternal(err) 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.WriteHeader(http.StatusOK)
c.Response().Writer.Header().Set("Content-Type", resource.Type) 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 { if _, err := c.Response().Writer.Write(resource.Blob); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to write resource blob").SetInternal(err) 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{ resourceFind := &api.ResourceFind{
ID: &resourceID, ID: &resourceID,
CreatorID: &userID,
} }
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) 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() currentTs := time.Now().Unix()
resourcePatch := &api.ResourcePatch{ resourcePatch := &api.ResourcePatch{
ID: resourceID,
UpdatedTs: &currentTs, UpdatedTs: &currentTs,
} }
if err := json.NewDecoder(c.Request().Body).Decode(resourcePatch); err != nil { if err := json.NewDecoder(c.Request().Body).Decode(resourcePatch); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted patch resource request").SetInternal(err) 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 { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch resource").SetInternal(err) 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 { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find resource").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find resource").SetInternal(err)
} }
if resource == nil { if resource.CreatorID != userID {
return echo.NewHTTPError(http.StatusNotFound, "Not find resource").SetInternal(err) return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized")
} }
resourceDelete := &api.ResourceDelete{ resourceDelete := &api.ResourceDelete{

View File

@ -21,13 +21,12 @@ func (s *Server) registerShortcutRoutes(g *echo.Group) {
if !ok { if !ok {
return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session") return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session")
} }
shortcutCreate := &api.ShortcutCreate{ shortcutCreate := &api.ShortcutCreate{}
CreatorID: userID,
}
if err := json.NewDecoder(c.Request().Body).Decode(shortcutCreate); err != nil { if err := json.NewDecoder(c.Request().Body).Decode(shortcutCreate); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post shortcut request").SetInternal(err) return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post shortcut request").SetInternal(err)
} }
shortcutCreate.CreatorID = userID
shortcut, err := s.Store.CreateShortcut(ctx, shortcutCreate) shortcut, err := s.Store.CreateShortcut(ctx, shortcutCreate)
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create shortcut").SetInternal(err) 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 { g.PATCH("/shortcut/:shortcutId", func(c echo.Context) error {
ctx := c.Request().Context() 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")) shortcutID, err := strconv.Atoi(c.Param("shortcutId"))
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("shortcutId"))).SetInternal(err) 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() currentTs := time.Now().Unix()
shortcutPatch := &api.ShortcutPatch{ shortcutPatch := &api.ShortcutPatch{
ID: shortcutID,
UpdatedTs: &currentTs, UpdatedTs: &currentTs,
} }
if err := json.NewDecoder(c.Request().Body).Decode(shortcutPatch); err != nil { if err := json.NewDecoder(c.Request().Body).Decode(shortcutPatch); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted patch shortcut request").SetInternal(err) 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 { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch shortcut").SetInternal(err) 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 { g.GET("/shortcut", func(c echo.Context) error {
ctx := c.Request().Context() ctx := c.Request().Context()
shortcutFind := &api.ShortcutFind{} userID, ok := c.Get(getUserIDContextKey()).(int)
if !ok {
if userID, err := strconv.Atoi(c.QueryParam("creatorId")); err == nil { return echo.NewHTTPError(http.StatusBadRequest, "Missing user id to find shortcut")
shortcutFind.CreatorID = &userID }
} else { shortcutFind := &api.ShortcutFind{
userID, ok := c.Get(getUserIDContextKey()).(int) CreatorID: &userID,
if !ok {
return echo.NewHTTPError(http.StatusBadRequest, "Missing user id to find shortcut")
}
shortcutFind.CreatorID = &userID
} }
list, err := s.Store.FindShortcutList(ctx, shortcutFind) 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 { g.DELETE("/shortcut/:shortcutId", func(c echo.Context) error {
ctx := c.Request().Context() 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")) shortcutID, err := strconv.Atoi(c.Param("shortcutId"))
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("shortcutId"))).SetInternal(err) 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{ shortcutDelete := &api.ShortcutDelete{
ID: &shortcutID, ID: &shortcutID,
} }

View File

@ -76,13 +76,24 @@ func (s *Server) registerSystemRoutes(g *echo.Group) {
systemStatus.AdditionalScript = value.(string) systemStatus.AdditionalScript = value.(string)
} else if systemSetting.Name == api.SystemSettingCustomizedProfileName { } else if systemSetting.Name == api.SystemSettingCustomizedProfileName {
valueMap := value.(map[string]interface{}) valueMap := value.(map[string]interface{})
systemStatus.CustomizedProfile = api.CustomizedProfile{ systemStatus.CustomizedProfile = api.CustomizedProfile{}
Name: valueMap["name"].(string), if v := valueMap["name"]; v != nil {
LogoURL: valueMap["logoUrl"].(string), systemStatus.CustomizedProfile.Name = v.(string)
Description: valueMap["description"].(string), }
Locale: valueMap["locale"].(string), if v := valueMap["logoUrl"]; v != nil {
Appearance: valueMap["appearance"].(string), systemStatus.CustomizedProfile.LogoURL = v.(string)
ExternalURL: valueMap["externalUrl"].(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 { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user").SetInternal(err)
} }
if user == nil { if user == nil || user.Role != api.Host {
return echo.NewHTTPError(http.StatusNotFound, "Current signin user not found")
} else if user.Role != api.Host {
return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized") return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized")
} }

View File

@ -23,9 +23,7 @@ func (s *Server) registerTagRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session") return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session")
} }
tagUpsert := &api.TagUpsert{ tagUpsert := &api.TagUpsert{}
CreatorID: userID,
}
if err := json.NewDecoder(c.Request().Body).Decode(tagUpsert); err != nil { if err := json.NewDecoder(c.Request().Body).Decode(tagUpsert); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post tag request").SetInternal(err) 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") return echo.NewHTTPError(http.StatusBadRequest, "Tag name shouldn't be empty")
} }
tagUpsert.CreatorID = userID
tag, err := s.Store.UpsertTag(ctx, tagUpsert) tag, err := s.Store.UpsertTag(ctx, tagUpsert)
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upsert tag").SetInternal(err) 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 { g.GET("/tag/suggestion", func(c echo.Context) error {
ctx := c.Request().Context() ctx := c.Request().Context()
userID, ok := c.Get(getUserIDContextKey()).(int)
if !ok {
return echo.NewHTTPError(http.StatusBadRequest, "Missing user session")
}
contentSearch := "#" contentSearch := "#"
normalRowStatus := api.Normal normalRowStatus := api.Normal
memoFind := api.MemoFind{ memoFind := api.MemoFind{
CreatorID: &userID,
ContentSearch: &contentSearch, ContentSearch: &contentSearch,
RowStatus: &normalRowStatus, 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 = &currentUserID
} else {
memoFind.VisibilityList = []api.Visibility{api.Public, api.Protected}
}
}
memoList, err := s.Store.FindMemoList(ctx, &memoFind) memoList, err := s.Store.FindMemoList(ctx, &memoFind)
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find memo list").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find memo list").SetInternal(err)

View File

@ -29,18 +29,20 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user by id").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user by id").SetInternal(err)
} }
if currentUser.Role != api.Host { 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{ userCreate := &api.UserCreate{}
OpenID: common.GenUUID(),
}
if err := json.NewDecoder(c.Request().Body).Decode(userCreate); err != nil { if err := json.NewDecoder(c.Request().Body).Decode(userCreate); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post user request").SetInternal(err) 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 { 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) passwordHash, err := bcrypt.GenerateFromPassword([]byte(userCreate.Password), bcrypt.DefaultCost)
@ -74,6 +76,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
for _, user := range userList { for _, user := range userList {
// data desensitize // data desensitize
user.OpenID = "" user.OpenID = ""
user.Email = ""
} }
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
@ -159,6 +162,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
if user != nil { if user != nil {
// data desensitize // data desensitize
user.OpenID = "" user.OpenID = ""
user.Email = ""
} }
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
@ -192,14 +196,14 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
currentTs := time.Now().Unix() currentTs := time.Now().Unix()
userPatch := &api.UserPatch{ userPatch := &api.UserPatch{
ID: userID,
UpdatedTs: &currentTs, UpdatedTs: &currentTs,
} }
if err := json.NewDecoder(c.Request().Body).Decode(userPatch); err != nil { if err := json.NewDecoder(c.Request().Body).Decode(userPatch); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted patch user request").SetInternal(err) return echo.NewHTTPError(http.StatusBadRequest, "Malformatted patch user request").SetInternal(err)
} }
userPatch.ID = userID
if err := userPatch.Validate(); err != nil { 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 != "" { if userPatch.Password != nil && *userPatch.Password != "" {