mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-07-28 09:59:44 +00:00
core, web: correctly process name verification for channels, show names in directory (#7238)
* core, web: correctly process name verification for channels, show names in directory * remove duplicate verification * remove plan * refactor * remove comments * stabilize tests --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
This commit is contained in:
co-authored by
Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
parent
414f4b6ce1
commit
c1cc568704
@@ -111,7 +111,8 @@ import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), PQSupport (..), patt
|
||||
import Simplex.Messaging.Encoding
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Parsers (base64P)
|
||||
import Simplex.Messaging.Protocol (AProtoServerWithAuth (..), AProtocolType (..), MsgFlags (..), NameRecord (..), NtfServer, ProtoServerWithAuth (..), ProtocolServer, ProtocolType (..), ProtocolTypeI (..), SProtocolType (..), SubscriptionMode (..), UserProtocol, userProtocol)
|
||||
import Simplex.Messaging.Protocol (AProtoServerWithAuth (..), AProtocolType (..), ErrorType (NAME), MsgFlags (..), NameRecord (..), NtfServer, ProtoServerWithAuth (..), ProtocolServer, ProtocolType (..), ProtocolTypeI (..), SProtocolType (..), SubscriptionMode (..), UserProtocol, userProtocol)
|
||||
import qualified Simplex.Messaging.Protocol as SMP
|
||||
import Simplex.Messaging.ServiceScheme (ServiceScheme (..))
|
||||
import qualified Simplex.Messaging.TMap as TM
|
||||
import Simplex.Messaging.Transport.Client (defaultSocksProxyWithAuth)
|
||||
@@ -1863,7 +1864,7 @@ processChatCommand cxt nm = \case
|
||||
(_, cData@(ContactLinkData _ UserContactData {relays = currentRelayLinks})) <- getShortLinkConnReq' nm user sLnk
|
||||
groupSLinkData_ <- liftIO $ decodeLinkUserData cData
|
||||
gInfo' <- case groupSLinkData_ of
|
||||
Just sLinkData -> fst <$> updateGroupFromLinkData user gInfo sLinkData
|
||||
Just sLinkData -> fst <$> updateGroupFromLinkData user gInfo sLinkData Nothing
|
||||
_ -> pure gInfo
|
||||
when (memberRole' (membership gInfo) /= GROwner && memberCurrent (membership gInfo)) $
|
||||
withGroupLock "syncSubscriberRelays" groupId $
|
||||
@@ -2307,11 +2308,18 @@ processChatCommand cxt nm = \case
|
||||
ct' <- maybe (pure ct) (\v -> withFastStore' $ \db -> setContactDomainVerified db user ct v) verified
|
||||
pure $ CRContactDomainVerified user ct' reason
|
||||
APIVerifyGroupDomain groupId -> withUser $ \user -> do
|
||||
g@GroupInfo {groupProfile = GroupProfile {publicGroup}, preparedGroup} <- withFastStore $ \db -> getGroupInfo db cxt user groupId
|
||||
let connLink_ = preparedGroup >>= \PreparedGroup {connLinkToConnect = CCLink _ sLnk_} -> ACSL SCMContact <$> sLnk_
|
||||
domain <- maybe (throwCmdError "group has no name to verify") pure $ publicGroup >>= publicGroupAccess >>= groupDomainClaim
|
||||
(verified, reason) <- verifyEntityDomain user nm NTPublicGroup domain connLink_
|
||||
g' <- maybe (pure g) (\v -> withFastStore' $ \db -> setGroupDomainVerified db user g v) verified
|
||||
g@GroupInfo {groupProfile = GroupProfile {publicGroup}} <- withFastStore $ \db -> getGroupInfo db cxt user groupId
|
||||
PublicGroupProfile {groupLink, publicGroupAccess} <- maybe (throwCmdError "not a public group") pure publicGroup
|
||||
claim <- maybe (throwCmdError "group has no name to verify") pure $ publicGroupAccess >>= groupDomainClaim
|
||||
-- checks the profile link, not the link we joined through (which may have rotated)
|
||||
(verified, reason) <-
|
||||
tryAllErrors (withAgent $ \a -> resolveSimplexName a nm (aUserId user) (claimDomain claim)) >>= \case
|
||||
Right NameRecord {nrSimplexChannel}
|
||||
| nameResolvesTo groupLink nrSimplexChannel -> pure (True, Nothing)
|
||||
| otherwise -> pure (False, Just "the name does not resolve to the link in the group profile")
|
||||
Left (ChatErrorAgent {agentError = SMP _ (NAME SMP.NOT_FOUND)}) -> pure (False, Just "the name is not registered")
|
||||
Left e -> throwError e
|
||||
g' <- withFastStore' $ \db -> setGroupDomainVerified db user g verified
|
||||
pure $ CRGroupDomainVerified user g' reason
|
||||
APIConnectContactViaAddress userId incognito contactId -> withUserId userId $ \user -> do
|
||||
ct@Contact {profile = LocalProfile {contactLink}} <- withFastStore $ \db -> getContact db cxt user contactId
|
||||
@@ -3139,7 +3147,7 @@ processChatCommand cxt nm = \case
|
||||
processChatCommand cxt nm $ APIListGroups userId (contactId' <$> ct_) search_
|
||||
APIUpdateGroupProfile groupId p' -> withUser $ \user -> do
|
||||
gInfo <- withFastStore $ \db -> getGroupInfo db cxt user groupId
|
||||
runUpdateGroupProfile user gInfo p'
|
||||
runUpdateGroupProfile user gInfo p' False
|
||||
UpdateGroupNames gName GroupProfile {displayName, fullName, shortDescr} ->
|
||||
updateGroupProfileByName gName $ \p -> p {displayName, fullName, shortDescr}
|
||||
ShowGroupProfile gName -> withUser $ \user ->
|
||||
@@ -3152,11 +3160,12 @@ processChatCommand cxt nm = \case
|
||||
gInfo@GroupInfo {groupProfile = p@GroupProfile {publicGroup}} <- withStore $ \db -> getGroupInfo db cxt user gId
|
||||
case publicGroup of
|
||||
Just pg@PublicGroupProfile {groupLink, publicGroupAccess = existingAccess} -> do
|
||||
let domainChanged = (claimDomain <$> newClaim) /= (claimDomain <$> (existingAccess >>= groupDomainClaim))
|
||||
forM_ (claimDomain <$> newClaim) $ \newDomain ->
|
||||
when (Just newDomain /= (claimDomain <$> (existingAccess >>= groupDomainClaim))) $ do
|
||||
when domainChanged $ do
|
||||
NameRecord {nrSimplexChannel} <- withAgent $ \a -> resolveSimplexName a nm (aUserId user) newDomain
|
||||
unless (nameResolvesTo groupLink nrSimplexChannel) $ throwChatError $ CESimplexDomainNotReady newDomain SDENoValidLink
|
||||
runUpdateGroupProfile user gInfo p {publicGroup = Just pg {publicGroupAccess = Just access}}
|
||||
runUpdateGroupProfile user gInfo p {publicGroup = Just pg {publicGroupAccess = Just access}} (isJust newClaim && domainChanged)
|
||||
Nothing -> throwChatError $ CECommandError "not a public group"
|
||||
APICreateGroupLink groupId mRole -> withUser $ \user -> withGroupLock "createGroupLink" groupId $ do
|
||||
gInfo@GroupInfo {groupProfile} <- withFastStore $ \db -> getGroupInfo db cxt user groupId
|
||||
@@ -3914,11 +3923,14 @@ processChatCommand cxt nm = \case
|
||||
void (sendDirectContactMessage user ct' $ XInfo p) `catchAllErrors` eToView
|
||||
lift . when (directOrUsed ct') $ createSndFeatureItems user ct ct'
|
||||
pure $ CRContactPrefsUpdated user ct ct'
|
||||
runUpdateGroupProfile :: User -> GroupInfo -> GroupProfile -> CM ChatResponse
|
||||
runUpdateGroupProfile user gInfo@GroupInfo {businessChat, groupProfile = p@GroupProfile {displayName = n}} p'@GroupProfile {displayName = n'} = do
|
||||
runUpdateGroupProfile :: User -> GroupInfo -> GroupProfile -> Bool -> CM ChatResponse
|
||||
runUpdateGroupProfile user gInfo@GroupInfo {businessChat, groupProfile = p@GroupProfile {displayName = n}} p'@GroupProfile {displayName = n'} domainVerified = do
|
||||
assertUserGroupRole gInfo GROwner
|
||||
when (n /= n') $ checkValidName n'
|
||||
gInfo' <- withStore $ \db -> updateGroupProfile db user gInfo p'
|
||||
-- updateGroupProfile clears domain verification; re-set it when the caller already re-resolved the name
|
||||
gInfo' <- withStore $ \db -> do
|
||||
g <- updateGroupProfile db user gInfo p'
|
||||
if domainVerified then liftIO $ setGroupDomainVerified db user g True else pure g
|
||||
msg <- case businessChat of
|
||||
Just BusinessChatInfo {businessId} -> do
|
||||
ms <- withStore' $ \db -> getGroupMembers db cxt user gInfo'
|
||||
@@ -4011,7 +4023,7 @@ processChatCommand cxt nm = \case
|
||||
applicable = if channel then groupFeatureInChannel feature else groupFeatureInRegularGroup feature
|
||||
unless applicable $
|
||||
throwCmdError $ T.unpack (groupFeatureNameText feature) <> " is not available in " <> (if channel then "channels" else "groups")
|
||||
runUpdateGroupProfile user gInfo $ update p
|
||||
runUpdateGroupProfile user gInfo (update p) False
|
||||
withCurrentCall :: ContactId -> (User -> Contact -> Call -> CM (Maybe Call)) -> CM ChatResponse
|
||||
withCurrentCall ctId action = do
|
||||
(user, ct) <- withStore $ \db -> do
|
||||
@@ -4338,8 +4350,8 @@ processChatCommand cxt nm = \case
|
||||
-- e.g. an un-upgraded relay dropped the claim); refresh its profile from the fresh link
|
||||
-- data and mark it verified, so the check below passes and future by-name lookups match
|
||||
plan <- case (planDomain, plan0, groupSLinkData_) of
|
||||
(Just _, CPGroupLink (GLPKnown g u o os), Just sLinkData) ->
|
||||
(\(g', _) -> CPGroupLink (GLPKnown g' u o os)) <$> updateGroupFromLinkData user g sLinkData
|
||||
(Just nameDomain, CPGroupLink (GLPKnown g u o os), Just sLinkData) ->
|
||||
(\(g', _) -> CPGroupLink (GLPKnown g' u o os)) <$> updateGroupFromLinkData user g sLinkData (Just nameDomain)
|
||||
_ -> pure plan0
|
||||
forM_ planDomain $ \nameDomain ->
|
||||
let domain_ = (\GroupProfile {publicGroup} -> claimDomain <$> (publicGroup >>= publicGroupAccess >>= groupDomainClaim)) =<< case plan of
|
||||
@@ -4366,7 +4378,7 @@ processChatCommand cxt nm = \case
|
||||
let ov = verifyLinkOwner rk owners l' sig_
|
||||
glOwners = map (\OwnerAuth {ownerId, ownerKey} -> GroupLinkOwner {memberId = MemberId ownerId, memberKey = ownerKey}) owners
|
||||
(g', updated) <- case groupSLinkData_ of
|
||||
Just sLinkData -> updateGroupFromLinkData user g sLinkData
|
||||
Just sLinkData -> updateGroupFromLinkData user g sLinkData Nothing
|
||||
_ -> pure (g, False)
|
||||
pure (con l' (linkConnReq fd), CPGroupLink (GLPKnown g' updated ov (ListDef glOwners)))
|
||||
-- resolve a name to its first contact/channel short link
|
||||
|
||||
@@ -1469,9 +1469,10 @@ updatePublicGroupData user gInfo
|
||||
withStore $ \db -> updatePublicMemberCount db cxt user gInfo
|
||||
| otherwise = pure gInfo
|
||||
|
||||
updateGroupFromLinkData :: User -> GroupInfo -> GroupShortLinkData -> CM (GroupInfo, Bool)
|
||||
updateGroupFromLinkData user gInfo@GroupInfo {groupProfile = p, groupDomainVerified, groupSummary = GroupSummary {publicMemberCount = localCount}} GroupShortLinkData {groupProfile, publicGroupData}
|
||||
| profileChanged || countChanged || verifyChanged = do
|
||||
-- must not resolve names here: a background link-data refresh would leak channel membership to the resolver
|
||||
updateGroupFromLinkData :: User -> GroupInfo -> GroupShortLinkData -> Maybe SimplexDomain -> CM (GroupInfo, Bool)
|
||||
updateGroupFromLinkData user gInfo@GroupInfo {groupProfile = p, groupSummary = GroupSummary {publicMemberCount = localCount}} GroupShortLinkData {groupProfile, publicGroupData} resolvedDomain_
|
||||
| profileChanged || countChanged || verifyResolved = do
|
||||
cxt <- chatStoreCxt
|
||||
withStore $ \db -> do
|
||||
g <- if profileChanged then updateGroupProfile db user gInfo groupProfile else pure gInfo
|
||||
@@ -1479,9 +1480,7 @@ updateGroupFromLinkData user gInfo@GroupInfo {groupProfile = p, groupDomainVerif
|
||||
Just PublicGroupData {publicMemberCount} | countChanged ->
|
||||
setPublicMemberCount db cxt user g publicMemberCount
|
||||
_ -> pure g
|
||||
-- the group's own link is authoritative for its domain claim, so a claim in the link profile is
|
||||
-- verified; updateGroupProfile above clears verification on a claim change, so set it afterwards
|
||||
g'' <- if verifyChanged then liftIO $ setGroupDomainVerified db user g' True else pure g'
|
||||
g'' <- if verifyResolved then liftIO $ setGroupDomainVerified db user g' True else pure g'
|
||||
pure (g'', profileChanged)
|
||||
| otherwise = pure (gInfo, False)
|
||||
where
|
||||
@@ -1491,7 +1490,7 @@ updateGroupFromLinkData user gInfo@GroupInfo {groupProfile = p, groupDomainVerif
|
||||
_ -> False
|
||||
groupClaim GroupProfile {publicGroup} = claimDomain <$> (publicGroup >>= publicGroupAccess >>= groupDomainClaim)
|
||||
newClaim = groupClaim groupProfile
|
||||
verifyChanged = isJust newClaim && (groupDomainVerified /= Just True || groupClaim p /= newClaim)
|
||||
verifyResolved = isJust resolvedDomain_ && resolvedDomain_ == newClaim
|
||||
|
||||
updateContactFromLinkData :: User -> Contact -> Profile -> CM Contact
|
||||
updateContactFromLinkData user ct@Contact {profile = profile@LocalProfile {contactDomain = prevClaim, contactDomainVerified}} linkProfile@Profile {contactDomain = newClaim}
|
||||
|
||||
Reference in New Issue
Block a user