зеркало из https://github.com/docker/hub-tool.git
Add tokens API CRUD to hub client
Signed-off-by: Silvin Lubecki <silvin.lubecki@docker.com>
This commit is contained in:
Родитель
c9ddc75f88
Коммит
70be35782e
1
go.mod
1
go.mod
|
@ -18,6 +18,7 @@ require (
|
|||
github.com/docker/go-units v0.4.0
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
|
||||
github.com/gofrs/uuid v3.3.0+incompatible // indirect
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/jinzhu/gorm v1.9.16 // indirect
|
||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a
|
||||
|
|
|
@ -159,6 +159,7 @@ func (c *Client) login(hubBaseURL string, hubAuthConfig types.AuthConfig) (strin
|
|||
|
||||
func (c *Client) doRequest(req *http.Request, reqOps ...RequestOp) ([]byte, error) {
|
||||
req.Header["Accept"] = []string{"application/json"}
|
||||
req.Header["Content-Type"] = []string{"application/json"}
|
||||
req.Header["User-Agent"] = []string{fmt.Sprintf("hub-tool/%s", internal.Version)}
|
||||
for _, op := range reqOps {
|
||||
if err := op(req); err != nil {
|
||||
|
|
|
@ -80,7 +80,7 @@ func (c *Client) GetTags(repository string, reqOps ...RequestOp) ([]Tag, error)
|
|||
}
|
||||
if c.fetchAllElements {
|
||||
for next != "" {
|
||||
pageTags, n, err := c.getTagsPage(next, repository)
|
||||
pageTags, n, err := c.getTagsPage(next, repository, reqOps...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
Copyright 2020 Docker Hub Tool authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package hub
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
// TokensURL path to the Hub API listing the Personal Access Tokens
|
||||
TokensURL = "/v2/api_tokens"
|
||||
// TokenURL path to the Hub API Personal Access Token
|
||||
TokenURL = "/v2/api_tokens/%s"
|
||||
)
|
||||
|
||||
//Token is a personal access token. The token field will only be filled at creation and can never been accessed again.
|
||||
type Token struct {
|
||||
UUID uuid.UUID
|
||||
ClientID string
|
||||
CreatorIP string
|
||||
CreatorUA string
|
||||
CreatedAt time.Time
|
||||
LastUsed time.Time
|
||||
GeneratedBy string
|
||||
IsActive bool
|
||||
Token string
|
||||
Description string
|
||||
}
|
||||
|
||||
// CreateToken creates a Personal Access Token and returns the token field only once
|
||||
func (c *Client) CreateToken(description string) (*Token, error) {
|
||||
data, err := json.Marshal(hubTokenRequest{Description: description})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body := bytes.NewBuffer(data)
|
||||
req, err := http.NewRequest("POST", c.domain+TokensURL, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := c.doRequest(req, WithHubToken(c.token))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var tokenResponse hubTokenResult
|
||||
if err := json.Unmarshal(response, &tokenResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token, err := convertToken(tokenResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
//GetTokens calls the hub repo API and returns all the information on all tokens
|
||||
func (c *Client) GetTokens() ([]Token, error) {
|
||||
u, err := url.Parse(c.domain + TokensURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q := url.Values{}
|
||||
q.Add("page_size", fmt.Sprintf("%v", itemsPerPage))
|
||||
q.Add("page", "1")
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
tokens, next, err := c.getTokensPage(u.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.fetchAllElements {
|
||||
for next != "" {
|
||||
pageTokens, n, err := c.getTokensPage(next)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
next = n
|
||||
tokens = append(tokens, pageTokens...)
|
||||
}
|
||||
}
|
||||
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
//GetToken calls the hub repo API and returns the information on one token
|
||||
func (c *Client) GetToken(tokenUUID string) (*Token, error) {
|
||||
req, err := http.NewRequest("GET", c.domain+fmt.Sprintf(TokenURL, tokenUUID), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := c.doRequest(req, WithHubToken(c.token))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var tokenResponse hubTokenResult
|
||||
if err := json.Unmarshal(response, &tokenResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token, err := convertToken(tokenResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
// UpdateToken updates a token's description and activeness
|
||||
func (c *Client) UpdateToken(tokenUUID, description string, isActive bool) (*Token, error) {
|
||||
data, err := json.Marshal(hubTokenRequest{Description: description, IsActive: isActive})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body := bytes.NewBuffer(data)
|
||||
req, err := http.NewRequest("PATCH", c.domain+fmt.Sprintf(TokenURL, tokenUUID), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := c.doRequest(req, WithHubToken(c.token))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var tokenResponse hubTokenResult
|
||||
if err := json.Unmarshal(response, &tokenResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token, err := convertToken(tokenResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
//RevokeToken revoke a token from personal access token
|
||||
func (c *Client) RevokeToken(tokenUUID string) error {
|
||||
//DELETE https://hub.docker.com/v2/api_tokens/8208674e-d08a-426f-b6f4-e3aba7058459 => 202
|
||||
req, err := http.NewRequest("DELETE", c.domain+fmt.Sprintf(TokenURL, tokenUUID), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = c.doRequest(req, WithHubToken(c.token))
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) getTokensPage(url string) ([]Token, string, error) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
response, err := c.doRequest(req, WithHubToken(c.token))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
var hubResponse hubTokenResponse
|
||||
if err := json.Unmarshal(response, &hubResponse); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
var tokens []Token
|
||||
for _, result := range hubResponse.Results {
|
||||
token, err := convertToken(result)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
tokens = append(tokens, token)
|
||||
}
|
||||
return tokens, hubResponse.Next, nil
|
||||
}
|
||||
|
||||
type hubTokenRequest struct {
|
||||
Description string `json:"token_label,omitempty"`
|
||||
IsActive bool `json:"is_active,omitempty"`
|
||||
}
|
||||
|
||||
type hubTokenResponse struct {
|
||||
Count int `json:"count"`
|
||||
Next string `json:"next,omitempty"`
|
||||
Previous string `json:"previous,omitempty"`
|
||||
Results []hubTokenResult `json:"results,omitempty"`
|
||||
}
|
||||
|
||||
type hubTokenResult struct {
|
||||
UUID string `json:"uuid"`
|
||||
ClientID string `json:"client_id"`
|
||||
CreatorIP string `json:"creator_ip"`
|
||||
CreatorUA string `json:"creator_ua"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
LastUsed time.Time `json:"last_used,omitempty"`
|
||||
GeneratedBy string `json:"generated_by"`
|
||||
IsActive bool `json:"is_active"`
|
||||
Token string `json:"token"`
|
||||
TokenLabel string `json:"token_label"`
|
||||
}
|
||||
|
||||
func convertToken(response hubTokenResult) (Token, error) {
|
||||
u, err := uuid.Parse(response.UUID)
|
||||
if err != nil {
|
||||
return Token{}, err
|
||||
}
|
||||
return Token{
|
||||
UUID: u,
|
||||
ClientID: response.ClientID,
|
||||
CreatorIP: response.CreatorIP,
|
||||
CreatorUA: response.CreatorUA,
|
||||
CreatedAt: response.CreatedAt,
|
||||
LastUsed: response.LastUsed,
|
||||
GeneratedBy: response.GeneratedBy,
|
||||
IsActive: response.IsActive,
|
||||
Token: response.Token,
|
||||
Description: response.TokenLabel,
|
||||
}, nil
|
||||
}
|
Загрузка…
Ссылка в новой задаче