bare.go

  1package identity
  2
  3import (
  4	"crypto/sha256"
  5	"encoding/json"
  6	"fmt"
  7	"strings"
  8
  9	"github.com/MichaelMure/git-bug/entity"
 10	"github.com/MichaelMure/git-bug/repository"
 11	"github.com/MichaelMure/git-bug/util/lamport"
 12	"github.com/MichaelMure/git-bug/util/text"
 13	"github.com/MichaelMure/git-bug/util/timestamp"
 14)
 15
 16var _ Interface = &Bare{}
 17var _ entity.Interface = &Bare{}
 18
 19// Bare is a very minimal identity, designed to be fully embedded directly along
 20// other data.
 21//
 22// in particular, this identity is designed to be compatible with the handling of
 23// identities in the early version of git-bug.
 24// Deprecated: legacy identity for compat, might make sense to ditch entirely for
 25// simplicity but that would be a breaking change.
 26type Bare struct {
 27	id        entity.Id
 28	name      string
 29	email     string
 30	avatarUrl string
 31}
 32
 33func NewBare(name string, email string) *Bare {
 34	return &Bare{id: entity.UnsetId, name: name, email: email}
 35}
 36
 37func NewBareFull(name string, email string, avatarUrl string) *Bare {
 38	return &Bare{id: entity.UnsetId, name: name, email: email, avatarUrl: avatarUrl}
 39}
 40
 41func deriveId(data []byte) entity.Id {
 42	sum := sha256.Sum256(data)
 43	return entity.Id(fmt.Sprintf("%x", sum))
 44}
 45
 46type bareIdentityJSON struct {
 47	Name      string `json:"name,omitempty"`
 48	Email     string `json:"email,omitempty"`
 49	Login     string `json:"login,omitempty"` // Deprecated, only kept to have the same ID when reading an old value
 50	AvatarUrl string `json:"avatar_url,omitempty"`
 51}
 52
 53func (i *Bare) MarshalJSON() ([]byte, error) {
 54	return json.Marshal(bareIdentityJSON{
 55		Name:      i.name,
 56		Email:     i.email,
 57		AvatarUrl: i.avatarUrl,
 58	})
 59}
 60
 61func (i *Bare) UnmarshalJSON(data []byte) error {
 62	// Compute the Id when loading the op from disk.
 63	i.id = deriveId(data)
 64
 65	aux := bareIdentityJSON{}
 66
 67	if err := json.Unmarshal(data, &aux); err != nil {
 68		return err
 69	}
 70
 71	i.name = aux.Name
 72	i.email = aux.Email
 73	i.avatarUrl = aux.AvatarUrl
 74
 75	return nil
 76}
 77
 78// Id return the Identity identifier
 79func (i *Bare) Id() entity.Id {
 80	// We don't have a proper Id at hand, so let's hash all the data to get one.
 81
 82	if i.id == "" {
 83		// something went really wrong
 84		panic("identity's id not set")
 85	}
 86	if i.id == entity.UnsetId {
 87		// This means we are trying to get the identity identifier *before* it has been stored
 88		// As the Id is computed based on the actual bytes written on the disk, we are going to predict
 89		// those and then get the Id. This is safe as it will be the exact same code writing on disk later.
 90
 91		data, err := json.Marshal(i)
 92		if err != nil {
 93			panic(err)
 94		}
 95
 96		i.id = deriveId(data)
 97	}
 98	return i.id
 99}
100
101// Name return the last version of the name
102func (i *Bare) Name() string {
103	return i.name
104}
105
106// Email return the last version of the email
107func (i *Bare) Email() string {
108	return i.email
109}
110
111// AvatarUrl return the last version of the Avatar URL
112func (i *Bare) AvatarUrl() string {
113	return i.avatarUrl
114}
115
116// Keys return the last version of the valid keys
117func (i *Bare) Keys() []*Key {
118	return nil
119}
120
121// ValidKeysAtTime return the set of keys valid at a given lamport time
122func (i *Bare) ValidKeysAtTime(_ lamport.Time) []*Key {
123	return nil
124}
125
126// DisplayName return a non-empty string to display, representing the
127// identity, based on the non-empty values.
128func (i *Bare) DisplayName() string {
129	return i.name
130}
131
132// Validate check if the Identity data is valid
133func (i *Bare) Validate() error {
134	if text.Empty(i.name) {
135		return fmt.Errorf("name is not set")
136	}
137
138	if strings.Contains(i.name, "\n") {
139		return fmt.Errorf("name should be a single line")
140	}
141
142	if !text.Safe(i.name) {
143		return fmt.Errorf("name is not fully printable")
144	}
145
146	if strings.Contains(i.email, "\n") {
147		return fmt.Errorf("email should be a single line")
148	}
149
150	if !text.Safe(i.email) {
151		return fmt.Errorf("email is not fully printable")
152	}
153
154	if i.avatarUrl != "" && !text.ValidUrl(i.avatarUrl) {
155		return fmt.Errorf("avatarUrl is not a valid URL")
156	}
157
158	return nil
159}
160
161// Write the identity into the Repository. In particular, this ensure that
162// the Id is properly set.
163func (i *Bare) Commit(repo repository.ClockedRepo) error {
164	// Nothing to do, everything is directly embedded
165	return nil
166}
167
168// If needed, write the identity into the Repository. In particular, this
169// ensure that the Id is properly set.
170func (i *Bare) CommitAsNeeded(repo repository.ClockedRepo) error {
171	// Nothing to do, everything is directly embedded
172	return nil
173}
174
175// IsProtected return true if the chain of git commits started to be signed.
176// If that's the case, only signed commit with a valid key for this identity can be added.
177func (i *Bare) IsProtected() bool {
178	return false
179}
180
181// LastModificationLamportTime return the Lamport time at which the last version of the identity became valid.
182func (i *Bare) LastModificationLamport() lamport.Time {
183	return 0
184}
185
186// LastModification return the timestamp at which the last version of the identity became valid.
187func (i *Bare) LastModification() timestamp.Timestamp {
188	return 0
189}