[feature] Store admin actions in the db, prevent conflicting actions (#2167)

This commit is contained in:
tobi 2023-09-04 15:55:17 +02:00 committed by GitHub
commit 3ed1ca68e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 1393 additions and 272 deletions

View file

@ -1,70 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package gtsmodel
import (
"net"
"time"
)
// AdminAccountAction models an action taken by an instance administrator on an account.
type AdminAccountAction struct {
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
AccountID string `bun:"type:CHAR(26),notnull,nullzero"` // Who performed this admin action.
Account *Account `bun:"rel:has-one"` // Account corresponding to accountID
TargetAccountID string `bun:"type:CHAR(26),notnull,nullzero"` // Who is the target of this action
TargetAccount *Account `bun:"rel:has-one"` // Account corresponding to targetAccountID
Text string `bun:""` // text explaining why this action was taken
Type AdminActionType `bun:",nullzero,notnull"` // type of action that was taken
SendEmail bool `bun:""` // should an email be sent to the account owner to explain what happened
ReportID string `bun:"type:CHAR(26),nullzero"` // id of a report connected to this action, if it exists
}
// AdminActionType describes a type of action taken on an entity by an admin
type AdminActionType string
const (
// AdminActionDisable -- the account or application etc has been disabled but not deleted.
AdminActionDisable AdminActionType = "disable"
// AdminActionSilence -- the account or application etc has been silenced.
AdminActionSilence AdminActionType = "silence"
// AdminActionSuspend -- the account or application etc has been deleted.
AdminActionSuspend AdminActionType = "suspend"
)
// NewSignup models parameters for the creation
// of a new user + account on this instance.
//
// Aside from username, email, and password, it is
// fine to use zero values on fields of this struct.
type NewSignup struct {
Username string // Username of the new account.
Email string // Email address of the user.
Password string // Plaintext (not yet hashed) password for the user.
Reason string // Reason given by the user when submitting a sign up request (optional).
PreApproved bool // Mark the new user/account as preapproved (optional)
SignUpIP net.IP // IP address from which the sign up request occurred (optional).
Locale string // Locale code for the new account/user (optional).
AppID string // ID of the application used to create this account (optional).
EmailVerified bool // Mark submitted email address as already verified (optional).
ExternalID string // ID of this user in external OIDC system (optional).
Admin bool // Mark new user as an admin user (optional).
}

View file

@ -0,0 +1,145 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package gtsmodel
import (
"path"
"time"
)
// AdminActionCategory describes the category
// of entity that this admin action targets.
type AdminActionCategory uint8
// Only ever add new action categories to the *END* of the list
// below, DO NOT insert them before/between other entries!
const (
AdminActionCategoryUnknown AdminActionCategory = iota
AdminActionCategoryAccount
AdminActionCategoryDomain
)
func (c AdminActionCategory) String() string {
switch c {
case AdminActionCategoryAccount:
return "account"
case AdminActionCategoryDomain:
return "domain"
default:
return "unknown"
}
}
func NewAdminActionCategory(in string) AdminActionCategory {
switch in {
case "account":
return AdminActionCategoryAccount
case "domain":
return AdminActionCategoryDomain
default:
return AdminActionCategoryUnknown
}
}
// AdminActionType describes a type of
// action taken on an entity by an admin.
type AdminActionType uint8
// Only ever add new action types to the *END* of the list
// below, DO NOT insert them before/between other entries!
const (
AdminActionUnknown AdminActionType = iota
AdminActionDisable
AdminActionReenable
AdminActionSilence
AdminActionUnsilence
AdminActionSuspend
AdminActionUnsuspend
)
func (t AdminActionType) String() string {
switch t {
case AdminActionDisable:
return "disable"
case AdminActionReenable:
return "reenable"
case AdminActionSilence:
return "silence"
case AdminActionUnsilence:
return "unsilence"
case AdminActionSuspend:
return "suspend"
case AdminActionUnsuspend:
return "unsuspend"
default:
return "unknown"
}
}
func NewAdminActionType(in string) AdminActionType {
switch in {
case "disable":
return AdminActionDisable
case "reenable":
return AdminActionReenable
case "silence":
return AdminActionSilence
case "unsilence":
return AdminActionUnsilence
case "suspend":
return AdminActionSuspend
case "unsuspend":
return AdminActionUnsuspend
default:
return AdminActionUnknown
}
}
// AdminAction models an action taken by an instance administrator towards an account, domain, etc.
type AdminAction struct {
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // ID of this item in the database.
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // Creation time of this item.
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // Last updated time of this item.
CompletedAt time.Time `bun:"type:timestamptz,nullzero"` // Completion time of this item.
TargetCategory AdminActionCategory `bun:",nullzero,notnull"` // Category of the entity targeted by this action.
TargetID string `bun:",nullzero,notnull"` // Identifier of the target. May be a ULID (in case of accounts), or a domain name (in case of domains).
Target interface{} `bun:"-"` // Target of the action. Might be a domain string, might be an account.
Type AdminActionType `bun:",nullzero,notnull"` // Type of action that was taken.
AccountID string `bun:"type:CHAR(26),notnull,nullzero"` // Who performed this admin action.
Account *Account `bun:"rel:has-one"` // Account corresponding to accountID
Text string `bun:",nullzero"` // Free text field for explaining why this action was taken, or adding a note about this action.
SendEmail *bool `bun:",nullzero,notnull,default:false"` // Send an email to the target account's user to explain what happened (local accounts only).
ReportIDs []string `bun:"reports,array"` // IDs of any reports cited when creating this action.
Reports []*Report `bun:"-"` // Reports corresponding to ReportIDs.
Errors []string `bun:",array"` // String value of any error(s) encountered while processing. May be helpful for admins to debug.
}
// Key returns a key for the AdminAction which is
// unique only on its TargetCategory and TargetID
// fields. This key can be used to check if this
// AdminAction overlaps with another action performed
// on the same target, regardless of the Type of
// either this or the other action.
func (a *AdminAction) Key() string {
return path.Join(
a.TargetCategory.String(),
a.TargetID,
)
}

View file

@ -57,3 +57,23 @@ type User struct {
ResetPasswordSentAt time.Time `bun:"type:timestamptz,nullzero"` // When did we email the user their reset-password email?
ExternalID string `bun:",nullzero,unique"` // If the login for the user is managed externally (e.g OIDC), we need to keep a stable reference to the external object (e.g OIDC sub claim)
}
// NewSignup models parameters for the creation
// of a new user + account on this instance.
//
// Aside from username, email, and password, it is
// fine to use zero values on fields of this struct.
type NewSignup struct {
Username string // Username of the new account.
Email string // Email address of the user.
Password string // Plaintext (not yet hashed) password for the user.
Reason string // Reason given by the user when submitting a sign up request (optional).
PreApproved bool // Mark the new user/account as preapproved (optional)
SignUpIP net.IP // IP address from which the sign up request occurred (optional).
Locale string // Locale code for the new account/user (optional).
AppID string // ID of the application used to create this account (optional).
EmailVerified bool // Mark submitted email address as already verified (optional).
ExternalID string // ID of this user in external OIDC system (optional).
Admin bool // Mark new user as an admin user (optional).
}