Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
小 白蛋
Harbor
Commits
a8dc75dd
Commit
a8dc75dd
authored
7 years ago
by
Wenkai Yin
Browse files
Options
Download
Email Patches
Plain Diff
update
parent
1da9b865
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
src/common/utils/notary/helper.go
+27
-4
src/common/utils/notary/helper.go
src/common/utils/registry/auth/tokenauthorizer.go
+21
-37
src/common/utils/registry/auth/tokenauthorizer.go
src/common/utils/registry/auth/tokenauthorizer_test.go
+4
-3
src/common/utils/registry/auth/tokenauthorizer_test.go
src/common/utils/registry/auth/util.go
+4
-4
src/common/utils/registry/auth/util.go
src/ui/service/token/authutils.go
+8
-33
src/ui/service/token/authutils.go
src/ui/service/token/creator.go
+1
-1
src/ui/service/token/creator.go
src/ui/service/token/token_test.go
+1
-1
src/ui/service/token/token_test.go
src/ui/utils/utils.go
+2
-1
src/ui/utils/utils.go
with
68 additions
and
84 deletions
+68
-84
src/common/utils/notary/helper.go
+
27
-
4
View file @
a8dc75dd
...
...
@@ -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 "1
0.117.4.117
/library/ubuntu", instead of "library/ubuntu" (fqRepo for fully-qualified repo)
// like "1
92.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
:=
&
notaryAuthorizer
{
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
}
This diff is collapsed.
Click to expand it.
src/common/utils/registry/auth/tokenauthorizer.go
+
21
-
37
View file @
a8dc75dd
...
...
@@ -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
=
scope
s
[
0
]
.
string
(
)
key
=
scope
String
(
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
=
scope
s
[
0
]
.
string
(
)
key
=
scope
String
(
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
.
s
tring
())
strs
=
append
(
strs
,
s
copeS
tring
(
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
{
...
...
This diff is collapsed.
Click to expand it.
src/common/utils/registry/auth/tokenauthorizer_test.go
+
4
-
3
View file @
a8dc75dd
...
...
@@ -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
{
...
...
This diff is collapsed.
Click to expand it.
src/common/utils/registry/auth/util.go
+
4
-
4
View file @
a8dc75dd
...
...
@@ -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
.
s
tring
())
query
.
Add
(
"scope"
,
scope
S
tring
(
scope
))
}
u
.
RawQuery
=
query
.
Encode
()
...
...
This diff is collapsed.
Click to expand it.
src/ui/service/token/authutils.go
+
8
-
33
View file @
a8dc75dd
...
...
@@ -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
:
r
aw
,
ExpiresIn
:
expires
,
IssuedAt
:
issued
.
Format
(
time
.
RFC3339
),
Token
:
r
s
,
ExpiresIn
:
expires
In
,
IssuedAt
:
issued
At
.
Format
(
time
.
RFC3339
),
},
nil
}
...
...
This diff is collapsed.
Click to expand it.
src/ui/service/token/creator.go
+
1
-
1
View file @
a8dc75dd
...
...
@@ -202,7 +202,7 @@ func (g generalCreator) Create(r *http.Request) (*models.Token, error) {
if
err
!=
nil
{
return
nil
,
err
}
return
m
akeToken
(
ctx
.
GetUsername
(),
g
.
service
,
access
)
return
M
akeToken
(
ctx
.
GetUsername
(),
g
.
service
,
access
)
}
func
parseScopes
(
u
*
url
.
URL
)
[]
string
{
...
...
This diff is collapsed.
Click to expand it.
src/ui/service/token/token_test.go
+
1
-
1
View file @
a8dc75dd
...
...
@@ -111,7 +111,7 @@ func TestMakeToken(t *testing.T) {
}}
svc
:=
"harbor-registry"
u
:=
"tester"
tokenJSON
,
err
:=
m
akeToken
(
u
,
svc
,
ra
)
tokenJSON
,
err
:=
M
akeToken
(
u
,
svc
,
ra
)
if
err
!=
nil
{
t
.
Errorf
(
"Error while making token: %v"
,
err
)
}
...
...
This diff is collapsed.
Click to expand it.
src/ui/utils/utils.go
+
2
-
1
View file @
a8dc75dd
...
...
@@ -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
{
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment
Menu
Projects
Groups
Snippets
Help