add mask info to hide exposed ip, add separate cache for admins

This commit is contained in:
Umputun
2018-02-16 22:26:05 -06:00
parent a83db17d90
commit 4711512282
5 changed files with 48 additions and 24 deletions
+16 -2
View File
@@ -126,14 +126,28 @@ func (a *admin) checkBlocked(siteID string, user store.User) bool {
}
// processes comments and hides text of all comments for blocked users.
// resets score and votes too
func (a *admin) maskBlockedUsers(comments []store.Comment) (res []store.Comment) {
// resets score and votes too. Also hides sensitive info for non-admin users
func (a *admin) maskInfo(comments []store.Comment, r *http.Request) (res []store.Comment) {
res = make([]store.Comment, len(comments))
isAdmin := false
if user, err := rest.GetUserInfo(r); err == nil && user.Admin { // make seprate cache key for admins
isAdmin = true
}
for i, c := range comments {
// process blocked users
if a.dataService.IsBlocked(c.Locator.SiteID, c.User.ID) {
c.Mask()
c.User.Blocked = true
}
// hide info from non-admins
if !isAdmin {
c.User.IP = ""
}
res[i] = c
}
return res
+18 -8
View File
@@ -228,12 +228,12 @@ func (s *Rest) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
log.Printf("[DEBUG] get comments for %+v", locator)
data, err := s.Cache.Get(r.URL.String(), time.Hour, func() ([]byte, error) {
data, err := s.Cache.Get(s.urlKey(r), time.Hour, func() ([]byte, error) {
comments, e := s.DataService.Find(locator, r.URL.Query().Get("sort"))
if e != nil {
return nil, e
}
maskedComments := s.mod.maskBlockedUsers(comments)
maskedComments := s.mod.maskInfo(comments, r)
var b []byte
switch r.URL.Query().Get("format") {
case "tree":
@@ -261,12 +261,12 @@ func (s *Rest) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) {
max = 0
}
data, err := s.Cache.Get(r.URL.String(), time.Hour, func() ([]byte, error) {
data, err := s.Cache.Get(s.urlKey(r), time.Hour, func() ([]byte, error) {
comments, e := s.DataService.Last(r.URL.Query().Get("site"), max)
if e != nil {
return nil, e
}
comments = s.mod.maskBlockedUsers(comments)
comments = s.mod.maskInfo(comments, r)
return encodeJSONWithHTML(comments)
})
@@ -291,7 +291,7 @@ func (s *Rest) commentByIDCtrl(w http.ResponseWriter, r *http.Request) {
rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get comment by id")
return
}
comment = s.mod.maskBlockedUsers([]store.Comment{comment})[0]
comment = s.mod.maskInfo([]store.Comment{comment}, r)[0]
render.Status(r, http.StatusOK)
renderJSONWithHTML(w, r, comment)
}
@@ -309,12 +309,12 @@ func (s *Rest) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) {
log.Printf("[DEBUG] get comments for userID %s, %s", userID, siteID)
data, err := s.Cache.Get(r.URL.String(), time.Hour, func() ([]byte, error) {
data, err := s.Cache.Get(s.urlKey(r), time.Hour, func() ([]byte, error) {
comments, count, e := s.DataService.User(siteID, userID)
if e != nil {
return nil, e
}
comments = s.mod.maskBlockedUsers(comments)
comments = s.mod.maskInfo(comments, r)
resp.Comments, resp.Count = comments, count
return encodeJSONWithHTML(resp)
})
@@ -374,7 +374,7 @@ func (s *Rest) countCtrl(w http.ResponseWriter, r *http.Request) {
func (s *Rest) listCtrl(w http.ResponseWriter, r *http.Request) {
siteID := r.URL.Query().Get("site")
data, err := s.Cache.Get(r.URL.String(), 8*time.Hour, func() ([]byte, error) {
data, err := s.Cache.Get(s.urlKey(r), 8*time.Hour, func() ([]byte, error) {
posts, e := s.DataService.List(siteID)
if e != nil {
return nil, e
@@ -471,6 +471,16 @@ func (s *Rest) addFileServer(r chi.Router, path string, root http.FileSystem) {
}))
}
// urlKey gets url from request to use is as cache key
// admins will have separate keys in order tp prevent leak of admin-only data to regular users
func (s *Rest) urlKey(r *http.Request) string {
key := r.URL.String()
if user, err := rest.GetUserInfo(r); err == nil && user.Admin { // make seprate cache key for admins
key = "admin!!" + key
}
return key
}
// renderJSONWithHTML allows html tags and forces charset=utf-8
func renderJSONWithHTML(w http.ResponseWriter, r *http.Request, v interface{}) {
data, err := encodeJSONWithHTML(v)
+1 -1
View File
@@ -82,7 +82,7 @@ func TestServer_CreateAndGet(t *testing.T) {
assert.Equal(t, "<p><strong>test</strong> <em>123</em> http://radio-t.com</p>", comment.Text)
assert.Equal(t, store.User{Name: "developer one", ID: "dev",
Picture: "https://friends.radio-t.com/resources/images/rt_logo_64.png",
Profile: "https://radio-t.com/info/", Admin: true, Blocked: false, IP: ""},
Profile: "https://radio-t.com/info/", Admin: true, Blocked: false, IP: "127.0.0.1"},
comment.User)
t.Logf("%+v", comment)
}
+12 -2
View File
@@ -34,10 +34,10 @@ type User struct {
Name string `json:"name"`
ID string `json:"id"`
Picture string `json:"picture"`
Profile string `json:"profile"`
Profile string `json:"profile,omitempty"`
Admin bool `json:"admin"`
Blocked bool `json:"block,omitempty"`
IP string `json:"-"`
IP string `json:"ip,omitempty"`
}
// Edit indication
@@ -64,6 +64,16 @@ type NotifUser struct {
Destination string `json:"destination"`
}
// NotifScope defines "enum" of notification scopes
type NotifScope int
// All NotifScope values
const (
ScopeSite NotifScope = 1
ScopePost NotifScope = 2
ScopeReply NotifScope = 3
)
// Sanitize clean dangerous html/js from the comment
func (c *Comment) Sanitize() {
p := bluemonday.UGCPolicy()
+1 -11
View File
@@ -33,23 +33,13 @@ type Admin interface {
Blocked(siteID string) ([]BlockedUser, error) // get list of blocked users
}
// Notifier defines all store ops for update modifications
// Notifier defines all store ops to store/retrive notification info for users
type Notifier interface {
Set(locator Locator, user NotifUser, scope NotifScope, status bool) error // subscribe / unsubscribe user to locator updates
Status(locator Locator, userID string) bool // get subscription status for user & locator
List(locator Locator) ([]NotifUser, error) // list all subscribed users
}
// NotifScope defines "enum" of notification scopes
type NotifScope int
// All NotifScope values
const (
ScopeSite NotifScope = 1
ScopePost NotifScope = 2
ScopeReply NotifScope = 3
)
func sortComments(comments []Comment, sortFld string) []Comment {
sort.Slice(comments, func(i, j int) bool {
switch sortFld {