harbor/src/core/service/token/token_test.go

301 lines
8.2 KiB
Go
Raw Normal View History

// Copyright 2018 Project Harbor Authors
2017-04-13 12:54:58 +02:00
//
// 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 token
import (
jwt "github.com/dgrijalva/jwt-go"
"github.com/docker/distribution/registry/auth/token"
"github.com/stretchr/testify/assert"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"net/url"
"os"
"path"
"runtime"
"testing"
2017-05-11 10:45:24 +02:00
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/core/config"
)
func TestMain(m *testing.M) {
config.Init()
2017-03-06 03:54:49 +01:00
InitCreators()
result := m.Run()
if result != 0 {
os.Exit(result)
}
}
func TestGetResourceActions(t *testing.T) {
2017-08-11 08:08:50 +02:00
cases := map[string]*token.ResourceActions{
"::": {
2017-08-11 08:08:50 +02:00
Type: "",
Name: "",
Actions: []string{},
},
"repository": {
2017-08-11 08:08:50 +02:00
Type: "repository",
Name: "",
Actions: []string{},
},
"repository:": {
2017-08-11 08:08:50 +02:00
Type: "repository",
Name: "",
Actions: []string{},
},
"repository:library/hello-world": {
2017-08-11 08:08:50 +02:00
Type: "repository",
Name: "library/hello-world",
Actions: []string{},
},
"repository:library/hello-world:": {
2017-08-11 08:08:50 +02:00
Type: "repository",
Name: "library/hello-world",
Actions: []string{},
},
"repository:library/hello-world:pull,push": {
2017-08-11 08:08:50 +02:00
Type: "repository",
Name: "library/hello-world",
Actions: []string{"pull", "push"},
},
"registry:catalog:*": {
Type: "registry",
Name: "catalog",
Actions: []string{"*"},
},
"repository:192.168.0.1:443/library/hello-world:pull,push": {
Type: "repository",
2017-08-11 08:08:50 +02:00
Name: "192.168.0.1:443/library/hello-world",
Actions: []string{"pull", "push"},
},
}
2017-08-11 08:08:50 +02:00
for k, v := range cases {
r := GetResourceActions([]string{k})[0]
assert.EqualValues(t, v, r)
}
}
func getKeyAndCertPath() (string, string) {
_, f, _, ok := runtime.Caller(0)
if !ok {
panic("Failed to get current directory")
}
return path.Join(path.Dir(f), "test/private_key.pem"), path.Join(path.Dir(f), "test/root.crt")
}
func getPublicKey(crtPath string) (*rsa.PublicKey, error) {
crt, err := ioutil.ReadFile(crtPath)
if err != nil {
return nil, err
}
block, _ := pem.Decode(crt)
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
return cert.PublicKey.(*rsa.PublicKey), nil
}
type harborClaims struct {
jwt.StandardClaims
// Private claims
Access []*token.ResourceActions `json:"access"`
}
func TestMakeToken(t *testing.T) {
pk, crt := getKeyAndCertPath()
// overwrite the config values for testing.
privateKey = pk
ra := []*token.ResourceActions{{
Type: "repository",
Name: "10.117.4.142/notary-test/hello-world-2",
Actions: []string{"pull", "push"},
}}
svc := "harbor-registry"
u := "tester"
2017-07-28 06:59:23 +02:00
tokenJSON, err := MakeToken(u, svc, ra)
if err != nil {
t.Errorf("Error while making token: %v", err)
}
2017-02-26 12:53:13 +01:00
tokenString := tokenJSON.Token
// t.Logf("privatekey: %s, crt: %s", tokenString, crt)
pubKey, err := getPublicKey(crt)
if err != nil {
t.Errorf("Error while getting public key from cert: %s", crt)
}
tok, err := jwt.ParseWithClaims(tokenString, &harborClaims{}, func(t *jwt.Token) (interface{}, error) {
if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", t.Header["alg"])
}
return pubKey, nil
})
2017-02-26 12:53:13 +01:00
t.Logf("Token validity: %v", tok.Valid)
if err != nil {
t.Errorf("Error while parsing the token: %v", err)
}
claims := tok.Claims.(*harborClaims)
assert.Equal(t, *(claims.Access[0]), *(ra[0]), "Access mismatch")
assert.Equal(t, claims.Audience, svc, "Audience mismatch")
2017-02-26 12:53:13 +01:00
}
2017-02-26 12:53:13 +01:00
func TestPermToActions(t *testing.T) {
perm1 := "RWM"
perm2 := "MRR"
perm3 := ""
expect1 := []string{"push", "*", "pull"}
expect2 := []string{"*", "pull"}
expect3 := []string{}
res1 := permToActions(perm1)
res2 := permToActions(perm2)
res3 := permToActions(perm3)
assert.Equal(t, res1, expect1, fmt.Sprintf("actions mismatch for permission: %s", perm1))
assert.Equal(t, res2, expect2, fmt.Sprintf("actions mismatch for permission: %s", perm2))
assert.Equal(t, res3, expect3, fmt.Sprintf("actions mismatch for permission: %s", perm3))
}
type parserTestRec struct {
input string
expect image
expectError bool
}
2017-02-26 15:05:13 +01:00
func TestInit(t *testing.T) {
InitCreators()
}
2017-02-26 12:53:13 +01:00
func TestBasicParser(t *testing.T) {
testList := []parserTestRec{{"library/ubuntu:14.04", image{"library", "ubuntu", "14.04"}, false},
{"test/hello", image{"test", "hello", ""}, false},
{"myimage:14.04", image{}, true},
{"org/team/img", image{"org", "team/img", ""}, false},
2017-02-26 12:53:13 +01:00
}
p := &basicParser{}
for _, rec := range testList {
r, err := p.parse(rec.input)
if rec.expectError {
assert.Error(t, err, fmt.Sprintf("Expected error for input: %s", rec.input))
2017-02-26 12:53:13 +01:00
} else {
assert.Nil(t, err, "Expected no error for input: %s", rec.input)
assert.Equal(t, rec.expect, *r, "result mismatch for input: %s", rec.input)
}
}
}
func TestEndpointParser(t *testing.T) {
p := &endpointParser{
"10.117.4.142:5000",
}
testList := []parserTestRec{{"10.117.4.142:5000/library/ubuntu:14.04", image{"library", "ubuntu", "14.04"}, false},
{"myimage:14.04", image{}, true},
{"10.117.4.142:80/library/myimage:14.04", image{}, true},
{"library/myimage:14.04", image{}, true},
{"10.117.4.142:5000/myimage:14.04", image{}, true},
{"10.117.4.142:5000/org/team/img", image{"org", "team/img", ""}, false},
2017-02-26 12:53:13 +01:00
}
for _, rec := range testList {
r, err := p.parse(rec.input)
if rec.expectError {
assert.Error(t, err, fmt.Sprintf("Expected error for input: %s", rec.input))
2017-02-26 12:53:13 +01:00
} else {
assert.Nil(t, err, "Expected no error for input: %s", rec.input)
assert.Equal(t, rec.expect, *r, "result mismatch for input: %s", rec.input)
}
}
}
2017-03-06 03:54:49 +01:00
2017-05-11 10:45:24 +02:00
type fakeSecurityContext struct {
isAdmin bool
}
func (f *fakeSecurityContext) Name() string {
return "fake"
}
2017-05-11 10:45:24 +02:00
func (f *fakeSecurityContext) IsAuthenticated() bool {
return true
}
func (f *fakeSecurityContext) GetUsername() string {
return "jack"
}
func (f *fakeSecurityContext) IsSysAdmin() bool {
return f.isAdmin
}
2017-07-20 10:27:58 +02:00
func (f *fakeSecurityContext) IsSolutionUser() bool {
return false
}
func (f *fakeSecurityContext) Can(action rbac.Action, resource rbac.Resource) bool {
return false
}
func (f *fakeSecurityContext) GetMyProjects() ([]*models.Project, error) {
return nil, nil
}
func (f *fakeSecurityContext) GetProjectRoles(interface{}) []int {
return nil
}
2017-05-11 10:45:24 +02:00
2017-03-06 03:54:49 +01:00
func TestFilterAccess(t *testing.T) {
// TODO put initial data in DB to verify repository filter.
2017-03-06 03:54:49 +01:00
var err error
s := []string{"registry:catalog:*"}
a1 := GetResourceActions(s)
a2 := GetResourceActions(s)
a3 := GetResourceActions(s)
2017-05-11 10:45:24 +02:00
2017-03-06 03:54:49 +01:00
ra1 := token.ResourceActions{
Type: "registry",
Name: "catalog",
Actions: []string{"*"},
}
ra2 := token.ResourceActions{
Type: "registry",
Name: "catalog",
Actions: []string{},
}
2017-05-11 10:45:24 +02:00
err = filterAccess(a1, &fakeSecurityContext{
isAdmin: true,
}, nil, registryFilterMap)
2017-03-06 03:54:49 +01:00
assert.Nil(t, err, "Unexpected error: %v", err)
assert.Equal(t, ra1, *a1[0], "Mismatch after registry filter Map")
2017-05-11 10:45:24 +02:00
err = filterAccess(a2, &fakeSecurityContext{
isAdmin: true,
}, nil, notaryFilterMap)
2017-03-06 03:54:49 +01:00
assert.Nil(t, err, "Unexpected error: %v", err)
assert.Equal(t, ra2, *a2[0], "Mismatch after notary filter Map")
2017-05-11 10:45:24 +02:00
err = filterAccess(a3, &fakeSecurityContext{
isAdmin: false,
}, nil, registryFilterMap)
assert.Nil(t, err, "Unexpected error: %v", err)
assert.Equal(t, ra2, *a3[0], "Mismatch after registry filter Map")
2017-03-06 03:54:49 +01:00
}
func TestParseScopes(t *testing.T) {
assert := assert.New(t)
u1 := "/service/token?account=admin&scope=repository%3Alibrary%2Fregistry%3Apush%2Cpull&scope=repository%3Ahello-world%2Fregistry%3Apull&service=harbor-registry"
r1, _ := url.Parse(u1)
l1 := parseScopes(r1)
assert.Equal([]string{"repository:library/registry:push,pull", "repository:hello-world/registry:pull"}, l1)
}