aboutsummaryrefslogtreecommitdiffstats
path: root/identity/version.go
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2022-08-18 23:34:05 +0200
committerMichael Muré <batolettre@gmail.com>2022-08-18 23:44:06 +0200
commit5511c230b678a181cc596238bf6669428d1b1902 (patch)
tree8701efc87732439f993eb4f1d00585fc419b87ab /identity/version.go
parent5ca686b59751e3c87740b84108c54fc675a074cf (diff)
downloadgit-bug-5511c230b678a181cc596238bf6669428d1b1902.tar.gz
move {bug,identity} to /entities, move input to /commands
Diffstat (limited to 'identity/version.go')
-rw-r--r--identity/version.go273
1 files changed, 0 insertions, 273 deletions
diff --git a/identity/version.go b/identity/version.go
deleted file mode 100644
index 9a52d089..00000000
--- a/identity/version.go
+++ /dev/null
@@ -1,273 +0,0 @@
-package identity
-
-import (
- "crypto/rand"
- "encoding/json"
- "fmt"
- "time"
-
- "github.com/pkg/errors"
-
- "github.com/MichaelMure/git-bug/entity"
- "github.com/MichaelMure/git-bug/repository"
- "github.com/MichaelMure/git-bug/util/lamport"
- "github.com/MichaelMure/git-bug/util/text"
-)
-
-// 1: original format
-// 2: Identity Ids are generated from the first version serialized data instead of from the first git commit
-// + Identity hold multiple lamport clocks from other entities, instead of just bug edit
-const formatVersion = 2
-
-// version is a complete set of information about an Identity at a point in time.
-type version struct {
- name string
- email string // as defined in git or from a bridge when importing the identity
- login string // from a bridge when importing the identity
- avatarURL string
-
- // The lamport times of the other entities at which this version become effective
- times map[string]lamport.Time
- unixTime int64
-
- // The set of keys valid at that time, from this version onward, until they get removed
- // in a new version. This allow to have multiple key for the same identity (e.g. one per
- // device) as well as revoke key.
- keys []*Key
-
- // mandatory random bytes to ensure a better randomness of the data of the first
- // version of an identity, used to later generate the ID
- // len(Nonce) should be > 20 and < 64 bytes
- // It has no functional purpose and should be ignored.
- // TODO: optional after first version?
- nonce []byte
-
- // A set of arbitrary key/value to store metadata about a version or about an Identity in general.
- metadata map[string]string
-
- // Not serialized. Store the version's id in memory.
- id entity.Id
- // Not serialized
- commitHash repository.Hash
-}
-
-func newVersion(repo repository.RepoClock, name string, email string, login string, avatarURL string, keys []*Key) (*version, error) {
- clocks, err := repo.AllClocks()
- if err != nil {
- return nil, err
- }
-
- times := make(map[string]lamport.Time)
- for name, clock := range clocks {
- times[name] = clock.Time()
- }
-
- return &version{
- id: entity.UnsetId,
- name: name,
- email: email,
- login: login,
- avatarURL: avatarURL,
- times: times,
- unixTime: time.Now().Unix(),
- keys: keys,
- nonce: makeNonce(20),
- }, nil
-}
-
-type versionJSON struct {
- // Additional field to version the data
- FormatVersion uint `json:"version"`
-
- Times map[string]lamport.Time `json:"times"`
- UnixTime int64 `json:"unix_time"`
- Name string `json:"name,omitempty"`
- Email string `json:"email,omitempty"`
- Login string `json:"login,omitempty"`
- AvatarUrl string `json:"avatar_url,omitempty"`
- Keys []*Key `json:"pub_keys,omitempty"`
- Nonce []byte `json:"nonce"`
- Metadata map[string]string `json:"metadata,omitempty"`
-}
-
-// Id return the identifier of the version
-func (v *version) Id() entity.Id {
- if v.id == "" {
- // something went really wrong
- panic("version's id not set")
- }
- if v.id == entity.UnsetId {
- // This means we are trying to get the version's Id *before* it has been stored.
- // As the Id is computed based on the actual bytes written on the disk, we are going to predict
- // those and then get the Id. This is safe as it will be the exact same code writing on disk later.
- data, err := json.Marshal(v)
- if err != nil {
- panic(err)
- }
- v.id = entity.DeriveId(data)
- }
- return v.id
-}
-
-// Make a deep copy
-func (v *version) Clone() *version {
- // copy direct fields
- clone := *v
-
- // reset some fields
- clone.commitHash = ""
- clone.id = entity.UnsetId
-
- clone.times = make(map[string]lamport.Time)
- for name, t := range v.times {
- clone.times[name] = t
- }
-
- clone.keys = make([]*Key, len(v.keys))
- for i, key := range v.keys {
- clone.keys[i] = key.Clone()
- }
-
- clone.nonce = make([]byte, len(v.nonce))
- copy(clone.nonce, v.nonce)
-
- // not copying metadata
-
- return &clone
-}
-
-func (v *version) MarshalJSON() ([]byte, error) {
- return json.Marshal(versionJSON{
- FormatVersion: formatVersion,
- Times: v.times,
- UnixTime: v.unixTime,
- Name: v.name,
- Email: v.email,
- Login: v.login,
- AvatarUrl: v.avatarURL,
- Keys: v.keys,
- Nonce: v.nonce,
- Metadata: v.metadata,
- })
-}
-
-func (v *version) UnmarshalJSON(data []byte) error {
- var aux versionJSON
-
- if err := json.Unmarshal(data, &aux); err != nil {
- return err
- }
-
- if aux.FormatVersion != formatVersion {
- return entity.NewErrInvalidFormat(aux.FormatVersion, formatVersion)
- }
-
- v.id = entity.DeriveId(data)
- v.times = aux.Times
- v.unixTime = aux.UnixTime
- v.name = aux.Name
- v.email = aux.Email
- v.login = aux.Login
- v.avatarURL = aux.AvatarUrl
- v.keys = aux.Keys
- v.nonce = aux.Nonce
- v.metadata = aux.Metadata
-
- return nil
-}
-
-func (v *version) Validate() error {
- // time must be set after a commit
- if v.commitHash != "" && v.unixTime == 0 {
- return fmt.Errorf("unix time not set")
- }
-
- if text.Empty(v.name) && text.Empty(v.login) {
- return fmt.Errorf("either name or login should be set")
- }
- if !text.SafeOneLine(v.name) {
- return fmt.Errorf("name has unsafe characters")
- }
-
- if !text.SafeOneLine(v.login) {
- return fmt.Errorf("login has unsafe characters")
- }
-
- if !text.SafeOneLine(v.email) {
- return fmt.Errorf("email has unsafe characters")
- }
-
- if v.avatarURL != "" && !text.ValidUrl(v.avatarURL) {
- return fmt.Errorf("avatarUrl is not a valid URL")
- }
-
- if len(v.nonce) > 64 {
- return fmt.Errorf("nonce is too big")
- }
- if len(v.nonce) < 20 {
- return fmt.Errorf("nonce is too small")
- }
-
- for _, k := range v.keys {
- if err := k.Validate(); err != nil {
- return errors.Wrap(err, "invalid key")
- }
- }
-
- return nil
-}
-
-// Write will serialize and store the version as a git blob and return
-// its hash
-func (v *version) Write(repo repository.Repo) (repository.Hash, error) {
- // make sure we don't write invalid data
- err := v.Validate()
- if err != nil {
- return "", errors.Wrap(err, "validation error")
- }
-
- data, err := json.Marshal(v)
- if err != nil {
- return "", err
- }
-
- hash, err := repo.StoreData(data)
- if err != nil {
- return "", err
- }
-
- // make sure we set the Id when writing in the repo
- v.id = entity.DeriveId(data)
-
- return hash, nil
-}
-
-func makeNonce(len int) []byte {
- result := make([]byte, len)
- _, err := rand.Read(result)
- if err != nil {
- panic(err)
- }
- return result
-}
-
-// SetMetadata store arbitrary metadata about a version or an Identity in general
-// If the version has been commit to git already, it won't be overwritten.
-// Beware: changing the metadata on a version will change it's ID
-func (v *version) SetMetadata(key string, value string) {
- if v.metadata == nil {
- v.metadata = make(map[string]string)
- }
- v.metadata[key] = value
-}
-
-// GetMetadata retrieve arbitrary metadata about the version
-func (v *version) GetMetadata(key string) (string, bool) {
- val, ok := v.metadata[key]
- return val, ok
-}
-
-// AllMetadata return all metadata for this version
-func (v *version) AllMetadata() map[string]string {
- return v.metadata
-}