From 8f49cccfeb3a842282a0a055a3b66bef2979f148 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Sun, 18 Oct 2020 15:04:11 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Implement=20user-by-share,=20login-?= =?UTF-8?q?by-share=20and=20share-by-user=20retrieval?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- store.go | 75 +++++++++++++++++++++++++++++++++++++++++++++++++-- store_test.go | 53 ++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 2 deletions(-) diff --git a/store.go b/store.go index f3ac563..58289ae 100644 --- a/store.go +++ b/store.go @@ -53,6 +53,8 @@ type ShareStore interface { RemoveLogin(share Share, username string, loginName string) error GetShares() ([]Share, error) + GetShareUsers(share Share) ([]ShareUser, error) + GetShareLogins(share Share, username string) ([]Login, error) FindShareByLogin(username, loginName string) (LoginShare, error) FindSharesByUser(username string) ([]UserShare, error) @@ -497,6 +499,44 @@ func (store *DBStore) GetShares() (shares []Share, err error) { return shares, err } +func (store *DBStore) GetShareUsers(share Share) (shareUsers []ShareUser, err error) { + err = store.db.View(func(tx *buntdb.Tx) error { + usersPrefix := share.userKey("") + if err := tx.AscendKeys(usersPrefix+"*", func(key, value string) bool { + var shareUser ShareUser + shareUser.Username = strings.TrimPrefix(key, usersPrefix) + shareUser.Role = ShareRole(value) + shareUsers = append(shareUsers, shareUser) + return true + }); err != nil { + return err + } + return nil + }) + return +} + +func (store *DBStore) GetShareLogins(share Share, username string) (logins []Login, err error) { + err = store.db.View(func(tx *buntdb.Tx) error { + var processingErr error + + loginsPrefix := share.loginKey(username, "") + if err := tx.AscendKeys(loginsPrefix+"*", func(key, value string) bool { + var login Login + if err := json.Unmarshal([]byte(value), &login); err != nil { + processingErr = err + return false + } + logins = append(logins, login) + return true + }); err != nil { + return err + } + return processingErr + }) + return +} + func (store *DBStore) FindShareByLogin(username, loginName string) (loginShare LoginShare, err error) { err = store.db.View(func(tx *buntdb.Tx) error { shareIdString, err := tx.Get(loginSharePrefix + username + ":" + loginName) @@ -549,8 +589,39 @@ func (store *DBStore) FindShareByLogin(username, loginName string) (loginShare L return } -func (store *DBStore) FindSharesByUser(username string) ([]UserShare, error) { - panic("implement me") +func (store *DBStore) FindSharesByUser(username string) (userShares []UserShare, err error) { + prefix := shareuserPrefix + suffix := ":" + username + + err = store.db.View(func(tx *buntdb.Tx) error { + var processingError error + if err := tx.AscendKeys(prefix+"*"+suffix, func(key, value string) bool { + var userShare UserShare + shareIdString := strings.TrimSuffix(strings.TrimPrefix(key, prefix), suffix) + + if sharePayload, err := tx.Get(sharePrefix + shareIdString); err == buntdb.ErrNotFound { + // TODO report inconsistency + processingError = ErrShareNotFound + return false + } else if err != nil { + processingError = err + return false + } else if err := json.Unmarshal([]byte(sharePayload), &userShare.Share); err != nil { + processingError = fmt.Errorf("cannot unmarshal share: %w", err) + return false + } + + userShare.Role = ShareRole(value) + + userShares = append(userShares, userShare) + + return true + }); err != nil { + return err + } + return processingError + }) + return } func unmarshalShare(idString, payload string) (Share, error) { diff --git a/store_test.go b/store_test.go index f90f1ff..0ea893e 100644 --- a/store_test.go +++ b/store_test.go @@ -227,6 +227,49 @@ func TestStoreShareHandling(t *testing.T) { } }) + t.Run("can list users", func(t *testing.T) { + users, err := store.GetShareUsers(share1) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if len(users) != 2 { + t.Errorf("unexpected amount of users: %d", len(users)) + } + }) + + t.Run("can find shares by user", func(t *testing.T) { + userShares, err := store.FindSharesByUser(user2.Username) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if len(userShares) != 2 { + t.Errorf("unexpected amount of shared: %d", len(userShares)) + } + share1Found := false + share2Found := false + for _, userShare := range userShares { + switch userShare.UUID { + case share1.UUID: + if userShare.Role != ShareRoleReader { + t.Errorf("invalid role for share1: %v", userShare.Role) + } + share1Found = true + case share2.UUID: + if userShare.Role != ShareRoleAdmin { + t.Errorf("invalid role for share2: %v", userShare.Role) + } + share2Found = true + } + } + + if !share1Found { + t.Errorf("share1 was not found") + } + if !share2Found { + t.Errorf("share2 was not found") + } + }) + t.Run("logins can be added", func(t *testing.T) { login1 := Login{LoginName: "login1"} login2 := Login{LoginName: "login2"} @@ -242,6 +285,16 @@ func TestStoreShareHandling(t *testing.T) { t.Errorf("adding login returned error: %v", err) } + t.Run("can list logins", func(t *testing.T) { + logins, err := store.GetShareLogins(share1, user2.Username) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if len(logins) != 2 { + t.Errorf("unexpected amount of users: %d", len(logins)) + } + }) + t.Run("duplicate login not allowed", func(t *testing.T) { // Different share, but same user:login pair as above. Must be a share where // that user is already assigned, though.