mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-27 04:35:16 +01:00
update
This commit is contained in:
parent
1da9b8653b
commit
a8dc75dd15
@ -17,19 +17,20 @@ package notary
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/registry/auth/token"
|
||||
"github.com/docker/notary"
|
||||
"github.com/docker/notary/client"
|
||||
"github.com/docker/notary/trustpinning"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/common/utils/registry"
|
||||
"github.com/vmware/harbor/src/common/utils/registry/auth"
|
||||
"github.com/vmware/harbor/src/ui/config"
|
||||
"github.com/vmware/harbor/src/ui/service/token"
|
||||
tokenutil "github.com/vmware/harbor/src/ui/service/token"
|
||||
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
@ -72,10 +73,22 @@ func GetInternalTargets(notaryEndpoint string, username string, repo string) ([]
|
||||
|
||||
// GetTargets is a help function called by API to fetch signature information of a given repository.
|
||||
// Per docker's convention the repository should contain the information of endpoint, i.e. it should look
|
||||
// like "10.117.4.117/library/ubuntu", instead of "library/ubuntu" (fqRepo for fully-qualified repo)
|
||||
// like "192.168.0.1/library/ubuntu", instead of "library/ubuntu" (fqRepo for fully-qualified repo)
|
||||
func GetTargets(notaryEndpoint string, username string, fqRepo string) ([]Target, error) {
|
||||
res := []Target{}
|
||||
authorizer := auth.NewRawTokenAuthorizer(username, token.Notary)
|
||||
t, err := tokenutil.MakeToken(username, tokenutil.Notary,
|
||||
[]*token.ResourceActions{
|
||||
&token.ResourceActions{
|
||||
Type: "repository",
|
||||
Name: fqRepo,
|
||||
Actions: []string{"pull"},
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorizer := ¬aryAuthorizer{
|
||||
token: t.Token,
|
||||
}
|
||||
tr := registry.NewTransport(registry.GetHTTPTransport(true), authorizer)
|
||||
gun := data.GUN(fqRepo)
|
||||
notaryRepo, err := client.NewFileCachedNotaryRepository(notaryCachePath, gun, notaryEndpoint, tr, mockRetriever, trustPin)
|
||||
@ -109,3 +122,13 @@ func DigestFromTarget(t Target) (string, error) {
|
||||
}
|
||||
return digest.NewDigestFromHex("sha256", hex.EncodeToString(sha)).String(), nil
|
||||
}
|
||||
|
||||
type notaryAuthorizer struct {
|
||||
token string
|
||||
}
|
||||
|
||||
func (n *notaryAuthorizer) Modify(req *http.Request) error {
|
||||
req.Header.Add(http.CanonicalHeaderKey("Authorization"),
|
||||
fmt.Sprintf("Bearer %s", n.token))
|
||||
return nil
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/registry/auth/token"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/common/utils/registry"
|
||||
@ -33,19 +34,8 @@ const (
|
||||
scheme = "bearer"
|
||||
)
|
||||
|
||||
// Scope ...
|
||||
type Scope struct {
|
||||
Type string
|
||||
Name string
|
||||
Actions []string
|
||||
}
|
||||
|
||||
func (s *Scope) string() string {
|
||||
return fmt.Sprintf("%s:%s:%s", s.Type, s.Name, strings.Join(s.Actions, ","))
|
||||
}
|
||||
|
||||
type tokenGenerator interface {
|
||||
generate(scopes []*Scope, endpoint string) (*models.Token, error)
|
||||
generate(scopes []*token.ResourceActions, endpoint string) (*models.Token, error)
|
||||
}
|
||||
|
||||
// tokenAuthorizer implements registry.Modifier interface. It parses scopses
|
||||
@ -84,7 +74,7 @@ func (t *tokenAuthorizer) Modify(req *http.Request) error {
|
||||
if len(scopes) <= 1 {
|
||||
key := ""
|
||||
if len(scopes) == 1 {
|
||||
key = scopes[0].string()
|
||||
key = scopeString(scopes[0])
|
||||
}
|
||||
token = t.getCachedToken(key)
|
||||
}
|
||||
@ -104,7 +94,7 @@ func (t *tokenAuthorizer) Modify(req *http.Request) error {
|
||||
if len(scopes) <= 1 {
|
||||
key := ""
|
||||
if len(scopes) == 1 {
|
||||
key = scopes[0].string()
|
||||
key = scopeString(scopes[0])
|
||||
}
|
||||
t.updateCachedToken(key, token)
|
||||
}
|
||||
@ -115,6 +105,13 @@ func (t *tokenAuthorizer) Modify(req *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func scopeString(scope *token.ResourceActions) string {
|
||||
if scope == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s:%s:%s", scope.Type, scope.Name, strings.Join(scope.Actions, ","))
|
||||
}
|
||||
|
||||
// some requests are sent to backend storage, such as s3, this method filters
|
||||
// the requests only sent to registry
|
||||
func (t *tokenAuthorizer) filterReq(req *http.Request) (bool, error) {
|
||||
@ -142,24 +139,24 @@ func (t *tokenAuthorizer) filterReq(req *http.Request) (bool, error) {
|
||||
}
|
||||
|
||||
// parse scopes from the request according to its method, path and query string
|
||||
func parseScopes(req *http.Request) ([]*Scope, error) {
|
||||
scopes := []*Scope{}
|
||||
func parseScopes(req *http.Request) ([]*token.ResourceActions, error) {
|
||||
scopes := []*token.ResourceActions{}
|
||||
|
||||
from := req.URL.Query().Get("from")
|
||||
if len(from) != 0 {
|
||||
scopes = append(scopes, &Scope{
|
||||
scopes = append(scopes, &token.ResourceActions{
|
||||
Type: "repository",
|
||||
Name: from,
|
||||
Actions: []string{"pull"},
|
||||
})
|
||||
}
|
||||
|
||||
var scope *Scope
|
||||
var scope *token.ResourceActions
|
||||
path := strings.TrimRight(req.URL.Path, "/")
|
||||
repository := parseRepository(path)
|
||||
if len(repository) > 0 {
|
||||
// pull, push, delete blob/manifest
|
||||
scope = &Scope{
|
||||
scope = &token.ResourceActions{
|
||||
Type: "repository",
|
||||
Name: repository,
|
||||
}
|
||||
@ -176,7 +173,7 @@ func parseScopes(req *http.Request) ([]*Scope, error) {
|
||||
}
|
||||
} else if catalog.MatchString(path) {
|
||||
// catalog
|
||||
scope = &Scope{
|
||||
scope = &token.ResourceActions{
|
||||
Type: "registry",
|
||||
Name: "catalog",
|
||||
Actions: []string{"*"},
|
||||
@ -195,7 +192,7 @@ func parseScopes(req *http.Request) ([]*Scope, error) {
|
||||
|
||||
strs := []string{}
|
||||
for _, s := range scopes {
|
||||
strs = append(strs, s.string())
|
||||
strs = append(strs, scopeString(s))
|
||||
}
|
||||
log.Debugf("scopses parsed from request: %s", strings.Join(strs, " "))
|
||||
|
||||
@ -295,7 +292,7 @@ type standardTokenGenerator struct {
|
||||
}
|
||||
|
||||
// get token from token service
|
||||
func (s *standardTokenGenerator) generate(scopes []*Scope, endpoint string) (*models.Token, error) {
|
||||
func (s *standardTokenGenerator) generate(scopes []*token.ResourceActions, endpoint string) (*models.Token, error) {
|
||||
// ping first if the realm or service is null
|
||||
if len(s.realm) == 0 || len(s.service) == 0 {
|
||||
realm, service, err := ping(s.client, endpoint)
|
||||
@ -336,21 +333,8 @@ type rawTokenGenerator struct {
|
||||
}
|
||||
|
||||
// generate token directly
|
||||
func (r *rawTokenGenerator) generate(scopes []*Scope, endpoint string) (*models.Token, error) {
|
||||
strs := []string{}
|
||||
for _, scope := range scopes {
|
||||
strs = append(strs, scope.string())
|
||||
}
|
||||
token, expiresIn, issuedAt, err := token_util.RegistryTokenForUI(r.username, r.service, strs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.Token{
|
||||
Token: token,
|
||||
ExpiresIn: expiresIn,
|
||||
IssuedAt: issuedAt.Format(time.RFC3339),
|
||||
}, nil
|
||||
func (r *rawTokenGenerator) generate(scopes []*token.ResourceActions, endpoint string) (*models.Token, error) {
|
||||
return token_util.MakeToken(r.username, r.service, scopes)
|
||||
}
|
||||
|
||||
func buildPingURL(endpoint string) string {
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/registry/auth/token"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
@ -81,7 +82,7 @@ func TestParseScopes(t *testing.T) {
|
||||
scopses, err := parseScopes(req)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(scopses))
|
||||
assert.EqualValues(t, &Scope{
|
||||
assert.EqualValues(t, &token.ResourceActions{
|
||||
Type: "repository",
|
||||
Name: "library",
|
||||
Actions: []string{
|
||||
@ -101,7 +102,7 @@ func TestParseScopes(t *testing.T) {
|
||||
scopses, err = parseScopes(req)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(scopses))
|
||||
assert.EqualValues(t, &Scope{
|
||||
assert.EqualValues(t, &token.ResourceActions{
|
||||
Type: "registry",
|
||||
Name: "catalog",
|
||||
Actions: []string{
|
||||
@ -114,7 +115,7 @@ func TestParseScopes(t *testing.T) {
|
||||
scopses, err = parseScopes(req)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(scopses))
|
||||
assert.EqualValues(t, &Scope{
|
||||
assert.EqualValues(t, &token.ResourceActions{
|
||||
Type: "repository",
|
||||
Name: "library/mysql/5.6",
|
||||
Actions: []string{
|
||||
|
@ -20,8 +20,8 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/docker/distribution/registry/auth/token"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
|
||||
registry_error "github.com/vmware/harbor/src/common/utils/error"
|
||||
"github.com/vmware/harbor/src/common/utils/registry"
|
||||
)
|
||||
@ -32,7 +32,7 @@ const (
|
||||
|
||||
// GetToken requests a token against the endpoint using credetial provided
|
||||
func GetToken(endpoint string, insecure bool, credential Credential,
|
||||
scopes []*Scope) (*models.Token, error) {
|
||||
scopes []*token.ResourceActions) (*models.Token, error) {
|
||||
client := &http.Client{
|
||||
Transport: registry.GetHTTPTransport(insecure),
|
||||
}
|
||||
@ -41,7 +41,7 @@ func GetToken(endpoint string, insecure bool, credential Credential,
|
||||
}
|
||||
|
||||
func getToken(client *http.Client, credential Credential, realm, service string,
|
||||
scopes []*Scope) (*models.Token, error) {
|
||||
scopes []*token.ResourceActions) (*models.Token, error) {
|
||||
u, err := url.Parse(realm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -49,7 +49,7 @@ func getToken(client *http.Client, credential Credential, realm, service string,
|
||||
query := u.Query()
|
||||
query.Add("service", service)
|
||||
for _, scope := range scopes {
|
||||
query.Add("scope", scope.string())
|
||||
query.Add("scope", scopeString(scope))
|
||||
}
|
||||
u.RawQuery = query.Encode()
|
||||
|
||||
|
@ -93,51 +93,26 @@ func filterAccess(access []*token.ResourceActions, ctx security.Context,
|
||||
return nil
|
||||
}
|
||||
|
||||
//RegistryTokenForUI calls genTokenForUI to get raw token for registry
|
||||
func RegistryTokenForUI(username string, service string, scopes []string) (string, int, *time.Time, error) {
|
||||
return genTokenForUI(username, service, scopes)
|
||||
}
|
||||
|
||||
//NotaryTokenForUI calls genTokenForUI to get raw token for notary
|
||||
func NotaryTokenForUI(username string, service string, scopes []string) (string, int, *time.Time, error) {
|
||||
return genTokenForUI(username, service, scopes)
|
||||
}
|
||||
|
||||
// genTokenForUI is for the UI process to call, so it won't establish a https connection from UI to proxy.
|
||||
func genTokenForUI(username string, service string,
|
||||
scopes []string) (string, int, *time.Time, error) {
|
||||
access := GetResourceActions(scopes)
|
||||
return MakeRawToken(username, service, access)
|
||||
}
|
||||
|
||||
// MakeRawToken makes a valid jwt token based on parms.
|
||||
func MakeRawToken(username, service string, access []*token.ResourceActions) (token string, expiresIn int, issuedAt *time.Time, err error) {
|
||||
// MakeToken makes a valid jwt token based on parms.
|
||||
func MakeToken(username, service string, access []*token.ResourceActions) (*models.Token, error) {
|
||||
pk, err := libtrust.LoadKeyFile(privateKey)
|
||||
if err != nil {
|
||||
return "", 0, nil, err
|
||||
return nil, err
|
||||
}
|
||||
expiration, err := config.TokenExpiration()
|
||||
if err != nil {
|
||||
return "", 0, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tk, expiresIn, issuedAt, err := makeTokenCore(issuer, username, service, expiration, access, pk)
|
||||
if err != nil {
|
||||
return "", 0, nil, err
|
||||
}
|
||||
rs := fmt.Sprintf("%s.%s", tk.Raw, base64UrlEncode(tk.Signature))
|
||||
return rs, expiresIn, issuedAt, nil
|
||||
}
|
||||
|
||||
func makeToken(username, service string, access []*token.ResourceActions) (*models.Token, error) {
|
||||
raw, expires, issued, err := MakeRawToken(username, service, access)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rs := fmt.Sprintf("%s.%s", tk.Raw, base64UrlEncode(tk.Signature))
|
||||
return &models.Token{
|
||||
Token: raw,
|
||||
ExpiresIn: expires,
|
||||
IssuedAt: issued.Format(time.RFC3339),
|
||||
Token: rs,
|
||||
ExpiresIn: expiresIn,
|
||||
IssuedAt: issuedAt.Format(time.RFC3339),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,7 @@ func (g generalCreator) Create(r *http.Request) (*models.Token, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return makeToken(ctx.GetUsername(), g.service, access)
|
||||
return MakeToken(ctx.GetUsername(), g.service, access)
|
||||
}
|
||||
|
||||
func parseScopes(u *url.URL) []string {
|
||||
|
@ -111,7 +111,7 @@ func TestMakeToken(t *testing.T) {
|
||||
}}
|
||||
svc := "harbor-registry"
|
||||
u := "tester"
|
||||
tokenJSON, err := makeToken(u, svc, ra)
|
||||
tokenJSON, err := MakeToken(u, svc, ra)
|
||||
if err != nil {
|
||||
t.Errorf("Error while making token: %v", err)
|
||||
}
|
||||
|
@ -122,7 +122,8 @@ func TriggerImageScan(repository string, tag string) error {
|
||||
return RequestAsUI("POST", url, bytes.NewBuffer(b), NewStatusRespHandler(http.StatusOK))
|
||||
}
|
||||
|
||||
// NewRepositoryClientForUI ...
|
||||
// NewRepositoryClientForUI creates a repository client that can only be used to
|
||||
// access the internal registry
|
||||
func NewRepositoryClientForUI(username, repository string) (*registry.Repository, error) {
|
||||
endpoint, err := config.RegistryURL()
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user