diff --git a/commonspace/object/acl/list/aclrecordbuilder.go b/commonspace/object/acl/list/aclrecordbuilder.go index 686ef3fa..f94553c1 100644 --- a/commonspace/object/acl/list/aclrecordbuilder.go +++ b/commonspace/object/acl/list/aclrecordbuilder.go @@ -17,7 +17,8 @@ type RootContent struct { } type AclRecordBuilder interface { - Unmarshall(rawIdRecord *aclrecordproto.RawAclRecordWithId) (rec *AclRecord, err error) + UnmarshallWithId(rawIdRecord *aclrecordproto.RawAclRecordWithId) (rec *AclRecord, err error) + Unmarshall(rawRecord *aclrecordproto.RawAclRecord) (rec *AclRecord, err error) BuildRoot(content RootContent) (rec *aclrecordproto.RawAclRecordWithId, err error) } @@ -33,7 +34,41 @@ func NewAclRecordBuilder(id string, keyStorage crypto.KeyStorage) AclRecordBuild } } -func (a *aclRecordBuilder) Unmarshall(rawIdRecord *aclrecordproto.RawAclRecordWithId) (rec *AclRecord, err error) { +func (a *aclRecordBuilder) Unmarshall(rawRecord *aclrecordproto.RawAclRecord) (rec *AclRecord, err error) { + aclRecord := &aclrecordproto.AclRecord{} + err = proto.Unmarshal(rawRecord.Payload, aclRecord) + if err != nil { + return + } + pubKey, err := a.keyStorage.PubKeyFromProto(aclRecord.Identity) + if err != nil { + return + } + aclData := &aclrecordproto.AclData{} + err = proto.Unmarshal(rawRecord.Payload, aclData) + if err != nil { + return + } + rec = &AclRecord{ + PrevId: aclRecord.PrevId, + Timestamp: aclRecord.Timestamp, + Data: aclRecord.Data, + Signature: rawRecord.Signature, + Identity: pubKey, + Model: aclData, + } + res, err := pubKey.Verify(rawRecord.Payload, rawRecord.Signature) + if err != nil { + return + } + if !res { + err = ErrInvalidSignature + return + } + return +} + +func (a *aclRecordBuilder) UnmarshallWithId(rawIdRecord *aclrecordproto.RawAclRecordWithId) (rec *AclRecord, err error) { var ( rawRec = &aclrecordproto.RawAclRecord{} pubKey crypto.PubKey @@ -69,6 +104,11 @@ func (a *aclRecordBuilder) Unmarshall(rawIdRecord *aclrecordproto.RawAclRecordWi if err != nil { return } + aclData := &aclrecordproto.AclData{} + err = proto.Unmarshal(rawRec.Payload, aclData) + if err != nil { + return + } rec = &AclRecord{ Id: rawIdRecord.Id, PrevId: aclRecord.PrevId, @@ -76,6 +116,7 @@ func (a *aclRecordBuilder) Unmarshall(rawIdRecord *aclrecordproto.RawAclRecordWi Data: aclRecord.Data, Signature: rawRec.Signature, Identity: pubKey, + Model: aclData, } } diff --git a/commonspace/object/acl/list/aclstate.go b/commonspace/object/acl/list/aclstate.go index 6f0c917b..52fe894b 100644 --- a/commonspace/object/acl/list/aclstate.go +++ b/commonspace/object/acl/list/aclstate.go @@ -88,6 +88,10 @@ func newAclState(id string) *AclState { return st } +func (st *AclState) Validator() ContentValidator { + return st.contentValidator +} + func (st *AclState) CurrentReadKeyId() string { return st.currentReadKeyId } @@ -242,7 +246,7 @@ func (st *AclState) applyPermissionChange(ch *aclrecordproto.AclAccountPermissio if err != nil { return err } - err = st.contentValidator.ValidatePermissionChange(ch, recordId, authorIdentity) + err = st.contentValidator.ValidatePermissionChange(ch, authorIdentity) if err != nil { return err } @@ -258,7 +262,7 @@ func (st *AclState) applyInvite(ch *aclrecordproto.AclAccountInvite, recordId st if err != nil { return err } - err = st.contentValidator.ValidateInvite(ch, recordId, authorIdentity) + err = st.contentValidator.ValidateInvite(ch, authorIdentity) if err != nil { return err } @@ -267,7 +271,7 @@ func (st *AclState) applyInvite(ch *aclrecordproto.AclAccountInvite, recordId st } func (st *AclState) applyInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, recordId string, authorIdentity crypto.PubKey) error { - err := st.contentValidator.ValidateInviteRevoke(ch, recordId, authorIdentity) + err := st.contentValidator.ValidateInviteRevoke(ch, authorIdentity) if err != nil { return err } @@ -276,7 +280,7 @@ func (st *AclState) applyInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, } func (st *AclState) applyRequestJoin(ch *aclrecordproto.AclAccountRequestJoin, recordId string, authorIdentity crypto.PubKey) error { - err := st.contentValidator.ValidateRequestJoin(ch, recordId, authorIdentity) + err := st.contentValidator.ValidateRequestJoin(ch, authorIdentity) if err != nil { return err } @@ -288,7 +292,7 @@ func (st *AclState) applyRequestJoin(ch *aclrecordproto.AclAccountRequestJoin, r } func (st *AclState) applyRequestAccept(ch *aclrecordproto.AclAccountRequestAccept, recordId string, authorIdentity crypto.PubKey) error { - err := st.contentValidator.ValidateRequestAccept(ch, recordId, authorIdentity) + err := st.contentValidator.ValidateRequestAccept(ch, authorIdentity) if err != nil { return err } @@ -314,18 +318,18 @@ func (st *AclState) applyRequestAccept(ch *aclrecordproto.AclAccountRequestAccep if err != nil { return err } - for recordId, key := range keys.ReadKeys { + for keyId, key := range keys.ReadKeys { sym, err := crypto.UnmarshallAESKey(key) if err != nil { return err } - st.userReadKeys[recordId] = sym + st.userReadKeys[keyId] = sym } return nil } func (st *AclState) applyRequestDecline(ch *aclrecordproto.AclAccountRequestDecline, recordId string, authorIdentity crypto.PubKey) error { - err := st.contentValidator.ValidateRequestDecline(ch, recordId, authorIdentity) + err := st.contentValidator.ValidateRequestDecline(ch, authorIdentity) if err != nil { return err } @@ -334,7 +338,7 @@ func (st *AclState) applyRequestDecline(ch *aclrecordproto.AclAccountRequestDecl } func (st *AclState) applyAccountRemove(ch *aclrecordproto.AclAccountRemove, recordId string, authorIdentity crypto.PubKey) error { - err := st.contentValidator.ValidateRemove(ch, recordId, authorIdentity) + err := st.contentValidator.ValidateRemove(ch, authorIdentity) if err != nil { return err } @@ -342,7 +346,7 @@ func (st *AclState) applyAccountRemove(ch *aclrecordproto.AclAccountRemove, reco } func (st *AclState) applyReadKeyChange(ch *aclrecordproto.AclReadKeyChange, recordId string, authorIdentity crypto.PubKey) error { - err := st.contentValidator.ValidateReadKeyChange(ch, recordId, authorIdentity) + err := st.contentValidator.ValidateReadKeyChange(ch, authorIdentity) if err != nil { return err } diff --git a/commonspace/object/acl/list/list.go b/commonspace/object/acl/list/list.go index 6c31c9de..b3bf8cd0 100644 --- a/commonspace/object/acl/list/list.go +++ b/commonspace/object/acl/list/list.go @@ -5,11 +5,12 @@ import ( "context" "errors" "fmt" + "sync" + "github.com/anyproto/any-sync/commonspace/object/accountdata" "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" "github.com/anyproto/any-sync/commonspace/object/acl/liststorage" "github.com/anyproto/any-sync/util/crypto" - "sync" ) type IterFunc = func(record *AclRecord) (IsContinue bool) @@ -33,8 +34,11 @@ type AclList interface { Get(id string) (*AclRecord, error) Iterate(iterFunc IterFunc) IterateFrom(startId string, iterFunc IterFunc) - KeyStorage() crypto.KeyStorage + KeyStorage() crypto.KeyStorage + RecordBuilder() AclRecordBuilder + + ValidateRawRecord(record *aclrecordproto.RawAclRecord) (err error) AddRawRecord(rawRec *aclrecordproto.RawAclRecordWithId) (added bool, err error) Close() (err error) @@ -77,7 +81,7 @@ func build(id string, keyStorage crypto.KeyStorage, stateBuilder *aclStateBuilde return } - record, err := recBuilder.Unmarshall(rawRecordWithId) + record, err := recBuilder.UnmarshallWithId(rawRecordWithId) if err != nil { return } @@ -89,7 +93,7 @@ func build(id string, keyStorage crypto.KeyStorage, stateBuilder *aclStateBuilde return } - record, err = recBuilder.Unmarshall(rawRecordWithId) + record, err = recBuilder.UnmarshallWithId(rawRecordWithId) if err != nil { return } @@ -132,15 +136,27 @@ func build(id string, keyStorage crypto.KeyStorage, stateBuilder *aclStateBuilde return } +func (a *aclList) RecordBuilder() AclRecordBuilder { + return a.recordBuilder +} + func (a *aclList) Records() []*AclRecord { return a.records } +func (a *aclList) ValidateRawRecord(rawRec *aclrecordproto.RawAclRecord) (err error) { + record, err := a.recordBuilder.Unmarshall(rawRec) + if err != nil { + return + } + return a.aclState.Validator().ValidateAclRecordContents(record) +} + func (a *aclList) AddRawRecord(rawRec *aclrecordproto.RawAclRecordWithId) (added bool, err error) { if _, ok := a.indexes[rawRec.Id]; ok { return } - record, err := a.recordBuilder.Unmarshall(rawRec) + record, err := a.recordBuilder.UnmarshallWithId(rawRec) if err != nil { return } @@ -159,7 +175,7 @@ func (a *aclList) AddRawRecord(rawRec *aclrecordproto.RawAclRecordWithId) (added } func (a *aclList) IsValidNext(rawRec *aclrecordproto.RawAclRecordWithId) (err error) { - _, err = a.recordBuilder.Unmarshall(rawRec) + _, err = a.recordBuilder.UnmarshallWithId(rawRec) if err != nil { return } diff --git a/commonspace/object/acl/list/validator.go b/commonspace/object/acl/list/validator.go index 51dfa575..75e0e251 100644 --- a/commonspace/object/acl/list/validator.go +++ b/commonspace/object/acl/list/validator.go @@ -6,14 +6,15 @@ import ( ) type ContentValidator interface { - ValidatePermissionChange(ch *aclrecordproto.AclAccountPermissionChange, id string, authorIdentity crypto.PubKey) (err error) - ValidateInvite(ch *aclrecordproto.AclAccountInvite, id string, authorIdentity crypto.PubKey) (err error) - ValidateInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, id string, authorIdentity crypto.PubKey) (err error) - ValidateRequestJoin(ch *aclrecordproto.AclAccountRequestJoin, id string, authorIdentity crypto.PubKey) (err error) - ValidateRequestAccept(ch *aclrecordproto.AclAccountRequestAccept, id string, authorIdentity crypto.PubKey) (err error) - ValidateRequestDecline(ch *aclrecordproto.AclAccountRequestDecline, id string, authorIdentity crypto.PubKey) (err error) - ValidateRemove(ch *aclrecordproto.AclAccountRemove, id string, authorIdentity crypto.PubKey) (err error) - ValidateReadKeyChange(ch *aclrecordproto.AclReadKeyChange, id string, authorIdentity crypto.PubKey) (err error) + ValidateAclRecordContents(ch *AclRecord) (err error) + ValidatePermissionChange(ch *aclrecordproto.AclAccountPermissionChange, authorIdentity crypto.PubKey) (err error) + ValidateInvite(ch *aclrecordproto.AclAccountInvite, authorIdentity crypto.PubKey) (err error) + ValidateInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, authorIdentity crypto.PubKey) (err error) + ValidateRequestJoin(ch *aclrecordproto.AclAccountRequestJoin, authorIdentity crypto.PubKey) (err error) + ValidateRequestAccept(ch *aclrecordproto.AclAccountRequestAccept, authorIdentity crypto.PubKey) (err error) + ValidateRequestDecline(ch *aclrecordproto.AclAccountRequestDecline, authorIdentity crypto.PubKey) (err error) + ValidateRemove(ch *aclrecordproto.AclAccountRemove, authorIdentity crypto.PubKey) (err error) + ValidateReadKeyChange(ch *aclrecordproto.AclReadKeyChange, authorIdentity crypto.PubKey) (err error) } type contentValidator struct { @@ -21,7 +22,44 @@ type contentValidator struct { aclState *AclState } -func (c *contentValidator) ValidatePermissionChange(ch *aclrecordproto.AclAccountPermissionChange, id string, authorIdentity crypto.PubKey) (err error) { +func (c *contentValidator) ValidateAclRecordContents(ch *AclRecord) (err error) { + if ch.PrevId != c.aclState.lastRecordId { + return ErrIncorrectRecordSequence + } + aclData := ch.Model.(*aclrecordproto.AclData) + for _, content := range aclData.AclContent { + err = c.validateAclRecordContent(content, ch.Identity) + if err != nil { + return + } + } + return +} + +func (c *contentValidator) validateAclRecordContent(ch *aclrecordproto.AclContentValue, authorIdentity crypto.PubKey) (err error) { + switch { + case ch.GetPermissionChange() != nil: + return c.ValidatePermissionChange(ch.GetPermissionChange(), authorIdentity) + case ch.GetInvite() != nil: + return c.ValidateInvite(ch.GetInvite(), authorIdentity) + case ch.GetInviteRevoke() != nil: + return c.ValidateInviteRevoke(ch.GetInviteRevoke(), authorIdentity) + case ch.GetRequestJoin() != nil: + return c.ValidateRequestJoin(ch.GetRequestJoin(), authorIdentity) + case ch.GetRequestAccept() != nil: + return c.ValidateRequestAccept(ch.GetRequestAccept(), authorIdentity) + case ch.GetRequestDecline() != nil: + return c.ValidateRequestDecline(ch.GetRequestDecline(), authorIdentity) + case ch.GetAccountRemove() != nil: + return c.ValidateRemove(ch.GetAccountRemove(), authorIdentity) + case ch.GetReadKeyChange() != nil: + return c.ValidateReadKeyChange(ch.GetReadKeyChange(), authorIdentity) + default: + return ErrUnexpectedContentType + } +} + +func (c *contentValidator) ValidatePermissionChange(ch *aclrecordproto.AclAccountPermissionChange, authorIdentity crypto.PubKey) (err error) { if !c.aclState.Permissions(authorIdentity).CanManageAccounts() { return ErrInsufficientPermissions } @@ -36,7 +74,7 @@ func (c *contentValidator) ValidatePermissionChange(ch *aclrecordproto.AclAccoun return } -func (c *contentValidator) ValidateInvite(ch *aclrecordproto.AclAccountInvite, id string, authorIdentity crypto.PubKey) (err error) { +func (c *contentValidator) ValidateInvite(ch *aclrecordproto.AclAccountInvite, authorIdentity crypto.PubKey) (err error) { if !c.aclState.Permissions(authorIdentity).CanManageAccounts() { return ErrInsufficientPermissions } @@ -44,7 +82,7 @@ func (c *contentValidator) ValidateInvite(ch *aclrecordproto.AclAccountInvite, i return } -func (c *contentValidator) ValidateInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, id string, authorIdentity crypto.PubKey) (err error) { +func (c *contentValidator) ValidateInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, authorIdentity crypto.PubKey) (err error) { if !c.aclState.Permissions(authorIdentity).CanManageAccounts() { return ErrInsufficientPermissions } @@ -55,7 +93,7 @@ func (c *contentValidator) ValidateInviteRevoke(ch *aclrecordproto.AclAccountInv return } -func (c *contentValidator) ValidateRequestJoin(ch *aclrecordproto.AclAccountRequestJoin, id string, authorIdentity crypto.PubKey) (err error) { +func (c *contentValidator) ValidateRequestJoin(ch *aclrecordproto.AclAccountRequestJoin, authorIdentity crypto.PubKey) (err error) { inviteKey, exists := c.aclState.inviteKeys[ch.InviteRecordId] if !exists { return ErrNoSuchInvite @@ -81,7 +119,7 @@ func (c *contentValidator) ValidateRequestJoin(ch *aclrecordproto.AclAccountRequ return } -func (c *contentValidator) ValidateRequestAccept(ch *aclrecordproto.AclAccountRequestAccept, id string, authorIdentity crypto.PubKey) (err error) { +func (c *contentValidator) ValidateRequestAccept(ch *aclrecordproto.AclAccountRequestAccept, authorIdentity crypto.PubKey) (err error) { if !c.aclState.Permissions(authorIdentity).CanManageAccounts() { return ErrInsufficientPermissions } @@ -102,7 +140,7 @@ func (c *contentValidator) ValidateRequestAccept(ch *aclrecordproto.AclAccountRe return } -func (c *contentValidator) ValidateRequestDecline(ch *aclrecordproto.AclAccountRequestDecline, id string, authorIdentity crypto.PubKey) (err error) { +func (c *contentValidator) ValidateRequestDecline(ch *aclrecordproto.AclAccountRequestDecline, authorIdentity crypto.PubKey) (err error) { if !c.aclState.Permissions(authorIdentity).CanManageAccounts() { return ErrInsufficientPermissions } @@ -113,7 +151,7 @@ func (c *contentValidator) ValidateRequestDecline(ch *aclrecordproto.AclAccountR return } -func (c *contentValidator) ValidateRemove(ch *aclrecordproto.AclAccountRemove, id string, authorIdentity crypto.PubKey) (err error) { +func (c *contentValidator) ValidateRemove(ch *aclrecordproto.AclAccountRemove, authorIdentity crypto.PubKey) (err error) { if !c.aclState.Permissions(authorIdentity).CanManageAccounts() { return ErrInsufficientPermissions } @@ -128,7 +166,7 @@ func (c *contentValidator) ValidateRemove(ch *aclrecordproto.AclAccountRemove, i return c.validateAccountReadKeys(ch.AccountKeys) } -func (c *contentValidator) ValidateReadKeyChange(ch *aclrecordproto.AclReadKeyChange, id string, authorIdentity crypto.PubKey) (err error) { +func (c *contentValidator) ValidateReadKeyChange(ch *aclrecordproto.AclReadKeyChange, authorIdentity crypto.PubKey) (err error) { return c.validateAccountReadKeys(ch.AccountKeys) }