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 index 1787336..ffe194b 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -1,11 +1,17 @@ package auth -import "gorm.io/gorm" +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) *Authentication { - return &Authentication{db} +func NewAuth(db *gorm.DB, store *storage.Storage) *Authentication { + return &Authentication{db, store} } diff --git a/auth/checks.go b/auth/checks.go index 9d927a9..05deb12 100644 --- a/auth/checks.go +++ b/auth/checks.go @@ -1,15 +1,107 @@ package auth -import "git.mstar.dev/mstar/linstrom/storage" +import ( + "git.mstar.dev/mstar/goutils/sliceutils" + "git.mstar.dev/mstar/linstrom/storage" + "github.com/rs/zerolog/log" +) -// Can actorId access the account with targetId? -func (a *Authentication) CanAccessAccount(actorId *string, targetId string) bool { return true } +// 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? -func (a *Authentication) CanEditAccount(actorId *string, targetIt *string) bool { return true } +// 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? -func (a *Authentication) CanDeleteAccount(actorId *string, targetIt *string) bool { return true } +// 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 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 } /*