diff --git a/pkg/controller/admin/feedback.go b/pkg/controller/admin/feedback.go index a429ca4..b8e7a06 100644 --- a/pkg/controller/admin/feedback.go +++ b/pkg/controller/admin/feedback.go @@ -80,7 +80,7 @@ func Feedback() http.HandlerFunc { if err != nil { session.FlashError(w, r, "Couldn't get reporting user ID %d: %s", fb.UserID, err) } else { - if err := session.ImpersonateUser(w, r, user, currentUser); err != nil { + if err := session.ImpersonateUser(w, r, user, currentUser, "Clicked from user reported Message via admin dashboard"); err != nil { session.FlashError(w, r, "Couldn't impersonate user: %s", err) } else { // Redirect to the thread. diff --git a/pkg/controller/admin/user_actions.go b/pkg/controller/admin/user_actions.go index fa456b6..d8166df 100644 --- a/pkg/controller/admin/user_actions.go +++ b/pkg/controller/admin/user_actions.go @@ -17,6 +17,7 @@ func UserActions() http.HandlerFunc { var ( intent = r.FormValue("intent") confirm = r.Method == http.MethodPost + reason = r.FormValue("reason") // for impersonation userId uint64 ) @@ -47,7 +48,7 @@ func UserActions() http.HandlerFunc { switch intent { case "impersonate": if confirm { - if err := session.ImpersonateUser(w, r, user, currentUser); err != nil { + if err := session.ImpersonateUser(w, r, user, currentUser, reason); err != nil { session.FlashError(w, r, "Failed to impersonate user: %s", err) } else { session.Flash(w, r, "You are now impersonating %s", user.Username) diff --git a/pkg/controller/photo/user_gallery.go b/pkg/controller/photo/user_gallery.go index 0ad749e..ae0157d 100644 --- a/pkg/controller/photo/user_gallery.go +++ b/pkg/controller/photo/user_gallery.go @@ -108,6 +108,7 @@ func UserPhotos() http.HandlerFunc { var vars = map[string]interface{}{ "IsOwnPhotos": currentUser.ID == user.ID, "IsMyPrivateUnlockedFor": isGranted, // have WE granted THIS USER to see our private pics? + "AreWeGrantedPrivate": isGrantee, // have THEY granted US private photo access. "User": user, "Photos": photos, "PhotoCount": models.CountPhotos(user.ID), diff --git a/pkg/session/session.go b/pkg/session/session.go index 0458261..515dd35 100644 --- a/pkg/session/session.go +++ b/pkg/session/session.go @@ -11,6 +11,7 @@ import ( "code.nonshy.com/nonshy/website/pkg/config" "code.nonshy.com/nonshy/website/pkg/log" + "code.nonshy.com/nonshy/website/pkg/mail" "code.nonshy.com/nonshy/website/pkg/models" "code.nonshy.com/nonshy/website/pkg/redis" "github.com/google/uuid" @@ -175,7 +176,7 @@ func LoginUser(w http.ResponseWriter, r *http.Request, u *models.User) error { } // ImpersonateUser assumes the role of the user impersonated by an admin uid. -func ImpersonateUser(w http.ResponseWriter, r *http.Request, u *models.User, impersonator *models.User) error { +func ImpersonateUser(w http.ResponseWriter, r *http.Request, u *models.User, impersonator *models.User, reason string) error { if u == nil || u.ID == 0 { return errors.New("not a valid user account") } @@ -189,6 +190,40 @@ func ImpersonateUser(w http.ResponseWriter, r *http.Request, u *models.User, imp sess.Impersonator = impersonator.ID sess.Save(w) + // Issue an admin notification that this has happened. + // NOTE: not DRY compared to contact.go + fb := &models.Feedback{ + Intent: "report", + Subject: "'Impersonate user' has been used", + TableName: "users", + TableID: impersonator.ID, + Message: fmt.Sprintf( + "The admin user **%s** (id:%d) has impersonated user **%s** (id:%d)\n\n"+ + "The reason they have given:\n\n%s", + impersonator.Username, impersonator.ID, + u.Username, u.ID, reason, + ), + } + + if err := models.CreateFeedback(fb); err != nil { + FlashError(w, r, "Couldn't create admin notification: %s", err) + } + + // Email the admins. + if err := mail.Send(mail.Message{ + To: config.Current.AdminEmail, + Subject: "Admin 'user impersonate' has been used", + Template: "email/admin_impersonate.html", + Data: map[string]interface{}{ + "Impersonator": impersonator, + "User": u, + "Reason": reason, + "AdminURL": config.Current.BaseURL + "/admin/feedback", + }, + }); err != nil { + log.Error("/contact page: couldn't send email: %s", err) + } + return u.Save() } diff --git a/web/templates/admin/dashboard.html b/web/templates/admin/dashboard.html index ed28c30..cb272a6 100644 --- a/web/templates/admin/dashboard.html +++ b/web/templates/admin/dashboard.html @@ -11,12 +11,112 @@
+ + Admin Guidelines NEW! +
++ Every picture uploaded to a user's profile page can be seen by admin users. The + admin gallery view can find all + user photos, whether private or friends-only, whether opted-in for the Site Gallery or not. +
+ ++ Be careful not to "Like" or comment on a picture if the user marked it + "Friends only" or "Private" and they wouldn't expect you to have been able to see it. "Like" + and "Comment" buttons are hidden in the admin gallery view to reduce accidents but they are + functional on the user's own gallery page. +
+ ++ Keep up with the newest forum posts and generally make sure + people aren't fighting or uploading inappropriate photos to one of the few photo boards. +
+ ++ If a user reports a Direct Message conversation + they're having, a link to view that chat thread will be available from the report. + This will impersonate the reporter and will be logged + - see "Impersonating users," below. +
+ ++ DMs are text-based only, so users won't be sending any image attachments that need + moderating and their privacy is to be respected. A user may report a problematic + conversation for us to take a look at. +
+ ++ From a user's profile page you can "impersonate," or log in as them. You should almost + never need to do this, and only to diagnose a reported issue from their account or + something like that. +
+ ++ You will need to write a reason for impersonating a user. The event is + logged and will be e-mailed to the admin team along with your reason. The admin team is + alerted any time an Impersonate action is performed. +
+ ++ Note: when you impersonate, their "Last logged in at" time is not updated by your actions. + So if a user were last seen a month ago, they will still appear last seen a month ago. + But other interactions you make as their account (e.g. marking notifications read, reading + their unread DMs) will work and may be noticed. +
+ ++ +
+ +- - Admin Dashboard + + Admin Menu
Notifications
-+ + This event is logged and will be noticed. + + Write an explanation below why you are impersonating this user. It will + be e-mailed to the admin mailing list and trigger an admin notification + and be logged as a Report to + the admin dashboard. Reports can be acknowledged, but not deleted. +
++ Good reasons may include: +