1
0
Fork 0
mirror of https://github.com/anyproto/any-sync.git synced 2025-06-08 05:57:03 +09:00
any-sync/commonspace/acl/aclclient/aclspaceclient.go
2025-05-19 11:37:43 +02:00

285 lines
8 KiB
Go

package aclclient
import (
"context"
"errors"
"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anyproto/any-sync/commonspace/object/acl/list"
"github.com/anyproto/any-sync/commonspace/object/acl/syncacl"
"github.com/anyproto/any-sync/commonspace/spacestate"
"github.com/anyproto/any-sync/consensus/consensusproto"
"github.com/anyproto/any-sync/node/nodeclient"
"github.com/anyproto/any-sync/util/crypto"
)
type InviteResponse struct {
InviteRec *consensusproto.RawRecord
InviteKey crypto.PrivKey
}
type InviteChange struct {
Perms list.AclPermissions
}
type GetRecordsResponse struct {
Records []*consensusproto.RawRecordWithId
}
type InviteSaveFunc func()
type AclSpaceClient interface {
app.Component
GenerateInvite(shouldRevokeAll, isRequestToJoin bool, permissions list.AclPermissions) (list.InviteResult, error)
ChangeInvite(ctx context.Context, inviteId string, permissions list.AclPermissions) error
StopSharing(ctx context.Context, readKeyChange list.ReadKeyChangePayload) (err error)
AddRecord(ctx context.Context, consRec *consensusproto.RawRecord) error
RemoveAccounts(ctx context.Context, payload list.AccountRemovePayload) error
AcceptRequest(ctx context.Context, payload list.RequestAcceptPayload) error
DeclineRequest(ctx context.Context, identity crypto.PubKey) (err error)
CancelRequest(ctx context.Context) (err error)
ChangePermissions(ctx context.Context, permChange list.PermissionChangesPayload) (err error)
RequestSelfRemove(ctx context.Context) (err error)
RevokeInvite(ctx context.Context, inviteRecordId string) (err error)
RevokeAllInvites(ctx context.Context) (err error)
AddAccounts(ctx context.Context, add list.AccountsAddPayload) (err error)
}
func NewAclSpaceClient() AclSpaceClient {
return &aclSpaceClient{}
}
type aclSpaceClient struct {
nodeClient nodeclient.NodeClient
acl list.AclList
spaceId string
}
func (c *aclSpaceClient) Init(a *app.App) (err error) {
c.nodeClient = a.MustComponent(nodeclient.CName).(nodeclient.NodeClient)
c.acl = a.MustComponent(syncacl.CName).(list.AclList)
c.spaceId = a.MustComponent(spacestate.CName).(*spacestate.SpaceState).SpaceId
return nil
}
func (c *aclSpaceClient) Name() (name string) {
return CName
}
func (c *aclSpaceClient) RevokeInvite(ctx context.Context, inviteRecordId string) (err error) {
c.acl.Lock()
res, err := c.acl.RecordBuilder().BuildInviteRevoke(inviteRecordId)
if err != nil {
c.acl.Unlock()
return
}
c.acl.Unlock()
return c.sendRecordAndUpdate(ctx, c.spaceId, res)
}
func (c *aclSpaceClient) RequestSelfRemove(ctx context.Context) (err error) {
c.acl.Lock()
res, err := c.acl.RecordBuilder().BuildRequestRemove()
if err != nil {
c.acl.Unlock()
return
}
c.acl.Unlock()
return c.sendRecordAndUpdate(ctx, c.spaceId, res)
}
func (c *aclSpaceClient) ChangePermissions(ctx context.Context, permChange list.PermissionChangesPayload) (err error) {
c.acl.Lock()
res, err := c.acl.RecordBuilder().BuildPermissionChanges(permChange)
if err != nil {
c.acl.Unlock()
return
}
c.acl.Unlock()
return c.sendRecordAndUpdate(ctx, c.spaceId, res)
}
func (c *aclSpaceClient) AddAccounts(ctx context.Context, add list.AccountsAddPayload) (err error) {
c.acl.Lock()
res, err := c.acl.RecordBuilder().BuildAccountsAdd(add)
if err != nil {
c.acl.Unlock()
return
}
c.acl.Unlock()
return c.sendRecordAndUpdate(ctx, c.spaceId, res)
}
func (c *aclSpaceClient) RemoveAccounts(ctx context.Context, payload list.AccountRemovePayload) (err error) {
c.acl.Lock()
res, err := c.acl.RecordBuilder().BuildAccountRemove(payload)
if err != nil {
c.acl.Unlock()
return
}
c.acl.Unlock()
return c.sendRecordAndUpdate(ctx, c.spaceId, res)
}
func (c *aclSpaceClient) RevokeAllInvites(ctx context.Context) (err error) {
c.acl.Lock()
payload := list.BatchRequestPayload{
InviteRevokes: c.acl.AclState().InviteIds(),
}
res, err := c.acl.RecordBuilder().BuildBatchRequest(payload)
if err != nil {
c.acl.Unlock()
return
}
c.acl.Unlock()
return c.sendRecordAndUpdate(ctx, c.spaceId, res.Rec)
}
func (c *aclSpaceClient) StopSharing(ctx context.Context, readKeyChange list.ReadKeyChangePayload) (err error) {
c.acl.Lock()
if c.acl.AclState().IsEmpty() {
c.acl.Unlock()
return
}
var (
identities []crypto.PubKey
recIds []string
)
for _, state := range c.acl.AclState().CurrentAccounts() {
if state.Permissions.NoPermissions() || state.Permissions.IsOwner() {
continue
}
identities = append(identities, state.PubKey)
}
recs, _ := c.acl.AclState().JoinRecords(false)
for _, rec := range recs {
recIds = append(recIds, rec.RecordId)
}
payload := list.BatchRequestPayload{
Removals: list.AccountRemovePayload{
Identities: identities,
Change: readKeyChange,
},
Declines: recIds,
InviteRevokes: c.acl.AclState().InviteIds(),
}
res, err := c.acl.RecordBuilder().BuildBatchRequest(payload)
if err != nil {
c.acl.Unlock()
return
}
c.acl.Unlock()
return c.sendRecordAndUpdate(ctx, c.spaceId, res.Rec)
}
func (c *aclSpaceClient) DeclineRequest(ctx context.Context, identity crypto.PubKey) (err error) {
c.acl.Lock()
pendingReq, err := c.acl.AclState().JoinRecord(identity, false)
if err != nil {
c.acl.Unlock()
return
}
res, err := c.acl.RecordBuilder().BuildRequestDecline(pendingReq.RecordId)
if err != nil {
c.acl.Unlock()
return
}
c.acl.Unlock()
return c.sendRecordAndUpdate(ctx, c.spaceId, res)
}
func (c *aclSpaceClient) CancelRequest(ctx context.Context) (err error) {
c.acl.Lock()
pendingReq, err := c.acl.AclState().Record(c.acl.AclState().Identity())
if err != nil {
c.acl.Unlock()
return
}
res, err := c.acl.RecordBuilder().BuildRequestCancel(pendingReq.RecordId)
if err != nil {
c.acl.Unlock()
return
}
c.acl.Unlock()
return c.sendRecordAndUpdate(ctx, c.spaceId, res)
}
func (c *aclSpaceClient) AcceptRequest(ctx context.Context, payload list.RequestAcceptPayload) (err error) {
c.acl.Lock()
res, err := c.acl.RecordBuilder().BuildRequestAccept(payload)
if err != nil {
c.acl.Unlock()
return
}
c.acl.Unlock()
return c.sendRecordAndUpdate(ctx, c.spaceId, res)
}
func (c *aclSpaceClient) ChangeInvite(ctx context.Context, inviteId string, permissions list.AclPermissions) error {
c.acl.Lock()
res, err := c.acl.RecordBuilder().BuildInviteChange(list.InviteChangePayload{
IniviteRecordId: inviteId,
Permissions: permissions,
})
if err != nil {
c.acl.Unlock()
return err
}
c.acl.Unlock()
return c.sendRecordAndUpdate(ctx, c.spaceId, res)
}
func (c *aclSpaceClient) GenerateInvite(isRevoke, isRequestToJoin bool, permissions list.AclPermissions) (list.InviteResult, error) {
c.acl.Lock()
defer c.acl.Unlock()
var inviteIds []string
if isRevoke {
for _, invite := range c.acl.AclState().Invites() {
if isRequestToJoin && invite.Type == aclrecordproto.AclInviteType_RequestToJoin {
return list.InviteResult{}, list.ErrDuplicateInvites
} else if invite.Permissions == permissions {
return list.InviteResult{}, list.ErrDuplicateInvites
}
}
inviteIds = c.acl.AclState().InviteIds()
}
var payload list.BatchRequestPayload
if isRequestToJoin {
payload = list.BatchRequestPayload{
InviteRevokes: inviteIds,
NewInvites: []list.AclPermissions{list.AclPermissionsNone},
}
} else {
payload = list.BatchRequestPayload{
InviteRevokes: inviteIds,
NewInvites: []list.AclPermissions{permissions},
}
}
res, err := c.acl.RecordBuilder().BuildBatchRequest(payload)
if err != nil {
return list.InviteResult{}, err
}
return list.InviteResult{
InviteRec: res.Rec,
InviteKey: res.Invites[0],
}, nil
}
func (c *aclSpaceClient) AddRecord(ctx context.Context, consRec *consensusproto.RawRecord) (err error) {
return c.sendRecordAndUpdate(ctx, c.spaceId, consRec)
}
func (c *aclSpaceClient) sendRecordAndUpdate(ctx context.Context, spaceId string, rec *consensusproto.RawRecord) (err error) {
res, err := c.nodeClient.AclAddRecord(ctx, spaceId, rec)
if err != nil {
return
}
c.acl.Lock()
defer c.acl.Unlock()
err = c.acl.AddRawRecord(res)
if errors.Is(err, list.ErrRecordAlreadyExists) {
err = nil
}
return
}