mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-26 12:15:20 +01:00
Merge pull request #2658 from ywk253100/170627_registry
Provide a method to get token from token service
This commit is contained in:
commit
ea827ffd6e
@ -15,20 +15,13 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
//"github.com/vmware/harbor/src/common/config"
|
|
||||||
"github.com/vmware/harbor/src/common/models"
|
|
||||||
"github.com/vmware/harbor/src/common/utils/log"
|
|
||||||
"github.com/vmware/harbor/src/common/utils/registry"
|
"github.com/vmware/harbor/src/common/utils/registry"
|
||||||
registry_error "github.com/vmware/harbor/src/common/utils/error"
|
|
||||||
token_util "github.com/vmware/harbor/src/ui/service/token"
|
token_util "github.com/vmware/harbor/src/ui/service/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,13 +29,14 @@ const (
|
|||||||
latency int = 10 //second, the network latency when token is received
|
latency int = 10 //second, the network latency when token is received
|
||||||
)
|
)
|
||||||
|
|
||||||
type scope struct {
|
// Scope ...
|
||||||
|
type Scope struct {
|
||||||
Type string
|
Type string
|
||||||
Name string
|
Name string
|
||||||
Actions []string
|
Actions []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *scope) string() string {
|
func (s *Scope) string() string {
|
||||||
return fmt.Sprintf("%s:%s:%s", s.Type, s.Name, strings.Join(s.Actions, ","))
|
return fmt.Sprintf("%s:%s:%s", s.Type, s.Name, strings.Join(s.Actions, ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +44,7 @@ type tokenGenerator func(realm, service string, scopes []string) (token string,
|
|||||||
|
|
||||||
// Implements interface Authorizer
|
// Implements interface Authorizer
|
||||||
type tokenAuthorizer struct {
|
type tokenAuthorizer struct {
|
||||||
scope *scope
|
scope *Scope
|
||||||
tg tokenGenerator
|
tg tokenGenerator
|
||||||
cache string // cached token
|
cache string // cached token
|
||||||
expiresAt *time.Time // The UTC standard time at when the token will expire
|
expiresAt *time.Time // The UTC standard time at when the token will expire
|
||||||
@ -64,13 +58,13 @@ func (t *tokenAuthorizer) Scheme() string {
|
|||||||
|
|
||||||
// AuthorizeRequest will add authorization header which contains a token before the request is sent
|
// AuthorizeRequest will add authorization header which contains a token before the request is sent
|
||||||
func (t *tokenAuthorizer) Authorize(req *http.Request, params map[string]string) error {
|
func (t *tokenAuthorizer) Authorize(req *http.Request, params map[string]string) error {
|
||||||
var scopes []*scope
|
var scopes []*Scope
|
||||||
var token string
|
var token string
|
||||||
|
|
||||||
hasFrom := false
|
hasFrom := false
|
||||||
from := req.URL.Query().Get("from")
|
from := req.URL.Query().Get("from")
|
||||||
if len(from) != 0 {
|
if len(from) != 0 {
|
||||||
s := &scope{
|
s := &Scope{
|
||||||
Type: "repository",
|
Type: "repository",
|
||||||
Name: from,
|
Name: from,
|
||||||
Actions: []string{"pull"},
|
Actions: []string{"pull"},
|
||||||
@ -154,7 +148,7 @@ func NewStandardTokenAuthorizer(credential Credential, insecure bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(scopeType) != 0 || len(scopeName) != 0 {
|
if len(scopeType) != 0 || len(scopeName) != 0 {
|
||||||
authorizer.scope = &scope{
|
authorizer.scope = &Scope{
|
||||||
Type: scopeType,
|
Type: scopeType,
|
||||||
Name: scopeName,
|
Name: scopeName,
|
||||||
Actions: scopeActions,
|
Actions: scopeActions,
|
||||||
@ -166,66 +160,21 @@ func NewStandardTokenAuthorizer(credential Credential, insecure bool,
|
|||||||
return authorizer
|
return authorizer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *standardTokenAuthorizer) generateToken(realm, service string, scopes []string) (token string, expiresIn int, issuedAt *time.Time, err error) {
|
func (s *standardTokenAuthorizer) generateToken(realm, service string, scopes []string) (string, int, *time.Time, error) {
|
||||||
realm = s.tokenURL(realm)
|
realm = s.tokenURL(realm)
|
||||||
|
tk, err := getToken(s.client, s.credential, realm,
|
||||||
|
service, scopes)
|
||||||
|
|
||||||
u, err := url.Parse(realm)
|
if len(tk.IssuedAt) == 0 {
|
||||||
|
return tk.Token, tk.ExpiresIn, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
issuedAt, err := time.Parse(time.RFC3339, tk.IssuedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return "", 0, nil, err
|
||||||
}
|
|
||||||
q := u.Query()
|
|
||||||
q.Add("service", service)
|
|
||||||
for _, scope := range scopes {
|
|
||||||
q.Add("scope", scope)
|
|
||||||
}
|
|
||||||
u.RawQuery = q.Encode()
|
|
||||||
r, err := http.NewRequest("GET", u.String(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.credential != nil {
|
return tk.Token, tk.ExpiresIn, &issuedAt, nil
|
||||||
s.credential.AddAuthorization(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := s.client.Do(r)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
err = ®istry_error.Error{
|
|
||||||
StatusCode: resp.StatusCode,
|
|
||||||
Detail: string(b),
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tk := models.Token{}
|
|
||||||
if err = json.Unmarshal(b, &tk); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
token = tk.Token
|
|
||||||
|
|
||||||
expiresIn = tk.ExpiresIn
|
|
||||||
|
|
||||||
if len(tk.IssuedAt) != 0 {
|
|
||||||
t, err := time.Parse(time.RFC3339, tk.IssuedAt)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("error occurred while parsing issued_at: %v", err)
|
|
||||||
err = nil
|
|
||||||
} else {
|
|
||||||
issuedAt = &t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// when the registry client is used inside Harbor, the token request
|
// when the registry client is used inside Harbor, the token request
|
||||||
@ -267,7 +216,7 @@ func newUsernameTokenAuthorizer(notary bool, username, scopeType, scopeName stri
|
|||||||
username: username,
|
username: username,
|
||||||
}
|
}
|
||||||
|
|
||||||
authorizer.scope = &scope{
|
authorizer.scope = &Scope{
|
||||||
Type: scopeType,
|
Type: scopeType,
|
||||||
Name: scopeName,
|
Name: scopeName,
|
||||||
Actions: scopeActions,
|
Actions: scopeActions,
|
||||||
|
93
src/common/utils/registry/auth/util.go
Normal file
93
src/common/utils/registry/auth/util.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
service = "harbor-registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetToken requests a token against the endpoint using credetial provided
|
||||||
|
func GetToken(endpoint string, insecure bool, credential Credential,
|
||||||
|
scopes []*Scope) (*models.Token, error) {
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: registry.GetHTTPTransport(insecure),
|
||||||
|
}
|
||||||
|
|
||||||
|
scopesStr := []string{}
|
||||||
|
for _, scope := range scopes {
|
||||||
|
scopesStr = append(scopesStr, scope.string())
|
||||||
|
}
|
||||||
|
|
||||||
|
return getToken(client, credential, endpoint, service, scopesStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getToken(client *http.Client, credential Credential, realm, service string,
|
||||||
|
scopes []string) (*models.Token, error) {
|
||||||
|
u, err := url.Parse(realm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
query := u.Query()
|
||||||
|
query.Add("service", service)
|
||||||
|
for _, scope := range scopes {
|
||||||
|
query.Add("scope", scope)
|
||||||
|
}
|
||||||
|
u.RawQuery = query.Encode()
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if credential != nil {
|
||||||
|
credential.AddAuthorization(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, ®istry_error.Error{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Detail: string(data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token := &models.Token{}
|
||||||
|
if err = json.Unmarshal(data, token); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, nil
|
||||||
|
}
|
@ -15,11 +15,8 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/vmware/harbor/src/common/models"
|
"github.com/vmware/harbor/src/common/models"
|
||||||
"github.com/vmware/harbor/src/common/utils/registry"
|
"github.com/vmware/harbor/src/common/utils/registry"
|
||||||
@ -64,39 +61,18 @@ func BuildBlobURL(endpoint, repository, digest string) string {
|
|||||||
return fmt.Sprintf("%s/v2/%s/blobs/%s", endpoint, repository, digest)
|
return fmt.Sprintf("%s/v2/%s/blobs/%s", endpoint, repository, digest)
|
||||||
}
|
}
|
||||||
|
|
||||||
//GetTokenForRepo is a temp solution for job handler to get a token for clair.
|
//GetTokenForRepo is used for job handler to get a token for clair.
|
||||||
//TODO: Get rid of it when it can get a token from repository client.
|
|
||||||
func GetTokenForRepo(repository string) (string, error) {
|
func GetTokenForRepo(repository string) (string, error) {
|
||||||
u, err := url.Parse(config.InternalTokenServiceEndpoint())
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
q := u.Query()
|
|
||||||
q.Add("service", "harbor-registry")
|
|
||||||
q.Add("scope", fmt.Sprintf("repository:%s:pull", repository))
|
|
||||||
u.RawQuery = q.Encode()
|
|
||||||
r, err := http.NewRequest("GET", u.String(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
c := &http.Cookie{Name: models.UISecretCookie, Value: config.JobserviceSecret()}
|
c := &http.Cookie{Name: models.UISecretCookie, Value: config.JobserviceSecret()}
|
||||||
r.AddCookie(c)
|
credentail := auth.NewCookieCredential(c)
|
||||||
client := &http.Client{}
|
token, err := auth.GetToken(config.InternalTokenServiceEndpoint(), true, credentail, []*auth.Scope{&auth.Scope{
|
||||||
resp, err := client.Do(r)
|
Type: "repository",
|
||||||
|
Name: repository,
|
||||||
|
Actions: []string{"pull"},
|
||||||
|
}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
return token.Token, nil
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return "", fmt.Errorf("Unexpected response from token service, code: %d, %s", resp.StatusCode, string(b))
|
|
||||||
}
|
|
||||||
tk := models.Token{}
|
|
||||||
if err := json.Unmarshal(b, &tk); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return tk.Token, nil
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user