1
0
Fork 0
mirror of https://github.com/anyproto/any-sync.git synced 2025-06-08 05:57:03 +09:00

Add batch invite logic

This commit is contained in:
Mikhail Rakhmanov 2025-05-13 21:27:17 +02:00
parent 854823d81e
commit 985a1d7184
No known key found for this signature in database
GPG key ID: DED12CFEF5B8396B
4 changed files with 163 additions and 51 deletions

View file

@ -19,6 +19,10 @@ type InviteResponse struct {
InviteKey crypto.PrivKey InviteKey crypto.PrivKey
} }
type InviteChange struct {
Perms list.AclPermissions
}
type GetRecordsResponse struct { type GetRecordsResponse struct {
Records []*consensusproto.RawRecordWithId Records []*consensusproto.RawRecordWithId
} }
@ -27,8 +31,8 @@ type InviteSaveFunc func()
type AclSpaceClient interface { type AclSpaceClient interface {
app.Component app.Component
GenerateInvite() (list.InviteResult, error) GenerateInvite(shouldRevokeAll, isRequestToJoin bool, permissions list.AclPermissions) (list.InviteResult, error)
GenerateAnyoneCanJoinInvite(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) StopSharing(ctx context.Context, readKeyChange list.ReadKeyChangePayload) (err error)
AddRecord(ctx context.Context, consRec *consensusproto.RawRecord) error AddRecord(ctx context.Context, consRec *consensusproto.RawRecord) error
RemoveAccounts(ctx context.Context, payload list.AccountRemovePayload) error RemoveAccounts(ctx context.Context, payload list.AccountRemovePayload) error
@ -129,7 +133,7 @@ func (c *aclSpaceClient) RevokeAllInvites(ctx context.Context) (err error) {
return return
} }
c.acl.Unlock() c.acl.Unlock()
return c.sendRecordAndUpdate(ctx, c.spaceId, res) return c.sendRecordAndUpdate(ctx, c.spaceId, res.Rec)
} }
func (c *aclSpaceClient) StopSharing(ctx context.Context, readKeyChange list.ReadKeyChangePayload) (err error) { func (c *aclSpaceClient) StopSharing(ctx context.Context, readKeyChange list.ReadKeyChangePayload) (err error) {
@ -166,7 +170,7 @@ func (c *aclSpaceClient) StopSharing(ctx context.Context, readKeyChange list.Rea
return return
} }
c.acl.Unlock() c.acl.Unlock()
return c.sendRecordAndUpdate(ctx, c.spaceId, res) return c.sendRecordAndUpdate(ctx, c.spaceId, res.Rec)
} }
func (c *aclSpaceClient) DeclineRequest(ctx context.Context, identity crypto.PubKey) (err error) { func (c *aclSpaceClient) DeclineRequest(ctx context.Context, identity crypto.PubKey) (err error) {
@ -212,10 +216,46 @@ func (c *aclSpaceClient) AcceptRequest(ctx context.Context, payload list.Request
return c.sendRecordAndUpdate(ctx, c.spaceId, res) return c.sendRecordAndUpdate(ctx, c.spaceId, res)
} }
func (c *aclSpaceClient) GenerateInvite() (resp list.InviteResult, err error) { 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() c.acl.Lock()
defer c.acl.Unlock() defer c.acl.Unlock()
return c.acl.RecordBuilder().BuildInvite() 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()
}
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) GenerateAnyoneCanJoinInvite(permissions list.AclPermissions) (resp list.InviteResult, err error) { func (c *aclSpaceClient) GenerateAnyoneCanJoinInvite(permissions list.AclPermissions) (resp list.InviteResult, err error) {

View file

@ -66,6 +66,10 @@ type AccountAdd struct {
Metadata []byte Metadata []byte
} }
type NewInvites struct {
Permissions AclPermissions
}
type BatchRequestPayload struct { type BatchRequestPayload struct {
Additions []AccountAdd Additions []AccountAdd
Changes []PermissionChangePayload Changes []PermissionChangePayload
@ -73,6 +77,8 @@ type BatchRequestPayload struct {
Approvals []RequestAcceptPayload Approvals []RequestAcceptPayload
Declines []string Declines []string
InviteRevokes []string InviteRevokes []string
InviteChanges []InviteChangePayload
NewInvites []AclPermissions
} }
type AccountRemovePayload struct { type AccountRemovePayload struct {
@ -85,12 +91,17 @@ type InviteResult struct {
InviteKey crypto.PrivKey InviteKey crypto.PrivKey
} }
type BatchResult struct {
Rec *consensusproto.RawRecord
Invites []crypto.PrivKey
}
type AclRecordBuilder interface { type AclRecordBuilder interface {
UnmarshallWithId(rawIdRecord *consensusproto.RawRecordWithId) (rec *AclRecord, err error) UnmarshallWithId(rawIdRecord *consensusproto.RawRecordWithId) (rec *AclRecord, err error)
Unmarshall(rawRecord *consensusproto.RawRecord) (rec *AclRecord, err error) Unmarshall(rawRecord *consensusproto.RawRecord) (rec *AclRecord, err error)
BuildRoot(content RootContent) (rec *consensusproto.RawRecordWithId, err error) BuildRoot(content RootContent) (rec *consensusproto.RawRecordWithId, err error)
BuildBatchRequest(payload BatchRequestPayload) (rawRecord *consensusproto.RawRecord, err error) BuildBatchRequest(payload BatchRequestPayload) (batchResult BatchResult, err error)
BuildInvite() (res InviteResult, err error) BuildInvite() (res InviteResult, err error)
BuildInviteAnyone(permissions AclPermissions) (res InviteResult, err error) BuildInviteAnyone(permissions AclPermissions) (res InviteResult, err error)
BuildInviteChange(inviteChange InviteChangePayload) (rawRecord *consensusproto.RawRecord, err error) BuildInviteChange(inviteChange InviteChangePayload) (rawRecord *consensusproto.RawRecord, err error)
@ -125,51 +136,84 @@ func NewAclRecordBuilder(id string, keyStorage crypto.KeyStorage, keys *accountd
} }
} }
func (a *aclRecordBuilder) BuildBatchRequest(payload BatchRequestPayload) (rawRec *consensusproto.RawRecord, err error) { func (a *aclRecordBuilder) BuildBatchRequest(payload BatchRequestPayload) (batchResult BatchResult, err error) {
var aclContent []*aclrecordproto.AclContentValue var (
contentList []*aclrecordproto.AclContentValue
content *aclrecordproto.AclContentValue
)
if len(payload.Removals.Identities) > 0 { if len(payload.Removals.Identities) > 0 {
content, err := a.buildAccountRemove(payload.Removals) content, err = a.buildAccountRemove(payload.Removals)
if err != nil { if err != nil {
return nil, err return
} }
aclContent = append(aclContent, content) contentList = append(contentList, content)
} }
if len(payload.Additions) > 0 { if len(payload.Additions) > 0 {
content, err := a.buildAccountsAdd(AccountsAddPayload{Additions: payload.Additions}, payload.Removals.Change.MetadataKey.GetPublic(), payload.Removals.Change.ReadKey) content, err = a.buildAccountsAdd(AccountsAddPayload{Additions: payload.Additions}, payload.Removals.Change.MetadataKey.GetPublic(), payload.Removals.Change.ReadKey)
if err != nil { if err != nil {
return nil, err return
} }
aclContent = append(aclContent, content) contentList = append(contentList, content)
} }
if len(payload.Changes) > 0 { if len(payload.Changes) > 0 {
content, err := a.buildPermissionChanges(PermissionChangesPayload{Changes: payload.Changes}) content, err = a.buildPermissionChanges(PermissionChangesPayload{Changes: payload.Changes})
if err != nil { if err != nil {
return nil, err return
} }
aclContent = append(aclContent, content) contentList = append(contentList, content)
} }
for _, acc := range payload.Approvals { for _, acc := range payload.Approvals {
content, err := a.buildRequestAccept(acc, payload.Removals.Change.ReadKey) content, err = a.buildRequestAccept(acc, payload.Removals.Change.ReadKey)
if err != nil { if err != nil {
return nil, err return
} }
aclContent = append(aclContent, content) contentList = append(contentList, content)
} }
for _, id := range payload.Declines { for _, id := range payload.Declines {
content, err := a.buildRequestDecline(id) content, err = a.buildRequestDecline(id)
if err != nil { if err != nil {
return nil, err return
} }
aclContent = append(aclContent, content) contentList = append(contentList, content)
} }
for _, id := range payload.InviteRevokes { for _, id := range payload.InviteRevokes {
content, err := a.buildInviteRevoke(id) content, err = a.buildInviteRevoke(id)
if err != nil { if err != nil {
return nil, err return
} }
aclContent = append(aclContent, content) contentList = append(contentList, content)
} }
return a.buildRecords(aclContent) for _, invite := range payload.InviteChanges {
content, err = a.buildInviteChange(invite)
if err != nil {
return
}
contentList = append(contentList, content)
}
for _, perms := range payload.NewInvites {
var privKey crypto.PrivKey
if perms.NoPermissions() {
privKey, content, err = a.buildInvite()
if err != nil {
return
}
contentList = append(contentList, content)
batchResult.Invites = append(batchResult.Invites, privKey)
} else {
privKey, content, err = a.buildInviteAnyone(perms)
if err != nil {
return
}
contentList = append(contentList, content)
batchResult.Invites = append(batchResult.Invites, privKey)
}
}
res, err := a.buildRecords(contentList)
if err != nil {
return
}
batchResult.Rec = res
return
} }
func (a *aclRecordBuilder) buildRecord(aclContent *aclrecordproto.AclContentValue) (rawRec *consensusproto.RawRecord, err error) { func (a *aclRecordBuilder) buildRecord(aclContent *aclrecordproto.AclContentValue) (rawRec *consensusproto.RawRecord, err error) {
@ -316,6 +360,20 @@ func (a *aclRecordBuilder) buildAccountsAdd(payload AccountsAddPayload, mkKey cr
} }
func (a *aclRecordBuilder) BuildInvite() (res InviteResult, err error) { func (a *aclRecordBuilder) BuildInvite() (res InviteResult, err error) {
privKey, content, err := a.buildInvite()
if err != nil {
return
}
rawRec, err := a.buildRecord(content)
if err != nil {
return
}
res.InviteKey = privKey
res.InviteRec = rawRec
return
}
func (a *aclRecordBuilder) buildInvite() (invKey crypto.PrivKey, content *aclrecordproto.AclContentValue, err error) {
if !a.state.Permissions(a.state.pubKey).CanManageAccounts() { if !a.state.Permissions(a.state.pubKey).CanManageAccounts() {
err = ErrInsufficientPermissions err = ErrInsufficientPermissions
return return
@ -329,7 +387,37 @@ func (a *aclRecordBuilder) BuildInvite() (res InviteResult, err error) {
return return
} }
inviteRec := &aclrecordproto.AclAccountInvite{InviteKey: invitePubKey} inviteRec := &aclrecordproto.AclAccountInvite{InviteKey: invitePubKey}
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_Invite{Invite: inviteRec}} content = &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_Invite{Invite: inviteRec}}
invKey = privKey
return
}
func (a *aclRecordBuilder) BuildInviteChange(inviteChange InviteChangePayload) (rawRecord *consensusproto.RawRecord, err error) {
content, err := a.buildInviteChange(inviteChange)
if err != nil {
return
}
return a.buildRecord(content)
}
func (a *aclRecordBuilder) buildInviteChange(inviteChange InviteChangePayload) (content *aclrecordproto.AclContentValue, err error) {
if !a.state.Permissions(a.state.pubKey).CanManageAccounts() {
err = ErrInsufficientPermissions
return
}
inviteRec := &aclrecordproto.AclAccountInviteChange{
InviteRecordId: inviteChange.IniviteRecordId,
Permissions: aclrecordproto.AclUserPermissions(inviteChange.Permissions),
}
content = &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_InviteChange{InviteChange: inviteRec}}
return
}
func (a *aclRecordBuilder) BuildInviteAnyone(permissions AclPermissions) (res InviteResult, err error) {
privKey, content, err := a.buildInviteAnyone(permissions)
if err != nil {
return
}
rawRec, err := a.buildRecord(content) rawRec, err := a.buildRecord(content)
if err != nil { if err != nil {
return return
@ -339,20 +427,7 @@ func (a *aclRecordBuilder) BuildInvite() (res InviteResult, err error) {
return return
} }
func (a *aclRecordBuilder) BuildInviteChange(inviteChange InviteChangePayload) (rawRecord *consensusproto.RawRecord, err error) { func (a *aclRecordBuilder) buildInviteAnyone(permissions AclPermissions) (invKey crypto.PrivKey, content *aclrecordproto.AclContentValue, err error) {
if !a.state.Permissions(a.state.pubKey).CanManageAccounts() {
err = ErrInsufficientPermissions
return
}
inviteRec := &aclrecordproto.AclAccountInviteChange{
InviteRecordId: inviteChange.IniviteRecordId,
Permissions: aclrecordproto.AclUserPermissions(inviteChange.Permissions),
}
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_InviteChange{InviteChange: inviteRec}}
return a.buildRecord(content)
}
func (a *aclRecordBuilder) BuildInviteAnyone(permissions AclPermissions) (res InviteResult, err error) {
if !a.state.Permissions(a.state.pubKey).CanManageAccounts() { if !a.state.Permissions(a.state.pubKey).CanManageAccounts() {
err = ErrInsufficientPermissions err = ErrInsufficientPermissions
return return
@ -383,13 +458,8 @@ func (a *aclRecordBuilder) BuildInviteAnyone(permissions AclPermissions) (res In
Permissions: aclrecordproto.AclUserPermissions_Reader, Permissions: aclrecordproto.AclUserPermissions_Reader,
EncryptedReadKey: encReadKey, EncryptedReadKey: encReadKey,
} }
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_Invite{Invite: inviteRec}} content = &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_Invite{Invite: inviteRec}}
rawRec, err := a.buildRecord(content) invKey = privKey
if err != nil {
return
}
res.InviteKey = privKey
res.InviteRec = rawRec
return return
} }

View file

@ -60,6 +60,7 @@ type Invite struct {
Key crypto.PubKey Key crypto.PubKey
Type aclrecordproto.AclInviteType Type aclrecordproto.AclInviteType
Permissions AclPermissions Permissions AclPermissions
Id string
encryptedKey []byte encryptedKey []byte
} }
@ -507,6 +508,7 @@ func (st *AclState) applyInvite(ch *aclrecordproto.AclAccountInvite, record *Acl
} }
st.invites[record.Id] = Invite{ st.invites[record.Id] = Invite{
Key: inviteKey, Key: inviteKey,
Id: record.Id,
Type: ch.InviteType, Type: ch.InviteType,
Permissions: AclPermissions(ch.Permissions), Permissions: AclPermissions(ch.Permissions),
encryptedKey: ch.EncryptedReadKey, encryptedKey: ch.EncryptedReadKey,

View file

@ -217,7 +217,7 @@ func (a *AclTestExecutor) buildBatchRequest(args []string, acl AclList, getPerm
if err != nil { if err != nil {
return nil, err return nil, err
} }
return afterAll, addRec(WrapAclRecord(res)) return afterAll, addRec(WrapAclRecord(res.Rec))
} }
func (a *AclTestExecutor) Execute(cmd string) (err error) { func (a *AclTestExecutor) Execute(cmd string) (err error) {