Support 2FA for basic auth & refactor Token functions (#335)
BREAKING: Token functions: remove username&passwd param - use default client auth way refactor add otp Field refacotr ... Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://gitea.com/gitea/go-sdk/pulls/335 Reviewed-by: John Olheiser <john.olheiser@gmail.com> Reviewed-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
parent
93087537ff
commit
70863f4458
3 changed files with 30 additions and 25 deletions
|
@ -31,6 +31,7 @@ type Client struct {
|
|||
accessToken string
|
||||
username string
|
||||
password string
|
||||
otp string
|
||||
sudo string
|
||||
client *http.Client
|
||||
serverVersion *version.Version
|
||||
|
@ -58,6 +59,11 @@ func (c *Client) SetBasicAuth(username, password string) {
|
|||
c.username, c.password = username, password
|
||||
}
|
||||
|
||||
// SetOTP sets OTP for 2FA
|
||||
func (c *Client) SetOTP(otp string) {
|
||||
c.otp = otp
|
||||
}
|
||||
|
||||
// SetHTTPClient replaces default http.Client with user given one.
|
||||
func (c *Client) SetHTTPClient(client *http.Client) {
|
||||
c.client = client
|
||||
|
@ -76,10 +82,13 @@ func (c *Client) doRequest(method, path string, header http.Header, body io.Read
|
|||
if len(c.accessToken) != 0 {
|
||||
req.Header.Set("Authorization", "token "+c.accessToken)
|
||||
}
|
||||
if len(c.otp) != 0 {
|
||||
req.Header.Set("X-GITEA-OTP", c.otp)
|
||||
}
|
||||
if len(c.username) != 0 {
|
||||
req.SetBasicAuth(c.username, c.password)
|
||||
}
|
||||
if c.sudo != "" {
|
||||
if len(c.sudo) != 0 {
|
||||
req.Header.Set("Sudo", c.sudo)
|
||||
}
|
||||
for k, v := range header {
|
||||
|
|
|
@ -7,17 +7,10 @@ package gitea
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// basicAuthEncode generate base64 of basic auth head
|
||||
func basicAuthEncode(user, pass string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(user + ":" + pass))
|
||||
}
|
||||
|
||||
// AccessToken represents an API access token.
|
||||
type AccessToken struct {
|
||||
ID int64 `json:"id"`
|
||||
|
@ -32,11 +25,13 @@ type ListAccessTokensOptions struct {
|
|||
}
|
||||
|
||||
// ListAccessTokens lists all the access tokens of user
|
||||
func (c *Client) ListAccessTokens(user, pass string, opts ListAccessTokensOptions) ([]*AccessToken, error) {
|
||||
func (c *Client) ListAccessTokens(opts ListAccessTokensOptions) ([]*AccessToken, error) {
|
||||
if len(c.username) == 0 {
|
||||
return nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")
|
||||
}
|
||||
opts.setDefaults()
|
||||
tokens := make([]*AccessToken, 0, opts.PageSize)
|
||||
return tokens, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/tokens?%s", user, opts.getURLQuery().Encode()),
|
||||
http.Header{"Authorization": []string{"Basic " + basicAuthEncode(user, pass)}}, nil, &tokens)
|
||||
return tokens, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/tokens?%s", c.username, opts.getURLQuery().Encode()), jsonHeader, nil, &tokens)
|
||||
}
|
||||
|
||||
// CreateAccessTokenOption options when create access token
|
||||
|
@ -45,22 +40,23 @@ type CreateAccessTokenOption struct {
|
|||
}
|
||||
|
||||
// CreateAccessToken create one access token with options
|
||||
func (c *Client) CreateAccessToken(user, pass string, opt CreateAccessTokenOption) (*AccessToken, error) {
|
||||
func (c *Client) CreateAccessToken(opt CreateAccessTokenOption) (*AccessToken, error) {
|
||||
if len(c.username) == 0 {
|
||||
return nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := new(AccessToken)
|
||||
return t, c.getParsedResponse("POST", fmt.Sprintf("/users/%s/tokens", user),
|
||||
http.Header{
|
||||
"content-type": []string{"application/json"},
|
||||
"Authorization": []string{"Basic " + basicAuthEncode(user, pass)}},
|
||||
bytes.NewReader(body), t)
|
||||
return t, c.getParsedResponse("POST", fmt.Sprintf("/users/%s/tokens", c.username), jsonHeader, bytes.NewReader(body), t)
|
||||
}
|
||||
|
||||
// DeleteAccessToken delete token with key id
|
||||
func (c *Client) DeleteAccessToken(user, pass string, keyID int64) error {
|
||||
_, err := c.getResponse("DELETE", fmt.Sprintf("/users/%s/tokens/%d", user, keyID),
|
||||
http.Header{"Authorization": []string{"Basic " + basicAuthEncode(user, pass)}}, nil)
|
||||
func (c *Client) DeleteAccessToken(keyID int64) error {
|
||||
if len(c.username) == 0 {
|
||||
return fmt.Errorf("\"username\" not set: only BasicAuth allowed")
|
||||
}
|
||||
_, err := c.getResponse("DELETE", fmt.Sprintf("/users/%s/tokens/%d", c.username, keyID), jsonHeader, nil)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -29,20 +29,20 @@ func TestUserApp(t *testing.T) {
|
|||
log.Println("== TestUserApp ==")
|
||||
c := newTestClient()
|
||||
|
||||
result, err := c.ListAccessTokens(c.username, c.password, ListAccessTokensOptions{})
|
||||
result, err := c.ListAccessTokens(ListAccessTokensOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.EqualValues(t, "gitea-admin", result[0].Name)
|
||||
|
||||
t1, err := c.CreateAccessToken(c.username, c.password, CreateAccessTokenOption{Name: "TestCreateAccessToken"})
|
||||
t1, err := c.CreateAccessToken(CreateAccessTokenOption{Name: "TestCreateAccessToken"})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "TestCreateAccessToken", t1.Name)
|
||||
result, _ = c.ListAccessTokens(c.username, c.password, ListAccessTokensOptions{})
|
||||
result, _ = c.ListAccessTokens(ListAccessTokensOptions{})
|
||||
assert.Len(t, result, 2)
|
||||
|
||||
err = c.DeleteAccessToken(c.username, c.password, t1.ID)
|
||||
err = c.DeleteAccessToken(t1.ID)
|
||||
assert.NoError(t, err)
|
||||
result, _ = c.ListAccessTokens(c.username, c.password, ListAccessTokensOptions{})
|
||||
result, _ = c.ListAccessTokens(ListAccessTokensOptions{})
|
||||
assert.Len(t, result, 1)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue