github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/registry/regclient/profile.go (about) 1 package regclient 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "net/http" 8 "strings" 9 10 crypto "github.com/libp2p/go-libp2p-core/crypto" 11 "github.com/qri-io/qri/registry" 12 ) 13 14 const proveKeyAPIEndpoint = "/registry/provekey" 15 16 // GetProfile fills in missing fields in p with registry data 17 func (c Client) GetProfile(p *registry.Profile) error { 18 pro, err := c.doJSONProfileReq("GET", p) 19 if err != nil { 20 return err 21 } 22 if pro != nil { 23 *p = *pro 24 } 25 return nil 26 } 27 28 // CreateProfile creates a user profile, associating a public key in the process 29 func (c *Client) CreateProfile(p *registry.Profile, pk crypto.PrivKey) (*registry.Profile, error) { 30 if c == nil { 31 return nil, registry.ErrNoRegistry 32 } 33 34 // TODO (b5) - pass full profile 35 pro, err := registry.ProfileFromPrivateKey(p, pk) 36 if err != nil { 37 return nil, err 38 } 39 40 return c.doJSONProfileReq("POST", pro) 41 } 42 43 // ProveKeyForProfile proves to the registry that the user owns the profile and 44 // is associating a new keypair 45 func (c *Client) ProveKeyForProfile(p *registry.Profile) (map[string]string, error) { 46 if c == nil { 47 return nil, registry.ErrNoRegistry 48 } 49 50 // Ensure all required fields are set 51 if p.ProfileID == "" { 52 return nil, fmt.Errorf("ProveKeyForProfile: ProfileID required") 53 } 54 if p.Username == "" { 55 return nil, fmt.Errorf("ProveKeyForProfile: Username required") 56 } 57 if p.Email == "" { 58 return nil, fmt.Errorf("ProveKeyForProfile: Email required") 59 } 60 if p.Password == "" { 61 return nil, fmt.Errorf("ProveKeyForProfile: Password required") 62 } 63 if p.PublicKey == "" { 64 return nil, fmt.Errorf("ProveKeyForProfile: PublicKey required") 65 } 66 if p.Signature == "" { 67 return nil, fmt.Errorf("ProveKeyForProfile: Signature required") 68 } 69 70 // Send proof request to the registry 71 res := RegistryResponse{} 72 err := c.doJSONRegistryRequest("PUT", proveKeyAPIEndpoint, p, &res) 73 if err != nil { 74 return nil, err 75 } 76 return res.Data, nil 77 } 78 79 // UpdateProfile updates some information about the profile in the registry 80 func (c *Client) UpdateProfile(p *registry.Profile, pk crypto.PrivKey) (*registry.Profile, error) { 81 if c == nil { 82 return nil, registry.ErrNoRegistry 83 } 84 85 // TODO (b5) - pass full profile 86 pro, err := registry.ProfileFromPrivateKey(p, pk) 87 if err != nil { 88 return nil, err 89 } 90 91 return c.doJSONProfileReq("PUT", pro) 92 } 93 94 // PutProfile adds a profile to the registry 95 func (c *Client) PutProfile(p *registry.Profile, privKey crypto.PrivKey) (*registry.Profile, error) { 96 if c == nil { 97 return nil, registry.ErrNoRegistry 98 } 99 100 p, err := registry.ProfileFromPrivateKey(p, privKey) 101 if err != nil { 102 return nil, err 103 } 104 105 return c.doJSONProfileReq("POST", p) 106 } 107 108 // DeleteProfile removes a profile from the registry 109 func (c *Client) DeleteProfile(p *registry.Profile, privKey crypto.PrivKey) error { 110 if c == nil { 111 return registry.ErrNoRegistry 112 } 113 114 p, err := registry.ProfileFromPrivateKey(p, privKey) 115 if err != nil { 116 return err 117 } 118 _, err = c.doJSONProfileReq("DELETE", p) 119 return err 120 } 121 122 // doJSONProfileReq is a common wrapper for /profile endpoint requests 123 func (c Client) doJSONProfileReq(method string, p *registry.Profile) (*registry.Profile, error) { 124 if c.cfg.Location == "" { 125 return nil, ErrNoRegistry 126 } 127 128 data, err := json.Marshal(p) 129 if err != nil { 130 return nil, err 131 } 132 133 req, err := http.NewRequest(method, fmt.Sprintf("%s/registry/profile", c.cfg.Location), bytes.NewReader(data)) 134 if err != nil { 135 return nil, err 136 } 137 req.Header.Set("Content-Type", "application/json") 138 // TODO(arqu): convert to lib/http/HTTPClient 139 res, err := HTTPClient.Do(req) 140 if err != nil { 141 if strings.Contains(err.Error(), "no such host") { 142 return nil, ErrNoRegistry 143 } 144 return nil, err 145 } 146 147 // add response to an envelope 148 env := struct { 149 Data *registry.Profile 150 Meta struct { 151 Error string 152 Status string 153 Code int 154 } 155 }{} 156 157 if err := json.NewDecoder(res.Body).Decode(&env); err != nil { 158 return nil, err 159 } 160 161 if res.StatusCode != http.StatusOK { 162 if strings.Contains(env.Meta.Error, "taken") { 163 return nil, registry.ErrUsernameTaken 164 } 165 return nil, fmt.Errorf("registry: %s", env.Meta.Error) 166 } 167 168 // Peername is in the process of being deprecated 169 // We want to favor Username, which is what we are 170 // using in all our cloud services 171 // this ensures any old references to Peername will not 172 // be lost 173 env.Data.Peername = env.Data.Username 174 return env.Data, nil 175 } 176 177 // RegistryResponse is a generic container for registry requests 178 // TODO(dustmop): Only used currently for ProveKey 179 // TODO(arqu): update with common API response object 180 type RegistryResponse struct { 181 Data map[string]string 182 Meta struct { 183 Error string 184 Status string 185 Code int 186 } 187 } 188 189 // doJSONProfileReq sends a json body to the registry 190 func (c Client) doJSONRegistryRequest(method, url string, input interface{}, output *RegistryResponse) error { 191 if c.cfg.Location == "" { 192 return ErrNoRegistry 193 } 194 195 data, err := json.Marshal(input) 196 if err != nil { 197 return err 198 } 199 200 fullurl := fmt.Sprintf("%s%s", c.cfg.Location, url) 201 req, err := http.NewRequest(method, fullurl, bytes.NewBuffer(data)) 202 if err != nil { 203 return err 204 } 205 req.Header.Set("Content-Type", "application/json") 206 // TODO(arqu): convert to lib/http/HTTPClient 207 res, err := HTTPClient.Do(req) 208 if err != nil { 209 if strings.Contains(err.Error(), "no such host") { 210 return ErrNoRegistry 211 } 212 return err 213 } 214 215 if err := json.NewDecoder(res.Body).Decode(output); err != nil { 216 return err 217 } 218 if res.StatusCode != http.StatusOK { 219 return fmt.Errorf("registry: %s", output.Meta.Error) 220 } 221 return nil 222 }