github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/teams/storage/generic.go (about) 1 package storage 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 "sync" 8 9 lru "github.com/hashicorp/golang-lru" 10 context "golang.org/x/net/context" 11 12 "github.com/keybase/client/go/encrypteddb" 13 "github.com/keybase/client/go/libkb" 14 "github.com/keybase/client/go/protocol/keybase1" 15 ) 16 17 // Store TeamData's of FastTeamData's on memory and disk. Threadsafe. 18 type storageGeneric struct { 19 sync.Mutex 20 mem *memoryStorageGeneric 21 disk *diskStorageGeneric 22 description string 23 } 24 25 func newStorageGeneric(g *libkb.GlobalContext, lruSize int, version int, dbObjTyp libkb.ObjType, reason libkb.EncryptionReason, description string, gdi func() diskItemGeneric) *storageGeneric { 26 return &storageGeneric{ 27 mem: newMemoryStorageGeneric(lruSize), 28 disk: newDiskStorageGeneric(g, version, dbObjTyp, reason, gdi), 29 description: description, 30 } 31 } 32 33 func (s *storageGeneric) put(mctx libkb.MetaContext, state teamDataGeneric) { 34 s.Lock() 35 defer s.Unlock() 36 37 s.mem.put(mctx, state) 38 39 err := s.disk.put(mctx, state) 40 if err != nil { 41 mctx.Warning("teams/storage.Generic#Put err: %v", err) 42 } 43 } 44 45 // Can return nil. 46 func (s *storageGeneric) get(mctx libkb.MetaContext, teamID keybase1.TeamID, public bool) teamDataGeneric { 47 s.Lock() 48 defer s.Unlock() 49 50 item := s.mem.get(mctx, teamID, public) 51 if item != nil { 52 mctx.VLogf(libkb.VLog0, "teams/storage.Generic#Get(%v) hit mem (%s)", teamID, s.description) 53 // Mem hit 54 return item 55 } 56 57 res, found, err := s.disk.get(mctx, teamID, public) 58 if found && err == nil { 59 // Disk hit 60 mctx.VLogf(libkb.VLog0, "teams/storage.Generic#Get(%v) hit disk (%s)", teamID, s.description) 61 s.mem.put(mctx, res) 62 return res 63 } 64 if err != nil { 65 mctx.Debug("teams/storage.Generic#Get(%v) disk err: %v", teamID, err) 66 } 67 mctx.VLogf(libkb.VLog0, "teams/storage.Generic#Get(%v) missed (%s)", teamID, s.description) 68 return nil 69 } 70 71 // Clear the in-memory storage. 72 func (s *storageGeneric) ClearMem() { 73 s.mem.clear() 74 } 75 76 func (s *storageGeneric) Shutdown() { 77 s.mem.shutdown() 78 } 79 80 func (s *storageGeneric) MemSize() int { 81 return s.mem.len() 82 } 83 84 // -------------------------------------------------- 85 86 type teamDataGeneric interface { 87 IsPublic() bool 88 ID() keybase1.TeamID 89 } 90 91 type diskItemGeneric interface { 92 version() int 93 value() teamDataGeneric 94 setVersion(i int) 95 setValue(o teamDataGeneric) error 96 } 97 98 // Store TeamData's on disk. Threadsafe. 99 type diskStorageGeneric struct { 100 sync.Mutex 101 encryptedDB *encrypteddb.EncryptedDB 102 version int 103 dbObjTyp libkb.ObjType 104 getEmptyDiskItem func() diskItemGeneric 105 } 106 107 func newDiskStorageGeneric(g *libkb.GlobalContext, version int, dbObjTyp libkb.ObjType, reason libkb.EncryptionReason, gdi func() diskItemGeneric) *diskStorageGeneric { 108 keyFn := func(ctx context.Context) ([32]byte, error) { 109 return encrypteddb.GetSecretBoxKey(ctx, g, reason, "encrypt teams storage") 110 } 111 dbFn := func(g *libkb.GlobalContext) *libkb.JSONLocalDb { 112 return g.LocalDb 113 } 114 return &diskStorageGeneric{ 115 encryptedDB: encrypteddb.New(g, dbFn, keyFn), 116 version: version, 117 dbObjTyp: dbObjTyp, 118 getEmptyDiskItem: gdi, 119 } 120 } 121 122 func (s *diskStorageGeneric) put(mctx libkb.MetaContext, state teamDataGeneric) error { 123 s.Lock() 124 defer s.Unlock() 125 126 if !mctx.ActiveDevice().Valid() && !state.IsPublic() { 127 mctx.Debug("skipping team store since user is logged out") 128 return nil 129 } 130 131 key := s.dbKey(mctx, state.ID(), state.IsPublic()) 132 item := s.getEmptyDiskItem() 133 item.setVersion(s.version) 134 err := item.setValue(state) 135 if err != nil { 136 return err 137 } 138 139 err = s.encryptedDB.Put(mctx.Ctx(), key, item) 140 if err != nil { 141 return err 142 } 143 return nil 144 } 145 146 // Res is valid if (found && err == nil) 147 func (s *diskStorageGeneric) get(mctx libkb.MetaContext, teamID keybase1.TeamID, public bool) (teamDataGeneric, bool, error) { 148 s.Lock() 149 defer s.Unlock() 150 151 key := s.dbKey(mctx, teamID, public) 152 item := s.getEmptyDiskItem() 153 found, err := s.encryptedDB.Get(mctx.Ctx(), key, item) 154 if (err != nil) || !found { 155 return nil, found, err 156 } 157 158 if item.version() != s.version { 159 // Pretend it wasn't found. 160 return nil, false, nil 161 } 162 163 ret := item.value() 164 165 // Sanity check 166 if len(ret.ID()) == 0 { 167 return nil, false, fmt.Errorf("decode from disk had empty team id") 168 } 169 if !ret.ID().Eq(teamID) { 170 return nil, false, fmt.Errorf("decode from disk had wrong team id %v != %v", ret.ID(), teamID) 171 } 172 if ret.IsPublic() != public { 173 return nil, false, fmt.Errorf("decode from disk had wrong publicness %v != %v (%v)", ret.IsPublic(), public, teamID) 174 } 175 176 return item.value(), true, nil 177 } 178 179 func (s *diskStorageGeneric) dbKey(mctx libkb.MetaContext, teamID keybase1.TeamID, public bool) libkb.DbKey { 180 key := fmt.Sprintf("tid:%s", teamID) 181 if public { 182 key = fmt.Sprintf("tid:%s|pub", teamID) 183 } 184 return libkb.DbKey{ 185 Typ: s.dbObjTyp, 186 Key: key, 187 } 188 } 189 190 // -------------------------------------------------- 191 192 // Store some TeamSigChainState's in memory. Threadsafe. 193 type memoryStorageGeneric struct { 194 sync.Mutex // protects the pointer on lru 195 lru *lru.Cache 196 } 197 198 func newMemoryStorageGeneric(size int) *memoryStorageGeneric { 199 nlru, err := lru.New(size) 200 if err != nil { 201 // lru.New only panics if size <= 0 202 log.Panicf("Could not create lru cache: %v", err) 203 } 204 return &memoryStorageGeneric{ 205 lru: nlru, 206 } 207 } 208 209 func (s *memoryStorageGeneric) getLRU() *lru.Cache { 210 s.Lock() 211 defer s.Unlock() 212 return s.lru 213 } 214 215 func (s *memoryStorageGeneric) put(mctx libkb.MetaContext, state teamDataGeneric) { 216 s.getLRU().Add(s.key(state.ID(), state.IsPublic()), state) 217 } 218 219 // Can return nil. 220 func (s *memoryStorageGeneric) get(mctx libkb.MetaContext, teamID keybase1.TeamID, public bool) teamDataGeneric { 221 untyped, ok := s.getLRU().Get(s.key(teamID, public)) 222 if !ok { 223 return nil 224 } 225 state, ok := untyped.(teamDataGeneric) 226 if !ok { 227 mctx.Warning("Team MemoryStorage got bad type from lru: %T", untyped) 228 return nil 229 } 230 return state 231 } 232 233 func (s *memoryStorageGeneric) shutdown() { 234 s.Lock() 235 defer s.Unlock() 236 var err error 237 s.lru, err = lru.New(1) 238 if err != nil { 239 panic(err) 240 } 241 } 242 243 func (s *memoryStorageGeneric) len() int { 244 return s.getLRU().Len() 245 } 246 247 func (s *memoryStorageGeneric) clear() { 248 s.getLRU().Purge() 249 } 250 251 func (s *memoryStorageGeneric) key(teamID keybase1.TeamID, public bool) (key string) { 252 return genericStringKey(teamID, public) 253 } 254 255 func genericStringKey(teamID keybase1.TeamID, public bool) (key string) { 256 key = fmt.Sprintf("tid:%s", teamID) 257 if public { 258 key = fmt.Sprintf("tid:%s|pub", teamID) 259 } 260 return key 261 } 262 263 // -------------------------------------------------- 264 265 // ParseTeamIDDBKey takes an tid:-style key (used by FTL and slow team loader) 266 // and returns a regular team id. We can safely strip away the |pub marker 267 // because the publicness of a team is encoded in its ID. 268 func ParseTeamIDDBKey(s string) (teamID keybase1.TeamID, err error) { 269 if !strings.HasPrefix(s, "tid:") { 270 return "", fmt.Errorf("does not start with team id prefix") 271 } 272 s = strings.TrimPrefix(s, "tid:") 273 s = strings.TrimSuffix(s, "|pub") 274 return keybase1.TeamID(s), nil 275 }