From 873f52d64f0c29dc43a13f59a3974213a0d1087d Mon Sep 17 00:00:00 2001 From: mStar Date: Thu, 31 Oct 2024 16:53:42 +0100 Subject: [PATCH] More work on the api. Also auth middleware stuff More work on the placeholder functions for the Linstrom API Additionally, started work on a slightly more sophisticated authentication control system And ran `go generate` again --- server/apiLinstrom.go | 76 ++- server/apiLinstromAccounts.go | 41 ++ server/apiLinstromMedia.go | 8 + server/apiLinstromNotes.go | 2 + server/apiLinstromTypes.go | 97 +++- server/apiRouter.go | 3 +- server/middlewareFixPasskeyPerms.go | 4 +- server/middlewares.go | 54 +- server/utils.go | 4 + storage/noteTargets.go | 2 +- ...et_string.go => noteaccesslevel_string.go} | 20 +- storage/roles.go | 9 +- storage/rolesDefaults.go | 78 ++- storage/rolesUtil_generated.go | 539 +++++++++--------- 14 files changed, 637 insertions(+), 300 deletions(-) create mode 100644 server/apiLinstromAccounts.go create mode 100644 server/apiLinstromMedia.go rename storage/{notetarget_string.go => noteaccesslevel_string.go} (50%) diff --git a/server/apiLinstrom.go b/server/apiLinstrom.go index 6355153..353b264 100644 --- a/server/apiLinstrom.go +++ b/server/apiLinstrom.go @@ -12,30 +12,70 @@ func setupLinstromApiRouter() http.Handler { func setupLinstromApiV1Router() http.Handler { router := http.NewServeMux() // Notes - router.HandleFunc("GET /note/{noteId}", linstromGetNote) - router.HandleFunc("POST /note", linstromNewNote) - router.HandleFunc("PUT /note/{noteId}", linstromUpdateNote) - router.HandleFunc("DELETE /note/{noteId}", linstromDeleteNote) + router.HandleFunc("GET /notes/{noteId}", linstromGetNote) + router.HandleFunc("POST /notes", linstromNewNote) + router.HandleFunc("PATCH /notes/{noteId}", linstromUpdateNote) + router.HandleFunc("DELETE /notes/{noteId}", linstromDeleteNote) // Reactions - router.HandleFunc("GET /note/{noteId}/reactions", linstromGetReactions) - router.HandleFunc("POST /note/{noteId}/reactions", linstromAddReaction) - router.HandleFunc("PUT /note/{noteId}/reactions", linstromUpdateReaction) - router.HandleFunc("DELETE /note/{noteId}/reactions", linstromDeleteReaction) + router.HandleFunc("GET /notes/{noteId}/reactions", linstromGetReactions) + router.HandleFunc("POST /notes/{noteId}/reactions", linstromAddReaction) + router.HandleFunc("PATCH /notes/{noteId}/reactions", linstromUpdateReaction) + router.HandleFunc("DELETE /notes/{noteId}/reactions", linstromDeleteReaction) // Boosts - router.HandleFunc("GET /note/{noteId}/boosts", linstromGetBoosts) - router.HandleFunc("POST /note/{noteId}/boosts", linstromAddBoost) - router.HandleFunc("DELETE /note/{noteId}/boosts", linstromRemoveBoost) + router.HandleFunc("GET /notes/{noteId}/boosts", linstromGetBoosts) + router.HandleFunc("POST /notes/{noteId}/boosts", linstromAddBoost) + router.HandleFunc("DELETE /notes/{noteId}/boosts", linstromRemoveBoost) // Quotes - router.HandleFunc("GET /note/{noteId}/quotes", linstromGetQuotes) - router.HandleFunc("POST /note/{noteId}/quotes", linstromAddQuote) + router.HandleFunc("GET /notes/{noteId}/quotes", linstromGetQuotes) + router.HandleFunc("POST /notes/{noteId}/quotes", linstromAddQuote) // Pinning - router.HandleFunc("POST /note/{noteId}/pin", linstromPinNote) - router.HandleFunc("DELETE /note/{noteId}/pin", linstromUnpinNote) + router.HandleFunc("POST /notes/{noteId}/pin", linstromPinNote) + router.HandleFunc("DELETE /notes/{noteId}/pin", linstromUnpinNote) // Reports - router.HandleFunc("POST /note/{noteId}/report", linstromReportNote) - router.HandleFunc("DELETE /note/{noteId}/report", linstromRetractReportNote) + router.HandleFunc("POST /notes/{noteId}/report", linstromReportNote) + router.HandleFunc("DELETE /notes/{noteId}/report", linstromRetractReportNote) // Admin - router.HandleFunc("POST /note/{noteId}/admin/cw", linstromForceCWNote) + router.HandleFunc("POST /notes/{noteId}/admin/cw", linstromForceCWNote) + + // Accounts + // Creating a new account happens either during fetch of a remote one or during registration with a passkey + router.HandleFunc("GET /accounts/{accountId}", linstromGetAccount) + router.HandleFunc("PATCH /accounts/{accountId}", linstromUpdateAccount) + router.HandleFunc("DELETE /accounts/{accountId}", linstromDeleteAccount) + // Follow + router.HandleFunc("GET /accounts/{accountId}/follow", linstromIsFollowingAccount) + router.HandleFunc("POST /accounts/{accountId}/follow", linstromFollowAccount) + router.HandleFunc("DELETE /accounts/{accountId}/follow", linstromUnfollowAccount) + // Block + router.HandleFunc("GET /accounts/{accountId}/block", linstromIsBlockingAccount) + router.HandleFunc("POST /accounts/{accountId}/block", linstromBlockAccount) + router.HandleFunc("DELETE /accounts/{accountId}/block", linstromUnblockAccount) + // Mute + router.HandleFunc("GET /accounts/{accountId}/mute", linstromIsMutedAccount) + router.HandleFunc("POST /accounts/{accountId}/mute", linstromMuteAccount) + router.HandleFunc("DELETE /accounts/{accountId}/mute", linstromUnmuteAccount) + // Report + router.HandleFunc("POST /accounts/{accountId}/reports", linstromReportAccount) + router.HandleFunc("DELETE /accounts/{accountId}/reports", linstromRetractReportAccount) + // Admin + router.HandleFunc("POST /accounts/{accountId}/admin/roles", linstromAdminAddRoleAccount) + router.HandleFunc( + "DELETE /accounts/{accountId}/admin/roles/{roleName}", + linstromAdminRemoveRoleAccount, + ) + router.HandleFunc("POST /accounts/{accountId}/admin/warn", linstromAdminWarnAccount) + + // Roles + router.HandleFunc("GET /roles/{roleId}", linstromGetRole) + router.HandleFunc("POST /roles", linstromCreateRole) + router.HandleFunc("PATCH /roles/{roleId}", linstromUpdateRole) + router.HandleFunc("DELETE /roles/{roleId}", linstromDeleteRole) + + // Media metadata + router.HandleFunc("GET /media/{mediaId}", linstromGetMediaMetadata) + router.HandleFunc("POST /media", linstromNewMediaMetadata) + router.HandleFunc("PATCH /media/{mediaId}", linstromUpdateMediaMetadata) + router.HandleFunc("DELETE /media/{mediaId}", linstromDeleteMediaMetadata) // Event streams router.HandleFunc("/streams", linstromEventStream) diff --git a/server/apiLinstromAccounts.go b/server/apiLinstromAccounts.go new file mode 100644 index 0000000..26e9433 --- /dev/null +++ b/server/apiLinstromAccounts.go @@ -0,0 +1,41 @@ +package server + +import ( + "net/http" + + "github.com/rs/zerolog/hlog" +) + +// No create account. That happens during passkey registration +// and remote accounts are getting created at fetch time +func linstromGetAccount(w http.ResponseWriter, r *http.Request) { + store := StorageFromRequest(r) + log := hlog.FromRequest(r) + +} +func linstromUpdateAccount(w http.ResponseWriter, r *http.Request) {} +func linstromDeleteAccount(w http.ResponseWriter, r *http.Request) {} + +func linstromIsFollowingAccount(w http.ResponseWriter, r *http.Request) {} +func linstromFollowAccount(w http.ResponseWriter, r *http.Request) {} +func linstromUnfollowAccount(w http.ResponseWriter, r *http.Request) {} + +func linstromIsBlockingAccount(w http.ResponseWriter, r *http.Request) {} +func linstromBlockAccount(w http.ResponseWriter, r *http.Request) {} +func linstromUnblockAccount(w http.ResponseWriter, r *http.Request) {} + +func linstromIsMutedAccount(w http.ResponseWriter, r *http.Request) {} +func linstromMuteAccount(w http.ResponseWriter, r *http.Request) {} +func linstromUnmuteAccount(w http.ResponseWriter, r *http.Request) {} + +func linstromReportAccount(w http.ResponseWriter, r *http.Request) {} +func linstromRetractReportAccount(w http.ResponseWriter, r *http.Request) {} + +func linstromAdminAddRoleAccount(w http.ResponseWriter, r *http.Request) {} +func linstromAdminRemoveRoleAccount(w http.ResponseWriter, r *http.Request) {} +func linstromAdminWarnAccount(w http.ResponseWriter, r *http.Request) {} + +func linstromGetRole(w http.ResponseWriter, r *http.Request) {} +func linstromCreateRole(w http.ResponseWriter, r *http.Request) {} +func linstromUpdateRole(w http.ResponseWriter, r *http.Request) {} +func linstromDeleteRole(w http.ResponseWriter, r *http.Request) {} diff --git a/server/apiLinstromMedia.go b/server/apiLinstromMedia.go new file mode 100644 index 0000000..6bfaaf5 --- /dev/null +++ b/server/apiLinstromMedia.go @@ -0,0 +1,8 @@ +package server + +import "net/http" + +func linstromGetMediaMetadata(w http.ResponseWriter, r *http.Request) {} +func linstromNewMediaMetadata(w http.ResponseWriter, r *http.Request) {} +func linstromUpdateMediaMetadata(w http.ResponseWriter, r *http.Request) {} +func linstromDeleteMediaMetadata(w http.ResponseWriter, r *http.Request) {} diff --git a/server/apiLinstromNotes.go b/server/apiLinstromNotes.go index 7864bd7..5fc20ce 100644 --- a/server/apiLinstromNotes.go +++ b/server/apiLinstromNotes.go @@ -9,6 +9,8 @@ func linstromGetNote(w http.ResponseWriter, r *http.Request) { store := StorageFromRequest(r) noteId := NoteIdFromRequest(r) note, err := store.FindNoteById(noteId) + _ = note + _ = err } func linstromUpdateNote(w http.ResponseWriter, r *http.Request) {} func linstromNewNote(w http.ResponseWriter, r *http.Request) {} diff --git a/server/apiLinstromTypes.go b/server/apiLinstromTypes.go index 3e79abc..2416c49 100644 --- a/server/apiLinstromTypes.go +++ b/server/apiLinstromTypes.go @@ -4,6 +4,15 @@ package server import "time" +var ( + _ = linstromNote{} + _ = linstromOriginServer{} + _ = linstromMediaMetadata{} + _ = linstromAccount{} + _ = linstromCustomAccountField{} + _ = linstromRole{} +) + type linstromNote struct { Id string `jsonapi:"primary,notes"` RawContent string `jsonapi:"attr,content"` @@ -29,7 +38,7 @@ type linstromOriginServer struct { Id int `jsonapi:"primary,origins"` CreatedAt time.Time `jsonapi:"attr,created_at"` UpdatedAt *time.Time `jsonapi:"attr,updated_at,omitempty"` - ServerType string `jsonapi:"attr,server_type"` // one of "Linstrom", "" + ServerType string `jsonapi:"attr,server_type"` // one of "Linstrom", "Mastodon", "Plemora", "Misskey" or "Wafrn" Domain string `jsonapi:"attr,domain"` DisplayName string `jsonapi:"attr,display_name"` Icon *linstromMediaMetadata `jsonapi:"relation,icon"` @@ -68,10 +77,11 @@ type linstromAccount struct { RestrictedFollow bool `jsonapi:"attr,restricted_follow"` IdentifiesAs []string `jsonapi:"attr,identifies_as"` Pronouns []string `jsonapi:"attr,pronouns"` + Roles []string `jsonapi:"attr,roles"` } type linstromCustomAccountField struct { - Id uint + Id uint `jsonapi:"primary,custom_account_fields"` CreatedAt time.Time `jsonapi:"attr,created_at"` UpdatedAt *time.Time `jsonapi:"attr,updated_at,omitempty"` Key string `jsonapi:"attr,key"` @@ -79,3 +89,86 @@ type linstromCustomAccountField struct { Verified *bool `jsonapi:"attr,verified,omitempty"` BelongsToId string `jsonapi:"attr,belongs_to_id"` } + +// Role is essentially just a carbon copy of storage/roles.go +type linstromRole struct { + Id uint `jsonapi:"primary,roles"` + CreatedAt time.Time `jsonapi:"attr,created_at"` + UpdatedAt *time.Time `jsonapi:"attr,updated_at,omitempty"` + + // Name of the role + Name string `jsonapi:"attr,name"` + + // 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 uint `jsonapi:"attr,priority"` + // 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 `jsonapi:"attr,is_user_role"` + + // Whether this role is one built into Linstrom from the start or not + // Note: Built-in roles can't be modified + IsBuiltIn bool `jsonapi:"attr,is_builtin"` + + // --- User permissions --- + CanSendMedia *bool `jsonapi:"attr,can_send_media"` + CanSendCustomEmotes *bool `jsonapi:"attr,can_send_custom_emotes"` + CanSendCustomReactions *bool `jsonapi:"attr,can_send_custom_reactions"` + CanSendPublicNotes *bool `jsonapi:"attr,can_send_public_notes"` + CanSendLocalNotes *bool `jsonapi:"attr,can_send_local_notes"` + CanSendFollowerOnlyNotes *bool `jsonapi:"attr,can_send_follower_only_notes"` + CanSendPrivateNotes *bool `jsonapi:"attr,can_send_private_notes"` + CanSendReplies *bool `jsonapi:"attr,can_send_replies"` + CanQuote *bool `jsonapi:"attr,can_quote"` + CanBoost *bool `jsonapi:"attr,can_boost"` + CanIncludeLinks *bool `jsonapi:"attr,can_include_links"` + CanIncludeSurvey *bool `jsonapi:"attr,can_include_survey"` + + CanChangeDisplayName *bool `jsonapi:"attr,can_change_display_name"` + + BlockedUsers []string `jsonapi:"attr,blocked_users"` + CanSubmitReports *bool `jsonapi:"attr,can_submit_reports"` + CanLogin *bool `jsonapi:"attr,can_login"` + + CanMentionOthers *bool `jsonapi:"attr,can_mention_others"` + HasMentionCountLimit *bool `jsonapi:"attr,has_mention_count_limit"` + MentionLimit *uint32 `jsonapi:"attr,mention_count_limit"` + + // CanViewBoosts *bool + // CanViewQuotes *bool + // CanViewMedia *bool + // CanViewCustomEmotes *bool + + // --- Automod --- + AutoNsfwMedia *bool `jsonapi:"attr,auto_nsfw_media"` + AutoCwPosts *bool `jsonapi:"attr,auto_cw_posts"` + AutoCwPostsText *string `jsonapi:"attr,auto_cw_posts_text"` + ScanCreatedPublicNotes *bool `jsonapi:"attr,scan_created_public_notes"` + ScanCreatedLocalNotes *bool `jsonapi:"attr,scan_created_local_notes"` + ScanCreatedFollowerOnlyNotes *bool `jsonapi:"attr,scan_created_follower_only_notes"` + ScanCreatedPrivateNotes *bool `jsonapi:"attr,scan_created_private_notes"` + DisallowInteractionsWith []string `jsonapi:"attr,disallow_interactions_with"` + + WithholdNotesForManualApproval *bool `jsonapi:"attr,withhold_notes_for_manual_approval"` + WithholdNotesBasedOnRegex *bool `jsonapi:"attr,withhold_notes_based_on_regex"` + WithholdNotesRegexes []string `jsonapi:"attr,withhold_notes_regexes"` + + // --- Admin perms --- + // If set, counts as all permissions being set as given and all restrictions being disabled + FullAdmin *bool `jsonapi:"attr,full_admin"` + CanAffectOtherAdmins *bool `jsonapi:"attr,can_affect_other_admins"` + CanDeleteNotes *bool `jsonapi:"attr,can_delete_notes"` + CanConfirmWithheldNotes *bool `jsonapi:"attr,can_confirm_withheld_notes"` + CanAssignRoles *bool `jsonapi:"attr,can_assign_roles"` + CanSupressInteractionsBetweenUsers *bool `jsonapi:"attr,can_supress_interactions_between_users"` + CanOverwriteDisplayNames *bool `jsonapi:"attr,can_overwrite_display_names"` + CanManageCustomEmotes *bool `jsonapi:"attr,can_manage_custom_emotes"` + CanViewDeletedNotes *bool `jsonapi:"attr,can_view_deleted_notes"` + CanRecoverDeletedNotes *bool `jsonapi:"attr,can_recover_deleted_notes"` + CanManageAvatarDecorations *bool `jsonapi:"attr,can_manage_avatar_decorations"` + CanManageAds *bool `jsonapi:"attr,can_manage_ads"` + CanSendAnnouncements *bool `jsonapi:"attr,can_send_announcements"` +} diff --git a/server/apiRouter.go b/server/apiRouter.go index 8745fcf..f15538a 100644 --- a/server/apiRouter.go +++ b/server/apiRouter.go @@ -8,11 +8,12 @@ func setupApiRouter() http.Handler { router.Handle("/linstrom/", setupLinstromApiRouter()) // Section MastoApi + // First segment are endpoints that will need to be moved to primary router since at top route router.HandleFunc("GET /oauth/authorize", placeholderEndpoint) router.HandleFunc("POST /oauth/token", placeholderEndpoint) router.HandleFunc("POST /oauth/revoke", placeholderEndpoint) router.HandleFunc("GET /.well-known/oauth-authorization-server", placeholderEndpoint) - + // These ones are actually mounted under /api/ router.HandleFunc("POST /v1/apps", placeholderEndpoint) router.HandleFunc("GET /v1/apps/verify_credentials", placeholderEndpoint) router.HandleFunc("POST /v1/emails/confirmations", placeholderEndpoint) diff --git a/server/middlewareFixPasskeyPerms.go b/server/middlewareFixPasskeyPerms.go index 8bdf2a7..b163905 100644 --- a/server/middlewareFixPasskeyPerms.go +++ b/server/middlewareFixPasskeyPerms.go @@ -51,7 +51,7 @@ func fuckWithRegisterRequest( ) { log := hlog.FromRequest(r) log.Debug().Msg("Messing with register start request") - store := StorageFromRequest(w, r) + store := StorageFromRequest(r) if store == nil { return } @@ -147,7 +147,7 @@ func fuckWithLoginRequest( ) { log := hlog.FromRequest(r) log.Debug().Msg("Messing with login start request") - store := StorageFromRequest(w, r) + store := StorageFromRequest(r) if store == nil { return } diff --git a/server/middlewares.go b/server/middlewares.go index 5efc928..d40f26d 100644 --- a/server/middlewares.go +++ b/server/middlewares.go @@ -60,7 +60,7 @@ func LoggingMiddleware(handler http.Handler) http.Handler { func passkeyIdToAccountIdTransformerMiddleware(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - s := StorageFromRequest(w, r) + s := StorageFromRequest(r) if s == nil { return } @@ -100,3 +100,55 @@ func profilingAuthenticationMiddleware(handler http.Handler) http.Handler { handler.ServeHTTP(w, r) }) } + +// Middleware for inserting a logged in account's id into the request context if a session exists +// Does not cancel requests ever. If an error occurs, it's treated as if no session is set +func checkSessionMiddleware(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + cookie, err := r.Cookie("sid") + log := hlog.FromRequest(r) + if err != nil { + // No cookie is ok, this function is only for inserting account id into the context + // if one exists, not for checking permissions + log.Debug().Msg("No session cookie, passing along") + handler.ServeHTTP(w, r) + return + } + store := StorageFromRequest(r) + session, ok := store.GetSession(cookie.Value) + if !ok { + // Failed to get session from cookie id. Log, then move on as if no session is set + log.Warn(). + Str("session-id", cookie.Value). + Msg("Cookie with session id found, but session doesn't exist") + handler.ServeHTTP(w, r) + return + } + if session.Expires.Before(time.Now()) { + // Session expired. Move on as if no session was set + store.DeleteSession(cookie.Value) + handler.ServeHTTP(w, r) + return + } + acc, err := store.FindAccountByPasskeyId(session.UserID) + if err != nil { + // Failed to get account for passkey id. Log, then move on as if no session is set + log.Error(). + Err(err). + Bytes("passkey-id", session.UserID). + Msg("Failed to get account with passkey id while checking session. Ignoring session") + handler.ServeHTTP(w, r) + return + } + handler.ServeHTTP( + w, + r.WithContext( + context.WithValue( + r.Context(), + ContextKeyActorId, + acc.ID, + ), + ), + ) + }) +} diff --git a/server/utils.go b/server/utils.go index 88006aa..a63bb5a 100644 --- a/server/utils.go +++ b/server/utils.go @@ -30,3 +30,7 @@ func StorageFromRequest(r *http.Request) *storage.Storage { func NoteIdFromRequest(r *http.Request) string { return r.PathValue("noteId") } + +func AccountIdFromRequest(r *http.Request) string { + return r.PathValue("accountId") +} diff --git a/storage/noteTargets.go b/storage/noteTargets.go index 3a6af6b..21bcb1d 100644 --- a/storage/noteTargets.go +++ b/storage/noteTargets.go @@ -7,7 +7,7 @@ import ( // For pretty printing during debug // If `go generate` is run, it'll generate the necessary function and data for pretty printing -//go:generate stringer -type NoteTarget +//go:generate stringer -type NoteAccessLevel // What feed a note is targeting (public, home, followers or dm) type NoteAccessLevel uint8 diff --git a/storage/notetarget_string.go b/storage/noteaccesslevel_string.go similarity index 50% rename from storage/notetarget_string.go rename to storage/noteaccesslevel_string.go index 74d3a30..6f71df3 100644 --- a/storage/notetarget_string.go +++ b/storage/noteaccesslevel_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type NoteTarget"; DO NOT EDIT. +// Code generated by "stringer -type NoteAccessLevel"; DO NOT EDIT. package storage @@ -15,23 +15,23 @@ func _() { } const ( - _NoteTarget_name_0 = "NOTE_TARGET_PUBLIC" - _NoteTarget_name_1 = "NOTE_TARGET_HOME" - _NoteTarget_name_2 = "NOTE_TARGET_FOLLOWERS" - _NoteTarget_name_3 = "NOTE_TARGET_DM" + _NoteAccessLevel_name_0 = "NOTE_TARGET_PUBLIC" + _NoteAccessLevel_name_1 = "NOTE_TARGET_HOME" + _NoteAccessLevel_name_2 = "NOTE_TARGET_FOLLOWERS" + _NoteAccessLevel_name_3 = "NOTE_TARGET_DM" ) func (i NoteAccessLevel) String() string { switch { case i == 0: - return _NoteTarget_name_0 + return _NoteAccessLevel_name_0 case i == 2: - return _NoteTarget_name_1 + return _NoteAccessLevel_name_1 case i == 4: - return _NoteTarget_name_2 + return _NoteAccessLevel_name_2 case i == 8: - return _NoteTarget_name_3 + return _NoteAccessLevel_name_3 default: - return "NoteTarget(" + strconv.FormatInt(int64(i), 10) + ")" + return "NoteAccessLevel(" + strconv.FormatInt(int64(i), 10) + ")" } } diff --git a/storage/roles.go b/storage/roles.go index f75edfc..4458a07 100644 --- a/storage/roles.go +++ b/storage/roles.go @@ -51,6 +51,8 @@ type Role struct { CanBoost *bool CanIncludeLinks *bool CanIncludeSurvey *bool + CanFederateFedi *bool + CanFederateBsky *bool CanChangeDisplayName *bool @@ -75,7 +77,12 @@ type Role struct { ScanCreatedLocalNotes *bool ScanCreatedFollowerOnlyNotes *bool ScanCreatedPrivateNotes *bool - DisallowInteractionsWith []string `gorm:"type:bytes;serializer:gob"` + // 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"` WithholdNotesForManualApproval *bool WithholdNotesBasedOnRegex *bool diff --git a/storage/rolesDefaults.go b/storage/rolesDefaults.go index 9330ee6..1989587 100644 --- a/storage/rolesDefaults.go +++ b/storage/rolesDefaults.go @@ -6,6 +6,8 @@ import ( "gitlab.com/mstarongitlab/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, @@ -19,10 +21,13 @@ var DefaultUserRole = Role{ 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), @@ -32,7 +37,9 @@ var DefaultUserRole = Role{ 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 + 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), @@ -59,7 +66,8 @@ var DefaultUserRole = Role{ CanSendAnnouncements: other.IntoPointer(false), } -var fullAdminRole = Role{ +// Role providing maximum permissions +var FullAdminRole = Role{ Name: "fullAdmin", Priority: math.MaxUint, IsUserRole: false, @@ -85,7 +93,9 @@ var fullAdminRole = Role{ 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 + 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), @@ -112,7 +122,67 @@ var fullAdminRole = Role{ CanSendAnnouncements: other.IntoPointer(true), } +// Role for totally freezing an account, blocking all activity from it +var AccountFreezeRole = Role{ + Name: "accountFreeze", + Priority: math.MaxUint - 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), + + BlockedUsers: []string{}, + 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 allDefaultRoles = []*Role{ &DefaultUserRole, - &fullAdminRole, + &FullAdminRole, + &AccountFreezeRole, } diff --git a/storage/rolesUtil_generated.go b/storage/rolesUtil_generated.go index c5f68b5..7d20868 100644 --- a/storage/rolesUtil_generated.go +++ b/storage/rolesUtil_generated.go @@ -2,146 +2,153 @@ // If you need to refresh the content, run go generate again package storage -import "slices" +import ( + "slices" + "gitlab.com/mstarongitlab/goutils/sliceutils") func CollapseRolesIntoOne(roles ...*Role) Role { startingRole := RoleDeepCopy(&DefaultUserRole) slices.SortFunc(roles, func(a, b *Role) int { return int(int64(a.Priority)-int64(b.Priority)) }) for _, role := range roles { - if role.CanSupressInteractionsBetweenUsers != nil { - *startingRole.CanSupressInteractionsBetweenUsers = *role.CanSupressInteractionsBetweenUsers - } - if role.CanQuote != nil { - *startingRole.CanQuote = *role.CanQuote - } - if role.CanIncludeSurvey != nil { - *startingRole.CanIncludeSurvey = *role.CanIncludeSurvey + if role.CanManageAvatarDecorations != nil { + *startingRole.CanManageAvatarDecorations = *role.CanManageAvatarDecorations } if role.BlockedUsers != nil { startingRole.BlockedUsers = append(startingRole.BlockedUsers, role.BlockedUsers...) } - if role.AutoCwPostsText != nil { - *startingRole.AutoCwPostsText = *role.AutoCwPostsText - } - if role.ScanCreatedLocalNotes != nil { - *startingRole.ScanCreatedLocalNotes = *role.ScanCreatedLocalNotes - } - if role.WithholdNotesForManualApproval != nil { - *startingRole.WithholdNotesForManualApproval = *role.WithholdNotesForManualApproval - } if role.FullAdmin != nil { *startingRole.FullAdmin = *role.FullAdmin } - if role.CanManageAvatarDecorations != nil { - *startingRole.CanManageAvatarDecorations = *role.CanManageAvatarDecorations - } - if role.CanSendCustomEmotes != nil { - *startingRole.CanSendCustomEmotes = *role.CanSendCustomEmotes - } - if role.CanSendCustomReactions != nil { - *startingRole.CanSendCustomReactions = *role.CanSendCustomReactions - } - if role.CanSendLocalNotes != nil { - *startingRole.CanSendLocalNotes = *role.CanSendLocalNotes - } - if role.CanSendPrivateNotes != nil { - *startingRole.CanSendPrivateNotes = *role.CanSendPrivateNotes - } - if role.AutoNsfwMedia != nil { - *startingRole.AutoNsfwMedia = *role.AutoNsfwMedia - } - if role.ScanCreatedPublicNotes != nil { - *startingRole.ScanCreatedPublicNotes = *role.ScanCreatedPublicNotes - } - if role.CanBoost != nil { - *startingRole.CanBoost = *role.CanBoost - } - if role.AutoCwPosts != nil { - *startingRole.AutoCwPosts = *role.AutoCwPosts - } - if role.WithholdNotesBasedOnRegex != nil { - *startingRole.WithholdNotesBasedOnRegex = *role.WithholdNotesBasedOnRegex - } - if role.CanSendAnnouncements != nil { - *startingRole.CanSendAnnouncements = *role.CanSendAnnouncements - } - if role.CanManageAds != nil { - *startingRole.CanManageAds = *role.CanManageAds - } - if role.CanIncludeLinks != nil { - *startingRole.CanIncludeLinks = *role.CanIncludeLinks - } - if role.CanSubmitReports != nil { - *startingRole.CanSubmitReports = *role.CanSubmitReports - } - if role.CanLogin != nil { - *startingRole.CanLogin = *role.CanLogin - } - if role.MentionLimit != nil { - *startingRole.MentionLimit = *role.MentionLimit - } - if role.ScanCreatedFollowerOnlyNotes != nil { - *startingRole.ScanCreatedFollowerOnlyNotes = *role.ScanCreatedFollowerOnlyNotes - } - if role.CanOverwriteDisplayNames != nil { - *startingRole.CanOverwriteDisplayNames = *role.CanOverwriteDisplayNames - } - if role.CanSendMedia != nil { - *startingRole.CanSendMedia = *role.CanSendMedia - } - if role.CanSendFollowerOnlyNotes != nil { - *startingRole.CanSendFollowerOnlyNotes = *role.CanSendFollowerOnlyNotes - } - if role.CanChangeDisplayName != nil { - *startingRole.CanChangeDisplayName = *role.CanChangeDisplayName - } - if role.DisallowInteractionsWith != nil { - startingRole.DisallowInteractionsWith = append(startingRole.DisallowInteractionsWith, role.DisallowInteractionsWith...) - } - if role.CanViewDeletedNotes != nil { - *startingRole.CanViewDeletedNotes = *role.CanViewDeletedNotes - } - if role.CanRecoverDeletedNotes != nil { - *startingRole.CanRecoverDeletedNotes = *role.CanRecoverDeletedNotes - } - if role.ScanCreatedPrivateNotes != nil { - *startingRole.ScanCreatedPrivateNotes = *role.ScanCreatedPrivateNotes - } - if role.WithholdNotesRegexes != nil { - startingRole.WithholdNotesRegexes = append(startingRole.WithholdNotesRegexes, role.WithholdNotesRegexes...) - } - if role.CanManageCustomEmotes != nil { - *startingRole.CanManageCustomEmotes = *role.CanManageCustomEmotes - } - if role.CanMentionOthers != nil { - *startingRole.CanMentionOthers = *role.CanMentionOthers - } - if role.HasMentionCountLimit != nil { - *startingRole.HasMentionCountLimit = *role.HasMentionCountLimit - } - if role.CanAffectOtherAdmins != nil { - *startingRole.CanAffectOtherAdmins = *role.CanAffectOtherAdmins - } if role.CanAssignRoles != nil { *startingRole.CanAssignRoles = *role.CanAssignRoles } + if role.CanManageCustomEmotes != nil { + *startingRole.CanManageCustomEmotes = *role.CanManageCustomEmotes + } + if role.CanViewDeletedNotes != nil { + *startingRole.CanViewDeletedNotes = *role.CanViewDeletedNotes + } if role.CanSendPublicNotes != nil { *startingRole.CanSendPublicNotes = *role.CanSendPublicNotes } - if role.CanSendReplies != nil { - *startingRole.CanSendReplies = *role.CanSendReplies + if role.CanMentionOthers != nil { + *startingRole.CanMentionOthers = *role.CanMentionOthers } - if role.CanDeleteNotes != nil { - *startingRole.CanDeleteNotes = *role.CanDeleteNotes + if role.ScanCreatedPublicNotes != nil { + *startingRole.ScanCreatedPublicNotes = *role.ScanCreatedPublicNotes + } + if role.CanRecoverDeletedNotes != nil { + *startingRole.CanRecoverDeletedNotes = *role.CanRecoverDeletedNotes + } + if role.CanSendAnnouncements != nil { + *startingRole.CanSendAnnouncements = *role.CanSendAnnouncements + } + if role.AutoCwPostsText != nil { + *startingRole.AutoCwPostsText = *role.AutoCwPostsText + } + if role.WithholdNotesRegexes != nil { + startingRole.WithholdNotesRegexes = append(startingRole.WithholdNotesRegexes, role.WithholdNotesRegexes...) + } + if role.CanSendFollowerOnlyNotes != nil { + *startingRole.CanSendFollowerOnlyNotes = *role.CanSendFollowerOnlyNotes + } + if role.CanSendPrivateNotes != nil { + *startingRole.CanSendPrivateNotes = *role.CanSendPrivateNotes + } + if role.CanBoost != nil { + *startingRole.CanBoost = *role.CanBoost + } + if role.CanFederateFedi != nil { + *startingRole.CanFederateFedi = *role.CanFederateFedi + } + if role.CanLogin != nil { + *startingRole.CanLogin = *role.CanLogin + } + if role.AutoNsfwMedia != nil { + *startingRole.AutoNsfwMedia = *role.AutoNsfwMedia + } + if role.WithholdNotesBasedOnRegex != nil { + *startingRole.WithholdNotesBasedOnRegex = *role.WithholdNotesBasedOnRegex + } + if role.CanAffectOtherAdmins != nil { + *startingRole.CanAffectOtherAdmins = *role.CanAffectOtherAdmins + } + if role.CanSendCustomReactions != nil { + *startingRole.CanSendCustomReactions = *role.CanSendCustomReactions + } + if role.CanIncludeLinks != nil { + *startingRole.CanIncludeLinks = *role.CanIncludeLinks + } + if role.CanIncludeSurvey != nil { + *startingRole.CanIncludeSurvey = *role.CanIncludeSurvey + } + if role.CanFederateBsky != nil { + *startingRole.CanFederateBsky = *role.CanFederateBsky + } + if role.CanChangeDisplayName != nil { + *startingRole.CanChangeDisplayName = *role.CanChangeDisplayName } if role.CanConfirmWithheldNotes != nil { *startingRole.CanConfirmWithheldNotes = *role.CanConfirmWithheldNotes } + if role.CanOverwriteDisplayNames != nil { + *startingRole.CanOverwriteDisplayNames = *role.CanOverwriteDisplayNames + } + if role.CanDeleteNotes != nil { + *startingRole.CanDeleteNotes = *role.CanDeleteNotes + } + if role.CanManageAds != nil { + *startingRole.CanManageAds = *role.CanManageAds + } + if role.CanSendMedia != nil { + *startingRole.CanSendMedia = *role.CanSendMedia + } + if role.CanSendReplies != nil { + *startingRole.CanSendReplies = *role.CanSendReplies + } + if role.HasMentionCountLimit != nil { + *startingRole.HasMentionCountLimit = *role.HasMentionCountLimit + } + if role.DisallowInteractionsWith != nil { + startingRole.DisallowInteractionsWith = append(startingRole.DisallowInteractionsWith, role.DisallowInteractionsWith...) + } + if role.WithholdNotesForManualApproval != nil { + *startingRole.WithholdNotesForManualApproval = *role.WithholdNotesForManualApproval + } + if role.CanSendLocalNotes != nil { + *startingRole.CanSendLocalNotes = *role.CanSendLocalNotes + } + if role.CanQuote != nil { + *startingRole.CanQuote = *role.CanQuote + } + if role.MentionLimit != nil { + *startingRole.MentionLimit = *role.MentionLimit + } + if role.CanSubmitReports != nil { + *startingRole.CanSubmitReports = *role.CanSubmitReports + } + if role.AutoCwPosts != nil { + *startingRole.AutoCwPosts = *role.AutoCwPosts + } + if role.CanSupressInteractionsBetweenUsers != nil { + *startingRole.CanSupressInteractionsBetweenUsers = *role.CanSupressInteractionsBetweenUsers + } + if role.CanSendCustomEmotes != nil { + *startingRole.CanSendCustomEmotes = *role.CanSendCustomEmotes + } + if role.ScanCreatedLocalNotes != nil { + *startingRole.ScanCreatedLocalNotes = *role.ScanCreatedLocalNotes + } + if role.ScanCreatedFollowerOnlyNotes != nil { + *startingRole.ScanCreatedFollowerOnlyNotes = *role.ScanCreatedFollowerOnlyNotes + } + if role.ScanCreatedPrivateNotes != nil { + *startingRole.ScanCreatedPrivateNotes = *role.ScanCreatedPrivateNotes + } } return startingRole } - func RoleDeepCopy(o *Role) Role { n := Role{} n.Model = o.Model @@ -149,168 +156,180 @@ func RoleDeepCopy(o *Role) Role { n.Priority = o.Priority n.IsUserRole = o.IsUserRole n.IsBuiltIn = o.IsBuiltIn - if o.CanOverwriteDisplayNames == nil { n.CanOverwriteDisplayNames = nil } else { - t := *o.CanOverwriteDisplayNames - n.CanOverwriteDisplayNames = &t - } - if o.CanManageAds == nil { n.CanManageAds = nil } else { - t := *o.CanManageAds - n.CanManageAds = &t - } - if o.CanIncludeLinks == nil { n.CanIncludeLinks = nil } else { - t := *o.CanIncludeLinks - n.CanIncludeLinks = &t - } - if o.CanSubmitReports == nil { n.CanSubmitReports = nil } else { - t := *o.CanSubmitReports - n.CanSubmitReports = &t - } - if o.CanLogin == nil { n.CanLogin = nil } else { - t := *o.CanLogin - n.CanLogin = &t - } - if o.MentionLimit == nil { n.MentionLimit = nil } else { - t := *o.MentionLimit - n.MentionLimit = &t - } - if o.ScanCreatedFollowerOnlyNotes == nil { n.ScanCreatedFollowerOnlyNotes = nil } else { - t := *o.ScanCreatedFollowerOnlyNotes - n.ScanCreatedFollowerOnlyNotes = &t - } - if o.CanRecoverDeletedNotes == nil { n.CanRecoverDeletedNotes = nil } else { - t := *o.CanRecoverDeletedNotes - n.CanRecoverDeletedNotes = &t - } - if o.CanSendMedia == nil { n.CanSendMedia = nil } else { - t := *o.CanSendMedia - n.CanSendMedia = &t - } - if o.CanSendFollowerOnlyNotes == nil { n.CanSendFollowerOnlyNotes = nil } else { - t := *o.CanSendFollowerOnlyNotes - n.CanSendFollowerOnlyNotes = &t - } - if o.CanChangeDisplayName == nil { n.CanChangeDisplayName = nil } else { - t := *o.CanChangeDisplayName - n.CanChangeDisplayName = &t - } - n.DisallowInteractionsWith = slices.Clone(o.DisallowInteractionsWith) - if o.CanViewDeletedNotes == nil { n.CanViewDeletedNotes = nil } else { - t := *o.CanViewDeletedNotes - n.CanViewDeletedNotes = &t - } - if o.ScanCreatedPrivateNotes == nil { n.ScanCreatedPrivateNotes = nil } else { - t := *o.ScanCreatedPrivateNotes - n.ScanCreatedPrivateNotes = &t - } - n.WithholdNotesRegexes = slices.Clone(o.WithholdNotesRegexes) if o.CanManageCustomEmotes == nil { n.CanManageCustomEmotes = nil } else { t := *o.CanManageCustomEmotes n.CanManageCustomEmotes = &t } - if o.CanMentionOthers == nil { n.CanMentionOthers = nil } else { - t := *o.CanMentionOthers - n.CanMentionOthers = &t - } - if o.HasMentionCountLimit == nil { n.HasMentionCountLimit = nil } else { - t := *o.HasMentionCountLimit - n.HasMentionCountLimit = &t - } - if o.CanAffectOtherAdmins == nil { n.CanAffectOtherAdmins = nil } else { - t := *o.CanAffectOtherAdmins - n.CanAffectOtherAdmins = &t - } - if o.CanAssignRoles == nil { n.CanAssignRoles = nil } else { - t := *o.CanAssignRoles - n.CanAssignRoles = &t - } - if o.CanSendPublicNotes == nil { n.CanSendPublicNotes = nil } else { - t := *o.CanSendPublicNotes - n.CanSendPublicNotes = &t - } - if o.CanSendReplies == nil { n.CanSendReplies = nil } else { - t := *o.CanSendReplies - n.CanSendReplies = &t - } - if o.CanDeleteNotes == nil { n.CanDeleteNotes = nil } else { - t := *o.CanDeleteNotes - n.CanDeleteNotes = &t - } - if o.CanConfirmWithheldNotes == nil { n.CanConfirmWithheldNotes = nil } else { - t := *o.CanConfirmWithheldNotes - n.CanConfirmWithheldNotes = &t - } - if o.WithholdNotesForManualApproval == nil { n.WithholdNotesForManualApproval = nil } else { - t := *o.WithholdNotesForManualApproval - n.WithholdNotesForManualApproval = &t - } - if o.CanSupressInteractionsBetweenUsers == nil { n.CanSupressInteractionsBetweenUsers = nil } else { - t := *o.CanSupressInteractionsBetweenUsers - n.CanSupressInteractionsBetweenUsers = &t - } - if o.CanQuote == nil { n.CanQuote = nil } else { - t := *o.CanQuote - n.CanQuote = &t - } - if o.CanIncludeSurvey == nil { n.CanIncludeSurvey = nil } else { - t := *o.CanIncludeSurvey - n.CanIncludeSurvey = &t - } - n.BlockedUsers = slices.Clone(o.BlockedUsers) - if o.AutoCwPostsText == nil { n.AutoCwPostsText = nil } else { - t := *o.AutoCwPostsText - n.AutoCwPostsText = &t - } - if o.ScanCreatedLocalNotes == nil { n.ScanCreatedLocalNotes = nil } else { - t := *o.ScanCreatedLocalNotes - n.ScanCreatedLocalNotes = &t - } - if o.ScanCreatedPublicNotes == nil { n.ScanCreatedPublicNotes = nil } else { - t := *o.ScanCreatedPublicNotes - n.ScanCreatedPublicNotes = &t - } - if o.FullAdmin == nil { n.FullAdmin = nil } else { - t := *o.FullAdmin - n.FullAdmin = &t + if o.CanViewDeletedNotes == nil { n.CanViewDeletedNotes = nil } else { + t := *o.CanViewDeletedNotes + n.CanViewDeletedNotes = &t } if o.CanManageAvatarDecorations == nil { n.CanManageAvatarDecorations = nil } else { t := *o.CanManageAvatarDecorations n.CanManageAvatarDecorations = &t } - if o.CanSendCustomEmotes == nil { n.CanSendCustomEmotes = nil } else { - t := *o.CanSendCustomEmotes - n.CanSendCustomEmotes = &t + n.BlockedUsers = slices.Clone(o.BlockedUsers) + if o.FullAdmin == nil { n.FullAdmin = nil } else { + t := *o.FullAdmin + n.FullAdmin = &t } - if o.CanSendCustomReactions == nil { n.CanSendCustomReactions = nil } else { - t := *o.CanSendCustomReactions - n.CanSendCustomReactions = &t + if o.CanAssignRoles == nil { n.CanAssignRoles = nil } else { + t := *o.CanAssignRoles + n.CanAssignRoles = &t } - if o.CanSendLocalNotes == nil { n.CanSendLocalNotes = nil } else { - t := *o.CanSendLocalNotes - n.CanSendLocalNotes = &t - } - if o.CanSendPrivateNotes == nil { n.CanSendPrivateNotes = nil } else { - t := *o.CanSendPrivateNotes - n.CanSendPrivateNotes = &t - } - if o.AutoNsfwMedia == nil { n.AutoNsfwMedia = nil } else { - t := *o.AutoNsfwMedia - n.AutoNsfwMedia = &t - } - if o.CanBoost == nil { n.CanBoost = nil } else { - t := *o.CanBoost - n.CanBoost = &t - } - if o.AutoCwPosts == nil { n.AutoCwPosts = nil } else { - t := *o.AutoCwPosts - n.AutoCwPosts = &t - } - if o.WithholdNotesBasedOnRegex == nil { n.WithholdNotesBasedOnRegex = nil } else { - t := *o.WithholdNotesBasedOnRegex - n.WithholdNotesBasedOnRegex = &t + if o.CanRecoverDeletedNotes == nil { n.CanRecoverDeletedNotes = nil } else { + t := *o.CanRecoverDeletedNotes + n.CanRecoverDeletedNotes = &t } if o.CanSendAnnouncements == nil { n.CanSendAnnouncements = nil } else { t := *o.CanSendAnnouncements n.CanSendAnnouncements = &t } + if o.CanSendPublicNotes == nil { n.CanSendPublicNotes = nil } else { + t := *o.CanSendPublicNotes + n.CanSendPublicNotes = &t + } + if o.CanMentionOthers == nil { n.CanMentionOthers = nil } else { + t := *o.CanMentionOthers + n.CanMentionOthers = &t + } + if o.ScanCreatedPublicNotes == nil { n.ScanCreatedPublicNotes = nil } else { + t := *o.ScanCreatedPublicNotes + n.ScanCreatedPublicNotes = &t + } + if o.CanFederateFedi == nil { n.CanFederateFedi = nil } else { + t := *o.CanFederateFedi + n.CanFederateFedi = &t + } + if o.CanLogin == nil { n.CanLogin = nil } else { + t := *o.CanLogin + n.CanLogin = &t + } + if o.AutoCwPostsText == nil { n.AutoCwPostsText = nil } else { + t := *o.AutoCwPostsText + n.AutoCwPostsText = &t + } + n.WithholdNotesRegexes = slices.Clone(o.WithholdNotesRegexes) + if o.CanSendFollowerOnlyNotes == nil { n.CanSendFollowerOnlyNotes = nil } else { + t := *o.CanSendFollowerOnlyNotes + n.CanSendFollowerOnlyNotes = &t + } + if o.CanSendPrivateNotes == nil { n.CanSendPrivateNotes = nil } else { + t := *o.CanSendPrivateNotes + n.CanSendPrivateNotes = &t + } + if o.CanBoost == nil { n.CanBoost = nil } else { + t := *o.CanBoost + n.CanBoost = &t + } + if o.CanFederateBsky == nil { n.CanFederateBsky = nil } else { + t := *o.CanFederateBsky + n.CanFederateBsky = &t + } + if o.CanChangeDisplayName == nil { n.CanChangeDisplayName = nil } else { + t := *o.CanChangeDisplayName + n.CanChangeDisplayName = &t + } + if o.AutoNsfwMedia == nil { n.AutoNsfwMedia = nil } else { + t := *o.AutoNsfwMedia + n.AutoNsfwMedia = &t + } + if o.WithholdNotesBasedOnRegex == nil { n.WithholdNotesBasedOnRegex = nil } else { + t := *o.WithholdNotesBasedOnRegex + n.WithholdNotesBasedOnRegex = &t + } + if o.CanAffectOtherAdmins == nil { n.CanAffectOtherAdmins = nil } else { + t := *o.CanAffectOtherAdmins + n.CanAffectOtherAdmins = &t + } + if o.CanSendCustomReactions == nil { n.CanSendCustomReactions = nil } else { + t := *o.CanSendCustomReactions + n.CanSendCustomReactions = &t + } + if o.CanIncludeLinks == nil { n.CanIncludeLinks = nil } else { + t := *o.CanIncludeLinks + n.CanIncludeLinks = &t + } + if o.CanIncludeSurvey == nil { n.CanIncludeSurvey = nil } else { + t := *o.CanIncludeSurvey + n.CanIncludeSurvey = &t + } + if o.CanConfirmWithheldNotes == nil { n.CanConfirmWithheldNotes = nil } else { + t := *o.CanConfirmWithheldNotes + n.CanConfirmWithheldNotes = &t + } + if o.CanOverwriteDisplayNames == nil { n.CanOverwriteDisplayNames = nil } else { + t := *o.CanOverwriteDisplayNames + n.CanOverwriteDisplayNames = &t + } + n.DisallowInteractionsWith = slices.Clone(o.DisallowInteractionsWith) + if o.WithholdNotesForManualApproval == nil { n.WithholdNotesForManualApproval = nil } else { + t := *o.WithholdNotesForManualApproval + n.WithholdNotesForManualApproval = &t + } + if o.CanDeleteNotes == nil { n.CanDeleteNotes = nil } else { + t := *o.CanDeleteNotes + n.CanDeleteNotes = &t + } + if o.CanManageAds == nil { n.CanManageAds = nil } else { + t := *o.CanManageAds + n.CanManageAds = &t + } + if o.CanSendMedia == nil { n.CanSendMedia = nil } else { + t := *o.CanSendMedia + n.CanSendMedia = &t + } + if o.CanSendReplies == nil { n.CanSendReplies = nil } else { + t := *o.CanSendReplies + n.CanSendReplies = &t + } + if o.HasMentionCountLimit == nil { n.HasMentionCountLimit = nil } else { + t := *o.HasMentionCountLimit + n.HasMentionCountLimit = &t + } + if o.CanSendLocalNotes == nil { n.CanSendLocalNotes = nil } else { + t := *o.CanSendLocalNotes + n.CanSendLocalNotes = &t + } + if o.CanQuote == nil { n.CanQuote = nil } else { + t := *o.CanQuote + n.CanQuote = &t + } + if o.MentionLimit == nil { n.MentionLimit = nil } else { + t := *o.MentionLimit + n.MentionLimit = &t + } + if o.CanSubmitReports == nil { n.CanSubmitReports = nil } else { + t := *o.CanSubmitReports + n.CanSubmitReports = &t + } + if o.AutoCwPosts == nil { n.AutoCwPosts = nil } else { + t := *o.AutoCwPosts + n.AutoCwPosts = &t + } + if o.CanSupressInteractionsBetweenUsers == nil { n.CanSupressInteractionsBetweenUsers = nil } else { + t := *o.CanSupressInteractionsBetweenUsers + n.CanSupressInteractionsBetweenUsers = &t + } + if o.ScanCreatedPrivateNotes == nil { n.ScanCreatedPrivateNotes = nil } else { + t := *o.ScanCreatedPrivateNotes + n.ScanCreatedPrivateNotes = &t + } + if o.CanSendCustomEmotes == nil { n.CanSendCustomEmotes = nil } else { + t := *o.CanSendCustomEmotes + n.CanSendCustomEmotes = &t + } + if o.ScanCreatedLocalNotes == nil { n.ScanCreatedLocalNotes = nil } else { + t := *o.ScanCreatedLocalNotes + n.ScanCreatedLocalNotes = &t + } + if o.ScanCreatedFollowerOnlyNotes == nil { n.ScanCreatedFollowerOnlyNotes = nil } else { + t := *o.ScanCreatedFollowerOnlyNotes + n.ScanCreatedFollowerOnlyNotes = &t + } return n } + +func CompareRoles(a, b *Role) bool { + return (a.WithholdNotesForManualApproval == nil || b.WithholdNotesForManualApproval == nil || a.WithholdNotesForManualApproval == b.WithholdNotesForManualApproval) && (a.CanDeleteNotes == nil || b.CanDeleteNotes == nil || a.CanDeleteNotes == b.CanDeleteNotes) && (a.CanManageAds == nil || b.CanManageAds == nil || a.CanManageAds == b.CanManageAds) && (a.CanSendMedia == nil || b.CanSendMedia == nil || a.CanSendMedia == b.CanSendMedia) && (a.CanSendReplies == nil || b.CanSendReplies == nil || a.CanSendReplies == b.CanSendReplies) && (a.HasMentionCountLimit == nil || b.HasMentionCountLimit == nil || a.HasMentionCountLimit == b.HasMentionCountLimit) && (a.DisallowInteractionsWith == nil || b.DisallowInteractionsWith == nil || sliceutils.CompareUnordered(a.DisallowInteractionsWith,b.DisallowInteractionsWith)) && (a.CanSendLocalNotes == nil || b.CanSendLocalNotes == nil || a.CanSendLocalNotes == b.CanSendLocalNotes) && (a.CanQuote == nil || b.CanQuote == nil || a.CanQuote == b.CanQuote) && (a.MentionLimit == nil || b.MentionLimit == nil || a.MentionLimit == b.MentionLimit) && (a.CanSubmitReports == nil || b.CanSubmitReports == nil || a.CanSubmitReports == b.CanSubmitReports) && (a.AutoCwPosts == nil || b.AutoCwPosts == nil || a.AutoCwPosts == b.AutoCwPosts) && (a.CanSupressInteractionsBetweenUsers == nil || b.CanSupressInteractionsBetweenUsers == nil || a.CanSupressInteractionsBetweenUsers == b.CanSupressInteractionsBetweenUsers) && (a.CanSendCustomEmotes == nil || b.CanSendCustomEmotes == nil || a.CanSendCustomEmotes == b.CanSendCustomEmotes) && (a.ScanCreatedLocalNotes == nil || b.ScanCreatedLocalNotes == nil || a.ScanCreatedLocalNotes == b.ScanCreatedLocalNotes) && (a.ScanCreatedFollowerOnlyNotes == nil || b.ScanCreatedFollowerOnlyNotes == nil || a.ScanCreatedFollowerOnlyNotes == b.ScanCreatedFollowerOnlyNotes) && (a.ScanCreatedPrivateNotes == nil || b.ScanCreatedPrivateNotes == nil || a.ScanCreatedPrivateNotes == b.ScanCreatedPrivateNotes) && (a.CanViewDeletedNotes == nil || b.CanViewDeletedNotes == nil || a.CanViewDeletedNotes == b.CanViewDeletedNotes) && (a.CanManageAvatarDecorations == nil || b.CanManageAvatarDecorations == nil || a.CanManageAvatarDecorations == b.CanManageAvatarDecorations) && (a.BlockedUsers == nil || b.BlockedUsers == nil || sliceutils.CompareUnordered(a.BlockedUsers,b.BlockedUsers)) && (a.FullAdmin == nil || b.FullAdmin == nil || a.FullAdmin == b.FullAdmin) && (a.CanAssignRoles == nil || b.CanAssignRoles == nil || a.CanAssignRoles == b.CanAssignRoles) && (a.CanManageCustomEmotes == nil || b.CanManageCustomEmotes == nil || a.CanManageCustomEmotes == b.CanManageCustomEmotes) && (a.CanSendAnnouncements == nil || b.CanSendAnnouncements == nil || a.CanSendAnnouncements == b.CanSendAnnouncements) && (a.CanSendPublicNotes == nil || b.CanSendPublicNotes == nil || a.CanSendPublicNotes == b.CanSendPublicNotes) && (a.CanMentionOthers == nil || b.CanMentionOthers == nil || a.CanMentionOthers == b.CanMentionOthers) && (a.ScanCreatedPublicNotes == nil || b.ScanCreatedPublicNotes == nil || a.ScanCreatedPublicNotes == b.ScanCreatedPublicNotes) && (a.CanRecoverDeletedNotes == nil || b.CanRecoverDeletedNotes == nil || a.CanRecoverDeletedNotes == b.CanRecoverDeletedNotes) && (a.CanLogin == nil || b.CanLogin == nil || a.CanLogin == b.CanLogin) && (a.AutoCwPostsText == nil || b.AutoCwPostsText == nil || a.AutoCwPostsText == b.AutoCwPostsText) && (a.WithholdNotesRegexes == nil || b.WithholdNotesRegexes == nil || sliceutils.CompareUnordered(a.WithholdNotesRegexes,b.WithholdNotesRegexes)) && (a.CanSendFollowerOnlyNotes == nil || b.CanSendFollowerOnlyNotes == nil || a.CanSendFollowerOnlyNotes == b.CanSendFollowerOnlyNotes) && (a.CanSendPrivateNotes == nil || b.CanSendPrivateNotes == nil || a.CanSendPrivateNotes == b.CanSendPrivateNotes) && (a.CanBoost == nil || b.CanBoost == nil || a.CanBoost == b.CanBoost) && (a.CanFederateFedi == nil || b.CanFederateFedi == nil || a.CanFederateFedi == b.CanFederateFedi) && (a.CanChangeDisplayName == nil || b.CanChangeDisplayName == nil || a.CanChangeDisplayName == b.CanChangeDisplayName) && (a.AutoNsfwMedia == nil || b.AutoNsfwMedia == nil || a.AutoNsfwMedia == b.AutoNsfwMedia) && (a.WithholdNotesBasedOnRegex == nil || b.WithholdNotesBasedOnRegex == nil || a.WithholdNotesBasedOnRegex == b.WithholdNotesBasedOnRegex) && (a.CanAffectOtherAdmins == nil || b.CanAffectOtherAdmins == nil || a.CanAffectOtherAdmins == b.CanAffectOtherAdmins) && (a.CanSendCustomReactions == nil || b.CanSendCustomReactions == nil || a.CanSendCustomReactions == b.CanSendCustomReactions) && (a.CanIncludeLinks == nil || b.CanIncludeLinks == nil || a.CanIncludeLinks == b.CanIncludeLinks) && (a.CanIncludeSurvey == nil || b.CanIncludeSurvey == nil || a.CanIncludeSurvey == b.CanIncludeSurvey) && (a.CanFederateBsky == nil || b.CanFederateBsky == nil || a.CanFederateBsky == b.CanFederateBsky) && (a.CanConfirmWithheldNotes == nil || b.CanConfirmWithheldNotes == nil || a.CanConfirmWithheldNotes == b.CanConfirmWithheldNotes) && (a.CanOverwriteDisplayNames == nil || b.CanOverwriteDisplayNames == nil || a.CanOverwriteDisplayNames == b.CanOverwriteDisplayNames) && (a == nil || b == nil || a.CanOverwriteDisplayNames == b.CanOverwriteDisplayNames) +} \ No newline at end of file