package server import ( "net/http" "github.com/google/jsonapi" "github.com/rs/zerolog/hlog" "gitlab.com/mstarongitlab/goutils/other" "gitlab.com/mstarongitlab/goutils/sliceutils" "gitlab.com/mstarongitlab/linstrom/storage" ) // 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) accId := AccountIdFromRequest(r) acc, err := store.FindAccountById(accId) switch err { case nil: // Ok, do nothing case storage.ErrEntryNotFound: other.HttpErr(w, HttpErrIdNotFound, "account not found", http.StatusNotFound) return default: log.Error().Err(err).Str("account-id", accId).Msg("Failed to get account from storage") other.HttpErr( w, HttpErrIdDbFailure, "Failed to get account from storage", http.StatusInternalServerError, ) return } actorId, ok := r.Context().Value(ContextKeyActorId).(string) if ok { // Logged in user is accessing account, check if target account has them blocked roles, err := store.FindRolesByNames(acc.Roles) if err != nil { log.Error(). Err(err). Strs("role-names", acc.Roles). Msg("Failed to get roles from storage") other.HttpErr( w, HttpErrIdDbFailure, "Failed to get roles of target account", http.StatusInternalServerError, ) return } collapsedRole := storage.CollapseRolesIntoOne(roles...) if sliceutils.Contains(collapsedRole.BlockedUsers, actorId) { // Actor account is in list of blocked accounts, deny access other.HttpErr(w, HttpErrIdNotAuthenticated, "Access forbidden", http.StatusForbidden) return } } outAccount, err := convertAccountStorageToLinstrom(acc, store) if err != nil { log.Error(). Err(err). Msg("Failed to convert storage account (and attached data) into linstrom API representation") other.HttpErr( w, HttpErrIdConverionFailure, "Failed to convert storage account and attached data into API representation", http.StatusInternalServerError, ) return } err = jsonapi.MarshalPayload(w, outAccount) if err != nil { log.Error().Err(err).Any("account", outAccount).Msg("Failed to marshal and write account") } } func linstromUpdateAccount(w http.ResponseWriter, r *http.Request) { store := StorageFromRequest(r) log := hlog.FromRequest(r) // Assumption: There must be a valid session once this function is called due to middlewares actorId, _ := ActorIdFromRequest(r) apiTarget := linstromAccount{} err := jsonapi.UnmarshalPayload(r.Body, &apiTarget) if err != nil { other.HttpErr(w, HttpErrIdBadRequest, "bad body", http.StatusBadRequest) return } targetAccId := AccountIdFromRequest(r) if apiTarget.Id != targetAccId { other.HttpErr( w, HttpErrIdBadRequest, "Provided entity's id doesn't match path id", http.StatusConflict, ) return } if !(actorId == apiTarget.Id) { other.HttpErr(w, HttpErrIdNotAuthenticated, "Invalid permissions", http.StatusForbidden) return } dbTarget, err := store.FindAccountById(apiTarget.Id) // Assumption: The only sort of errors that can be returned are db failures. // The account not existing is not possible anymore since this is in a valid session // and a session is only injected if the actor account can be found if err != nil { log.Error(). Err(err). Str("account-id", actorId). Msg("Failed to get account from db despite valid session") other.HttpErr( w, HttpErrIdDbFailure, "Failed to get account despite valid session", http.StatusInternalServerError, ) return } // location, birthday, icon, banner, background, custom fields // bluesky federation, uhhh dbTarget.DisplayName = apiTarget.DisplayName dbTarget.Indexable = apiTarget.Indexable dbTarget.Description = apiTarget.Description // TODO: Figure out how to properly update custom fields dbTarget.Gender = apiTarget.Pronouns dbTarget.IdentifiesAs = sliceutils.Map( sliceutils.Filter(apiTarget.IdentifiesAs, func(t string) bool { return storage.IsValidBeing(t) }), func(t string) storage.Being { return storage.Being(t) }, ) dbTarget.Indexable = apiTarget.Indexable dbTarget.RestrictedFollow = apiTarget.RestrictedFollow err = store.UpdateAccount(dbTarget) if err != nil { log.Error().Err(err).Msg("Failed to update account in db") other.HttpErr( w, HttpErrIdDbFailure, "Failed to update db entries", http.StatusInternalServerError, ) return } w.WriteHeader(http.StatusOK) newAccData, err := convertAccountStorageToLinstrom(dbTarget, store) if err != nil { log.Error().Err(err).Msg("Failed to convert updated account back into api form") other.HttpErr( w, HttpErrIdConverionFailure, "Failed to convert updated account back into api form", http.StatusInternalServerError, ) return } err = jsonapi.MarshalPayload(w, newAccData) if err != nil { log.Error().Err(err).Msg("Failed to marshal and write updated account") } } func linstromDeleteAccount(w http.ResponseWriter, r *http.Request) { actorId, _ := ActorIdFromRequest(r) log := hlog.FromRequest(r) store := StorageFromRequest(r) targetAccountId := AccountIdFromRequest(r) if targetAccountId != actorId { other.HttpErr(w, HttpErrIdNotAuthenticated, "Action forbidden", http.StatusForbidden) return } acc, err := store.FindAccountById(targetAccountId) if err != nil { log.Error().Err(err).Str("account-id", actorId).Msg("Failed to get account for deletion") other.HttpErr( w, HttpErrIdDbFailure, "Failed to get account from db", http.StatusInternalServerError, ) return } // allRoles, err := store.FindRolesByNames(acc.Roles) // collapsedRole := storage.CollapseRolesIntoOne(allRoles...) // TODO: Start job of sending out deletion messages to all federated servers // Clean up related data first // User role // Custom fields _ = acc store.DeleteAccount(actorId) } 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) {}