linstrom/storage/roles.go

249 lines
8.2 KiB
Go
Raw Permalink Normal View History

2024-08-28 15:20:38 +00:00
package storage
2024-09-17 13:46:05 +00:00
import (
"github.com/rs/zerolog/log"
2024-12-18 14:24:56 +00:00
"git.mstar.dev/mstar/goutils/sliceutils"
"git.mstar.dev/mstar/linstrom/util"
2024-09-17 13:46:05 +00:00
"gorm.io/gorm"
)
// Could I collapse all these go:generate command into more condensed ones?
// Yes
// Will I do that?
// No
// This is explicit in what is being done. And easier to understand
2024-09-18 09:15:44 +00:00
//go:generate go build -o RolesGenerator ../cmd/RolesGenerator/main.go
2024-09-18 11:49:10 +00:00
//go:generate ./RolesGenerator -input=roles.go -output=rolesUtil_generated.go
//go:generate rm RolesGenerator
//go:generate go build -o ApiGenerator ../cmd/RolesApiTypeGenerator/main.go
//go:generate ./ApiGenerator -input=roles.go -output=../server/apiLinstromTypes_generated.go
//go:generate rm ApiGenerator
//go:generate go build -o HelperGenerator ../cmd/RolesApiConverter/main.go
//go:generate ./HelperGenerator -input=roles.go -output=../server/apiLinstromTypeHelpers_generated.go
//go:generate rm HelperGenerator
//go:generate go build -o FrontendGenerator ../cmd/RolesFrontendGenerator/main.go
//go:generate ./FrontendGenerator -input=roles.go -output=../frontend-reactive/app/models/role.ts
//go:generate rm FrontendGenerator
2024-09-17 13:46:05 +00:00
// A role is, in concept, similar to how Discord handles roles
// Some permission can be either disallowed (&false), don't care (nil) or allowed (&true)
// Don't care just says to use the value from the next lower role where it is set
2024-08-28 15:20:38 +00:00
type Role struct {
2024-09-13 13:02:32 +00:00
// TODO: More control options
// Extend upon whatever Masto, Akkoma and Misskey have
// Lots of details please
2024-09-17 13:46:05 +00:00
// --- Role metadata ---
// Include full db model stuff
gorm.Model
// Name of the role
Name string `gorm:"primaryKey;unique"`
2024-09-17 13:46:05 +00:00
// Priority of the role
// Lower priority gets applied first and thus overwritten by higher priority ones
// If two roles have the same priority, the order is undetermined and may be random
// Default priority for new roles is 1 to always overwrite default user
// And full admin has max priority possible
Priority uint32
2024-09-17 13:46:05 +00:00
// Whether this role is for a for a single user only (like custom, per user permissions in Discord)
// If yes, Name will be the id of the user in question
IsUserRole bool
// Whether this role is one built into Linstrom from the start or not
// Note: Built-in roles can't be modified
IsBuiltIn bool
// --- User permissions ---
CanSendMedia *bool // Local & remote
CanSendCustomEmotes *bool // Local & remote
CanSendCustomReactions *bool // Local & remote
CanSendPublicNotes *bool // Local & remote
CanSendLocalNotes *bool // Local & remote
CanSendFollowerOnlyNotes *bool // Local & remote
CanSendPrivateNotes *bool // Local & remote
CanSendReplies *bool // Local & remote
CanQuote *bool // Local only
CanBoost *bool // Local only
CanIncludeLinks *bool // Local & remote
CanIncludeSurvey *bool // Local
CanFederateFedi *bool // Local & remote
CanFederateBsky *bool // Local
CanChangeDisplayName *bool // Local
// Internal ids of accounts blocked by this role
BlockedUsers []string `gorm:"type:bytes;serializer:gob"` // Local
CanSubmitReports *bool // Local & remote
CanLogin *bool // Local
CanMentionOthers *bool // Local & remote
HasMentionCountLimit *bool // Local & remote
MentionLimit *uint32 // Local & remote
2024-09-17 13:46:05 +00:00
// CanViewBoosts *bool
// CanViewQuotes *bool
// CanViewMedia *bool
// CanViewCustomEmotes *bool
// --- Automod ---
AutoNsfwMedia *bool // Local & remote
AutoCwPosts *bool // Local & remote
AutoCwPostsText *string // Local & remote
ScanCreatedPublicNotes *bool // Local & remote
ScanCreatedLocalNotes *bool // Local & remote
ScanCreatedFollowerOnlyNotes *bool // Local & remote
ScanCreatedPrivateNotes *bool // Local & remote
// Blocks all interactions and federation between users with the role and all included ids/handles
// TODO: Decide whether this is a list of handles or of account ids
// Handles would increase the load due to having to search for them first
// while ids would require to store every single account mentioned
// which could cause escalating storage costs
DisallowInteractionsWith []string `gorm:"type:bytes;serializer:gob"` // Local & remote
2024-09-17 13:46:05 +00:00
WithholdNotesForManualApproval *bool // Local & remote
WithholdNotesBasedOnRegex *bool // Local & remote
WithholdNotesRegexes []string `gorm:"type:bytes;serializer:gob"` // Local & remote
2024-09-17 13:46:05 +00:00
// --- Admin perms ---
// If set, counts as all permissions being set as given and all restrictions being disabled
FullAdmin *bool // Local
CanAffectOtherAdmins *bool // Local
CanDeleteNotes *bool // Local
CanConfirmWithheldNotes *bool // Local
CanAssignRoles *bool // Local
CanSupressInteractionsBetweenUsers *bool // Local
CanOverwriteDisplayNames *bool // Local
CanManageCustomEmotes *bool // Local
CanViewDeletedNotes *bool // Local
CanRecoverDeletedNotes *bool // Local
CanManageAvatarDecorations *bool // Local
CanManageAds *bool // Local
CanSendAnnouncements *bool // Local
2024-08-28 15:20:38 +00:00
}
/*
Mastodon permissions (highest permission to lowest):
- Admin
- Devops
- View Audit log
- View Dashboard
- Manage Reports
- Manage Federation
- Manage Settings
- Manage Blocks
- Manage Taxonomies
- Manage Appeals
- Manage Users
- Manage Invites
- Manage Rules
- Manage Announcements
- Manage Custom Emojis
- Manage Webhooks
- Invite Users
- Manage Roles
- Manage User Access
- Delete User Data
*/
/*
Misskey "permissions" (no order):
- Global timeline available (interact with global timeline I think)
- Local timeline available (same as global, but for local)
- b-something timeline available
- Can send public notes
- How many mentions a note can have
- Can invite others
- How many invites can be sent
- InviteLimitCycle (whatever that means)
- Invite Expiration time (duration of how long invites stay valid I think)
- Manage custom emojis
- Manage custom avatar decorations
- Seach for notes
- Use translator
- Hide ads from self
- How much storage space the user has
- Whether to mark all posts from account as nsfw
- max number of pinned messages
- max number of antennas
- max number of muted words
- max number of webhooks
- max number of clips
- max number of notes contained in a clip (? I think. Don't know enough about clips)
- Max number of lists of users
- max number of users in a user list
- rate limit multiplier
- max number of applied avatar decorations
*/
2024-09-17 15:11:55 +00:00
func (s *Storage) NewEmptyRole(name string) (*Role, error) {
defer util.Untrace(util.Trace(&log.Logger))
// Check if a role with the given name already exists
2024-09-17 15:11:55 +00:00
_, err := s.FindRoleByName(name)
switch err {
case nil:
return nil, ErrEntryAlreadyExists
case ErrEntryNotFound: // Empty case, since this is what we want
default:
return nil, err
}
// New roles have a priority of 1 by default
newRole := Role{Name: name, Priority: 1}
2024-09-17 15:11:55 +00:00
err = s.db.Create(&newRole).Error
if err != nil {
return nil, err
}
return &newRole, nil
}
func (s *Storage) FindRoleByName(name string) (*Role, error) {
defer util.Untrace(util.Trace(&log.Logger))
2024-09-17 15:11:55 +00:00
role := Role{}
err := s.db.Where("name = ?", name).First(&role).Error
switch err {
case nil:
return &role, nil
case gorm.ErrRecordNotFound:
return nil, ErrEntryNotFound
default:
return nil, err
}
}
func (s *Storage) FindRolesByNames(names []string) ([]Role, error) {
defer util.Untrace(util.Trace(&log.Logger))
roles := []Role{}
err := s.db.Where("name IN ?", names).Find(&roles).Error
switch err {
case nil:
return roles, nil
case gorm.ErrRecordNotFound:
return nil, ErrEntryNotFound
default:
return nil, err
}
}
func (s *Storage) UpdateRole(role *Role) error {
defer util.Untrace(util.Trace(&log.Logger))
return s.db.Save(role).Error
}
func (s *Storage) DeleteRoleByName(name string) error {
// Prevent deletion of built-in roles
if sliceutils.Contains(
sliceutils.Map(allDefaultRoles, func(t *Role) string { return t.Name }),
name,
) {
return ErrNotAllowed
}
defer util.Untrace(util.Trace(&log.Logger))
return s.db.Where(&Role{Name: name, IsBuiltIn: false}).Delete(&Role{}).Error
}