Notification when admin users are blocked
This commit is contained in:
parent
79ea384d40
commit
8d9588b039
|
@ -117,11 +117,17 @@ func UserActions() http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get their block lists.
|
||||||
insights, err := models.GetBlocklistInsights(user)
|
insights, err := models.GetBlocklistInsights(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.FlashError(w, r, "Error getting blocklist insights: %s", err)
|
session.FlashError(w, r, "Error getting blocklist insights: %s", err)
|
||||||
}
|
}
|
||||||
vars["BlocklistInsights"] = insights
|
vars["BlocklistInsights"] = insights
|
||||||
|
|
||||||
|
// Also surface counts of admin blocks.
|
||||||
|
count, total := models.CountBlockedAdminUsers(user)
|
||||||
|
vars["AdminBlockCount"] = count
|
||||||
|
vars["AdminBlockTotal"] = total
|
||||||
case "essays":
|
case "essays":
|
||||||
// Edit their profile essays easily.
|
// Edit their profile essays easily.
|
||||||
if !currentUser.HasAdminScope(config.ScopePhotoModerator) {
|
if !currentUser.HasAdminScope(config.ScopePhotoModerator) {
|
||||||
|
|
|
@ -112,17 +112,35 @@ func BlockUser() http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't block admins who have the unblockable scope.
|
// If the target user is an admin, log this to the admin reports page.
|
||||||
if user.IsAdmin && user.HasAdminScope(config.ScopeUnblockable) {
|
if user.IsAdmin {
|
||||||
|
// Is the target admin user unblockable?
|
||||||
|
var (
|
||||||
|
unblockable = user.HasAdminScope(config.ScopeUnblockable)
|
||||||
|
footer string // qualifier for the admin report body
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add a footer to the report to indicate whether the block goes through.
|
||||||
|
if unblockable {
|
||||||
|
footer = "**Unblockable:** this admin can not be blocked, so the block was not added and the user was shown an error message."
|
||||||
|
} else {
|
||||||
|
footer = "**Notice:** This admin is not unblockable, so the block has been added successfully."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also, include this user's current count of blocked admin users.
|
||||||
|
count, total := models.CountBlockedAdminUsers(currentUser)
|
||||||
|
footer += fmt.Sprintf("\n\nThis user now blocks %d of %d admin user(s) on this site.", count+1, total)
|
||||||
|
|
||||||
// For curiosity's sake, log a report.
|
// For curiosity's sake, log a report.
|
||||||
fb := &models.Feedback{
|
fb := &models.Feedback{
|
||||||
Intent: "report",
|
Intent: "report",
|
||||||
Subject: "A user tried to block an admin",
|
Subject: "A user tried to block an admin",
|
||||||
Message: fmt.Sprintf(
|
Message: fmt.Sprintf(
|
||||||
"A user has tried to block an admin user account!\n\n"+
|
"A user has tried to block an admin user account!\n\n"+
|
||||||
"* Username: %s\n* Tried to block: %s",
|
"* Username: %s\n* Tried to block: %s\n\n%s",
|
||||||
currentUser.Username,
|
currentUser.Username,
|
||||||
user.Username,
|
user.Username,
|
||||||
|
footer,
|
||||||
),
|
),
|
||||||
UserID: currentUser.ID,
|
UserID: currentUser.ID,
|
||||||
TableName: "users",
|
TableName: "users",
|
||||||
|
@ -132,9 +150,12 @@ func BlockUser() http.HandlerFunc {
|
||||||
log.Error("Could not log feedback for user %s trying to block admin %s: %s", currentUser.Username, user.Username, err)
|
log.Error("Could not log feedback for user %s trying to block admin %s: %s", currentUser.Username, user.Username, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
session.FlashError(w, r, "You can not block site administrators.")
|
// If the admin is unblockable, give the user an error message and return.
|
||||||
templates.Redirect(w, "/u/"+username)
|
if unblockable {
|
||||||
return
|
session.FlashError(w, r, "You can not block site administrators.")
|
||||||
|
templates.Redirect(w, "/u/"+username)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block the target user.
|
// Block the target user.
|
||||||
|
|
|
@ -199,6 +199,7 @@ func GetBlocklistInsights(user *User) (*BlocklistInsight, error) {
|
||||||
reverse = []*Block{} // Users who block the target
|
reverse = []*Block{} // Users who block the target
|
||||||
userIDs = []uint64{user.ID}
|
userIDs = []uint64{user.ID}
|
||||||
usernames = map[uint64]string{}
|
usernames = map[uint64]string{}
|
||||||
|
admins = map[uint64]bool{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get the complete blocklist and bucket them into forward and reverse.
|
// Get the complete blocklist and bucket them into forward and reverse.
|
||||||
|
@ -218,6 +219,7 @@ func GetBlocklistInsights(user *User) (*BlocklistInsight, error) {
|
||||||
type scanItem struct {
|
type scanItem struct {
|
||||||
ID uint64
|
ID uint64
|
||||||
Username string
|
Username string
|
||||||
|
IsAdmin bool
|
||||||
}
|
}
|
||||||
var scan = []scanItem{}
|
var scan = []scanItem{}
|
||||||
if res := DB.Table(
|
if res := DB.Table(
|
||||||
|
@ -225,6 +227,7 @@ func GetBlocklistInsights(user *User) (*BlocklistInsight, error) {
|
||||||
).Select(
|
).Select(
|
||||||
"id",
|
"id",
|
||||||
"username",
|
"username",
|
||||||
|
"is_admin",
|
||||||
).Where(
|
).Where(
|
||||||
"id IN ?", userIDs,
|
"id IN ?", userIDs,
|
||||||
).Scan(&scan); res.Error != nil {
|
).Scan(&scan); res.Error != nil {
|
||||||
|
@ -233,6 +236,7 @@ func GetBlocklistInsights(user *User) (*BlocklistInsight, error) {
|
||||||
|
|
||||||
for _, row := range scan {
|
for _, row := range scan {
|
||||||
usernames[row.ID] = row.Username
|
usernames[row.ID] = row.Username
|
||||||
|
admins[row.ID] = row.IsAdmin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,6 +249,7 @@ func GetBlocklistInsights(user *User) (*BlocklistInsight, error) {
|
||||||
if username, ok := usernames[row.TargetUserID]; ok {
|
if username, ok := usernames[row.TargetUserID]; ok {
|
||||||
result.Blocks = append(result.Blocks, BlocklistInsightUser{
|
result.Blocks = append(result.Blocks, BlocklistInsightUser{
|
||||||
Username: username,
|
Username: username,
|
||||||
|
IsAdmin: admins[row.TargetUserID],
|
||||||
Date: row.CreatedAt,
|
Date: row.CreatedAt,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -253,6 +258,7 @@ func GetBlocklistInsights(user *User) (*BlocklistInsight, error) {
|
||||||
if username, ok := usernames[row.SourceUserID]; ok {
|
if username, ok := usernames[row.SourceUserID]; ok {
|
||||||
result.BlockedBy = append(result.BlockedBy, BlocklistInsightUser{
|
result.BlockedBy = append(result.BlockedBy, BlocklistInsightUser{
|
||||||
Username: username,
|
Username: username,
|
||||||
|
IsAdmin: admins[row.SourceUserID],
|
||||||
Date: row.CreatedAt,
|
Date: row.CreatedAt,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -268,6 +274,7 @@ type BlocklistInsight struct {
|
||||||
|
|
||||||
type BlocklistInsightUser struct {
|
type BlocklistInsightUser struct {
|
||||||
Username string
|
Username string
|
||||||
|
IsAdmin bool
|
||||||
Date time.Time
|
Date time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -595,6 +595,33 @@ func MapAdminUsers(user *User) (UserMap, error) {
|
||||||
return MapUsers(user, userIDs)
|
return MapUsers(user, userIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CountBlockedAdminUsers returns a count of how many admin users the current user has blocked, out of how many total.
|
||||||
|
func CountBlockedAdminUsers(user *User) (count, total int64) {
|
||||||
|
// Count the blocked admins.
|
||||||
|
DB.Model(&User{}).Select(
|
||||||
|
"count(users.id) AS cnt",
|
||||||
|
).Joins(
|
||||||
|
"JOIN blocks ON (blocks.target_user_id = users.id)",
|
||||||
|
).Where(
|
||||||
|
"blocks.source_user_id = ? AND users.is_admin IS TRUE",
|
||||||
|
user.ID,
|
||||||
|
).Count(&count)
|
||||||
|
|
||||||
|
// And the total number of available admins.
|
||||||
|
total = CountAdminUsers()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountAdminUsers returns a count of how many admin users exist on the site.
|
||||||
|
func CountAdminUsers() (count int64) {
|
||||||
|
DB.Model(&User{}).Select(
|
||||||
|
"count(id) AS cnt",
|
||||||
|
).Where(
|
||||||
|
"users.is_admin IS TRUE",
|
||||||
|
).Count(&count)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Has a user ID in the map?
|
// Has a user ID in the map?
|
||||||
func (um UserMap) Has(id uint64) bool {
|
func (um UserMap) Has(id uint64) bool {
|
||||||
_, ok := um[id]
|
_, ok := um[id]
|
||||||
|
|
|
@ -100,11 +100,17 @@
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
It is also against the <a href="/tos#child-exploitation">Terms of Service</a> of this website, and
|
It is also against the <a href="/tos#child-exploitation">Terms of Service</a> of this website, and
|
||||||
members who violate this rule will be banned. This website is actively monitored to keep on top of this stuff,
|
members who violate this rule will be banned. <strong>This website is actively monitored</strong> to keep on top of this stuff,
|
||||||
and we cooperate enthusiastically with
|
and we cooperate enthusiastically with
|
||||||
<a href="https://www.missingkids.org/" title="National Center for Missing and Exploited Children">NCMEC</a>
|
<a href="https://www.missingkids.org/" title="National Center for Missing and Exploited Children">NCMEC</a>
|
||||||
and relevant law enforcement agencies.
|
and relevant law enforcement agencies.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This incident has been reported to the website administrator. If you are surprised to see this message and
|
||||||
|
it was on accident, don't worry -- but <strong>repeated attempts to bypass this search filter</strong> by trying
|
||||||
|
other related keywords <strong>will be noticed</strong> and may attract extra admin attention to your account.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,23 @@
|
||||||
|
|
||||||
<h3>Block Lists</h3>
|
<h3>Block Lists</h3>
|
||||||
|
|
||||||
|
<!-- Surface if admin users are blocked -->
|
||||||
|
{{if .AdminBlockCount}}
|
||||||
|
<h5 class="has-text-danger">
|
||||||
|
<i class="fa fa-peace"></i>
|
||||||
|
Blocked Admins <span class="tag is-danger">{{.AdminBlockCount}}</span>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This user blocks <strong>{{.AdminBlockCount}}</strong> out of {{.AdminBlockTotal}} admin{{Pluralize64 .AdminBlockTotal}} of this website.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If this number is unusually high, it can indicate this user may be proactively blocking all the admins in order to be
|
||||||
|
sneaky or evade moderation.
|
||||||
|
</p>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<h5 class="has-text-warning">Forward List <span class="tag is-warning">{{len .BlocklistInsights.Blocks}}</span></h5>
|
<h5 class="has-text-warning">Forward List <span class="tag is-warning">{{len .BlocklistInsights.Blocks}}</span></h5>
|
||||||
|
@ -118,6 +135,9 @@
|
||||||
{{range .BlocklistInsights.Blocks}}
|
{{range .BlocklistInsights.Blocks}}
|
||||||
<li>
|
<li>
|
||||||
<a href="/u/{{.Username}}">{{.Username}}</a>
|
<a href="/u/{{.Username}}">{{.Username}}</a>
|
||||||
|
{{if .IsAdmin}}
|
||||||
|
<sup class="has-text-warning fa fa-peace" title="Admin user"></sup>
|
||||||
|
{{end}}
|
||||||
<small class="has-text-grey" title="{{.Date}}">{{.Date.Format "2006-01-02"}}</small>
|
<small class="has-text-grey" title="{{.Date}}">{{.Date.Format "2006-01-02"}}</small>
|
||||||
</li>
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -141,6 +161,9 @@
|
||||||
{{range .BlocklistInsights.BlockedBy}}
|
{{range .BlocklistInsights.BlockedBy}}
|
||||||
<li>
|
<li>
|
||||||
<a href="/u/{{.Username}}">{{.Username}}</a>
|
<a href="/u/{{.Username}}">{{.Username}}</a>
|
||||||
|
{{if .IsAdmin}}
|
||||||
|
<sup class="has-text-warning fa fa-peace" title="Admin user"></sup>
|
||||||
|
{{end}}
|
||||||
<small class="has-text-grey" title="{{.Date}}">{{.Date.Format "2006-01-02"}}</small>
|
<small class="has-text-grey" title="{{.Date}}">{{.Date.Format "2006-01-02"}}</small>
|
||||||
</li>
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user