core, ui: security code verification in channels (verify member key) (#7255)

This commit is contained in:
spaced4ndy
2026-07-15 10:46:31 +00:00
committed by GitHub
parent 6e2995cab6
commit 0fef34914d
28 changed files with 250 additions and 88 deletions
@@ -126,28 +126,25 @@ struct GroupMemberInfoView: View {
&& member.memberRole != .relay
&& ((groupInfo.fullGroupPreferences.support.on && member.memberRole < .moderator)
|| member.supportChat != nil)
let canVerifyCode = connectionCode != nil && member.memberRole != .relay
let canSyncConn = connectionStats?.ratchetSyncAllowed ?? false
if member.memberActive {
if (member.memberActive || (groupInfo.useRelays && member.memberCurrent))
&& (showMemberSupportChat || canVerifyCode || canSyncConn) {
Section {
if showMemberSupportChat {
MemberInfoSupportChatNavLink(groupInfo: groupInfo, member: groupMember, scrollToItemId: $scrollToItemId)
}
if let code = connectionCode,
!(groupInfo.useRelays && member.memberRole == .relay) {
if canVerifyCode, let code = connectionCode {
verifyCodeButton(code)
}
if let connStats = connectionStats,
connStats.ratchetSyncAllowed {
if canSyncConn {
synchronizeConnectionButton()
}
// } else if developerTools {
// synchronizeConnectionButtonForce()
// }
}
} else if groupInfo.useRelays && member.memberCurrent && showMemberSupportChat {
Section {
MemberInfoSupportChatNavLink(groupInfo: groupInfo, member: groupMember, scrollToItemId: $scrollToItemId)
}
}
if let contactLink = member.contactLink {
@@ -300,7 +297,8 @@ struct GroupMemberInfoView: View {
newRole = member.memberRole
do {
let (_, stats) = try await apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId)
let (mem, code) = member.memberActive ? try await apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil)
let getCode = (member.memberActive || (groupInfo.useRelays && member.memberCurrent)) && member.memberRole != .relay
let (mem, code) = getCode ? try await apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil)
await MainActor.run {
_ = chatModel.upsertGroupMember(groupInfo, mem)
connectionStats = stats
@@ -585,12 +583,17 @@ struct GroupMemberInfoView: View {
let (verified, existingCode) = r
let connCode = verified ? SecurityCode(securityCode: existingCode, verifiedAt: .now) : nil
connectionCode = existingCode
member.activeConn?.connectionCode = connCode
if groupInfo.useRelays {
member.memberVerifiedCode = connCode
} else {
member.activeConn?.connectionCode = connCode
}
_ = chatModel.upsertGroupMember(groupInfo, member)
return r
}
return nil
}
},
verificationText: groupInfo.useRelays ? "To verify keys with this subscriber, compare (or scan) the code on your devices." : nil
)
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("Security code")
@@ -15,6 +15,7 @@ struct VerifyCodeView: View {
@State var connectionCode: String?
@State var connectionVerified: Bool
var verify: (String?) -> (Bool, String)?
var verificationText: LocalizedStringKey? = nil
@State private var showCodeError = false
var body: some View {
@@ -44,7 +45,7 @@ struct VerifyCodeView: View {
Text("\(displayName) is not verified").textCase(.none)
}
} footer: {
Text("To verify end-to-end encryption with your contact compare (or scan) the code on your devices.")
Text(verificationText ?? "To verify end-to-end encryption with your contact compare (or scan) the code on your devices.")
}
Section {
+2 -1
View File
@@ -2931,6 +2931,7 @@ public struct GroupMember: Identifiable, Decodable, Hashable {
public var supportChat: GroupSupportChat?
public var memberChatVRange: VersionRange
public var relayLink: String?
public var memberVerifiedCode: SecurityCode?
public var id: String { "#\(groupId) @\(groupMemberId)" }
public var ready: Bool { get { activeConn?.connStatus == .ready } }
@@ -2952,7 +2953,7 @@ public struct GroupMember: Identifiable, Decodable, Hashable {
public var image: String? { get { memberProfile.image } }
public var contactLink: String? { get { memberProfile.contactLink } }
public var nameBadge: LocalBadge? { memberProfile.localBadge }
public var verified: Bool { activeConn?.connectionCode != nil }
public var verified: Bool { memberVerifiedCode != nil || activeConn?.connectionCode != nil }
public var blocked: Bool { blockedByAdmin || !memberSettings.showMessages }
var directChatId: ChatId? {
@@ -2535,7 +2535,8 @@ data class GroupMember (
var activeConn: Connection? = null,
val supportChat: GroupSupportChat? = null,
val memberChatVRange: VersionRange,
val relayLink: String? = null
val relayLink: String? = null,
val memberVerifiedCode: SecurityCode? = null
): NamedChat {
val id: String get() = "#$groupId @$groupMemberId"
val ready get() = activeConn?.connStatus == ConnStatus.Ready
@@ -2555,7 +2556,7 @@ data class GroupMember (
override val shortDescr: String? get() = memberProfile.shortDescr
override val image: String? get() = memberProfile.image
val contactLink: String? = memberProfile.contactLink
val verified get() = activeConn?.connectionCode != null
val verified get() = memberVerifiedCode != null || activeConn?.connectionCode != null
// the badge shown for a member's name; a badge that expired over a month ago (ExpiredOld) is not shown
val nameBadge: LocalBadge? get() {
val badge = memberProfile.localBadge
@@ -12,6 +12,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboardManager
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.StringResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
@@ -29,12 +30,14 @@ fun VerifyCodeView(
connectionVerified: Boolean,
verify: suspend (String?) -> Pair<Boolean, String>?,
close: () -> Unit,
verifyDescription: StringResource = MR.strings.to_verify_compare,
) {
if (connectionCode != null) {
VerifyCodeLayout(
displayName,
connectionCode,
connectionVerified,
verifyDescription,
verifyCode = { newCode ->
val res = verify(newCode)
if (res != null) {
@@ -54,6 +57,7 @@ private fun VerifyCodeLayout(
displayName: String,
connectionCode: String,
connectionVerified: Boolean,
verifyDescription: StringResource,
verifyCode: suspend (String?) -> Boolean,
) {
ColumnWithScrollBar(Modifier.padding(horizontal = DEFAULT_PADDING)) {
@@ -90,7 +94,7 @@ private fun VerifyCodeLayout(
}
Text(
generalGetString(MR.strings.to_verify_compare),
generalGetString(verifyDescription),
Modifier.padding(bottom = DEFAULT_PADDING)
)
@@ -126,7 +126,7 @@ fun ModalData.GroupChatInfoView(
withBGApi {
val r = chatModel.controller.apiGroupMemberInfo(rhId, groupInfo.groupId, member.groupMemberId)
val stats = r?.second
val (_, code) = if (member.memberActive) {
val (_, code) = if ((member.memberActive || (groupInfo.useRelays && member.memberCurrent)) && member.memberRole != GroupMemberRole.Relay) {
val memCode = chatModel.controller.apiGetGroupMemberCode(rhId, groupInfo.apiId, member.groupMemberId)
member to memCode?.second
} else {
@@ -214,11 +214,12 @@ fun GroupMemberInfoView(
verify = { code ->
chatModel.controller.apiVerifyGroupMember(rhId, mem.groupId, mem.groupMemberId, code)?.let { r ->
val (verified, existingCode) = r
val copy = mem.copy(
activeConn = mem.activeConn?.copy(
connectionCode = if (verified) SecurityCode(existingCode, Clock.System.now()) else null
)
)
val code = if (verified) SecurityCode(existingCode, Clock.System.now()) else null
val copy = if (groupInfo.useRelays) {
mem.copy(memberVerifiedCode = code)
} else {
mem.copy(activeConn = mem.activeConn?.copy(connectionCode = code))
}
withContext(Dispatchers.Main) {
chatModel.chatsContext.upsertGroupMember(rhId, groupInfo, copy)
}
@@ -229,6 +230,7 @@ fun GroupMemberInfoView(
}
},
close,
verifyDescription = if (groupInfo.useRelays) MR.strings.to_verify_channel_member_key else MR.strings.to_verify_compare,
)
}
}
@@ -541,16 +543,19 @@ fun GroupMemberInfoLayout(
member.memberRole != GroupMemberRole.Relay &&
((groupInfo.fullGroupPreferences.support.on && member.memberRole < GroupMemberRole.Moderator)
|| member.supportChat != null)
val canVerifyCode = connectionCode != null && member.memberRole != GroupMemberRole.Relay
val canSyncConn = cStats != null && cStats.ratchetSyncAllowed
if (member.memberActive) {
if ((member.memberActive || (groupInfo.useRelays && member.memberCurrent))
&& (showMemberSupportChat || canVerifyCode || canSyncConn)) {
SectionView {
if (showMemberSupportChat) {
SupportChatButton()
}
if (connectionCode != null && !(groupInfo.useRelays && member.memberRole == GroupMemberRole.Relay)) {
if (canVerifyCode) {
VerifyCodeButton(member.verified, verifyClicked)
}
if (cStats != null && cStats.ratchetSyncAllowed) {
if (canSyncConn) {
SynchronizeConnectionButton(syncMemberConnection)
}
// } else if (developerTools) {
@@ -558,11 +563,6 @@ fun GroupMemberInfoLayout(
// }
}
SectionDividerSpaced()
} else if (groupInfo.useRelays && member.memberCurrent && showMemberSupportChat) {
SectionView {
SupportChatButton()
}
SectionDividerSpaced()
}
if (member.contactLink != null) {
@@ -79,7 +79,7 @@ fun MemberSupportChatAppBar(
withBGApi {
val r = chatModel.controller.apiGroupMemberInfo(rhId, groupInfo.groupId, scopeMember_.groupMemberId)
val stats = r?.second
val code = if (scopeMember_.memberActive) {
val code = if ((scopeMember_.memberActive || (groupInfo.useRelays && scopeMember_.memberCurrent)) && scopeMember_.memberRole != GroupMemberRole.Relay) {
val memCode = chatModel.controller.apiGetGroupMemberCode(rhId, groupInfo.apiId, scopeMember_.groupMemberId)
memCode?.second
} else {
@@ -1013,6 +1013,7 @@
<string name="mark_code_verified">Mark verified</string>
<string name="clear_verification">Clear verification</string>
<string name="to_verify_compare">To verify end-to-end encryption with your contact compare (or scan) the code on your devices.</string>
<string name="to_verify_channel_member_key">To verify keys with this subscriber, compare (or scan) the code on your devices.</string>
<string name="is_verified">%s is verified</string>
<string name="is_not_verified">%s is not verified</string>
+1
View File
@@ -2455,6 +2455,7 @@ UpdateRequired:
- supportChat: [GroupSupportChat](#groupsupportchat)?
- memberPubKey: string?
- relayLink: string?
- memberVerifiedCode: [SecurityCode](#securitycode)?
---
@@ -2747,6 +2747,7 @@ export interface GroupMember {
supportChat?: GroupSupportChat
memberPubKey?: string
relayLink?: string
memberVerifiedCode?: SecurityCode
}
export interface GroupMemberAdmission {
@@ -1935,6 +1935,7 @@ class GroupMember(TypedDict):
supportChat: NotRequired["GroupSupportChat"]
memberPubKey: NotRequired[str]
relayLink: NotRequired[str]
memberVerifiedCode: NotRequired["SecurityCode"]
class GroupMemberAdmission(TypedDict):
review: NotRequired["MemberCriteria"]
+2
View File
@@ -148,6 +148,7 @@ library
Simplex.Chat.Store.Postgres.Migrations.M20260603_simplex_name
Simplex.Chat.Store.Postgres.Migrations.M20260629_roster_catchup
Simplex.Chat.Store.Postgres.Migrations.M20260707_file_digest
Simplex.Chat.Store.Postgres.Migrations.M20260714_member_security_code
else
exposed-modules:
Simplex.Chat.Archive
@@ -313,6 +314,7 @@ library
Simplex.Chat.Store.SQLite.Migrations.M20260603_simplex_name
Simplex.Chat.Store.SQLite.Migrations.M20260629_roster_catchup
Simplex.Chat.Store.SQLite.Migrations.M20260707_file_digest
Simplex.Chat.Store.SQLite.Migrations.M20260714_member_security_code
other-modules:
Paths_simplex_chat
hs-source-dirs:
+38 -10
View File
@@ -1990,28 +1990,41 @@ processChatCommand cxt nm = \case
Nothing -> throwChatError $ CEContactNotActive ct
APIGetGroupMemberCode gId gMemberId -> withUser $ \user -> do
(g, m@GroupMember {activeConn}) <- withFastStore $ \db -> (,) <$> getGroupInfo db cxt user gId <*> getGroupMember db cxt user gId gMemberId
case activeConn of
Just conn@Connection {connId} -> do
code <- getConnectionCode $ aConnId conn
if useRelays' g
then do
code <- getChannelMemberCode g m
m' <- case memberSecurityCode m of
Just SecurityCode {securityCode}
| sameVerificationCode code securityCode -> pure m
| otherwise -> do
withFastStore' $ \db -> setConnectionVerified db user connId Nothing
pure (m :: GroupMember) {activeConn = Just $ (conn :: Connection) {connectionCode = Nothing}}
withFastStore' $ \db -> setGroupMemberVerified db user (groupMemberId' m) Nothing
pure m {memberVerifiedCode = Nothing}
_ -> pure m
pure $ CRGroupMemberCode user g m' code
_ -> throwChatError CEGroupMemberNotActive
else case activeConn of
Just conn@Connection {connId} -> do
code <- getConnectionCode $ aConnId conn
m' <- case memberSecurityCode m of
Just SecurityCode {securityCode}
| sameVerificationCode code securityCode -> pure m
| otherwise -> do
withFastStore' $ \db -> setConnectionVerified db user connId Nothing
pure (m :: GroupMember) {activeConn = Just $ (conn :: Connection) {connectionCode = Nothing}}
_ -> pure m
pure $ CRGroupMemberCode user g m' code
_ -> throwChatError CEGroupMemberNotActive
APIVerifyContact contactId code -> withUser $ \user -> do
ct@Contact {activeConn} <- withFastStore $ \db -> getContact db cxt user contactId
case activeConn of
Just conn -> verifyConnectionCode user conn code
Nothing -> throwChatError $ CEContactNotActive ct
APIVerifyGroupMember gId gMemberId code -> withUser $ \user -> do
GroupMember {activeConn} <- withFastStore $ \db -> getGroupMember db cxt user gId gMemberId
case activeConn of
Just conn -> verifyConnectionCode user conn code
_ -> throwChatError CEGroupMemberNotActive
(g, m@GroupMember {activeConn}) <- withFastStore $ \db -> (,) <$> getGroupInfo db cxt user gId <*> getGroupMember db cxt user gId gMemberId
if useRelays' g
then verifyChannelMemberCode user g m code
else case activeConn of
Just conn -> verifyConnectionCode user conn code
_ -> throwChatError CEGroupMemberNotActive
APIEnableContact contactId -> withUser $ \user -> do
ct@Contact {activeConn} <- withFastStore $ \db -> getContact db cxt user contactId
case activeConn of
@@ -3661,6 +3674,11 @@ processChatCommand cxt nm = \case
getGroupAndMemberId user gName mName >>= processChatCommand cxt nm . uncurry cmd
getConnectionCode :: ConnId -> CM Text
getConnectionCode connId = verificationCode <$> withAgent (`getConnectionRatchetAdHash` connId)
getChannelMemberCode :: GroupInfo -> GroupMember -> CM Text
getChannelMemberCode GroupInfo {membership} m =
case (memberPubKey membership, memberPubKey m) of
(Just ownKey, Just memKey) -> pure $ channelMemberCode ownKey memKey
_ -> throwCmdError "no member key to compute security code"
verifyConnectionCode :: User -> Connection -> Maybe Text -> CM ChatResponse
verifyConnectionCode user conn@Connection {connId} (Just code) = do
code' <- getConnectionCode $ aConnId conn
@@ -3671,6 +3689,16 @@ processChatCommand cxt nm = \case
code' <- getConnectionCode $ aConnId conn
withFastStore' $ \db -> setConnectionVerified db user connId Nothing
pure $ CRConnectionVerified user False code'
verifyChannelMemberCode :: User -> GroupInfo -> GroupMember -> Maybe Text -> CM ChatResponse
verifyChannelMemberCode user g m (Just code) = do
code' <- getChannelMemberCode g m
let verified = sameVerificationCode code code'
when verified . withFastStore' $ \db -> setGroupMemberVerified db user (groupMemberId' m) $ Just code'
pure $ CRConnectionVerified user verified code'
verifyChannelMemberCode user g m _ = do
code' <- getChannelMemberCode g m
withFastStore' $ \db -> setGroupMemberVerified db user (groupMemberId' m) Nothing
pure $ CRConnectionVerified user False code'
getSentChatItemIdByText :: User -> ChatRef -> Text -> CM Int64
getSentChatItemIdByText user@User {userId, localDisplayName} (ChatRef cType cId _scope) msg = case cType of
CTDirect -> withFastStore $ \db -> getDirectChatItemIdByText db userId cId SMDSnd msg
+2 -2
View File
@@ -159,13 +159,13 @@ getConnectionEntity db cxt user@User {userId, userContactId} agentConnId = do
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link, mu.member_security_code, mu.member_security_code_verified_at,
-- from GroupMember
m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at
FROM group_members m
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
JOIN groups g ON g.group_id = m.group_id
+18 -6
View File
@@ -107,6 +107,7 @@ module Simplex.Chat.Store.Groups
deleteRosterTransfer,
deleteGroupRosterTransfers,
setGroupMemberKeyRole,
setGroupMemberVerified,
createRelayForOwner,
getCreateRelayForMember,
createRelayConnection,
@@ -259,11 +260,11 @@ import Database.SQLite.Simple (Only (..), Query, (:.) (..))
import Database.SQLite.Simple.QQ (sql)
#endif
type MaybeGroupMemberRow = (Maybe GroupMemberId, Maybe GroupId, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId) :. ((Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe LocalAlias, Maybe Preferences) :. BadgeRow :. ContactDomainRow) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact)
type MaybeGroupMemberRow = (Maybe GroupMemberId, Maybe GroupId, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId) :. ((Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe LocalAlias, Maybe Preferences) :. BadgeRow :. ContactDomainRow) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact, Maybe Text, Maybe UTCTime)
toMaybeGroupMember :: UTCTime -> Int64 -> MaybeGroupMemberRow -> Maybe GroupMember
toMaybeGroupMember now userContactId ((Just groupMemberId, Just groupId, Just indexInGroup, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId) :. ((Just profileId, Just displayName, Just fullName, shortDescr, image, contactLink, peerType, Just localAlias, contactPreferences) :. badgeRow :. domainRow) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink)) =
Just $ toGroupMember now userContactId ((groupMemberId, groupId, indexInGroup, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. ((profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, contactPreferences) :. badgeRow :. domainRow) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink))
toMaybeGroupMember now userContactId ((Just groupMemberId, Just groupId, Just indexInGroup, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId) :. ((Just profileId, Just displayName, Just fullName, shortDescr, image, contactLink, peerType, Just localAlias, contactPreferences) :. badgeRow :. domainRow) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink, memberCode_, memberCodeVerifiedAt_)) =
Just $ toGroupMember now userContactId ((groupMemberId, groupId, indexInGroup, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. ((profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, contactPreferences) :. badgeRow :. domainRow) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink, memberCode_, memberCodeVerifiedAt_))
toMaybeGroupMember _ _ _ = Nothing
createGroupLink :: DB.Connection -> TVar ChaChaDRG -> User -> GroupInfo -> ConnId -> CreatedLinkContact -> GroupLinkId -> GroupMemberRole -> SubscriptionMode -> ExceptT StoreError IO GroupLink
@@ -598,7 +599,8 @@ createContactMemberInv_ db User {userId, userContactId} groupId invitedByGroupMe
updatedAt = createdAt,
supportChat = Nothing,
memberPubKey,
relayLink = Nothing
relayLink = Nothing,
memberVerifiedCode = Nothing
}
where
memberChatVRange@(VersionRange minV maxV) = vr
@@ -1401,7 +1403,8 @@ createNewContactMember db gVar User {userId, userContactId} GroupInfo {groupId,
updatedAt = createdAt,
supportChat = Nothing,
memberPubKey = Nothing,
relayLink = Nothing
relayLink = Nothing,
memberVerifiedCode = Nothing
}
where
insertMember_ = do
@@ -1685,6 +1688,14 @@ setGroupMemberKeyRole db GroupMember {groupMemberId} pubKey role = do
currentTs <- getCurrentTime
DB.execute db "UPDATE group_members SET member_pub_key = ?, member_role = ?, updated_at = ? WHERE group_member_id = ?" (pubKey, role, currentTs, groupMemberId)
setGroupMemberVerified :: DB.Connection -> User -> GroupMemberId -> Maybe Text -> IO ()
setGroupMemberVerified db User {userId} groupMemberId code = do
updatedAt <- getCurrentTime
DB.execute
db
"UPDATE group_members SET member_security_code = ?, member_security_code_verified_at = ?, updated_at = ? WHERE user_id = ? AND group_member_id = ?"
(code, code $> updatedAt, updatedAt, userId, groupMemberId)
createRelayForOwner :: DB.Connection -> StoreCxt -> TVar ChaChaDRG -> User -> GroupInfo -> UserChatRelay -> ExceptT StoreError IO GroupMember
createRelayForOwner db cxt gVar user@User {userId, userContactId} GroupInfo {groupId, membership} UserChatRelay {relayProfile = RelayProfile {displayName}} = do
currentTs <- liftIO getCurrentTime
@@ -2516,7 +2527,8 @@ createNewMember_
updatedAt = createdAt,
supportChat = Nothing,
memberPubKey,
relayLink = Nothing
relayLink = Nothing,
memberVerifiedCode = Nothing
}
checkGroupMemberHasItems :: DB.Connection -> User -> GroupMember -> IO (Maybe ChatItemId)
+4 -4
View File
@@ -717,7 +717,7 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at
FROM group_members m
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
LEFT JOIN contacts c ON m.contact_id = c.contact_id
@@ -3072,7 +3072,7 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
-- quoted ChatItem
ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent,
-- quoted GroupMember
@@ -3081,14 +3081,14 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
rp.display_name, rp.full_name, rp.short_descr, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences,
rp.badge_proof, rp.badge_pres_header, rp.badge_expiry, rp.badge_type, rp.badge_verified, rp.badge_extra, rp.badge_master_key, rp.badge_signature, rp.badge_key_idx, rp.contact_domain, rp.contact_domain_proof, rp.contact_domain_verified,
rm.created_at, rm.updated_at,
rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts, rm.member_pub_key, rm.relay_link,
rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts, rm.member_pub_key, rm.relay_link, rm.member_security_code, rm.member_security_code_verified_at,
-- deleted by GroupMember
dbm.group_member_id, dbm.group_id, dbm.index_in_group, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category,
dbm.member_status, dbm.show_messages, dbm.member_restriction, dbm.invited_by, dbm.invited_by_group_member_id, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id,
dbp.display_name, dbp.full_name, dbp.short_descr, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences,
dbp.badge_proof, dbp.badge_pres_header, dbp.badge_expiry, dbp.badge_type, dbp.badge_verified, dbp.badge_extra, dbp.badge_master_key, dbp.badge_signature, dbp.badge_key_idx, dbp.contact_domain, dbp.contact_domain_proof, dbp.contact_domain_verified,
dbm.created_at, dbm.updated_at,
dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts, dbm.member_pub_key, dbm.relay_link
dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts, dbm.member_pub_key, dbm.relay_link, dbm.member_security_code, dbm.member_security_code_verified_at
FROM chat_items i
LEFT JOIN files f ON f.chat_item_id = i.chat_item_id
LEFT JOIN group_members m ON m.group_member_id = i.group_member_id
@@ -41,6 +41,7 @@ import Simplex.Chat.Store.Postgres.Migrations.M20260602_group_roster
import Simplex.Chat.Store.Postgres.Migrations.M20260603_simplex_name
import Simplex.Chat.Store.Postgres.Migrations.M20260629_roster_catchup
import Simplex.Chat.Store.Postgres.Migrations.M20260707_file_digest
import Simplex.Chat.Store.Postgres.Migrations.M20260714_member_security_code
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
schemaMigrations :: [(String, Text, Maybe Text)]
@@ -81,7 +82,8 @@ schemaMigrations =
("20260602_group_roster", m20260602_group_roster, Just down_m20260602_group_roster),
("20260603_simplex_name", m20260603_simplex_name, Just down_m20260603_simplex_name),
("20260629_roster_catchup", m20260629_roster_catchup, Just down_m20260629_roster_catchup),
("20260707_file_digest", m20260707_file_digest, Just down_m20260707_file_digest)
("20260707_file_digest", m20260707_file_digest, Just down_m20260707_file_digest),
("20260714_member_security_code", m20260714_member_security_code, Just down_m20260714_member_security_code)
]
-- | The list of migrations in ascending order by date
@@ -0,0 +1,21 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
module Simplex.Chat.Store.Postgres.Migrations.M20260714_member_security_code where
import Data.Text (Text)
import Text.RawString.QQ (r)
m20260714_member_security_code :: Text
m20260714_member_security_code =
[r|
ALTER TABLE group_members ADD COLUMN member_security_code TEXT;
ALTER TABLE group_members ADD COLUMN member_security_code_verified_at TIMESTAMPTZ;
|]
down_m20260714_member_security_code :: Text
down_m20260714_member_security_code =
[r|
ALTER TABLE group_members DROP COLUMN member_security_code;
ALTER TABLE group_members DROP COLUMN member_security_code_verified_at;
|]
@@ -836,7 +836,9 @@ CREATE TABLE test_chat_schema.group_members (
relay_link bytea,
member_pub_key bytea,
removed_at timestamp with time zone,
roster_served_version bigint
roster_served_version bigint,
member_security_code text,
member_security_code_verified_at timestamp with time zone
);
+3 -1
View File
@@ -164,6 +164,7 @@ import Simplex.Chat.Store.SQLite.Migrations.M20260602_group_roster
import Simplex.Chat.Store.SQLite.Migrations.M20260603_simplex_name
import Simplex.Chat.Store.SQLite.Migrations.M20260629_roster_catchup
import Simplex.Chat.Store.SQLite.Migrations.M20260707_file_digest
import Simplex.Chat.Store.SQLite.Migrations.M20260714_member_security_code
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
schemaMigrations :: [(String, Query, Maybe Query)]
@@ -327,7 +328,8 @@ schemaMigrations =
("20260602_group_roster", m20260602_group_roster, Just down_m20260602_group_roster),
("20260603_simplex_name", m20260603_simplex_name, Just down_m20260603_simplex_name),
("20260629_roster_catchup", m20260629_roster_catchup, Just down_m20260629_roster_catchup),
("20260707_file_digest", m20260707_file_digest, Just down_m20260707_file_digest)
("20260707_file_digest", m20260707_file_digest, Just down_m20260707_file_digest),
("20260714_member_security_code", m20260714_member_security_code, Just down_m20260714_member_security_code)
]
-- | The list of migrations in ascending order by date
@@ -0,0 +1,20 @@
{-# LANGUAGE QuasiQuotes #-}
module Simplex.Chat.Store.SQLite.Migrations.M20260714_member_security_code where
import Database.SQLite.Simple (Query)
import Database.SQLite.Simple.QQ (sql)
m20260714_member_security_code :: Query
m20260714_member_security_code =
[sql|
ALTER TABLE group_members ADD COLUMN member_security_code TEXT;
ALTER TABLE group_members ADD COLUMN member_security_code_verified_at TEXT;
|]
down_m20260714_member_security_code :: Query
down_m20260714_member_security_code =
[sql|
ALTER TABLE group_members DROP COLUMN member_security_code;
ALTER TABLE group_members DROP COLUMN member_security_code_verified_at;
|]
@@ -159,13 +159,13 @@ Query:
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link, mu.member_security_code, mu.member_security_code_verified_at,
-- from GroupMember
m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at
FROM group_members m
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
JOIN groups g ON g.group_id = m.group_id
@@ -1070,7 +1070,7 @@ Query:
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at
FROM group_members m
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
LEFT JOIN contacts c ON m.contact_id = c.contact_id
@@ -1379,7 +1379,7 @@ Query:
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
-- quoted ChatItem
ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent,
-- quoted GroupMember
@@ -1388,14 +1388,14 @@ Query:
rp.display_name, rp.full_name, rp.short_descr, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences,
rp.badge_proof, rp.badge_pres_header, rp.badge_expiry, rp.badge_type, rp.badge_verified, rp.badge_extra, rp.badge_master_key, rp.badge_signature, rp.badge_key_idx, rp.contact_domain, rp.contact_domain_proof, rp.contact_domain_verified,
rm.created_at, rm.updated_at,
rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts, rm.member_pub_key, rm.relay_link,
rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts, rm.member_pub_key, rm.relay_link, rm.member_security_code, rm.member_security_code_verified_at,
-- deleted by GroupMember
dbm.group_member_id, dbm.group_id, dbm.index_in_group, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category,
dbm.member_status, dbm.show_messages, dbm.member_restriction, dbm.invited_by, dbm.invited_by_group_member_id, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id,
dbp.display_name, dbp.full_name, dbp.short_descr, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences,
dbp.badge_proof, dbp.badge_pres_header, dbp.badge_expiry, dbp.badge_type, dbp.badge_verified, dbp.badge_extra, dbp.badge_master_key, dbp.badge_signature, dbp.badge_key_idx, dbp.contact_domain, dbp.contact_domain_proof, dbp.contact_domain_verified,
dbm.created_at, dbm.updated_at,
dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts, dbm.member_pub_key, dbm.relay_link
dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts, dbm.member_pub_key, dbm.relay_link, dbm.member_security_code, dbm.member_security_code_verified_at
FROM chat_items i
LEFT JOIN files f ON f.chat_item_id = i.chat_item_id
LEFT JOIN group_members m ON m.group_member_id = i.group_member_id
@@ -5556,7 +5556,7 @@ Query:
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link, mu.member_security_code, mu.member_security_code_verified_at
FROM groups g
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
@@ -5594,7 +5594,7 @@ Query:
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link, mu.member_security_code, mu.member_security_code_verified_at
FROM groups g
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
@@ -5625,7 +5625,7 @@ Query:
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link, mu.member_security_code, mu.member_security_code_verified_at
FROM groups g
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
@@ -5678,7 +5678,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -5706,7 +5706,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -5727,7 +5727,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -5747,7 +5747,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -5767,7 +5767,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -5787,7 +5787,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -5807,7 +5807,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -5827,7 +5827,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -5847,7 +5847,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -5867,7 +5867,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -5887,7 +5887,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -5907,7 +5907,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -5927,7 +5927,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -5947,7 +5947,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -5967,7 +5967,7 @@ Query:
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -7720,6 +7720,10 @@ Query: UPDATE group_members SET member_role = ?, member_pub_key = NULL WHERE loc
Plan:
SCAN group_members
Query: UPDATE group_members SET member_security_code = ?, member_security_code_verified_at = ?, updated_at = ? WHERE user_id = ? AND group_member_id = ?
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query: UPDATE group_members SET roster_served_version = ?, updated_at = ? WHERE group_member_id = ?
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
@@ -252,6 +252,8 @@ CREATE TABLE group_members(
member_pub_key BLOB,
removed_at TEXT,
roster_served_version INTEGER,
member_security_code TEXT,
member_security_code_verified_at TEXT,
FOREIGN KEY(user_id, local_display_name)
REFERENCES display_names(user_id, local_display_name)
ON DELETE CASCADE
+5 -4
View File
@@ -687,7 +687,7 @@ type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe
type PublicGroupAccessRow = (Maybe Text, Maybe SimplexDomain, Maybe BoolInt, Maybe BoolInt, Maybe SimplexDomainProof)
type GroupMemberRow = (GroupMemberId, GroupId, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId) :. ProfileRow :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact)
type GroupMemberRow = (GroupMemberId, GroupId, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId) :. ProfileRow :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact, Maybe Text, Maybe UTCTime)
type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, Maybe Preferences) :. BadgeRow :. ContactDomainRow
@@ -737,12 +737,13 @@ toGroupKeys (Just publicGroupId) (rootPrivKey_, rootPubKey_, Just memberPrivKey)
toGroupKeys _ _ = Nothing
toGroupMember :: UTCTime -> Int64 -> GroupMemberRow -> GroupMember
toGroupMember now userContactId ((groupMemberId, groupId, indexInGroup, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. profileRow :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink)) =
toGroupMember now userContactId ((groupMemberId, groupId, indexInGroup, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. profileRow :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink, memberCode_, memberCodeVerifiedAt_)) =
let memberProfile = rowToLocalProfile now profileRow
memberSettings = GroupMemberSettings {showMessages}
blockedByAdmin = maybe False mrsBlocked memberRestriction_
invitedBy = toInvitedBy userContactId invitedById
activeConn = Nothing
memberVerifiedCode = SecurityCode <$> memberCode_ <*> memberCodeVerifiedAt_
memberChatVRange = fromMaybe (versionToRange maxVer) $ safeVersionRange minVer maxVer
supportChat = case supportChatTs_ of
Just chatTs ->
@@ -765,7 +766,7 @@ groupMemberQuery =
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
p.badge_proof, p.badge_pres_header, p.badge_expiry, p.badge_type, p.badge_verified, p.badge_extra, p.badge_master_key, p.badge_signature, p.badge_key_idx, p.contact_domain, p.contact_domain_proof, p.contact_domain_verified,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link, m.member_security_code, m.member_security_code_verified_at,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -810,7 +811,7 @@ groupInfoQueryFields =
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
pu.badge_proof, pu.badge_pres_header, pu.badge_expiry, pu.badge_type, pu.badge_verified, pu.badge_extra, pu.badge_master_key, pu.badge_signature, pu.badge_key_idx, pu.contact_domain, pu.contact_domain_proof, pu.contact_domain_verified,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link, mu.member_security_code, mu.member_security_code_verified_at
|]
groupInfoQueryFrom :: Query
+14 -2
View File
@@ -1139,7 +1139,10 @@ data GroupMember = GroupMember
updatedAt :: UTCTime,
supportChat :: Maybe GroupSupportChat,
memberPubKey :: Maybe C.PublicKeyEd25519,
relayLink :: Maybe ShortLinkContact
relayLink :: Maybe ShortLinkContact,
-- out-of-band verified security code for connectionless (channel) members;
-- regular members carry it in activeConn instead (see memberSecurityCode)
memberVerifiedCode :: Maybe SecurityCode
}
deriving (Eq, Show)
@@ -1219,7 +1222,7 @@ incognitoMembershipProfile GroupInfo {membership = m@GroupMember {memberProfile}
| otherwise = Nothing
memberSecurityCode :: GroupMember -> Maybe SecurityCode
memberSecurityCode GroupMember {activeConn} = connectionCode =<< activeConn
memberSecurityCode GroupMember {activeConn, memberVerifiedCode} = memberVerifiedCode <|> (connectionCode =<< activeConn)
memberBlocked :: GroupMember -> Bool
memberBlocked m = blockedByAdmin m || not (showMessages $ memberSettings m)
@@ -1904,6 +1907,15 @@ sameVerificationCode c1 c2 = noSpaces c1 == noSpaces c2
where
noSpaces = T.filter (/= ' ')
-- keys are ordered so both members derive the same code regardless of who computes it
channelMemberCode :: C.PublicKeyEd25519 -> C.PublicKeyEd25519 -> Text
channelMemberCode k1 k2 =
let (lo, hi) = if b1 <= b2 then (b1, b2) else (b2, b1)
in verificationCode $ C.sha256Hash (lo <> hi)
where
b1 = C.pubKeyBytes k1
b2 = C.pubKeyBytes k2
aConnId :: Connection -> ConnId
aConnId Connection {agentConnId = AgentConnId cId} = cId
+3 -1
View File
@@ -1864,8 +1864,10 @@ viewGroupMemberInfo GroupInfo {groupId} m@GroupMember {groupMemberId, memberProf
<> maybe ["member not connected"] viewConnectionStats stats
<> maybe [] (\l -> ["contact address: " <> (plain . strEncode) (simplexChatContact' l)]) contactLink
<> ["alias: " <> plain localAlias | localAlias /= ""]
<> [viewConnectionVerified (memberSecurityCode m) | isJust stats]
<> [viewConnectionVerified mSecurityCode | isJust stats || isJust mSecurityCode]
<> maybe [] (\ac -> [viewPeerChatVRange (peerChatVRange ac)]) activeConn
where
mSecurityCode = memberSecurityCode m
viewConnectionVerified :: Maybe SecurityCode -> StyledString
viewConnectionVerified (Just _) = "connection verified" -- TODO show verification time?
+38
View File
@@ -281,6 +281,7 @@ chatGroupTests = do
it "should change member role (signed)" testChannelChangeRoleSigned
it "should block member for all (signed)" testChannelBlockMemberSigned
it "should remove member (signed)" testChannelRemoveMemberSigned
it "should verify member security code via membership keys" testChannelMemberSecurityCode
it "should delete channel (signed)" testChannelDeleteGroupSigned
it "should delete channel and clean up relay connections" testChannelDeleteGroupCleanup
it "owner should leave channel (signed)" testChannelOwnerLeave
@@ -8790,6 +8791,43 @@ setupRelay owner relay = do
owner <## "ok"
pure relaySLink
testChannelMemberSecurityCode :: HasCallStack => TestParams -> IO ()
testChannelMemberSecurityCode ps =
withNewTestChat ps "alice" aliceProfile $ \alice ->
withNewTestChatOpts ps relayTestOpts "bob" bobProfile $ \bob ->
withNewTestChat ps "cath" cathProfile $ \cath -> do
(shortLink, fullLink) <- prepareChannel1Relay "team" alice bob
memberJoinChannel "team" [bob] [alice] shortLink fullLink cath
-- a channel message lets the relay-forwarded member keys settle on both sides
alice #> "#team hi"
bob <# "#team> hi"
cath <# "#team> hi [>>]"
threadDelay 1000000
-- owner and subscriber derive the same code from their membership keys
alice ##> "/code #team cath"
aCode <- getTermLine alice
cath ##> "/code #team alice"
cCode <- getTermLine cath
aCode `shouldBe` cCode
-- a wrong code does not verify
alice ##> "/verify #team cath 123"
alice <##. "connection not verified, current code is "
-- the correct code verifies and the verification persists
alice ##> ("/verify #team cath " <> aCode)
alice <## "connection verified"
alice ##> "/i #team cath"
alice <## "group ID: 1"
alice <##. "member ID: "
alice <## "member not connected"
alice <## "connection verified"
-- verification can be cleared
alice ##> "/verify #team cath"
alice <##. "connection not verified, current code is "
alice ##> "/i #team cath"
alice <## "group ID: 1"
alice <##. "member ID: "
alice <## "member not connected"
prepareChannel1Relay :: String -> TestCC -> TestCC -> IO (String, String)
prepareChannel1Relay gName owner relay = do
_ <- setupRelay owner relay