github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/profile/profile.go (about) 1 // Package profile defines a qri peer profile 2 package profile 3 4 import ( 5 "fmt" 6 "strings" 7 "time" 8 9 logger "github.com/ipfs/go-log" 10 "github.com/libp2p/go-libp2p-core/crypto" 11 peer "github.com/libp2p/go-libp2p-core/peer" 12 ma "github.com/multiformats/go-multiaddr" 13 "github.com/qri-io/qri/auth/key" 14 "github.com/qri-io/qri/config" 15 ) 16 17 var log = logger.Logger("profile") 18 19 // Profile defines peer profile details 20 type Profile struct { 21 // All Profiles are built on public key infrastructure 22 // PrivKey is the peer's private key, should only be present for the current peer 23 PrivKey crypto.PrivKey `json:"_,omitempty"` 24 // PubKey is the peer's public key 25 PubKey crypto.PubKey `json:"key,omitempty"` 26 // KeyID is the key identifier used for the keystore 27 KeyID key.ID `json:"key_id,omitempty"` 28 29 ID ID `json:"id"` 30 // Created timestamp 31 Created time.Time `json:"created,omitempty"` 32 // Updated timestamp 33 Updated time.Time `json:"updated,omitempty"` 34 // Peername a handle for the user. min 1 character, max 80. composed of [_,-,a-z,A-Z,1-9] 35 Peername string `json:"peername"` 36 // specifies weather this is a user or an organization 37 Type Type `json:"type"` 38 // user's email address 39 Email string `json:"email"` 40 // user name field. could be first[space]last, but not strictly enforced 41 Name string `json:"name"` 42 // user-filled description of self 43 Description string `json:"description"` 44 // url this user wants the world to click 45 HomeURL string `json:"homeUrl"` 46 // color this user likes to use as their theme color 47 Color string `json:"color"` 48 // Thumb path for user's thumbnail 49 Thumb string `json:"thumb"` 50 // Profile photo 51 Photo string `json:"photo"` 52 // Poster photo for users's profile page 53 Poster string `json:"poster"` 54 // Twitter is a peer's twitter handle 55 Twitter string `json:"twitter"` 56 // Online indicates if this peer is currently connected to the network 57 Online bool `json:"online,omitempty"` 58 59 // PeerIDs lists any network PeerIDs associated with this profile 60 // in the form /network/peerID 61 PeerIDs []peer.ID `json:"peerIDs"` 62 // NetworkAddrs keeps a list of locations for this profile on the network as multiaddr strings 63 NetworkAddrs []ma.Multiaddr `json:"networkAddrs,omitempty"` 64 } 65 66 // NewProfile allocates a profile from a CodingProfile 67 func NewProfile(p *config.ProfilePod) (pro *Profile, err error) { 68 pro = &Profile{} 69 err = pro.Decode(p) 70 return 71 } 72 73 // NewSparsePKProfile creates a sparsely-populated profile from a username and 74 // private key 75 func NewSparsePKProfile(username string, pk crypto.PrivKey) (*Profile, error) { 76 keyID, err := key.IDFromPrivKey(pk) 77 if err != nil { 78 return nil, err 79 } 80 81 id, err := IDB58Decode(keyID) 82 if err != nil { 83 return nil, err 84 } 85 86 return &Profile{ 87 Peername: username, 88 ID: id, 89 PubKey: pk.GetPublic(), 90 PrivKey: pk, 91 }, nil 92 } 93 94 // Decode turns a ProfilePod into a profile.Profile 95 func (p *Profile) Decode(sp *config.ProfilePod) error { 96 id, err := IDB58Decode(sp.ID) 97 if err != nil { 98 return fmt.Errorf("parsing profile.ID %q: %w", sp.ID, err) 99 } 100 101 t, err := ParseType(sp.Type) 102 if err != nil { 103 return fmt.Errorf("parsing profileType %q: %w", sp.Type, err) 104 } 105 106 pids := make([]peer.ID, len(sp.PeerIDs)) 107 for i, idstr := range sp.PeerIDs { 108 idstr = strings.TrimPrefix(idstr, "/ipfs/") 109 if id, err := peer.IDB58Decode(idstr); err == nil { 110 pids[i] = id 111 } 112 } 113 114 keyID := IDB58DecodeOrEmpty(sp.KeyID) 115 pro := Profile{ 116 ID: id, 117 KeyID: key.ID(keyID), 118 Type: t, 119 Peername: sp.Peername, 120 Created: sp.Created, 121 Updated: sp.Updated, 122 Email: sp.Email, 123 Name: sp.Name, 124 Description: sp.Description, 125 HomeURL: sp.HomeURL, 126 Color: sp.Color, 127 Twitter: sp.Twitter, 128 PeerIDs: pids, 129 } 130 131 if sp.PrivKey != "" { 132 pro.PrivKey, err = key.DecodeB64PrivKey(sp.PrivKey) 133 if err != nil { 134 return err 135 } 136 pro.PubKey = pro.PrivKey.GetPublic() 137 pro.KeyID = pro.GetKeyID() 138 } 139 140 if sp.Thumb != "" { 141 pro.Thumb = sp.Thumb 142 } 143 144 if sp.Poster != "" { 145 pro.Poster = sp.Poster 146 } 147 148 if sp.Photo != "" { 149 pro.Photo = sp.Photo 150 } 151 152 for _, addrStr := range sp.NetworkAddrs { 153 if maddr, err := ma.NewMultiaddr(addrStr); err == nil { 154 pro.NetworkAddrs = append(pro.NetworkAddrs, maddr) 155 } 156 } 157 158 *p = pro 159 return nil 160 } 161 162 // Encode returns a ProfilePod for a given profile 163 func (p Profile) Encode() (*config.ProfilePod, error) { 164 pids := make([]string, len(p.PeerIDs)) 165 for i, pid := range p.PeerIDs { 166 pids[i] = fmt.Sprintf("/ipfs/%s", pid.Pretty()) 167 } 168 var addrs []string 169 for _, maddr := range p.NetworkAddrs { 170 addrs = append(addrs, maddr.String()) 171 } 172 pp := &config.ProfilePod{ 173 ID: p.ID.Encode(), 174 Type: p.Type.String(), 175 Peername: p.Peername, 176 Created: p.Created, 177 Updated: p.Updated, 178 Email: p.Email, 179 Name: p.Name, 180 Description: p.Description, 181 HomeURL: p.HomeURL, 182 Color: p.Color, 183 Twitter: p.Twitter, 184 Poster: p.Poster, 185 Photo: p.Photo, 186 Thumb: p.Thumb, 187 Online: p.Online, 188 PeerIDs: pids, 189 NetworkAddrs: addrs, 190 } 191 if p.PrivKey != nil { 192 var err error 193 pp.PrivKey, err = key.EncodePrivKeyB64(p.PrivKey) 194 if err != nil { 195 return nil, err 196 } 197 } 198 return pp, nil 199 } 200 201 // ValidOwnerProfile checks if a profile can be used as an owner profile 202 func (p *Profile) ValidOwnerProfile() error { 203 if p == nil { 204 return fmt.Errorf("profile cannot be nil") 205 } 206 if p.PrivKey == nil { 207 return fmt.Errorf("private key is required") 208 } 209 // TODO (b5) - confirm PrivKey is valid 210 return nil 211 } 212 213 // GetKeyID returns a KeyID assigned to the profile or falls back 214 // to the profile ID if none is present 215 func (p *Profile) GetKeyID() key.ID { 216 if p.KeyID == key.ID("") { 217 p.KeyID = key.ID(string(p.ID)) 218 } 219 return p.KeyID 220 }