Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ec7de50e3 | |||
| 9c465cbd4c |
@@ -23,7 +23,7 @@ steps:
|
|||||||
dockerfile: ui/Dockerfile.ui
|
dockerfile: ui/Dockerfile.ui
|
||||||
context: ui
|
context: ui
|
||||||
build_args:
|
build_args:
|
||||||
- BASE_PATH=/ui
|
BASE_PATH: /ui
|
||||||
username: droneci
|
username: droneci
|
||||||
password:
|
password:
|
||||||
from_secret: DRONECI_PASSWORD
|
from_secret: DRONECI_PASSWORD
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.unkin.net/unkin/artifactapi/internal/cache"
|
"git.unkin.net/unkin/artifactapi/internal/cache"
|
||||||
@@ -147,6 +149,21 @@ func (e *Engine) fetchFromUpstream(ctx context.Context, remote models.Remote, pa
|
|||||||
return nil, &UpstreamError{Err: err}
|
return nil, &UpstreamError{Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusUnauthorized {
|
||||||
|
resp.Body.Close()
|
||||||
|
token, err := fetchBearerToken(ctx, resp.Header.Get("Www-Authenticate"), remote)
|
||||||
|
if err == nil && token != "" {
|
||||||
|
req2, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
|
req2.Header.Set("Authorization", "Bearer "+token)
|
||||||
|
resp, err = http.DefaultClient.Do(req2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &UpstreamError{Err: err}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, &ProxyError{Status: http.StatusUnauthorized, Message: "upstream returned 401"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
return nil, &ProxyError{Status: resp.StatusCode, Message: fmt.Sprintf("upstream returned %d", resp.StatusCode)}
|
return nil, &ProxyError{Status: resp.StatusCode, Message: fmt.Sprintf("upstream returned %d", resp.StatusCode)}
|
||||||
@@ -319,6 +336,71 @@ func (r readerAt) ReadAt(p []byte, off int64) (n int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fetchBearerToken(ctx context.Context, wwwAuth string, remote models.Remote) (string, error) {
|
||||||
|
if !strings.HasPrefix(wwwAuth, "Bearer ") {
|
||||||
|
return "", fmt.Errorf("not a Bearer challenge")
|
||||||
|
}
|
||||||
|
|
||||||
|
params := map[string]string{}
|
||||||
|
for _, part := range strings.Split(wwwAuth[7:], ",") {
|
||||||
|
part = strings.TrimSpace(part)
|
||||||
|
eq := strings.Index(part, "=")
|
||||||
|
if eq < 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := part[:eq]
|
||||||
|
val := strings.Trim(part[eq+1:], `"`)
|
||||||
|
params[key] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
realm := params["realm"]
|
||||||
|
if realm == "" {
|
||||||
|
return "", fmt.Errorf("no realm in Bearer challenge")
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenURL := realm
|
||||||
|
sep := "?"
|
||||||
|
if s, ok := params["service"]; ok {
|
||||||
|
tokenURL += sep + "service=" + s
|
||||||
|
sep = "&"
|
||||||
|
}
|
||||||
|
if s, ok := params["scope"]; ok {
|
||||||
|
tokenURL += sep + "scope=" + s
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, tokenURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if remote.Username != "" && remote.Password != "" {
|
||||||
|
req.SetBasicAuth(remote.Username, remote.Password)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return "", fmt.Errorf("token endpoint returned %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokenResp struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if tokenResp.Token != "" {
|
||||||
|
return tokenResp.Token, nil
|
||||||
|
}
|
||||||
|
return tokenResp.AccessToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
type ProxyError struct {
|
type ProxyError struct {
|
||||||
Status int
|
Status int
|
||||||
Message string
|
Message string
|
||||||
|
|||||||
Reference in New Issue
Block a user