github.com/hoffie/larasync@v0.0.0-20151025221940-0384d2bddcef/repository/repository.go (about) 1 package repository 2 3 import ( 4 "bytes" 5 "errors" 6 "io" 7 "io/ioutil" 8 "os" 9 10 "github.com/hoffie/larasync/repository/content" 11 "github.com/hoffie/larasync/repository/nib" 12 ) 13 14 const ( 15 // internal directory names 16 managementDirName = ".lara" 17 objectsDirName = "objects" 18 nibsDirName = "nibs" 19 transactionsDirName = "transactions" 20 authorizationsDirName = "authorizations" 21 keysDirName = "keys" 22 stateConfigFileName = "state.json" 23 24 // default permissions 25 defaultFilePerms = 0600 26 defaultDirPerms = 0700 27 28 // chunk splitting size 29 chunkSize = 1 * 1024 * 1024 30 ) 31 32 // Repository represents an on-disk repository and provides methods to 33 // access its sub-items. 34 type Repository struct { 35 Path string 36 keys *KeyStore 37 objectStorage content.Storage 38 nibStore *NIBStore 39 transactionManager *TransactionManager 40 authorizationManager *AuthorizationManager 41 managementDir *managementDirectory 42 } 43 44 // New returns a new repository instance with the given base path 45 func New(path string) *Repository { 46 r := &Repository{Path: path} 47 48 r.managementDir = newManagementDirectory(r) 49 50 r.objectStorage = content.NewFileStorage(r.subPathFor(objectsDirName)) 51 52 r.transactionManager = newTransactionManager( 53 content.NewFileStorage(r.subPathFor(transactionsDirName)), 54 r.managementDir.getDir(), 55 ) 56 r.authorizationManager = newAuthorizationManager( 57 content.NewFileStorage(r.subPathFor(authorizationsDirName)), 58 ) 59 60 r.keys = NewKeyStore(content.NewFileStorage(r.subPathFor(keysDirName))) 61 r.nibStore = newNIBStore( 62 content.NewFileStorage(r.subPathFor(nibsDirName)), 63 r.keys, 64 r.transactionManager, 65 ) 66 67 return r 68 } 69 70 // subPathFor returns the full path for the given entry. 71 func (r *Repository) subPathFor(name string) string { 72 return r.managementDir.subPathFor(name) 73 } 74 75 // CreateManagementDir ensures that this repository's management 76 // directory exists. 77 func (r *Repository) CreateManagementDir() error { 78 return r.managementDir.create() 79 } 80 81 // GetManagementDir returns the path to the management directory. 82 func (r *Repository) GetManagementDir() string { 83 return r.managementDir.getDir() 84 } 85 86 // Create initially creates the repository directory structure. 87 func (r *Repository) Create() error { 88 err := os.Mkdir(r.Path, defaultDirPerms) 89 if err != nil { 90 return err 91 } 92 err = r.CreateManagementDir() 93 if err != nil { 94 return err 95 } 96 97 return err 98 } 99 100 // AddObject adds an object into the storage with the given 101 // id and adds the data in the reader to it. 102 func (r *Repository) AddObject(objectID string, data io.Reader) error { 103 return r.objectStorage.Set(objectID, data) 104 } 105 106 // HasObject returns whether the given object id exists in the object 107 // store. 108 func (r *Repository) HasObject(objectID string) bool { 109 return r.objectStorage.Exists(objectID) 110 } 111 112 // VerifyAndParseNIBBytes checks the signature of the given NIB and 113 // deserializes it if the signature could be validated. 114 func (r *Repository) VerifyAndParseNIBBytes(data []byte) (*nib.NIB, error) { 115 return r.nibStore.VerifyAndParseBytes(data) 116 } 117 118 // AddNIBContent adds NIBData to the repository after verifying it. 119 func (r *Repository) AddNIBContent(nibReader io.Reader) error { 120 nibStore := r.nibStore 121 122 data, err := ioutil.ReadAll(nibReader) 123 if err != nil { 124 return err 125 } 126 127 nib, err := r.VerifyAndParseNIBBytes(data) 128 if err != nil { 129 return err 130 } 131 132 missingObjectIDs := []string{} 133 for _, objectID := range nib.AllObjectIDs() { 134 if !r.HasObject(objectID) { 135 missingObjectIDs = append(missingObjectIDs, objectID) 136 } 137 } 138 139 if len(missingObjectIDs) > 0 { 140 return NewErrNIBContentMissing(missingObjectIDs) 141 } 142 143 err = r.ensureConflictFreeNIBImport(nib) 144 if err != nil { 145 return err 146 } 147 return nibStore.AddContent(nib.ID, bytes.NewReader(data)) 148 } 149 150 // ensureConflictFreeNIBImport returns an error if we cannot import 151 // the given NIB without conflicts or nil if everything is good. 152 func (r *Repository) ensureConflictFreeNIBImport(otherNIB *nib.NIB) error { 153 if !r.HasNIB(otherNIB.ID) { 154 return nil 155 } 156 myNIB, err := r.GetNIB(otherNIB.ID) 157 if err != nil { 158 return err 159 } 160 if myNIB.IsParentOf(otherNIB) { 161 return nil 162 } 163 return ErrNIBConflict 164 } 165 166 // GetNIB returns a NIB for the given ID in this repository. 167 func (r *Repository) GetNIB(id string) (*nib.NIB, error) { 168 return r.nibStore.Get(id) 169 } 170 171 // GetNIBReader returns the NIB with the given id in this repository. 172 func (r *Repository) GetNIBReader(id string) (io.ReadCloser, error) { 173 return r.nibStore.getReader(id) 174 } 175 176 // GetNIBBytesFrom returns the signed byte structure for NIBs from the given 177 // transaction id 178 func (r *Repository) GetNIBBytesFrom(fromTransactionID int64) (<-chan []byte, error) { 179 return r.nibStore.GetBytesFrom(fromTransactionID) 180 } 181 182 // GetNIBsFrom returns nibs added since the passed transaction ID. 183 func (r *Repository) GetNIBsFrom(fromTransactionID int64) (<-chan *nib.NIB, error) { 184 return r.nibStore.GetFrom(fromTransactionID) 185 } 186 187 // GetAllNIBBytes returns all NIBs signed byte representations in this repository. 188 func (r *Repository) GetAllNIBBytes() (<-chan []byte, error) { 189 return r.nibStore.GetAllBytes() 190 } 191 192 // GetAllNibs returns all the nibs which are stored in this repository. 193 // Those will be returned with the oldest one first and the newest added 194 // last. 195 func (r *Repository) GetAllNibs() (<-chan *nib.NIB, error) { 196 return r.nibStore.GetAll() 197 } 198 199 // HasNIB checks if a NIB with the given ID exists in the repository. 200 func (r *Repository) HasNIB(id string) bool { 201 return r.nibStore.Exists(id) 202 } 203 204 // CurrentTransaction returns the currently newest Transaction for this 205 // repository. 206 func (r *Repository) CurrentTransaction() (*Transaction, error) { 207 return r.transactionManager.CurrentTransaction() 208 } 209 210 // GetAuthorizationReader returns the authorization configuration for the 211 // passed PublicKey. 212 func (r *Repository) GetAuthorizationReader(publicKey [PublicKeySize]byte) (io.ReadCloser, error) { 213 return r.authorizationManager.GetReader(publicKey) 214 } 215 216 // SetAuthorizationData adds for the given publicKey the authorization structure 217 func (r *Repository) SetAuthorizationData(publicKey [PublicKeySize]byte, authData io.Reader) error { 218 return r.authorizationManager.SetData(publicKey, authData) 219 } 220 221 // DeleteAuthorization removes the authorization with the given publicKey. 222 func (r *Repository) DeleteAuthorization(publicKey [PublicKeySize]byte) error { 223 return r.authorizationManager.Delete(publicKey) 224 } 225 226 // SerializeAuthorization returns the encrypted and authorization which can be passed 227 // safely to the server. 228 func (r *Repository) SerializeAuthorization(encryptionKey [EncryptionKeySize]byte, 229 authorization *Authorization) ([]byte, error) { 230 return r.authorizationManager.Serialize(encryptionKey, authorization) 231 } 232 233 // GetObjectData returns the data stored for the given objectID in this 234 // repository. 235 func (r *Repository) GetObjectData(objectID string) (io.ReadCloser, error) { 236 return r.objectStorage.Get(objectID) 237 } 238 239 // getRepoRelativePath turns the given path into a path relative to the 240 // repository root and returns it. 241 func (r *Repository) getRepoRelativePath(absPath string) (string, error) { 242 if len(absPath) < len(r.Path)+1 { 243 return "", errors.New("unable to resolve path: path too short") 244 } 245 rel := absPath[len(r.Path)+1:] 246 return rel, nil 247 } 248 249 // GetSigningPublicKey exposes the signing public key as it is required 250 // in foreign packages such as api. 251 func (r *Repository) GetSigningPublicKey() ([PublicKeySize]byte, error) { 252 return r.keys.SigningPublicKey() 253 } 254 255 // SetKeysFromAuth takes the keys passed through the authorization and puts 256 // them into the keystore. 257 func (r *Repository) SetKeysFromAuth(auth *Authorization) error { 258 keys := r.keys 259 err := keys.SetEncryptionKey(auth.EncryptionKey) 260 if err != nil { 261 return err 262 } 263 err = keys.SetHashingKey(auth.HashingKey) 264 if err != nil { 265 return err 266 } 267 err = keys.SetSigningPrivateKey(auth.SigningKey) 268 if err != nil { 269 return err 270 } 271 return nil 272 }