Managed Group Refinement
continuous-integration/drone/pr Build is passing Details

- Add NoAccessControl Fallback
- Prevent Facade Contacts from the Contact Retry Plugin
- Force Group Manager to Save History by Default
- Fix Ack'ing on Channel_Manager
This commit is contained in:
Sarah Jamie Lewis 2024-06-11 10:54:00 -07:00
parent c5aa6905a4
commit 229743c507
Signed by: sarah
GPG Key ID: F27FD21A270837EF
5 changed files with 34 additions and 10 deletions

View File

@ -83,6 +83,7 @@ func AuthenticateMessage(message HybridGroupMessage) bool {
return ed25519.Verify(decodedPub[:32], data, message.Signature)
}
}
log.Errorf("invalid signature on message from %s", message)
return false
}

View File

@ -180,6 +180,7 @@ func (f *GroupManagerFunctionality) ManageNewGroup(profile peer.CwtchPeer) (int,
ac.ManageGroup = true
acl := model.AccessControlList{}
acl[profile.GetOnion()] = ac
acl[MANAGED_GROUP_HANDLE] = model.NoAccessControl()
ci, err := profile.NewConversation(MANAGED_GROUP_HANDLE, acl)
if err != nil {
return -1, err
@ -212,6 +213,8 @@ func (f *GroupManagerFunctionality) AddHybridContact(profile peer.CwtchPeer, han
profile.InitChannel(ci, constants.CHANNEL_MANAGER)
key := fmt.Sprintf("channel.%d", constants.CHANNEL_MANAGER)
profile.SetConversationAttribute(ci, attr.LocalScope.ConstructScopedZonedPath(attr.ConversationZone.ConstructZonedPath(key)), constants.True)
// Group managers need to always save history (and manually deal with purging...)
profile.SetConversationAttribute(ci, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(event.SaveHistoryKey)), event.SaveHistoryConfirmed)
return nil
}

View File

@ -35,6 +35,14 @@ func DefaultP2PAccessControl() AccessControl {
AutoConnect: true, ShareFiles: false, RenderImages: false}
}
// NoAccessControl defaults to a none-trusted peer with no access to special extensions.
// This is used as a fall back (if due to a software glitch a contact was setup without an access control, or for
// special contacts that should never be involvedt in external networking e.g. notes-to-self, or managed peers)
func NoAccessControl() AccessControl {
return AccessControl{Read: false, Append: false, ExchangeAttributes: false, Blocked: false,
AutoConnect: false, ShareFiles: false, RenderImages: false}
}
// AccessControlList represents an access control list for a conversation. Mapping handles to conversation
// functions
type AccessControlList map[string]AccessControl
@ -99,8 +107,8 @@ func (ci *Conversation) GetPeerAC() AccessControl {
if acl, exists := ci.ACL[ci.Handle]; exists {
return acl
}
log.Errorf("attempted to access a Peer Access Control object from %v but peer ACL is undefined. This is likely a programming error", ci.Handle)
return DefaultP2PAccessControl()
log.Errorf("attempted to access a Peer Access Control object from %v but peer ACL is undefined. This is likely a programming error - fallback to a NoAccess AC", ci.Handle)
return NoAccessControl()
}
// HasChannel returns true if the requested channel has been setup for this conversation

View File

@ -1176,6 +1176,12 @@ func (cp *cwtchPeer) DisconnectFromServer(onion string) {
// QueuePeeringWithOnion sends the request to peer with an onion directly to the contact retry queue; this is a mechanism to not flood tor with circuit requests
// Status: Ready for 1.10
func (cp *cwtchPeer) QueuePeeringWithOnion(handle string) {
// don't queue peers that do not have an onion addresses as a handle
// TODO establish a more robust separation between peerable contacts and
// facade contacts (managed groups, notes-to-self etc.)
if !tor.IsValidHostname(handle) {
return
}
ci, err := cp.FetchConversationInfo(handle)
if err == nil {
lastSeen := cp.GetConversationLastSeenTime(ci.ID)
@ -1882,7 +1888,7 @@ func (cp *cwtchPeer) findChannelMessageBySignature(ci *model.Conversation, signa
if ci.HasChannel(constants.CHANNEL_MANAGER) {
id, err = cp.GetChannelMessageBySignature(ci.ID, constants.CHANNEL_MANAGER, signature)
if err == nil {
return constants.CHANNEL_CHAT, id, nil
return constants.CHANNEL_MANAGER, id, nil
}
}
return -1, -1, err

View File

@ -3,19 +3,21 @@ package peer
import (
"archive/tar"
"compress/gzip"
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/model"
"cwtch.im/cwtch/model/attr"
"database/sql"
"errors"
"fmt"
"git.openprivacy.ca/openprivacy/log"
"io"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/model"
"cwtch.im/cwtch/model/attr"
"cwtch.im/cwtch/model/constants"
"git.openprivacy.ca/openprivacy/log"
)
// StorageKeyType is an interface wrapper around storage key types
@ -864,7 +866,8 @@ func (cps *CwtchProfileStorage) PurgeConversationChannel(conversation int, chann
defer cps.mutex.Unlock()
conversationStmt, err := cps.db.Prepare(fmt.Sprintf(purgeMessagesFromConversationSQLStmt, conversation, channel))
if err != nil {
log.Errorf("error executing transaction: %v", err)
// Not all tables exist so this error can be spurious. Instead of logging here
// we will delegate handling to the caller...
return err
}
conversationStmt.Exec()
@ -888,7 +891,7 @@ func (cps *CwtchProfileStorage) PurgeNonSavedMessages() {
if err == nil {
for _, conversation := range ci {
// unless this is a server or a group...for which we default save always (for legacy reasons)
// FIXME: revisit this for hybrid groups.
// Note: Hybrid Groups are Peers so this check does not apply...
if !conversation.IsGroup() && !conversation.IsServer() {
// Note that we only check for confirmed status here...if it is set to any other value we will fallthrough to the default.
saveHistoryConfirmed := conversation.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(event.SaveHistoryKey)).ToString()] == event.SaveHistoryConfirmed
@ -899,7 +902,10 @@ func (cps *CwtchProfileStorage) PurgeNonSavedMessages() {
if deleteHistoryConfirmed || (!saveHistoryConfirmed && !defaultSave) {
log.Debugf("purging conversation...")
// TODO: At some point in the future this needs to iterate over channels and make a decision for each on..
cps.PurgeConversationChannel(conversation.ID, 0)
// TODO: We need a way to differentiate between channels that don't exist, and database errors
// this should probably incorporate higher level logic (e.g. has channel...)
cps.PurgeConversationChannel(conversation.ID, constants.CHANNEL_CHAT)
cps.PurgeConversationChannel(conversation.ID, constants.CHANNEL_MANAGER)
}
}
}