version.go

  1package identity
  2
  3import (
  4	"crypto/rand"
  5	"encoding/json"
  6	"fmt"
  7	"strings"
  8
  9	"github.com/MichaelMure/git-bug/repository"
 10	"github.com/MichaelMure/git-bug/util/git"
 11	"github.com/MichaelMure/git-bug/util/lamport"
 12	"github.com/MichaelMure/git-bug/util/text"
 13)
 14
 15// Version is a complete set of informations about an Identity at a point in time.
 16type Version struct {
 17	// Private field so not serialized
 18	commitHash git.Hash
 19
 20	// The lamport time at which this version become effective
 21	// The reference time is the bug edition lamport clock
 22	Time lamport.Time `json:"time"`
 23
 24	Name      string `json:"name"`
 25	Email     string `json:"email"`
 26	Login     string `json:"login"`
 27	AvatarUrl string `json:"avatar_url"`
 28
 29	// The set of keys valid at that time, from this version onward, until they get removed
 30	// in a new version. This allow to have multiple key for the same identity (e.g. one per
 31	// device) as well as revoke key.
 32	Keys []Key `json:"pub_keys"`
 33
 34	// This optional array is here to ensure a better randomness of the identity id to avoid collisions.
 35	// It has no functional purpose and should be ignored.
 36	// It is advised to fill this array if there is not enough entropy, e.g. if there is no keys.
 37	Nonce []byte `json:"nonce,omitempty"`
 38}
 39
 40func (v *Version) Validate() error {
 41	if text.Empty(v.Name) && text.Empty(v.Login) {
 42		return fmt.Errorf("either name or login should be set")
 43	}
 44
 45	if strings.Contains(v.Name, "\n") {
 46		return fmt.Errorf("name should be a single line")
 47	}
 48
 49	if !text.Safe(v.Name) {
 50		return fmt.Errorf("name is not fully printable")
 51	}
 52
 53	if strings.Contains(v.Login, "\n") {
 54		return fmt.Errorf("login should be a single line")
 55	}
 56
 57	if !text.Safe(v.Login) {
 58		return fmt.Errorf("login is not fully printable")
 59	}
 60
 61	if strings.Contains(v.Email, "\n") {
 62		return fmt.Errorf("email should be a single line")
 63	}
 64
 65	if !text.Safe(v.Email) {
 66		return fmt.Errorf("email is not fully printable")
 67	}
 68
 69	if v.AvatarUrl != "" && !text.ValidUrl(v.AvatarUrl) {
 70		return fmt.Errorf("avatarUrl is not a valid URL")
 71	}
 72
 73	if len(v.Nonce) > 64 {
 74		return fmt.Errorf("nonce is too big")
 75	}
 76
 77	return nil
 78}
 79
 80// Write will serialize and store the Version as a git blob and return
 81// its hash
 82func (v *Version) Write(repo repository.Repo) (git.Hash, error) {
 83	data, err := json.Marshal(v)
 84
 85	if err != nil {
 86		return "", err
 87	}
 88
 89	hash, err := repo.StoreData(data)
 90
 91	if err != nil {
 92		return "", err
 93	}
 94
 95	return hash, nil
 96}
 97
 98func makeNonce(len int) []byte {
 99	result := make([]byte, len)
100	_, err := rand.Read(result)
101	if err != nil {
102		panic(err)
103	}
104	return result
105}