~evanj/cms

c8b1933e8dc5f2b8d53e75c2f9f98d54a62ea430 — Evan M Jones 5 months ago 03d18c9
Fix(Security): Fixing issue with caching where users could access spaces
they were not suppose to.
M internal/c/space/space.go => internal/c/space/space.go +2 -2
@@ 32,7 32,7 @@ type dber interface {
	SpaceGet(user user.User, spaceID string) (space.Space, error)
	SpaceUpdate(user user.User, space space.Space, name, desc string) (space.Space, error)
	SpaceCopy(user user.User, space space.Space, name, desc string) (space.Space, error)
	SpaceDelete(space space.Space) error
	SpaceDelete(user user.User, space space.Space) error
	ContentTypesPerSpace(space space.Space, page int) ([]contenttype.ContentType, error)
	HookPerSpace(space space.Space, page int) ([]hook.Hook, error)
}


@@ 181,7 181,7 @@ func (s *Space) delete(w http.ResponseWriter, r *http.Request) {
		return
	}

	if err := s.db.SpaceDelete(space); err != nil {
	if err := s.db.SpaceDelete(user, space); err != nil {
		s.log.Println(err)
		s.Error(w, r, http.StatusInternalServerError, "failed to delete space")
		return

M internal/s/cache/content.go => internal/s/cache/content.go +6 -6
@@ 27,7 27,7 @@ func (c *Cache) ContentNew(space space.Space, ct contenttype.ContentType, params

	return c.content(
		true,
		fmt.Sprintf("%s::%s::%s::%s", c.baseKey, space.ID(), ct.ID(), thing.ID()),
		fmt.Sprintf("content::%s::%s::%s::%s", c.baseKey, space.ID(), ct.ID(), thing.ID()),
		func() (content.Content, error) { return thing, err },
	)
}


@@ 35,7 35,7 @@ func (c *Cache) ContentNew(space space.Space, ct contenttype.ContentType, params
func (c *Cache) ContentGet(space space.Space, ct contenttype.ContentType, contentID string) (content.Content, error) {
	return c.content(
		false,
		fmt.Sprintf("%s::%s::%s::%s", c.baseKey, space.ID(), ct.ID(), contentID),
		fmt.Sprintf("content::%s::%s::%s::%s", c.baseKey, space.ID(), ct.ID(), contentID),
		func() (content.Content, error) { return c.db.ContentGet(space, ct, contentID) },
	)
}


@@ 43,7 43,7 @@ func (c *Cache) ContentGet(space space.Space, ct contenttype.ContentType, conten
func (c *Cache) ContentUpdate(space space.Space, ct contenttype.ContentType, item content.Content, newParams []db.ContentNewParam, updateParams []db.ContentUpdateParam) (content.Content, error) {
	content, err := c.content(
		true,
		fmt.Sprintf("%s::%s::%s::%s", c.baseKey, space.ID(), ct.ID(), item.ID()),
		fmt.Sprintf("content::%s::%s::%s::%s", c.baseKey, space.ID(), ct.ID(), item.ID()),
		func() (content.Content, error) { return c.db.ContentUpdate(space, ct, item, newParams, updateParams) },
	)
	if err != nil {


@@ 57,7 57,7 @@ func (c *Cache) ContentUpdate(space space.Space, ct contenttype.ContentType, ite

	// Remove content from cache that referenced this.
	for _, ref := range list {
		err := c.mc.Delete(fmt.Sprintf("%s::%s::%s::%s", c.baseKey, space.ID(), ref.ContentTypeID, ref.ContentID))
		err := c.mc.Delete(fmt.Sprintf("content::%s::%s::%s::%s", c.baseKey, space.ID(), ref.ContentTypeID, ref.ContentID))
		if err != nil && !errors.Is(err, memcache.ErrCacheMiss) { // Don't care about cache miss.
			return nil, err
		}


@@ 67,7 67,7 @@ func (c *Cache) ContentUpdate(space space.Space, ct contenttype.ContentType, ite
}

func (c *Cache) ContentDelete(space space.Space, ct contenttype.ContentType, item content.Content) error {
	key := fmt.Sprintf("%s::%s::%s::%s", c.baseKey, space.ID(), ct.ID(), item.ID())
	key := fmt.Sprintf("content::%s::%s::%s::%s", c.baseKey, space.ID(), ct.ID(), item.ID())

	list, err := c.db.ContentRefererList(item.ID())
	if err != nil {


@@ 90,7 90,7 @@ func (c *Cache) ContentDelete(space space.Space, ct contenttype.ContentType, ite

	// Remove content from cache that referenced this.
	for _, ref := range list {
		err := c.mc.Delete(fmt.Sprintf("%s::%s::%s::%s", c.baseKey, space.ID(), ref.ContentTypeID, ref.ContentID))
		err := c.mc.Delete(fmt.Sprintf("content::%s::%s::%s::%s", c.baseKey, space.ID(), ref.ContentTypeID, ref.ContentID))
		if err != nil && !errors.Is(err, memcache.ErrCacheMiss) { // Don't care about cache miss.
			return err
		}

M internal/s/cache/contenttype.go => internal/s/cache/contenttype.go +6 -6
@@ 31,7 31,7 @@ func (c *Cache) ContentTypeNew(space space.Space, name string, params []db.Conte

	return c.contenttype(
		true,
		fmt.Sprintf("%s::%s::%s", c.baseKey, space.ID(), thing.ID()),
		fmt.Sprintf("contenttype::%s::%s::%s", c.baseKey, space.ID(), thing.ID()),
		func() (contenttype.ContentType, error) { return thing, err },
	)
}


@@ 55,7 55,7 @@ func (c *Cache) ContentTypeUpdate(space space.Space, prev contenttype.ContentTyp
			return nil, err
		}

		err = c.mc.Delete(fmt.Sprintf("%s::%s::%s::%s", c.baseKey, space.ID(), next.ID(), prevC.ID()))
		err = c.mc.Delete(fmt.Sprintf("contenttype::%s::%s::%s::%s", c.baseKey, space.ID(), next.ID(), prevC.ID()))
		if err != nil && !errors.Is(err, memcache.ErrCacheMiss) { // Don't care about cache miss.
			return nil, errors.Wrap(err, "failed to cache break content")
		}


@@ 67,7 67,7 @@ func (c *Cache) ContentTypeUpdate(space space.Space, prev contenttype.ContentTyp

	return c.contenttype(
		true,
		fmt.Sprintf("%s::%s::%s", c.baseKey, space.ID(), next.ID()),
		fmt.Sprintf("contenttype::%s::%s::%s", c.baseKey, space.ID(), next.ID()),
		func() (contenttype.ContentType, error) { return next, err },
	)
}


@@ 75,13 75,13 @@ func (c *Cache) ContentTypeUpdate(space space.Space, prev contenttype.ContentTyp
func (c *Cache) ContentTypeGet(space space.Space, thingID string) (contenttype.ContentType, error) {
	return c.contenttype(
		false,
		fmt.Sprintf("%s::%s::%s", c.baseKey, space.ID(), thingID),
		fmt.Sprintf("contenttype::%s::%s::%s", c.baseKey, space.ID(), thingID),
		func() (contenttype.ContentType, error) { return c.db.ContentTypeGet(space, thingID) },
	)
}

func (c *Cache) ContentTypeDelete(space space.Space, ct contenttype.ContentType) error {
	key := fmt.Sprintf("%s::%s::%s", c.baseKey, space.ID(), ct.ID())
	key := fmt.Sprintf("contenttype::%s::%s::%s", c.baseKey, space.ID(), ct.ID())

	// Copy all contents and their values.
	iter, t, err := c.db.ContentIter(space, ct, "name") // TODO: What is this sort type for?


@@ 121,7 121,7 @@ func (c *Cache) ContentTypeDelete(space space.Space, ct contenttype.ContentType)

	// Remove content from cache that referenced this.
	for _, ref := range list {
		err := c.mc.Delete(fmt.Sprintf("%s::%s::%s::%s", c.baseKey, space.ID(), ref.ContentTypeID, ref.ContentID))
		err := c.mc.Delete(fmt.Sprintf("contenttype::%s::%s::%s::%s", c.baseKey, space.ID(), ref.ContentTypeID, ref.ContentID))
		if err != nil && !errors.Is(err, memcache.ErrCacheMiss) { // Don't care about cache miss.
			return errors.Wrap(err, "failed to cache break referring content")
		}

M internal/s/cache/space.go => internal/s/cache/space.go +6 -6
@@ 24,7 24,7 @@ func (c *Cache) SpaceNew(user user.User, name, desc string) (space.Space, error)

	return c.space(
		true,
		fmt.Sprintf("%s::%s", c.baseKey, thing.ID()),
		fmt.Sprintf("space::%s::%s::%s", c.baseKey, user.ID(), thing.ID()),
		func() (space.Space, error) { return thing, err },
	)
}


@@ 32,7 32,7 @@ func (c *Cache) SpaceNew(user user.User, name, desc string) (space.Space, error)
func (c *Cache) SpaceGet(user user.User, thingID string) (space.Space, error) {
	return c.space(
		false,
		fmt.Sprintf("%s::%s", c.baseKey, thingID),
		fmt.Sprintf("space::%s::%s::%s", c.baseKey, user.ID(), thingID),
		func() (space.Space, error) { return c.db.SpaceGet(user, thingID) },
	)
}


@@ 40,20 40,20 @@ func (c *Cache) SpaceGet(user user.User, thingID string) (space.Space, error) {
func (c *Cache) SpaceUpdate(u user.User, s space.Space, n, d string) (space.Space, error) {
	return c.space(
		true,
		fmt.Sprintf("%s::%s", c.baseKey, s.ID()),
		fmt.Sprintf("space::%s::%s::%s", c.baseKey, u.ID(), s.ID()),
		func() (space.Space, error) { return c.db.SpaceUpdate(u, s, n, d) },
	)
}

func (c *Cache) SpaceDelete(s space.Space) error {
	key := fmt.Sprintf("%s::%s", c.baseKey, s.ID())
func (c *Cache) SpaceDelete(u user.User, s space.Space) error {
	key := fmt.Sprintf("space::%s::%s::%s", c.baseKey, u.ID(), s.ID())

	var deleteErr error
	_, _ = c.space(
		true,
		key,
		func() (space.Space, error) {
			deleteErr = c.db.SpaceDelete(s)
			deleteErr = c.db.SpaceDelete(u, s)
			return nil, deleteErr
		},
	)

M internal/s/db/space.go => internal/s/db/space.go +6 -15
@@ 23,11 23,9 @@ type Space struct {
var (
	queryCreateNewSpace       = `INSERT INTO cms_space (NAME, DESCRIPTION) VALUES (?, ?);`
	queryUpdateSpace          = `UPDATE cms_space SET NAME = ?, DESCRIPTION = ? WHERE ID = ?;`
	queryDeleteSpace          = `DELETE FROM cms_space WHERE ID = ?;`
	queryFindSpaceByID        = `SELECT ID, NAME, DESCRIPTION FROM cms_space WHERE ID = ?;`
	queryDeleteSpaceByID      = `DELETE FROM cms_space WHERE ID = ?;`
	queryDeleteSpace          = `DELETE cms_user_to_space, cms_space FROM cms_space JOIN cms_user_to_space ON cms_user_to_space.SPACE_ID = cms_space.ID WHERE SPACE_ID = ? AND USER_ID = ?;`
	queryCreateNewUserToSpace = `INSERT INTO cms_user_to_space (USER_ID, SPACE_ID) VALUES (?, ?);`
	queryFindUserToSpace      = `SELECT SPACE_ID FROM cms_user_to_space WHERE USER_ID = ? AND SPACE_ID = ?;`
	queryFindSpaceByUserAndID = `SELECT cms_space.ID, cms_space.NAME, cms_space.DESCRIPTION FROM cms_space JOIN cms_user_to_space ON SPACE_ID=cms_space.ID WHERE USER_ID=? AND SPACE_ID=?;`
	queryFindSpacesByUser     = `SELECT DISTINCT cms_space.ID, NAME, DESCRIPTION FROM cms_space JOIN cms_user_to_space ON cms_space.ID = cms_user_to_space.SPACE_ID WHERE USER_ID = ? LIMIT ? OFFSET ?;`

	copyUsersQuery = `


@@ 71,7 69,7 @@ func (db *DB) spaceNew(t *sql.Tx, user user.User, name, desc string) (space.Spac
	}

	var space Space
	if err := t.QueryRow(queryFindSpaceByID, id).Scan(&space.SpaceID, &space.SpaceName, &space.SpaceDesc); err != nil {
	if err := t.QueryRow(queryFindSpaceByUserAndID, user.ID(), id).Scan(&space.SpaceID, &space.SpaceName, &space.SpaceDesc); err != nil {
		return nil, fmt.Errorf("failed to find space created")
	}



@@ 302,18 300,11 @@ func (db *DB) spaceCopyContent(t *sql.Tx, next space.Space, ct contenttype.Conte
}

func (db *DB) spaceGet(t *sql.Tx, user user.User, spaceID string) (space.Space, error) {
	var id string

	if err := t.QueryRow(queryFindUserToSpace, user.ID(), spaceID).Scan(&id); err != nil {
		return nil, fmt.Errorf("failed to find space for user")
	}

	var space Space
	err := t.QueryRow(queryFindSpaceByID, id).Scan(&space.SpaceID, &space.SpaceName, &space.SpaceDesc)
	err := t.QueryRow(queryFindSpaceByUserAndID, user.ID(), spaceID).Scan(&space.SpaceID, &space.SpaceName, &space.SpaceDesc)
	if err != nil {
		return nil, fmt.Errorf("failed to find space")
	}

	return &space, nil
}



@@ 332,14 323,14 @@ func (db *DB) SpaceGet(user user.User, spaceID string) (space.Space, error) {
	return space, t.Commit()
}

func (db *DB) SpaceDelete(space space.Space) error {
func (db *DB) SpaceDelete(user user.User, space space.Space) error {
	t, err := db.Begin()
	if err != nil {
		return err
	}
	defer t.Rollback()

	if _, err := t.Exec(queryDeleteSpace, space.ID()); err != nil {
	if _, err := t.Exec(queryDeleteSpace, space.ID(), user.ID()); err != nil {
		return err
	}