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  }