diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 199b639..f7e8031 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,28 +1,50 @@ # Contribution Guide -Thank you for your interest in contributing to Linstrom! All contributors are welcome, regardless of their level of experience. +Thank you for your interest in contributing to Linstrom! +All contributors are welcome, regardless of their level of experience. + +Unless it's primarely AI generated. Then you're going to be blocked immediately +and publicly shamed. ## Bug Reports -Use the [bug report issue template](https://git.mstar.dev/mstar/linstrom/issues/new?template=bug-report.md) to file a bug report. Please include a detailed description of the events leading up to the problem, your system configuration, and the program logs. If you're able to reproduce the bug reliably, attaching a debugger to the program, triggering it, and uploading the results would be very helpful. +Use the [bug report issue template](https://git.mstar.dev/mstar/linstrom/issues/new?template=bug-report.md) +to file a bug report. Please include a detailed description of the events +leading up to the problem, your system configuration, and the program logs. +If you're able to reproduce the bug reliably, attaching a debugger to the program, +triggering it, and uploading the results would be very helpful. -This section *should* tell you how to find your logs, attach the debugger, and do whatever else you need for a detailed bug report. But nobody filled it out. Attach a picture of Goatse to your bug reports until we fix this. +This section _should_ tell you how to find your logs, attach the debugger, +and do whatever else you need for a detailed bug report. But nobody filled it out. +Attach a picture of Goatse to your bug reports until we fix this. ## Feature Requests -Use the [feature request issue template](https://git.mstar.dev/mstar/linstrom/issues/new?template=suggestion.md) to suggest new features. Please note that we haven't replaced this placeholder text with the actual criteria we're looking for, which means you should spam us with utterly nonsensical ideas. +Use the [feature request issue template](https://git.mstar.dev/mstar/linstrom/issues/new?template=suggestion.md) +to suggest new features. Please note that we haven't replaced this placeholder +text with the actual criteria we're looking for, which means +you should spam us with utterly nonsensical ideas. ## Submitting Translations -Translation files are part of the project codebase, so you'll have to fork the repository and file a pull request (see [Contributing Code](CONTRIBUTING.md#contributing-code) below). You don't need any programming knowledge to edit the translation files, though. +Translation files are part of the project codebase, so you'll have to +fork the repository and file a pull request +(see [Contributing Code](CONTRIBUTING.md#contributing-code) below). +You don't need any programming knowledge to edit the translation files, though. -This should have been removed and replaced with a quick overview of where the files are and what translators need to do in order to edit them. Nobody did that, so think of this as a free pass to scream profanities into the issue tracker in your native language. +This should have been removed and replaced with a quick overview +of where the files are and what translators need to do in order to edit them. +Nobody did that, so think of this as a free pass to scream profanities +into the issue tracker in your native language. ## Contributing Code ### Forking -If you'd like to have a go at writing some code for Linstrom, fork the repository, then create a new branch with a name that describes the changes you're making. If there's a [relevant issue](https://git.mstar.dev/mstar/linstrom/issues), include the issue number in the branch name: +If you'd like to have a go at writing some code for Linstrom, +fork the repository, then create a new branch with a name that +describes the changes you're making. If there's a [relevant issue](https://git.mstar.dev/mstar/linstrom/issues), +include the issue number in the branch name: ```sh git checkout -b 1337-prevent-computer-from-exploding @@ -30,10 +52,14 @@ git checkout -b 1337-prevent-computer-from-exploding ### Development Environment -The project utilises Go (version 1.23+) for almost everything and node/npm (version 18+) for building the frontend. -The go side also makes use of `go generate` for multiple things, primarely in the storage module +The project utilises Go (version 1.23+) for almost everything and +node/npm (version 18+) for building the frontend. +The go side also makes use of `go generate` for multiple things, +primarely in the storage module -We don't have a development environment, because nobody bothered to fill this out. Please add a new build system to the project specifically for your modifications. Bonus points if it's entirely nonsensical, like `npm` in a C project. +We don't have a development environment, because nobody bothered to fill this out. +Please add a new build system to the project specifically for your modifications. +Bonus points if it's entirely nonsensical, like `npm` in a C project. ### Code Style @@ -43,15 +69,18 @@ For anything node: uhhh, yolo, idk yet ### Pull Requests -Once your modifications are complete, you'll want to fetch the latest changes from this repository, rebase your branch, and publish your changes: +Once your modifications are complete, you'll want to fetch the latest changes +from this repository, rebase your branch, and publish your changes: ```sh git remote add upstream https://git.mstar.dev/mstar/linstrom.git git checkout main git pull upstream main git checkout 1337-prevent-computer-from-exploding -git rebase master +git rebase main git push --set-upstream origin 1337-prevent-computer-from-exploding ``` -Finally, you can [create a pull request](https://git.mstar.dev/mstar/linstrom/pulls). It might not get approved, or you might have to make some additional changes to your code - but don't give up! +Finally, you can [create a pull request](https://git.mstar.dev/mstar/linstrom/pulls). +It might not get approved, or you might have to make some additional changes +to your code - but don't give up! diff --git a/README.md b/README.md index 8513be3..7333691 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ -Linstrom is a new social media server with the focus on providing users, moderators and admins alike -simple, yet powerful features to moderate the experience. +# Linstrom + +Linstrom is a new social media server with the focus on providing +users, moderators and admins alike simple, yet powerful features +to moderate the experience. ## Federation @@ -20,3 +23,18 @@ And they all have different woes with their software. I want to try and make a server that combines the strengths of various project's tooling while also hopefully solving some of the weaknesses. + +## Permission system + +All permissions operate on a role based system, similar to what Discord offers. +Accounts in power (admins and moderators) are only able to manipulate other accounts +via this role system. This prevents moderators from directly manipulating accounts, +such as changing their username, info fields or otherwise, while still providing +ample control over the activities these accounts can perform. + +These same roles, while most powerful for local accounts, also apply to remote accounts +as much as realisticly possible. Preventing a login on a remote server for example +might not be possible, but blocking all inbound traffic from that account is. + +However, roles are not only for moderators and admins to use. +Normal accounts can also make use of them, all be it in a more limited way. diff --git a/auth/auth.go b/auth/auth.go new file mode 100644 index 0000000..ffe194b --- /dev/null +++ b/auth/auth.go @@ -0,0 +1,17 @@ +package auth + +import ( + "git.mstar.dev/mstar/linstrom/storage" + "gorm.io/gorm" +) + +type Authentication struct { + // For when in-depth access is needed + db *gorm.DB + // Primary method to acquire account data + store *storage.Storage +} + +func NewAuth(db *gorm.DB, store *storage.Storage) *Authentication { + return &Authentication{db, store} +} diff --git a/auth/checks.go b/auth/checks.go new file mode 100644 index 0000000..05deb12 --- /dev/null +++ b/auth/checks.go @@ -0,0 +1,118 @@ +package auth + +import ( + "git.mstar.dev/mstar/goutils/sliceutils" + "git.mstar.dev/mstar/linstrom/storage" + "github.com/rs/zerolog/log" +) + +// Can actorId read the account with targetId? +func (a *Authentication) CanReadAccount(actorId *string, targetId string) bool { + targetAccount, err := a.store.FindAccountById(targetId) + if err != nil { + if err == storage.ErrEntryNotFound { + return true + } + log.Error(). + Err(err). + Str("account-id", targetId). + Msg("Failed to receive account for permission check") + return false + } + if actorId == nil { + // TODO: Decide if roles should have a field to declare an account as follow only/hidden + // and then check for that flag here + return true + } + roles, err := a.store.FindRolesByNames(targetAccount.Roles) + if err != nil { + log.Error(). + Err(err). + Strs("role-names", targetAccount.Roles). + Msg("Failed to get roles for target account") + return false + } + combined := storage.CollapseRolesIntoOne(roles...) + if sliceutils.Contains(combined.BlockedUsers, *actorId) { + return false + } + return true +} + +// Can actorId edit the account with targetId? +// If actorId is nil, it is assumed to be an anonymous user trying to edit the target account +// if targetId is nil, it is assumed that the actor is editing themselves +func (a *Authentication) CanEditAccount(actorId *string, targetId *string) bool { + // FIXME: This entire function feels wrong, idk + // Only the owner of an account should be able to edit said account's data + // But how do moderation actions play with this? Do they count as edit or as something separate? + if actorId == nil { + return false + } + if targetId == nil { + targetId = actorId + } + targetAccount, err := a.store.FindAccountById(*targetId) + if err != nil { + if err != storage.ErrEntryNotFound { + log.Error(). + Err(err). + Str("target-id", *targetId). + Msg("Failed to receive account for permission checks") + } + return false + } + if targetId == actorId { + targetRoles, err := a.store.FindRolesByNames(targetAccount.Roles) + if err != nil { + log.Error(). + Err(err). + Strs("role-names", targetAccount.Roles). + Msg("Failed to get roles from storage") + return false + } + combined := storage.CollapseRolesIntoOne(targetRoles...) + return *combined.CanLogin + } else { + return false + } +} + +// Can actorId delete the account with targetId? +// If actorId is nil, it is assumed to be an anonymous user trying to delete the target account +// if targetId is nil, it is assumed that the actor is deleting themselves +func (a *Authentication) CanDeleteAccount(actorId *string, targetId *string) bool { + if actorId == nil { + return false + } + acc, err := a.store.FindAccountById(*actorId) + if err != nil { + // TODO: Logging + return false + } + roles, err := a.store.FindRolesByNames(acc.Roles) + if err != nil { + // TODO: Logging + return false + } + collapsed := storage.CollapseRolesIntoOne(roles...) + if targetId == nil { + return *collapsed.CanLogin + } else { + return *collapsed.CanDeleteAccounts + } +} + +// Can actorId create a new post at all? +// Specific restrictions regarding the content are not checked +func (a *Authentication) CanCreatePost(actorId string) bool { return true } + +// Ensures that a given post conforms with all roles attached to the author account. +// Returns the conforming note (or nil of it can't be changed to conform) +// and whether the note was changed +func (a *Authentication) EnsureNoteConformsWithRoles(note *storage.Note) (*storage.Note, bool) { + return note, false +} + +// Does the given note conform with the roles attached to the author account? +func (a *Authentication) DoesNoteConform(note *storage.Note) bool { return true } diff --git a/storage-new/gormLogger.go b/storage-new/gormLogger.go new file mode 100644 index 0000000..ed8f976 --- /dev/null +++ b/storage-new/gormLogger.go @@ -0,0 +1,64 @@ +package storage + +import ( + "context" + "time" + + "github.com/rs/zerolog" + "gorm.io/gorm/logger" +) + +type gormLogger struct { + logger zerolog.Logger +} + +func newGormLogger(zerologger zerolog.Logger) *gormLogger { + return &gormLogger{zerologger} +} + +func (g *gormLogger) LogMode(newLevel logger.LogLevel) logger.Interface { + switch newLevel { + case logger.Error: + g.logger = g.logger.Level(zerolog.ErrorLevel) + case logger.Warn: + g.logger = g.logger.Level(zerolog.WarnLevel) + case logger.Info: + g.logger = g.logger.Level(zerolog.InfoLevel) + case logger.Silent: + g.logger = g.logger.Level(zerolog.Disabled) + } + return g +} +func (g *gormLogger) Info(ctx context.Context, format string, args ...interface{}) { + g.logger.Info().Ctx(ctx).Msgf(format, args...) +} +func (g *gormLogger) Warn(ctx context.Context, format string, args ...interface{}) { + g.logger.Warn().Ctx(ctx).Msgf(format, args...) +} +func (g *gormLogger) Error(ctx context.Context, format string, args ...interface{}) { + g.logger.Error().Ctx(ctx).Msgf(format, args...) +} + +func (g *gormLogger) Trace( + ctx context.Context, + begin time.Time, + fc func() (sql string, rowsAffected int64), + err error, +) { + sql, rowsAffected := fc() + g.logger.Trace(). + Ctx(ctx). + Time("gorm-begin", begin). + Err(err). + Str("gorm-query", sql). + Int64("gorm-rows-affected", rowsAffected). + Send() +} + +func (g *gormLogger) OverwriteLoggingLevel(new zerolog.Level) { + g.logger = g.logger.Level(new) +} + +func (g *gormLogger) OverwriteLogger(new zerolog.Logger) { + g.logger = new +} diff --git a/storage-new/migrations.go b/storage-new/migrations.go new file mode 100644 index 0000000..fb6d7a7 --- /dev/null +++ b/storage-new/migrations.go @@ -0,0 +1,102 @@ +package storage + +import ( + "fmt" + "strings" + + "git.mstar.dev/mstar/goutils/sliceutils" + "gorm.io/gorm" + + "git.mstar.dev/mstar/linstrom/storage-new/models" +) + +func migrateTypes(db *gorm.DB) error { + if err := db.AutoMigrate( + &models.Emote{}, + &models.MediaMetadata{}, + &models.Note{}, + &models.NoteToAttachment{}, + &models.NoteToEmote{}, + &models.NoteToPing{}, + &models.NoteTag{}, + &models.Reaction{}, + &models.RemoteServer{}, + &models.Role{}, + &models.User{}, + &models.UserAuthMethod{}, + &models.UserBeings{}, + &models.UserInfoField{}, + &models.UserRelation{}, + &models.UserRemoteLinks{}, + &models.UserRole{}, + &models.UserTag{}, + ); err != nil { + return fmt.Errorf("storage: automigrate structs: %w", err) + } + return nil +} + +// Ensure the being enum exists for the user +func createBeingType(db *gorm.DB) error { + return migrateEnum( + db, + "being_type", + sliceutils.Map(models.AllBeings, func(t models.BeingType) string { return string(t) }), + ) +} + +func createAccountRelationType(db *gorm.DB) error { + return migrateEnum( + db, + "relation_type", + sliceutils.Map( + models.AllRelations, + func(t models.RelationType) string { return string(t) }, + ), + ) +} + +func createAccountAuthMethodType(db *gorm.DB) error { + return migrateEnum( + db, + "auth_method_type", + sliceutils.Map( + models.AllAuthMethods, + func(t models.AuthenticationMethodType) string { return string(t) }, + ), + ) +} + +func createRemoteServerSoftwareType(db *gorm.DB) error { + return migrateEnum( + db, + "server_software_type", + sliceutils.Map( + models.AllServerSoftwareTypes, + func(t models.ServerSoftwareType) string { return string(t) }, + ), + ) +} + +// Helper function for ensuring the existence of an enum with the given values +func migrateEnum(db *gorm.DB, name string, values []string) error { + if err := db.Exec("DROP TYPE IF EXISTS " + name).Error; err != nil { + return fmt.Errorf("storage: migrate %s: %w", name, err) + } + queryBuilder := strings.Builder{} + queryBuilder.WriteString("CREATE TYPE") + queryBuilder.WriteString(name) + queryBuilder.WriteString("AS ENUM (") + blen := len(values) + for i, btype := range values { + queryBuilder.WriteString("'" + string(btype) + "'") + // Append comma everywhere except last entry + if i+1 < blen { + queryBuilder.WriteString(",") + } + } + if err := db.Exec(queryBuilder.String()).Error; err != nil { + return fmt.Errorf("storage: migrate %s: %w", name, err) + } + return nil +} diff --git a/storage-new/models/Emote.go b/storage-new/models/Emote.go new file mode 100644 index 0000000..3e28ff9 --- /dev/null +++ b/storage-new/models/Emote.go @@ -0,0 +1,12 @@ +package models + +import "gorm.io/gorm" + +type Emote struct { + gorm.Model + // Metadata MediaMetadata // `gorm:"foreignKey:MetadataId"` + MetadataId string + Name string + // Server RemoteServer // `gorm:"foreignKey:ServerId;references:ID"` + ServerId uint +} diff --git a/storage-new/models/MediaMetadata.go b/storage-new/models/MediaMetadata.go new file mode 100644 index 0000000..a411c81 --- /dev/null +++ b/storage-new/models/MediaMetadata.go @@ -0,0 +1,29 @@ +package models + +import ( + "time" + + "gorm.io/gorm" +) + +type MediaMetadata struct { + ID string `gorm:"primarykey"` // The unique ID of this media file + CreatedAt time.Time // When this entry was created + UpdatedAt time.Time // When this entry was last updated + // When this entry was deleted (for soft deletions) + // Soft delete means that this entry still exists in the db, but gorm won't include it anymore unless specifically told to + // If not null, this entry is marked as deleted + DeletedAt gorm.DeletedAt `gorm:"index"` + OwnedBy string // Account id this media belongs to + Remote bool // whether the attachment is a remote one + // Where the media is stored. Url + Location string + Type string // What media type this is following mime types, eg image/png + // Name of the file + // Could be .png, .webp for example. Or the name the file was uploaded with + Name string + // Alternative description of the media file's content + AltText string + // Whether the media is to be blurred by default + Blurred bool +} diff --git a/storage-new/models/Note.go b/storage-new/models/Note.go new file mode 100644 index 0000000..a236244 --- /dev/null +++ b/storage-new/models/Note.go @@ -0,0 +1,33 @@ +package models + +import ( + "time" + + "gorm.io/gorm" +) + +// Data defined in extra structs: +// - Attachments: models.NoteToAttachment +// - Emotes: models.NoteToEmote +// - Pings: models.NoteToPing +// - Tags: models.NoteTag +type Note struct { + ID string `gorm:"primarykey"` // Make ID a string (uuid) for other implementations + CreatedAt time.Time // When this entry was created + UpdatedAt time.Time // When this entry was last updated + // When this entry was deleted (for soft deletions) + // Soft delete means that this entry still exists in the db, but gorm won't include it anymore unless specifically told to + // If not null, this entry is marked as deleted + DeletedAt gorm.DeletedAt `gorm:"index"` + // Creator Account // `gorm:"foreignKey:CreatorId;references:ID"` // Account that created the post + CreatorId string + Remote bool // Whether the note is originally a remote one and just "cached" + // Raw content of the note. So without additional formatting applied + // Might already have formatting applied beforehand from the origin server + RawContent string + ContentWarning *string // Content warnings of the note, if it contains any + RepliesTo *string // Url of the message this replies to + Quotes *string // url of the message this note quotes + AccessLevel NoteAccessLevel // Where to send this message to (public, home, followers, dm) + OriginServer string // Url of the origin server. Also the primary key for those +} diff --git a/storage-new/models/NoteAccessLevelType.go b/storage-new/models/NoteAccessLevelType.go new file mode 100644 index 0000000..15a725f --- /dev/null +++ b/storage-new/models/NoteAccessLevelType.go @@ -0,0 +1,28 @@ +package models + +import "database/sql/driver" + +type NoteAccessLevel uint8 + +const ( + // The note is intended for the public + NOTE_TARGET_PUBLIC NoteAccessLevel = 0 + // The note is intended only for the home screen + // not really any idea what the difference is compared to public + // Maybe home notes don't show up on the server feed but still for everyone's home feed if it reaches them via follow or boost + NOTE_TARGET_HOME NoteAccessLevel = 1 << iota + // The note is intended only for followers + NOTE_TARGET_FOLLOWERS + // The note is intended only for a DM to one or more targets + NOTE_TARGET_DM +) + +// Converts the NoteTarget value into a type the DB can use +func (n *NoteAccessLevel) Value() (driver.Value, error) { + return n, nil +} + +func (ct *NoteAccessLevel) Scan(value any) error { + *ct = NoteAccessLevel(value.(uint8)) + return nil +} diff --git a/storage-new/models/NoteAttachments.go b/storage-new/models/NoteAttachments.go new file mode 100644 index 0000000..e29585f --- /dev/null +++ b/storage-new/models/NoteAttachments.go @@ -0,0 +1,6 @@ +package models + +type NoteToAttachment struct { + UserId string + AttachmentId string +} diff --git a/storage-new/models/NoteEmotes.go b/storage-new/models/NoteEmotes.go new file mode 100644 index 0000000..124f1dc --- /dev/null +++ b/storage-new/models/NoteEmotes.go @@ -0,0 +1,6 @@ +package models + +type NoteToEmote struct { + UserId string + EmoteId string +} diff --git a/storage-new/models/NotePings.go b/storage-new/models/NotePings.go new file mode 100644 index 0000000..5e7e90b --- /dev/null +++ b/storage-new/models/NotePings.go @@ -0,0 +1,6 @@ +package models + +type NoteToPing struct { + UserId string + PingTargetId string +} diff --git a/storage-new/models/NoteTags.go b/storage-new/models/NoteTags.go new file mode 100644 index 0000000..31ee396 --- /dev/null +++ b/storage-new/models/NoteTags.go @@ -0,0 +1,6 @@ +package models + +type NoteTag struct { + UserId string + Tag string +} diff --git a/storage-new/models/Reaction.go b/storage-new/models/Reaction.go new file mode 100644 index 0000000..43633cb --- /dev/null +++ b/storage-new/models/Reaction.go @@ -0,0 +1,10 @@ +package models + +import "gorm.io/gorm" + +type Reaction struct { + gorm.Model + NoteId string + ReactorId string + EmoteId uint +} diff --git a/storage-new/models/RemoteServer.go b/storage-new/models/RemoteServer.go new file mode 100644 index 0000000..af063ed --- /dev/null +++ b/storage-new/models/RemoteServer.go @@ -0,0 +1,12 @@ +package models + +import "gorm.io/gorm" + +type RemoteServer struct { + gorm.Model + ServerType ServerSoftwareType // What software the server is running. Useful for formatting + Domain string // `gorm:"primaryKey"` // Domain the server exists under. Additional primary key + Name string // What the server wants to be known as (usually same as url) + Icon string // ID of a media file + IsSelf bool // Whether this server is yours truly +} diff --git a/storage-new/models/RemoteServerSoftwareType.go b/storage-new/models/RemoteServerSoftwareType.go new file mode 100644 index 0000000..f0834f2 --- /dev/null +++ b/storage-new/models/RemoteServerSoftwareType.go @@ -0,0 +1,37 @@ +package models + +import ( + "database/sql/driver" +) + +type ServerSoftwareType string + +const ( + // Includes forks like glitch-soc, etc + ServerSoftwareMastodon = ServerSoftwareType("Mastodon") + // Includes forks like Ice Shrimp, Sharkey, Cutiekey, etc + ServerSoftwareMisskey = ServerSoftwareType("Misskey") + // Includes Akkoma + ServerSoftwarePlemora = ServerSoftwareType("Plemora") + // Wafrn is a new entry + ServerSoftwareWafrn = ServerSoftwareType("Wafrn") + // And of course, yours truly + ServerSoftwareLinstrom = ServerSoftwareType("Linstrom") +) + +var AllServerSoftwareTypes = []ServerSoftwareType{ + ServerSoftwareMastodon, + ServerSoftwareMisskey, + ServerSoftwarePlemora, + ServerSoftwareWafrn, + ServerSoftwareLinstrom, +} + +func (r *ServerSoftwareType) Value() (driver.Value, error) { + return r, nil +} + +func (ct *ServerSoftwareType) Scan(value any) error { + *ct = ServerSoftwareType(value.(string)) + return nil +} diff --git a/storage-new/models/Role.go b/storage-new/models/Role.go new file mode 100644 index 0000000..8a60476 --- /dev/null +++ b/storage-new/models/Role.go @@ -0,0 +1,182 @@ +package models + +import ( + "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 + +//go:generate go build -o RolesGenerator ../cmd/RolesGenerator/main.go +//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 + +// 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 +// Blocks are part of the user relations +type Role struct { + // TODO: More control options + // Extend upon whatever Masto, Akkoma and Misskey have + // Lots of details please + + // --- Role metadata --- + + // Include full db model stuff + gorm.Model + + // Name of the role + Name string `gorm:"primaryKey;unique"` + + // 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 + // 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 + + CanSubmitReports *bool // Local & remote + // If disabled, an account can no longer be interacted with. The owner can no longer change anything about it + // And the UI will show a notice (and maybe include that info in the AP data too) + // Only moderators and admins will be able to edit the account's roles + CanLogin *bool // Local + + CanMentionOthers *bool // Local & remote + HasMentionCountLimit *bool // Local & remote + MentionLimit *uint32 // Local & remote + + // 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 + + WithholdNotesForManualApproval *bool // Local & remote + WithholdNotesBasedOnRegex *bool // Local & remote + WithholdNotesRegexes []string `gorm:"type:bytes;serializer:gob"` // Local & remote + + // --- 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 + CanDeleteAccounts *bool // Local +} + +/* +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 +*/ diff --git a/storage-new/models/RolesDefaults.go b/storage-new/models/RolesDefaults.go new file mode 100644 index 0000000..fc1b746 --- /dev/null +++ b/storage-new/models/RolesDefaults.go @@ -0,0 +1,240 @@ +package models + +import ( + "math" + + "git.mstar.dev/mstar/goutils/other" +) + +// Default role every user has. Defines sane defaults for a normal user +// Will get overwritten by just about every other role due to every other role having higher priority +var DefaultUserRole = Role{ + Name: "Default", + Priority: 0, + IsUserRole: false, + IsBuiltIn: true, + + CanSendMedia: other.IntoPointer(true), + CanSendCustomEmotes: other.IntoPointer(true), + CanSendCustomReactions: other.IntoPointer(true), + CanSendPublicNotes: other.IntoPointer(true), + CanSendLocalNotes: other.IntoPointer(true), + CanSendFollowerOnlyNotes: other.IntoPointer(true), + CanSendPrivateNotes: other.IntoPointer(true), + CanSendReplies: other.IntoPointer(true), + CanQuote: other.IntoPointer(true), + CanBoost: other.IntoPointer(true), + CanIncludeLinks: other.IntoPointer(true), + CanIncludeSurvey: other.IntoPointer(true), + CanFederateFedi: other.IntoPointer(true), + CanFederateBsky: other.IntoPointer(true), + + CanChangeDisplayName: other.IntoPointer(true), + + CanSubmitReports: other.IntoPointer(true), + CanLogin: other.IntoPointer(true), + + CanMentionOthers: other.IntoPointer(true), + HasMentionCountLimit: other.IntoPointer(false), + MentionLimit: other.IntoPointer( + uint32(math.MaxUint32), + ), // Set this to max, even if not used due to *HasMentionCountLimit == false + + AutoNsfwMedia: other.IntoPointer(false), + AutoCwPosts: other.IntoPointer(false), + AutoCwPostsText: nil, + WithholdNotesForManualApproval: other.IntoPointer(false), + ScanCreatedPublicNotes: other.IntoPointer(false), + ScanCreatedLocalNotes: other.IntoPointer(false), + ScanCreatedFollowerOnlyNotes: other.IntoPointer(false), + ScanCreatedPrivateNotes: other.IntoPointer(false), + DisallowInteractionsWith: []string{}, + + FullAdmin: other.IntoPointer(false), + CanAffectOtherAdmins: other.IntoPointer(false), + CanDeleteNotes: other.IntoPointer(false), + CanConfirmWithheldNotes: other.IntoPointer(false), + CanAssignRoles: other.IntoPointer(false), + CanSupressInteractionsBetweenUsers: other.IntoPointer(false), + CanOverwriteDisplayNames: other.IntoPointer(false), + CanManageCustomEmotes: other.IntoPointer(false), + CanViewDeletedNotes: other.IntoPointer(false), + CanRecoverDeletedNotes: other.IntoPointer(false), + CanManageAvatarDecorations: other.IntoPointer(false), + CanManageAds: other.IntoPointer(false), + CanSendAnnouncements: other.IntoPointer(false), +} + +// Role providing maximum permissions +var FullAdminRole = Role{ + Name: "fullAdmin", + Priority: math.MaxUint32, + IsUserRole: false, + IsBuiltIn: true, + + CanSendMedia: other.IntoPointer(true), + CanSendCustomEmotes: other.IntoPointer(true), + CanSendCustomReactions: other.IntoPointer(true), + CanSendPublicNotes: other.IntoPointer(true), + CanSendLocalNotes: other.IntoPointer(true), + CanSendFollowerOnlyNotes: other.IntoPointer(true), + CanSendPrivateNotes: other.IntoPointer(true), + CanQuote: other.IntoPointer(true), + CanBoost: other.IntoPointer(true), + CanIncludeLinks: other.IntoPointer(true), + CanIncludeSurvey: other.IntoPointer(true), + + CanChangeDisplayName: other.IntoPointer(true), + + CanSubmitReports: other.IntoPointer(true), + CanLogin: other.IntoPointer(true), + + CanMentionOthers: other.IntoPointer(true), + HasMentionCountLimit: other.IntoPointer(false), + MentionLimit: other.IntoPointer( + uint32(math.MaxUint32), + ), // Set this to max, even if not used due to *HasMentionCountLimit == false + + AutoNsfwMedia: other.IntoPointer(false), + AutoCwPosts: other.IntoPointer(false), + AutoCwPostsText: nil, + WithholdNotesForManualApproval: other.IntoPointer(false), + ScanCreatedPublicNotes: other.IntoPointer(false), + ScanCreatedLocalNotes: other.IntoPointer(false), + ScanCreatedFollowerOnlyNotes: other.IntoPointer(false), + ScanCreatedPrivateNotes: other.IntoPointer(false), + DisallowInteractionsWith: []string{}, + + FullAdmin: other.IntoPointer(true), + CanAffectOtherAdmins: other.IntoPointer(true), + CanDeleteNotes: other.IntoPointer(true), + CanConfirmWithheldNotes: other.IntoPointer(true), + CanAssignRoles: other.IntoPointer(true), + CanSupressInteractionsBetweenUsers: other.IntoPointer(true), + CanOverwriteDisplayNames: other.IntoPointer(true), + CanManageCustomEmotes: other.IntoPointer(true), + CanViewDeletedNotes: other.IntoPointer(true), + CanRecoverDeletedNotes: other.IntoPointer(true), + CanManageAvatarDecorations: other.IntoPointer(true), + CanManageAds: other.IntoPointer(true), + CanSendAnnouncements: other.IntoPointer(true), +} + +// Role for totally freezing an account, blocking all activity from it +var AccountFreezeRole = Role{ + Name: "accountFreeze", + Priority: math.MaxUint32 - 1, + IsUserRole: false, + IsBuiltIn: true, + + CanSendMedia: other.IntoPointer(false), + CanSendCustomEmotes: other.IntoPointer(false), + CanSendCustomReactions: other.IntoPointer(false), + CanSendPublicNotes: other.IntoPointer(false), + CanSendLocalNotes: other.IntoPointer(false), + CanSendFollowerOnlyNotes: other.IntoPointer(false), + CanSendPrivateNotes: other.IntoPointer(false), + CanSendReplies: other.IntoPointer(false), + CanQuote: other.IntoPointer(false), + CanBoost: other.IntoPointer(false), + CanIncludeLinks: other.IntoPointer(false), + CanIncludeSurvey: other.IntoPointer(false), + CanFederateBsky: other.IntoPointer(false), + CanFederateFedi: other.IntoPointer(false), + + CanChangeDisplayName: other.IntoPointer(false), + + CanSubmitReports: other.IntoPointer(false), + CanLogin: other.IntoPointer(false), + + CanMentionOthers: other.IntoPointer(false), + HasMentionCountLimit: other.IntoPointer(false), + MentionLimit: other.IntoPointer( + uint32(math.MaxUint32), + ), // Set this to max, even if not used due to *HasMentionCountLimit == false + + AutoNsfwMedia: other.IntoPointer(true), + AutoCwPosts: other.IntoPointer(false), + AutoCwPostsText: other.IntoPointer("Account frozen"), + WithholdNotesForManualApproval: other.IntoPointer(true), + ScanCreatedPublicNotes: other.IntoPointer(false), + ScanCreatedLocalNotes: other.IntoPointer(false), + ScanCreatedFollowerOnlyNotes: other.IntoPointer(false), + ScanCreatedPrivateNotes: other.IntoPointer(false), + DisallowInteractionsWith: []string{}, + + FullAdmin: other.IntoPointer(false), + CanAffectOtherAdmins: other.IntoPointer(false), + CanDeleteNotes: other.IntoPointer(false), + CanConfirmWithheldNotes: other.IntoPointer(false), + CanAssignRoles: other.IntoPointer(false), + CanSupressInteractionsBetweenUsers: other.IntoPointer(false), + CanOverwriteDisplayNames: other.IntoPointer(false), + CanManageCustomEmotes: other.IntoPointer(false), + CanViewDeletedNotes: other.IntoPointer(false), + CanRecoverDeletedNotes: other.IntoPointer(false), + CanManageAvatarDecorations: other.IntoPointer(false), + CanManageAds: other.IntoPointer(false), + CanSendAnnouncements: other.IntoPointer(false), +} + +var ServerActorRole = Role{ + Name: "ServerActor", + Priority: math.MaxUint32, + IsUserRole: true, + IsBuiltIn: true, + + CanSendMedia: other.IntoPointer(true), + CanSendCustomEmotes: other.IntoPointer(true), + CanSendCustomReactions: other.IntoPointer(true), + CanSendPublicNotes: other.IntoPointer(true), + CanSendLocalNotes: other.IntoPointer(true), + CanSendFollowerOnlyNotes: other.IntoPointer(true), + CanSendPrivateNotes: other.IntoPointer(true), + CanQuote: other.IntoPointer(true), + CanBoost: other.IntoPointer(true), + CanIncludeLinks: other.IntoPointer(true), + CanIncludeSurvey: other.IntoPointer(true), + + CanChangeDisplayName: other.IntoPointer(true), + + CanSubmitReports: other.IntoPointer(true), + CanLogin: other.IntoPointer(true), + + CanMentionOthers: other.IntoPointer(true), + HasMentionCountLimit: other.IntoPointer(false), + MentionLimit: other.IntoPointer( + uint32(math.MaxUint32), + ), // Set this to max, even if not used due to *HasMentionCountLimit == false + + AutoNsfwMedia: other.IntoPointer(false), + AutoCwPosts: other.IntoPointer(false), + AutoCwPostsText: nil, + WithholdNotesForManualApproval: other.IntoPointer(false), + ScanCreatedPublicNotes: other.IntoPointer(false), + ScanCreatedLocalNotes: other.IntoPointer(false), + ScanCreatedFollowerOnlyNotes: other.IntoPointer(false), + ScanCreatedPrivateNotes: other.IntoPointer(false), + DisallowInteractionsWith: []string{}, + + FullAdmin: other.IntoPointer(true), + CanAffectOtherAdmins: other.IntoPointer(true), + CanDeleteNotes: other.IntoPointer(true), + CanConfirmWithheldNotes: other.IntoPointer(true), + CanAssignRoles: other.IntoPointer(true), + CanSupressInteractionsBetweenUsers: other.IntoPointer(true), + CanOverwriteDisplayNames: other.IntoPointer(true), + CanManageCustomEmotes: other.IntoPointer(true), + CanViewDeletedNotes: other.IntoPointer(true), + CanRecoverDeletedNotes: other.IntoPointer(true), + CanManageAvatarDecorations: other.IntoPointer(true), + CanManageAds: other.IntoPointer(true), + CanSendAnnouncements: other.IntoPointer(true), +} + +var allDefaultRoles = []*Role{ + &DefaultUserRole, + &FullAdminRole, + &AccountFreezeRole, + &ServerActorRole, +} diff --git a/storage-new/models/User.go b/storage-new/models/User.go new file mode 100644 index 0000000..80853a9 --- /dev/null +++ b/storage-new/models/User.go @@ -0,0 +1,53 @@ +package models + +import ( + "time" + + "gorm.io/gorm" +) + +// Data stored in external types: +// - Custom info fields +// - Being types +// - Tags +// - Relations +// - Pronouns +// - Roles +// - AP remote links +// - Auth methods and tokens (hashed pw, totp key, passkey id) +type User struct { + ID string `gorm:"primarykey"` // ID is a uuid for this account + // Username of the user (eg "max" if the full username is @max@example.com) + // Assume unchangable (once set by a user) to be kind to other implementations + // Would be an easy avenue to fuck with them though + Username string `gorm:"unique"` + CreatedAt time.Time // When this entry was created. Automatically set by gorm + // When this account was last updated. Will also be used for refreshing remote accounts. Automatically set by gorm + UpdatedAt time.Time + // When this entry was deleted (for soft deletions) + // Soft delete means that this entry still exists in the db, but gorm won't include it anymore unless specifically told to + // If not null, this entry is marked as deleted + DeletedAt gorm.DeletedAt `gorm:"index"` + // Server RemoteServer // `gorm:"foreignKey:ServerId;references:ID"` // The server this user is from + ServerId uint // Id of the server this user is from, needed for including RemoteServer + DisplayName string // The display name of the user. Can be different from the handle + Description string // The description of a user account + IsBot bool // Whether to mark this account as a script controlled one + Icon string // ID of a media file used as icon + Background *string // ID of a media file used as background image + Banner *string // ID of a media file used as banner + Indexable bool // Whether this account can be found by crawlers + PublicKey []byte // The public key of the account + // Whether this account restricts following + // If true, the owner must approve of a follow request first + RestrictedFollow bool + + Location *string + Birthday *time.Time + + // --- And internal account stuff --- + // Whether the account got verified and is allowed to be active + // For local accounts being active means being allowed to login and perform interactions + // For remote users, if an account is not verified, any interactions it sends are discarded + Verified bool +} diff --git a/storage-new/models/UserAuthentication.go b/storage-new/models/UserAuthentication.go new file mode 100644 index 0000000..9cd0ac8 --- /dev/null +++ b/storage-new/models/UserAuthentication.go @@ -0,0 +1,7 @@ +package models + +type UserAuthMethod struct { + UserId string + AuthMethod AuthenticationMethodType `gorm:"type:auth_method_type"` + Token []byte +} diff --git a/storage-new/models/UserAuthenticationMethod.go b/storage-new/models/UserAuthenticationMethod.go new file mode 100644 index 0000000..73a1552 --- /dev/null +++ b/storage-new/models/UserAuthenticationMethod.go @@ -0,0 +1,26 @@ +package models + +import "database/sql/driver" + +type AuthenticationMethodType string + +const ( + AuthMethodPassword AuthenticationMethodType = "password" + AuthMethodGAuth AuthenticationMethodType = "g-auth" // Google Authenticator / totp + AuthMethodMail AuthenticationMethodType = "mail" + AuthMethodPasskey2fa AuthenticationMethodType = "passkey-2fa" // Passkey used as 2fa factor + AuthMethodPasskey AuthenticationMethodType = "passkey" // Passkey as only auth key +) + +var AllAuthMethods = []AuthenticationMethodType{ + AuthMethodPassword, AuthMethodGAuth, AuthMethodMail, AuthMethodPasskey, AuthMethodPasskey2fa, +} + +func (ct *AuthenticationMethodType) Scan(value any) error { + *ct = AuthenticationMethodType(value.([]byte)) + return nil +} + +func (ct AuthenticationMethodType) Value() (driver.Value, error) { + return string(ct), nil +} diff --git a/storage-new/models/UserBeingType.go b/storage-new/models/UserBeingType.go new file mode 100644 index 0000000..de699ba --- /dev/null +++ b/storage-new/models/UserBeingType.go @@ -0,0 +1,27 @@ +package models + +import ( + "database/sql/driver" +) + +type BeingType string + +const ( + BEING_HUMAN = BeingType("human") + BEING_CAT = BeingType("cat") + BEING_FOX = BeingType("fox") + BEING_DOG = BeingType("dog") + BEING_ROBOT = BeingType("robot") + BEING_DOLL = BeingType("doll") +) + +var AllBeings = []BeingType{BEING_HUMAN, BEING_CAT, BEING_FOX, BEING_DOG, BEING_ROBOT, BEING_DOLL} + +func (ct *BeingType) Scan(value any) error { + *ct = BeingType(value.([]byte)) + return nil +} + +func (ct BeingType) Value() (driver.Value, error) { + return string(ct), nil +} diff --git a/storage-new/models/UserBeings.go b/storage-new/models/UserBeings.go new file mode 100644 index 0000000..632d76c --- /dev/null +++ b/storage-new/models/UserBeings.go @@ -0,0 +1,6 @@ +package models + +type UserBeings struct { + UserId string + Being BeingType `gorm:"type:being_type"` +} diff --git a/storage-new/models/UserInfoField.go b/storage-new/models/UserInfoField.go new file mode 100644 index 0000000..a259148 --- /dev/null +++ b/storage-new/models/UserInfoField.go @@ -0,0 +1,19 @@ +package models + +import ( + "time" + + "gorm.io/gorm" +) + +type UserInfoField struct { + gorm.Model // Can actually just embed this as is here as those are not something directly exposed :3 + Name string + Value string + LastUrlCheckDate *time.Time // Used if the value is an url to somewhere. Empty if value is not an url + // If the value is an url, this attribute indicates whether Linstrom was able to verify ownership + // of the provided url via the common method of + // "Does the target url contain a rel='me' link to the owner's account" + Confirmed bool + UserId string // Id of account this info field belongs to +} diff --git a/storage-new/models/UserRelation.go b/storage-new/models/UserRelation.go new file mode 100644 index 0000000..7b599c7 --- /dev/null +++ b/storage-new/models/UserRelation.go @@ -0,0 +1,7 @@ +package models + +type UserRelation struct { + UserId string + TargetUserId string + Relation RelationType `gorm:"type:relation_type"` +} diff --git a/storage-new/models/UserRelationType.go b/storage-new/models/UserRelationType.go new file mode 100644 index 0000000..fef44fb --- /dev/null +++ b/storage-new/models/UserRelationType.go @@ -0,0 +1,31 @@ +package models + +import "database/sql/driver" + +type RelationType string + +const ( + RelationFollow RelationType = "follow" + RelationMute RelationType = "mute" + RelationNoBoosts RelationType = "no-boosts" + RelationBlock RelationType = "block" + RelationPreventFollow RelationType = "prevent-follow" +) + +// var AllBeings = []BeingType{BEING_HUMAN, BEING_CAT, BEING_FOX, BEING_DOG, BEING_ROBOT, BEING_DOLL} +var AllRelations = []RelationType{ + RelationFollow, + RelationMute, + RelationNoBoosts, + RelationBlock, + RelationPreventFollow, +} + +func (ct *RelationType) Scan(value any) error { + *ct = RelationType(value.([]byte)) + return nil +} + +func (ct RelationType) Value() (driver.Value, error) { + return string(ct), nil +} diff --git a/storage-new/models/UserRemote.go b/storage-new/models/UserRemote.go new file mode 100644 index 0000000..a4e24f5 --- /dev/null +++ b/storage-new/models/UserRemote.go @@ -0,0 +1,21 @@ +package models + +import "gorm.io/gorm" + +type UserRemoteLinks struct { + // ---- Section: gorm + // Sets this struct up as a value that an Account may have + gorm.Model + UserId string + + // Just about every link here is optional to accomodate for servers with only minimal accounts + // Minimal being handle, ap link and inbox + ApLink string + ViewLink *string + FollowersLink *string + FollowingLink *string + InboxLink string + OutboxLink *string + FeaturedLink *string + FeaturedTagsLink *string +} diff --git a/storage-new/models/UserRoles.go b/storage-new/models/UserRoles.go new file mode 100644 index 0000000..641c5f0 --- /dev/null +++ b/storage-new/models/UserRoles.go @@ -0,0 +1,6 @@ +package models + +type UserRole struct { + UserId string + RoleId uint +} diff --git a/storage-new/models/UserTags.go b/storage-new/models/UserTags.go new file mode 100644 index 0000000..9093551 --- /dev/null +++ b/storage-new/models/UserTags.go @@ -0,0 +1,6 @@ +package models + +type UserTag struct { + UserId string + Tag string +} diff --git a/storage-new/storage.go b/storage-new/storage.go new file mode 100644 index 0000000..82be054 --- /dev/null +++ b/storage-new/storage.go @@ -0,0 +1 @@ +package storage diff --git a/storage/roles.go b/storage/roles.go index 9bb8a42..2f50d37 100644 --- a/storage/roles.go +++ b/storage/roles.go @@ -1,9 +1,9 @@ package storage import ( - "github.com/rs/zerolog/log" "git.mstar.dev/mstar/goutils/sliceutils" "git.mstar.dev/mstar/linstrom/util" + "github.com/rs/zerolog/log" "gorm.io/gorm" ) @@ -80,7 +80,10 @@ type Role struct { // Internal ids of accounts blocked by this role BlockedUsers []string `gorm:"type:bytes;serializer:gob"` // Local CanSubmitReports *bool // Local & remote - CanLogin *bool // Local + // If disabled, an account can no longer be interacted with. The owner can no longer change anything about it + // And the UI will show a notice (and maybe include that info in the AP data too) + // Only moderators and admins will be able to edit the account's roles + CanLogin *bool // Local CanMentionOthers *bool // Local & remote HasMentionCountLimit *bool // Local & remote @@ -125,6 +128,7 @@ type Role struct { CanManageAvatarDecorations *bool // Local CanManageAds *bool // Local CanSendAnnouncements *bool // Local + CanDeleteAccounts *bool // Local } /* diff --git a/storage/userRestrictionType.go b/storage/userRestrictionType.go deleted file mode 100644 index 543a544..0000000 --- a/storage/userRestrictionType.go +++ /dev/null @@ -1,34 +0,0 @@ -package storage - -type AccountRestriction int64 - -const ( - // Account has no restrictions applied - ACCOUNT_RESTRICTION_NONE = AccountRestriction(0) - // All messages of the account get a content warning applied if none is set - // Warning could be something like "Message auto-CWd by server" - ACCOUNT_RESTRICTION_AUTO_CW = AccountRestriction(1 << iota) - // Disable accessing the account via login or access token - ACCOUNT_RESTRICTION_DISABLE_LOGIN - // Disable sending activities to other servers if the account is local - // Or reject all activities if the account is remote - ACCOUNT_RESTRICTION_NO_FEDERATION - // Disallow sending direct messages from that account - ACCOUNT_RESTRICTION_NO_DMS - // Disallow sending follower only messages from that account - ACCOUNT_RESTRICTION_NO_FOLLOWER_POSTS - // Disable outbound follow requests (restricted account can't send follow requests) - ACCOUNT_RESTRICTION_DISABLE_OUTBOUND_FOLLOWS - // Disable inbound follow requests (all follow requests to that account are automatically rejected) - ACCOUNT_RESTRICTION_DISABLE_INBOUND_FOLLOWS - // Forces all posts by that account to be follower only - ACCOUNT_RESTRICTION_FORCE_FOLLOWERS_ONLY - // Disable all outbound activities of an account. - // Includes sending, updating or deleting own notes - // as well as boosting or reacting to any notes - ACCOUNT_RESTRICTION_DISABLE_ACTIVITIES - // Account can only be viewed while logged in or via verified requests to the AP endpoints - ACCOUNT_RESTRICTIONS_NO_PUBLIC_ACCESS - // Force tag all media the account posts as sensitive - ACCOUNT_RESTRICTIONS_FORCE_MEDIA_SENSITIVE -)