seekia/internal/profiles/viewableProfiles/viewableProfiles.go

301 lines
12 KiB
Go
Raw Normal View History

// viewableProfiles provides functions to retrieve viewable user profiles
package viewableProfiles
// A viewable profile is one that can be shown to non-moderators during normal use of Seekia
// A viewable Mate profile is one that has been approved.
// A viewable Host/Moderator profile is one that has not been banned.
// A viewable profile must also be created by a non-banned identity
// All viewable statuses are derived from the profile/identity sticky consensus statuses, not their realtime consensus statuses
import "seekia/internal/badgerDatabase"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import "seekia/internal/moderation/verifiedStickyStatus"
import "seekia/internal/moderation/trustedViewableStatus"
import "seekia/internal/profiles/calculatedAttributes"
import "seekia/internal/profiles/readProfiles"
import messagepack "github.com/vmihailenco/msgpack/v5"
import "errors"
// This function gets any attribute from a user's newest viewable profile. It includes calculated attributes.
//Outputs:
// -bool: Profile exists
// -int: Profile version
// -bool: Attribute exists
// -string: Attribute value
// -error
func GetAnyAttributeFromNewestViewableUserProfile(identityHash [16]byte, networkType byte, attribute string, allowTrustedStatus bool, allowUnknownStatus bool, alwaysAllowDisabled bool)(bool, int, bool, string, error){
isValid := helpers.VerifyNetworkType(networkType)
if (isValid == false){
networkTypeString := helpers.ConvertByteToString(networkType)
return false, 0, false, "", errors.New("GetAnyAttributeFromNewestViewableUserProfile called with invalid networkType: " + networkTypeString)
}
profileExists, _, retrieveAnyAttributeFunction, err := GetRetrieveAnyNewestViewableUserProfileAttributeFunction(identityHash, networkType, allowTrustedStatus, allowUnknownStatus, alwaysAllowDisabled)
if (err != nil) { return false, 0, false, "", err }
if (profileExists == false){
return false, 0, false, "", nil
}
exists, profileVersion, retrievedAttribute, err := retrieveAnyAttributeFunction(attribute)
if (err != nil) { return false, 0, false, "", err }
if (exists == false){
return true, 0, false, "", nil
}
return true, profileVersion, true, retrievedAttribute, nil
}
// This function returns a function to retrieve any attribute from a user's newest viewable profile.
// This makes retrieving multiple attributes faster.
// The newest viewable profile only needs to be found and read into memory once.
//Outputs:
// -bool: Profile exists
// -[28]byte: Profile hash
// -func(string)(bool, int, string, error) - Retrieve any attribute function
// -error
func GetRetrieveAnyNewestViewableUserProfileAttributeFunction(identityHash [16]byte, networkType byte, allowTrustedStatus bool, allowUnknownStatus bool, alwaysAllowDisabled bool)(bool, [28]byte, func(string)(bool, int, string, error), error){
isValid := helpers.VerifyNetworkType(networkType)
if (isValid == false){
networkTypeString := helpers.ConvertByteToString(networkType)
return false, [28]byte{}, nil, errors.New("GetRetrieveAnyNewestViewableUserProfileAttributeFunction called with invalid networkType: " + networkTypeString)
}
profileExists, profileVersion, profileHash, _, _, rawProfileMap, err := GetNewestViewableUserProfile(identityHash, networkType, allowTrustedStatus, allowUnknownStatus, alwaysAllowDisabled)
if (err != nil) { return false, [28]byte{}, nil, err }
if (profileExists == false){
return false, [28]byte{}, nil, nil
}
getAnyAttributeFunction, err := calculatedAttributes.GetRetrieveAnyProfileAttributeIncludingCalculatedFunction(profileVersion, rawProfileMap)
if (err != nil){ return false, [28]byte{}, nil, err }
return true, profileHash, getAnyAttributeFunction, nil
}
//Inputs:
// -[16]byte: Identity hash of user whose profile we are retrieving
// -byte: Network type to retrieve profile for
// -bool: Allow trusted viewable status to be retrieved.
// -If false, only verified viewable statuses will be allowed (this is used for hosts)
// -bool: Allow Unknown Status
// -This is only used to find hosts to query from. This must be false for hosts retrieving viewable host profiles to share.
// -bool: Always allow disabled profiles
// -If true, the function will return disabled profiles, even if the identity is banned.
// -If false, a disabled profile will be considered unviewable if the identity is banned.
//Outputs:
// -bool: Viewable Profile exists
// -int: Profile version
// -[28]byte: Viewable Profile hash
// -[]byte: Viewable Profile bytes
// -int64: Profile broadcast time
// -map[int]messagepack.RawMessage: Viewable Profile Map
// -error
func GetNewestViewableUserProfile(identityHash [16]byte, networkType byte, allowTrustedStatus bool, allowUnknownStatus bool, alwaysAllowDisabled bool)(bool, int, [28]byte, []byte, int64, map[int]messagepack.RawMessage, error){
identityType, err := identity.GetIdentityTypeFromIdentityHash(identityHash)
if (err != nil) {
identityHashHex := encoding.EncodeBytesToHexString(identityHash[:])
return false, 0, [28]byte{}, nil, 0, nil, errors.New("GetNewestViewableUserProfile called with invalid identityHash: " + identityHashHex)
}
isValid := helpers.VerifyNetworkType(networkType)
if (isValid == false){
networkTypeString := helpers.ConvertByteToString(networkType)
return false, 0, [28]byte{}, nil, 0, nil, errors.New("GetNewestViewableUserProfile called with invalid networkType: " + networkTypeString)
}
// We check if the identity is banned
getIdentityIsViewableStatus := func()(bool, error){
downloadingRequiredReviews, parametersExist, stickyConsensusEstablished, identityIsViewableStatus, err := verifiedStickyStatus.GetVerifiedIdentityIsViewableStickyStatus(identityHash, networkType)
if (err != nil) { return false, err }
if (downloadingRequiredReviews == true && parametersExist == true && stickyConsensusEstablished == true){
return identityIsViewableStatus, nil
}
if (allowTrustedStatus == false){
// The trusted viewable status of the identity is unknown
// We are not downloading the required reviews
if (allowUnknownStatus == true){
return true, nil
}
return false, nil
}
statusIsKnown, identityIsViewable, _, err := trustedViewableStatus.GetTrustedIdentityIsViewableStatus(identityHash, networkType)
if (err != nil) { return false, err }
if (statusIsKnown == true){
return identityIsViewable, nil
}
if (allowUnknownStatus == true){
return true, nil
}
return false, nil
}
identityIsViewableStatus, err := getIdentityIsViewableStatus()
if (err != nil) { return false, 0, [28]byte{}, nil, 0, nil, err }
if (identityIsViewableStatus == false && alwaysAllowDisabled == false){
return false, 0, [28]byte{}, nil, 0, nil, nil
}
exists, profileHashesList, err := badgerDatabase.GetIdentityProfileHashesList(identityHash)
if (err != nil) { return false, 0, [28]byte{}, nil, 0, nil, err }
if (exists == false){
return false, 0, [28]byte{}, nil, 0, nil, nil
}
newestViewableProfileFound := false
newestViewableProfileVersion := 0
var newestViewableProfileHash [28]byte
newestViewableProfileIsDisabled := false
newestViewableProfileRawProfileMap := make(map[int]messagepack.RawMessage)
newestViewableProfileBytes := make([]byte, 0)
newestViewableProfileBroadcastTime := int64(0)
for _, profileHash := range profileHashesList{
exists, profileBytes, err := badgerDatabase.GetUserProfile(identityType, profileHash)
if (err != nil) { return false, 0, [28]byte{}, nil, 0, nil, err }
if (exists == false){
// Identity profile hashes list is outdated, it will be updated automatically in the background
continue
}
ableToRead, profileVersion, profileNetworkType, profileIdentityHash, profileBroadcastTime, profileIsDisabled, rawProfileMap, err := readProfiles.ReadProfile(false, profileBytes)
if (err != nil) { return false, 0, [28]byte{}, nil, 0, nil, err }
if (ableToRead == false){
return false, 0, [28]byte{}, nil, 0, nil, errors.New("Database corrupt: Contains invalid profile.")
}
if (profileIdentityHash != identityHash){
return false, 0, [28]byte{}, nil, 0, nil, errors.New("Database corrupt: Identity Profiles list profile does not match identity hash.")
}
if (profileNetworkType != networkType){
// The profile belongs to a different networkType
// We skip it.
continue
}
// Disabled profiles are always viewable, unless the author identity is banned.
if (profileIsDisabled == false){
profileIsDisabled, profileMetadataIsKnown, profileNetworkType, profileIdentityHash, downloadingRequiredReviews, networkParametersExist, stickyConsensusEstablished, profileIsViewableStatus, err := verifiedStickyStatus.GetVerifiedProfileIsViewableStickyStatus(profileHash)
if (err != nil) { return false, 0, [28]byte{}, nil, 0, nil, err }
if (profileIsDisabled == true){
return false, 0, [28]byte{}, nil, 0, nil, errors.New("GetVerifiedProfileIsViewableStickyStatus returning mismatched profileIsDisabled status")
}
if (profileMetadataIsKnown == false){
// The metadata must have been deleted after we retrieved profiles from database. Skip profile
continue
}
if (profileNetworkType != networkType){
return false, 0, [28]byte{}, nil, 0, nil, errors.New("GetVerifiedProfileIsViewableStickyStatus returning mismatched profileNetworkType.")
}
if (profileIdentityHash != identityHash){
return false, 0, [28]byte{}, nil, 0, nil, errors.New("GetVerifiedProfileIsViewableStickyStatus returning invalid profileIdentityHash")
}
// Outputs:
// -bool: Profile is viewable
// -error
getProfileIsViewableStatus := func()(bool, error){
if (downloadingRequiredReviews == true && networkParametersExist == true && stickyConsensusEstablished == true){
return profileIsViewableStatus, nil
}
// Verified status is unknown. We must attempt to get trusted status
if (allowTrustedStatus == false){
// The trusted viewable status of the profile is unknown
// We are not downloading the required reviews, or the profile is not downloaded
if (allowUnknownStatus == true){
return true, nil
}
// The profile cannot be verified as Viewable
return false, nil
}
statusIsKnown, isViewable, _, err := trustedViewableStatus.GetTrustedProfileIsViewableStatus(profileHash)
if (err != nil) { return false, err }
if (statusIsKnown == true){
return isViewable, nil
}
// We do not have a trusted or verified verdict. Profile's viewable status is unknown.
if (allowUnknownStatus == true){
return true, nil
}
return false, nil
}
profileIsViewable, err := getProfileIsViewableStatus()
if (err != nil) { return false, 0, [28]byte{}, nil, 0, nil, err }
if (profileIsViewable == false){
// Profile is not viewable, so we skip it
if (newestViewableProfileFound == true && newestViewableProfileIsDisabled == true && newestViewableProfileBroadcastTime < profileBroadcastTime){
// This user broadcasted a non-disabled profile after broadcasting their disabled profile
// We reset the newestViewableProfileFound status so that we do not return the disabled profile
// We only allow the disabled profile to be returned if it is the newest profile the user has broadcasted
// Otherwise, users could see a user as being disabled, when in reality their newest profile has not been approved yet
newestViewableProfileFound = false
}
continue
}
}
if (newestViewableProfileFound == false || newestViewableProfileBroadcastTime < profileBroadcastTime){
newestViewableProfileFound = true
newestViewableProfileVersion = profileVersion
newestViewableProfileHash = profileHash
newestViewableProfileIsDisabled = profileIsDisabled
newestViewableProfileBytes = profileBytes
newestViewableProfileRawProfileMap = rawProfileMap
newestViewableProfileBroadcastTime = profileBroadcastTime
}
}
if (newestViewableProfileFound == false){
return false, 0, [28]byte{}, nil, 0, nil, nil
}
if (newestViewableProfileIsDisabled == true && alwaysAllowDisabled == true){
return true, newestViewableProfileVersion, newestViewableProfileHash, newestViewableProfileBytes, newestViewableProfileBroadcastTime, newestViewableProfileRawProfileMap, nil
}
if (identityIsViewableStatus == false){
return false, 0, [28]byte{}, nil, 0, nil, nil
}
return true, newestViewableProfileVersion, newestViewableProfileHash, newestViewableProfileBytes, newestViewableProfileBroadcastTime, newestViewableProfileRawProfileMap, nil
}