forked from cwtch.im/cwtch
First Draft of Enhanced Permissions API
This commit is contained in:
parent
cb3b0b4c46
commit
1c7003fb96
|
@ -44,6 +44,7 @@ func TestContactRetryQueue(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should very quickly become connecting...
|
// We should very quickly become connecting...
|
||||||
|
|
|
@ -38,14 +38,14 @@ func (i *ImagePreviewsFunctionality) OnEvent(ev event.Event, profile peer.CwtchP
|
||||||
case event.NewMessageFromPeer:
|
case event.NewMessageFromPeer:
|
||||||
ci, err := profile.FetchConversationInfo(ev.Data["RemotePeer"])
|
ci, err := profile.FetchConversationInfo(ev.Data["RemotePeer"])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if ci.Accepted {
|
if ci.GetPeerAC().RenderImages {
|
||||||
i.handleImagePreviews(profile, &ev, ci.ID, ci.ID)
|
i.handleImagePreviews(profile, &ev, ci.ID, ci.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case event.NewMessageFromGroup:
|
case event.NewMessageFromGroup:
|
||||||
ci, err := profile.FetchConversationInfo(ev.Data["RemotePeer"])
|
ci, err := profile.FetchConversationInfo(ev.Data["RemotePeer"])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if ci.Accepted {
|
if ci.GetPeerAC().RenderImages {
|
||||||
i.handleImagePreviews(profile, &ev, ci.ID, ci.ID)
|
i.handleImagePreviews(profile, &ev, ci.ID, ci.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ func (i *ImagePreviewsFunctionality) OnContactReceiveValue(profile peer.CwtchPee
|
||||||
_, zone, path := path.GetScopeZonePath()
|
_, zone, path := path.GetScopeZonePath()
|
||||||
if exists && zone == attr.ProfileZone && path == constants.CustomProfileImageKey {
|
if exists && zone == attr.ProfileZone && path == constants.CustomProfileImageKey {
|
||||||
// We only download from accepted conversations
|
// We only download from accepted conversations
|
||||||
if conversation.Accepted {
|
if conversation.GetPeerAC().RenderImages {
|
||||||
fileKey := value
|
fileKey := value
|
||||||
basepath := i.downloadFolder
|
basepath := i.downloadFolder
|
||||||
fsf := FunctionalityGate()
|
fsf := FunctionalityGate()
|
||||||
|
@ -123,6 +123,16 @@ func (i *ImagePreviewsFunctionality) OnContactReceiveValue(profile peer.CwtchPee
|
||||||
// handleImagePreviews checks settings and, if appropriate, auto-downloads any images
|
// handleImagePreviews checks settings and, if appropriate, auto-downloads any images
|
||||||
func (i *ImagePreviewsFunctionality) handleImagePreviews(profile peer.CwtchPeer, ev *event.Event, conversationID, senderID int) {
|
func (i *ImagePreviewsFunctionality) handleImagePreviews(profile peer.CwtchPeer, ev *event.Event, conversationID, senderID int) {
|
||||||
if profile.IsFeatureEnabled(constants.FileSharingExperiment) && profile.IsFeatureEnabled(constants.ImagePreviewsExperiment) {
|
if profile.IsFeatureEnabled(constants.FileSharingExperiment) && profile.IsFeatureEnabled(constants.ImagePreviewsExperiment) {
|
||||||
|
ci, err := profile.GetConversationInfo(senderID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("attempted to call handleImagePreviews with unknown conversation: %v", senderID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ci.GetPeerAC().ShareFiles || !ci.GetPeerAC().RenderImages {
|
||||||
|
log.Infof("refusing to autodownload files from sender: %v. conversation AC does not permit image rendering", senderID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Short-circuit failures
|
// Short-circuit failures
|
||||||
// Don't auto-download images if the download path does not exist.
|
// Don't auto-download images if the download path does not exist.
|
||||||
|
@ -142,7 +152,7 @@ func (i *ImagePreviewsFunctionality) handleImagePreviews(profile peer.CwtchPeer,
|
||||||
|
|
||||||
// Now look at the image preview experiment
|
// Now look at the image preview experiment
|
||||||
var cm model.MessageWrapper
|
var cm model.MessageWrapper
|
||||||
err := json.Unmarshal([]byte(ev.Data[event.Data]), &cm)
|
err = json.Unmarshal([]byte(ev.Data[event.Data]), &cm)
|
||||||
if err == nil && cm.Overlay == model.OverlayFileSharing {
|
if err == nil && cm.Overlay == model.OverlayFileSharing {
|
||||||
log.Debugf("Received File Sharing Message")
|
log.Debugf("Received File Sharing Message")
|
||||||
var fm OverlayMessage
|
var fm OverlayMessage
|
||||||
|
|
|
@ -67,3 +67,7 @@ const ProfileAttribute3 = "profile-attribute-3"
|
||||||
|
|
||||||
// Description is used on server contacts,
|
// Description is used on server contacts,
|
||||||
const Description = "description"
|
const Description = "description"
|
||||||
|
|
||||||
|
// Used to store the status of acl migrations
|
||||||
|
const ACLVersion = "acl-version"
|
||||||
|
const ACLVersionOne = "acl-v1"
|
||||||
|
|
|
@ -4,19 +4,31 @@ import (
|
||||||
"cwtch.im/cwtch/model/attr"
|
"cwtch.im/cwtch/model/attr"
|
||||||
"cwtch.im/cwtch/model/constants"
|
"cwtch.im/cwtch/model/constants"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AccessControl is a type determining client assigned authorization to a peer
|
// AccessControl is a type determining client assigned authorization to a peer
|
||||||
|
// for a given conversation
|
||||||
type AccessControl struct {
|
type AccessControl struct {
|
||||||
Blocked bool // Any attempts from this handle to connect are blocked
|
Blocked bool // Any attempts from this handle to connect are blocked overrides all other settings
|
||||||
Read bool // Allows a handle to access the conversation
|
|
||||||
Append bool // Allows a handle to append new messages to the conversation
|
// Basic Conversation Rights
|
||||||
|
Read bool // Allows a handle to access the conversation
|
||||||
|
Append bool // Allows a handle to append new messages to the conversation
|
||||||
|
|
||||||
|
AutoConnect bool // Profile should automatically try to connect with peer
|
||||||
|
ExchangeAttributes bool // Profile should automatically exchange attributes like Name, Profile Image, etc.
|
||||||
|
|
||||||
|
// Extension Related Permissions
|
||||||
|
ShareFiles bool // Allows a handle to share files to a conversation
|
||||||
|
RenderImages bool // Indicates that certain filetypes should be autodownloaded and rendered when shared by this contact
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultP2PAccessControl - because in the year 2021, go does not support constant structs...
|
// DefaultP2PAccessControl defaults to a semi-trusted peer with no access to special extensions.
|
||||||
func DefaultP2PAccessControl() AccessControl {
|
func DefaultP2PAccessControl() AccessControl {
|
||||||
return AccessControl{Read: true, Append: true, Blocked: false}
|
return AccessControl{Read: true, Append: true, ExchangeAttributes: true, Blocked: false,
|
||||||
|
AutoConnect: true, ShareFiles: false, RenderImages: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccessControlList represents an access control list for a conversation. Mapping handles to conversation
|
// AccessControlList represents an access control list for a conversation. Mapping handles to conversation
|
||||||
|
@ -30,10 +42,10 @@ func (acl *AccessControlList) Serialize() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeserializeAccessControlList takes in JSON and returns an AccessControlList
|
// DeserializeAccessControlList takes in JSON and returns an AccessControlList
|
||||||
func DeserializeAccessControlList(data []byte) AccessControlList {
|
func DeserializeAccessControlList(data []byte) (AccessControlList, error) {
|
||||||
var acl AccessControlList
|
var acl AccessControlList
|
||||||
json.Unmarshal(data, &acl)
|
err := json.Unmarshal(data, &acl)
|
||||||
return acl
|
return acl, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attributes a type-driven encapsulation of an Attribute map.
|
// Attributes a type-driven encapsulation of an Attribute map.
|
||||||
|
@ -60,7 +72,9 @@ type Conversation struct {
|
||||||
Handle string
|
Handle string
|
||||||
Attributes Attributes
|
Attributes Attributes
|
||||||
ACL AccessControlList
|
ACL AccessControlList
|
||||||
Accepted bool
|
|
||||||
|
// Deprecated, please use ACL for permissions related functions
|
||||||
|
Accepted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAttribute is a helper function that fetches a conversation attribute by scope, zone and key
|
// GetAttribute is a helper function that fetches a conversation attribute by scope, zone and key
|
||||||
|
@ -71,6 +85,16 @@ func (ci *Conversation) GetAttribute(scope attr.Scope, zone attr.Zone, key strin
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPeerAC returns a suitable Access Control object for a the given peer conversation
|
||||||
|
// If this is called for a group conversation, this method will error and return a safe default AC.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
// IsGroup is a helper attribute that identifies whether a conversation is a legacy group
|
// IsGroup is a helper attribute that identifies whether a conversation is a legacy group
|
||||||
func (ci *Conversation) IsGroup() bool {
|
func (ci *Conversation) IsGroup() bool {
|
||||||
if _, exists := ci.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupID)).ToString()]; exists {
|
if _, exists := ci.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupID)).ToString()]; exists {
|
||||||
|
|
|
@ -310,7 +310,30 @@ func (cp *cwtchPeer) GenerateProtocolEngine(acn connectivity.ACN, bus event.Mana
|
||||||
|
|
||||||
authorizations := make(map[string]model.Authorization)
|
authorizations := make(map[string]model.Authorization)
|
||||||
for _, conversation := range conversations {
|
for _, conversation := range conversations {
|
||||||
|
|
||||||
if tor.IsValidHostname(conversation.Handle) {
|
if tor.IsValidHostname(conversation.Handle) {
|
||||||
|
|
||||||
|
if _, exists := conversation.GetAttribute(attr.LocalScope, attr.ProfileZone, constants.ACLVersion); !exists {
|
||||||
|
if conversation.Accepted {
|
||||||
|
// migrate the old accepted AC to a new fine-grained one
|
||||||
|
// we only do this for previously trusted connections
|
||||||
|
// NOTE: this does not supercede global cwthch experiments settings
|
||||||
|
// if share files is turned off globally then acl.ShareFiles will be ignored.
|
||||||
|
if ac, exists := conversation.ACL[conversation.Handle]; exists {
|
||||||
|
ac.ShareFiles = true
|
||||||
|
ac.RenderImages = true
|
||||||
|
ac.AutoConnect = true
|
||||||
|
ac.ExchangeAttributes = true
|
||||||
|
conversation.ACL[conversation.Handle] = ac
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the ACL Version
|
||||||
|
cp.storage.SetConversationAttribute(conversation.ID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.ACLVersion)), constants.ACLVersionOne)
|
||||||
|
// Store the updated ACL
|
||||||
|
cp.storage.SetConversationACL(conversation.ID, conversation.ACL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if conversation.ACL[conversation.Handle].Blocked {
|
if conversation.ACL[conversation.Handle].Blocked {
|
||||||
authorizations[conversation.Handle] = model.AuthBlocked
|
authorizations[conversation.Handle] = model.AuthBlocked
|
||||||
} else {
|
} else {
|
||||||
|
@ -682,6 +705,7 @@ func (cp *cwtchPeer) NewContactConversation(handle string, acl model.AccessContr
|
||||||
conversationInfo, _ := cp.storage.GetConversationByHandle(handle)
|
conversationInfo, _ := cp.storage.GetConversationByHandle(handle)
|
||||||
if conversationInfo == nil {
|
if conversationInfo == nil {
|
||||||
conversationID, err := cp.storage.NewConversation(handle, model.Attributes{event.SaveHistoryKey: event.DeleteHistoryDefault}, model.AccessControlList{handle: acl}, accepted)
|
conversationID, err := cp.storage.NewConversation(handle, model.Attributes{event.SaveHistoryKey: event.DeleteHistoryDefault}, model.AccessControlList{handle: acl}, accepted)
|
||||||
|
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.ACLVersion)), constants.ACLVersionOne)
|
||||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.AttrLastConnectionTime)), time.Now().Format(time.RFC3339Nano))
|
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.AttrLastConnectionTime)), time.Now().Format(time.RFC3339Nano))
|
||||||
cp.eventBus.Publish(event.NewEvent(event.ContactCreated, map[event.Field]string{event.ConversationID: strconv.Itoa(conversationID), event.RemotePeer: handle}))
|
cp.eventBus.Publish(event.NewEvent(event.ContactCreated, map[event.Field]string{event.ConversationID: strconv.Itoa(conversationID), event.RemotePeer: handle}))
|
||||||
return conversationID, err
|
return conversationID, err
|
||||||
|
@ -689,6 +713,33 @@ func (cp *cwtchPeer) NewContactConversation(handle string, acl model.AccessContr
|
||||||
return -1, fmt.Errorf("contact conversation already exists")
|
return -1, fmt.Errorf("contact conversation already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateConversationAccessControlList is a genric ACL update method
|
||||||
|
func (cp *cwtchPeer) UpdateConversationAccessControlList(id int, acl model.AccessControlList) error {
|
||||||
|
return cp.storage.SetConversationACL(id, acl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnhancedGetConversationAccessControlList serialzies the access control list associated with the conversation
|
||||||
|
func (cp *cwtchPeer) EnhancedGetConversationAccessControlList(id int) (string, error) {
|
||||||
|
ci, err := cp.GetConversationInfo(id)
|
||||||
|
if err == nil {
|
||||||
|
return string(ci.ACL.Serialize()), nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConversationAccessControlListJSON wraps UpdateConversationAccessControlList and allows updating via a serialized JSON struct
|
||||||
|
func (cp *cwtchPeer) UpdateConversationAccessControlListJSON(id int, json string) error {
|
||||||
|
_, err := cp.GetConversationInfo(id)
|
||||||
|
if err == nil {
|
||||||
|
acl, err := model.DeserializeAccessControlList([]byte(json))
|
||||||
|
if err == nil {
|
||||||
|
return cp.UpdateConversationAccessControlList(id, acl)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// AcceptConversation looks up a conversation by `handle` and sets the Accepted status to `true`
|
// AcceptConversation looks up a conversation by `handle` and sets the Accepted status to `true`
|
||||||
// This will cause Cwtch to auto connect to this conversation on start up
|
// This will cause Cwtch to auto connect to this conversation on start up
|
||||||
func (cp *cwtchPeer) AcceptConversation(id int) error {
|
func (cp *cwtchPeer) AcceptConversation(id int) error {
|
||||||
|
@ -701,6 +752,21 @@ func (cp *cwtchPeer) AcceptConversation(id int) error {
|
||||||
log.Errorf("Could not get conversation for %v: %v", id, err)
|
log.Errorf("Could not get conversation for %v: %v", id, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ac, exists := ci.ACL[ci.Handle]; exists {
|
||||||
|
ac.ShareFiles = true
|
||||||
|
ac.AutoConnect = true
|
||||||
|
ac.RenderImages = true
|
||||||
|
ac.ExchangeAttributes = true
|
||||||
|
ci.ACL[ci.Handle] = ac
|
||||||
|
}
|
||||||
|
err = cp.storage.SetConversationACL(id, ci.ACL)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Could not set conversation acl for %v: %v", id, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if !ci.IsGroup() && !ci.IsServer() {
|
if !ci.IsGroup() && !ci.IsServer() {
|
||||||
cp.sendUpdateAuth(id, ci.Handle, ci.Accepted, ci.ACL[ci.Handle].Blocked)
|
cp.sendUpdateAuth(id, ci.Handle, ci.Accepted, ci.ACL[ci.Handle].Blocked)
|
||||||
cp.PeerWithOnion(ci.Handle)
|
cp.PeerWithOnion(ci.Handle)
|
||||||
|
@ -748,7 +814,7 @@ func (cp *cwtchPeer) UnblockConversation(id int) error {
|
||||||
// TODO at some point in the future engine needs to understand ACLs not just legacy auth status
|
// TODO at some point in the future engine needs to understand ACLs not just legacy auth status
|
||||||
cp.sendUpdateAuth(id, ci.Handle, ci.Accepted, ci.ACL[ci.Handle].Blocked)
|
cp.sendUpdateAuth(id, ci.Handle, ci.Accepted, ci.ACL[ci.Handle].Blocked)
|
||||||
|
|
||||||
if !ci.IsGroup() && !ci.IsServer() && ci.Accepted {
|
if !ci.IsGroup() && !ci.IsServer() && ci.GetPeerAC().AutoConnect {
|
||||||
cp.PeerWithOnion(ci.Handle)
|
cp.PeerWithOnion(ci.Handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1056,7 +1122,7 @@ func (cp *cwtchPeer) QueuePeeringWithOnion(handle string) {
|
||||||
ci, err := cp.FetchConversationInfo(handle)
|
ci, err := cp.FetchConversationInfo(handle)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
lastSeen := cp.GetConversationLastSeenTime(ci.ID)
|
lastSeen := cp.GetConversationLastSeenTime(ci.ID)
|
||||||
if !ci.ACL[ci.Handle].Blocked && ci.Accepted {
|
if !ci.ACL[ci.Handle].Blocked {
|
||||||
cp.eventBus.Publish(event.NewEvent(event.QueuePeerRequest, map[event.Field]string{event.RemotePeer: handle, event.LastSeen: lastSeen.Format(time.RFC3339Nano)}))
|
cp.eventBus.Publish(event.NewEvent(event.QueuePeerRequest, map[event.Field]string{event.RemotePeer: handle, event.LastSeen: lastSeen.Format(time.RFC3339Nano)}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1316,7 +1382,7 @@ func (cp *cwtchPeer) getConnectionsSortedByLastSeen(doPeers, doServers bool) []*
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !doPeers || !conversation.Accepted {
|
if !doPeers {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1339,7 +1405,9 @@ func (cp *cwtchPeer) StartConnections(doPeers, doServers bool) {
|
||||||
cp.QueueJoinServer(conversation.model.Handle)
|
cp.QueueJoinServer(conversation.model.Handle)
|
||||||
} else {
|
} else {
|
||||||
log.Debugf(" QueuePeerWithOnion(%v)", conversation.model.Handle)
|
log.Debugf(" QueuePeerWithOnion(%v)", conversation.model.Handle)
|
||||||
cp.QueuePeeringWithOnion(conversation.model.Handle)
|
if conversation.model.GetPeerAC().AutoConnect {
|
||||||
|
cp.QueuePeeringWithOnion(conversation.model.Handle)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
@ -1508,7 +1576,7 @@ func (cp *cwtchPeer) eventHandler() {
|
||||||
|
|
||||||
log.Debugf("confo info lookup newgetval %v %v %v", onion, conversationInfo, err)
|
log.Debugf("confo info lookup newgetval %v %v %v", onion, conversationInfo, err)
|
||||||
// only accepted contacts can look up information
|
// only accepted contacts can look up information
|
||||||
if conversationInfo != nil && conversationInfo.Accepted {
|
if conversationInfo != nil && conversationInfo.GetPeerAC().ExchangeAttributes {
|
||||||
// Type Safe Scoped/Zoned Path
|
// Type Safe Scoped/Zoned Path
|
||||||
zscope := attr.IntoScope(scope)
|
zscope := attr.IntoScope(scope)
|
||||||
zone, zpath := attr.ParseZone(zpath)
|
zone, zpath := attr.ParseZone(zpath)
|
||||||
|
@ -1540,7 +1608,7 @@ func (cp *cwtchPeer) eventHandler() {
|
||||||
|
|
||||||
conversationInfo, _ := cp.FetchConversationInfo(handle)
|
conversationInfo, _ := cp.FetchConversationInfo(handle)
|
||||||
// only accepted contacts can look up information
|
// only accepted contacts can look up information
|
||||||
if conversationInfo != nil && conversationInfo.Accepted {
|
if conversationInfo != nil && conversationInfo.GetPeerAC().ExchangeAttributes {
|
||||||
// Type Safe Scoped/Zoned Path
|
// Type Safe Scoped/Zoned Path
|
||||||
zscope := attr.IntoScope(scope)
|
zscope := attr.IntoScope(scope)
|
||||||
zone, zpath := attr.ParseZone(zpath)
|
zone, zpath := attr.ParseZone(zpath)
|
||||||
|
|
|
@ -376,7 +376,12 @@ func (cps *CwtchProfileStorage) GetConversationByHandle(handle string) (*model.C
|
||||||
}
|
}
|
||||||
rows.Close()
|
rows.Close()
|
||||||
|
|
||||||
return &model.Conversation{ID: id, Handle: handle, ACL: model.DeserializeAccessControlList(acl), Attributes: model.DeserializeAttributes(attributes), Accepted: accepted}, nil
|
cacl, err := model.DeserializeAccessControlList(acl)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error deserializing ACL from database, database maybe corrupted: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &model.Conversation{ID: id, Handle: handle, ACL: cacl, Attributes: model.DeserializeAttributes(attributes), Accepted: accepted}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchConversations returns *all* active conversations. This method should only be called
|
// FetchConversations returns *all* active conversations. This method should only be called
|
||||||
|
@ -412,7 +417,13 @@ func (cps *CwtchProfileStorage) FetchConversations() ([]*model.Conversation, err
|
||||||
rows.Close()
|
rows.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
conversations = append(conversations, &model.Conversation{ID: id, Handle: handle, ACL: model.DeserializeAccessControlList(acl), Attributes: model.DeserializeAttributes(attributes), Accepted: accepted})
|
|
||||||
|
cacl, err := model.DeserializeAccessControlList(acl)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error deserializing ACL from database, database maybe corrupted: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conversations = append(conversations, &model.Conversation{ID: id, Handle: handle, ACL: cacl, Attributes: model.DeserializeAttributes(attributes), Accepted: accepted})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -445,7 +456,12 @@ func (cps *CwtchProfileStorage) GetConversation(id int) (*model.Conversation, er
|
||||||
}
|
}
|
||||||
rows.Close()
|
rows.Close()
|
||||||
|
|
||||||
return &model.Conversation{ID: id, Handle: handle, ACL: model.DeserializeAccessControlList(acl), Attributes: model.DeserializeAttributes(attributes), Accepted: accepted}, nil
|
cacl, err := model.DeserializeAccessControlList(acl)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error deserializing ACL from database, database maybe corrupted: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &model.Conversation{ID: id, Handle: handle, ACL: cacl, Attributes: model.DeserializeAttributes(attributes), Accepted: accepted}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AcceptConversation sets the accepted status of a conversation to true in the backing datastore
|
// AcceptConversation sets the accepted status of a conversation to true in the backing datastore
|
||||||
|
|
|
@ -120,9 +120,17 @@ type CwtchPeer interface {
|
||||||
ArchiveConversation(conversation int)
|
ArchiveConversation(conversation int)
|
||||||
GetConversationInfo(conversation int) (*model.Conversation, error)
|
GetConversationInfo(conversation int) (*model.Conversation, error)
|
||||||
FetchConversationInfo(handle string) (*model.Conversation, error)
|
FetchConversationInfo(handle string) (*model.Conversation, error)
|
||||||
|
|
||||||
|
// API-level management of conversation access control
|
||||||
|
UpdateConversationAccessControlList(id int, acl model.AccessControlList) error
|
||||||
|
EnhancedGetConversationAccessControlList(conversation int) (string, error)
|
||||||
|
UpdateConversationAccessControlListJSON(conversation int, acjson string) error
|
||||||
|
|
||||||
|
// Convieniance Functions for ACL Management
|
||||||
AcceptConversation(conversation int) error
|
AcceptConversation(conversation int) error
|
||||||
BlockConversation(conversation int) error
|
BlockConversation(conversation int) error
|
||||||
UnblockConversation(conversation int) error
|
UnblockConversation(conversation int) error
|
||||||
|
|
||||||
SetConversationAttribute(conversation int, path attr.ScopedZonedPath, value string) error
|
SetConversationAttribute(conversation int, path attr.ScopedZonedPath, value string) error
|
||||||
GetConversationAttribute(conversation int, path attr.ScopedZonedPath) (string, error)
|
GetConversationAttribute(conversation int, path attr.ScopedZonedPath) (string, error)
|
||||||
DeleteConversation(conversation int) error
|
DeleteConversation(conversation int) error
|
||||||
|
|
|
@ -145,10 +145,23 @@ func TestFileSharing(t *testing.T) {
|
||||||
alice.NewContactConversation(bob.GetOnion(), model.DefaultP2PAccessControl(), true)
|
alice.NewContactConversation(bob.GetOnion(), model.DefaultP2PAccessControl(), true)
|
||||||
alice.PeerWithOnion(bob.GetOnion())
|
alice.PeerWithOnion(bob.GetOnion())
|
||||||
|
|
||||||
|
json, err := alice.EnhancedGetConversationAccessControlList(1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error!: %v", err)
|
||||||
|
}
|
||||||
|
t.Logf("alice<->bob ACL: %s", json)
|
||||||
|
|
||||||
t.Logf("Waiting for alice and Bob to peer...")
|
t.Logf("Waiting for alice and Bob to peer...")
|
||||||
waitForPeerPeerConnection(t, alice, bob)
|
waitForPeerPeerConnection(t, alice, bob)
|
||||||
alice.AcceptConversation(1)
|
err = alice.AcceptConversation(1)
|
||||||
bob.AcceptConversation(1)
|
if err != nil {
|
||||||
|
t.Fatalf("Error!: %v", err)
|
||||||
|
}
|
||||||
|
err = bob.AcceptConversation(1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error!: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
t.Logf("Alice and Bob are Connected!!")
|
t.Logf("Alice and Bob are Connected!!")
|
||||||
|
|
||||||
filesharingFunctionality := filesharing.FunctionalityGate()
|
filesharingFunctionality := filesharing.FunctionalityGate()
|
||||||
|
|
Loading…
Reference in New Issue