linstrom/server/apiLinstromAccounts.go

264 lines
8.2 KiB
Go
Raw Normal View History

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 {
log.Debug().
Str("actor-id", actorId).
Str("target-id", targetAccountId).
Msg("Invalid attempt to delete account")
other.HttpErr(w, HttpErrIdNotAuthenticated, "Action forbidden", http.StatusForbidden)
return
}
log.Info().Str("account-id", actorId).Msg("Deleting account")
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
// TODO: Also delete media files
err = store.DeleteRoleByName(acc.ID)
if err != nil {
log.Error().
Err(err).
Str("role-name", acc.ID).
Msg("Failed to delete user role for account deletion request")
other.HttpErr(
w,
HttpErrIdDbFailure,
"Failed to delete user role",
http.StatusInternalServerError,
)
return
}
err = store.DeleteAllUserFieldsForAccountId(acc.ID)
if err != nil {
log.Error().
Err(err).
Str("account-id", acc.ID).
Msg("Failed to delete custom info fields for account deletion")
other.HttpErr(
w,
HttpErrIdDbFailure,
"Failed to delete custom info fields",
http.StatusInternalServerError,
)
return
}
err = store.DeleteAccount(actorId)
if err != nil {
log.Error().Err(err).Str("account-id", acc.ID).Msg("Failed to delete account")
other.HttpErr(
w,
HttpErrIdDbFailure,
"Failed to delete account from db",
http.StatusInternalServerError,
)
return
}
}
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) {}