mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-22 16:48:30 +01:00
Switch to basic authentication for registry
1. Add basic authorizer for registry which modify the request to add basic authorization header to request based on configuration. 2. Set basic auth header for proxy when accessing registry 3. Switche the registry to use basic auth by default and use the basic authorizer to access Harbor. 4. Make necessary change to test cases, particularly "test_robot_account.py" and "docker_api.py", because the error is changed after siwtched to basic auth from token auth. #10604 is opened to track the follow up work. Signed-off-by: Daniel Jiang <jiangd@vmware.com>
This commit is contained in:
parent
a1b25e1fec
commit
2064a1cd6d
@ -26,9 +26,6 @@ https:
|
||||
# Remember Change the admin password from UI after launching Harbor.
|
||||
harbor_admin_password: Harbor12345
|
||||
|
||||
#TODO: remove this temporary flag before ships v1.11/v2, this should always be true
|
||||
registry_use_basic_auth: false
|
||||
|
||||
# Harbor DB configuration
|
||||
database:
|
||||
# The password for the root user of Harbor DB. Change this before any production use.
|
||||
|
@ -26,17 +26,9 @@ http:
|
||||
debug:
|
||||
addr: localhost:5001
|
||||
auth:
|
||||
{% if registry_use_basic_auth %}
|
||||
htpasswd:
|
||||
realm: harbor-registry-basic-realm
|
||||
path: /etc/registry/passwd
|
||||
{% else %}
|
||||
token:
|
||||
issuer: harbor-token-issuer
|
||||
realm: {{public_url}}/service/token
|
||||
rootcertbundle: /etc/registry/root.crt
|
||||
service: harbor-registry
|
||||
{% endif %}
|
||||
validation:
|
||||
disabled: true
|
||||
notifications:
|
||||
|
@ -327,10 +327,6 @@ def parse_yaml_config(config_file_path, with_notary, with_clair, with_chartmuseu
|
||||
|
||||
config_dict['registry_username'] = REGISTRY_USER_NAME
|
||||
config_dict['registry_password'] = generate_random_string(32)
|
||||
|
||||
# TODO: remove the flag before release
|
||||
config_dict['registry_use_basic_auth'] = configs['registry_use_basic_auth']
|
||||
|
||||
return config_dict
|
||||
|
||||
|
||||
|
@ -24,8 +24,7 @@ def prepare_registry(config_dict):
|
||||
prepare_dir(registry_data_dir, uid=DEFAULT_UID, gid=DEFAULT_GID)
|
||||
prepare_dir(registry_config_dir)
|
||||
|
||||
if config_dict['registry_use_basic_auth']:
|
||||
gen_passwd_file(config_dict)
|
||||
gen_passwd_file(config_dict)
|
||||
storage_provider_info = get_storage_provider_info(
|
||||
config_dict['storage_provider_name'],
|
||||
config_dict['storage_provider_config'])
|
||||
|
@ -21,7 +21,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/common/utils/registry"
|
||||
"github.com/goharbor/harbor/src/common/utils/registry/auth"
|
||||
"github.com/goharbor/harbor/src/core/config"
|
||||
"github.com/goharbor/harbor/src/core/service/token"
|
||||
coreutils "github.com/goharbor/harbor/src/core/utils"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"io/ioutil"
|
||||
@ -86,7 +85,7 @@ func newRepositoryClient(repository string) (*registry.Repository, error) {
|
||||
uam := &auth.UserAgentModifier{
|
||||
UserAgent: "harbor-registry-client",
|
||||
}
|
||||
authorizer := auth.NewRawTokenAuthorizer("admin", token.Registry)
|
||||
authorizer := auth.DefaultBasicAuthorizer()
|
||||
transport := registry.NewTransport(http.DefaultTransport, authorizer, uam)
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
|
40
src/common/utils/registry/auth/basicauthorizer.go
Normal file
40
src/common/utils/registry/auth/basicauthorizer.go
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 (
|
||||
"github.com/goharbor/harbor/src/common/http/modifier"
|
||||
"github.com/goharbor/harbor/src/core/config"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// NewBasicAuthorizer create an authorizer to add basic auth header as is set in the parameter
|
||||
func NewBasicAuthorizer(u, p string) modifier.Modifier {
|
||||
return NewBasicAuthCredential(u, p)
|
||||
}
|
||||
|
||||
var (
|
||||
defaultAuthorizer modifier.Modifier
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// DefaultBasicAuthorizer returns the basic authorizer that sets the basic auth as configured in env variables
|
||||
func DefaultBasicAuthorizer() modifier.Modifier {
|
||||
once.Do(func() {
|
||||
u, p := config.RegistryCredential()
|
||||
defaultAuthorizer = NewBasicAuthCredential(u, p)
|
||||
})
|
||||
return defaultAuthorizer
|
||||
}
|
39
src/common/utils/registry/auth/basicauthorizer_test.go
Normal file
39
src/common/utils/registry/auth/basicauthorizer_test.go
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDefaultBasicAuthorizer(t *testing.T) {
|
||||
os.Setenv("REGISTRY_CREDENTIAL_USERNAME", "testuser")
|
||||
os.Setenv("REGISTRY_CREDENTIAL_PASSWORD", "testpassword")
|
||||
defer func() {
|
||||
os.Unsetenv("REGISTRY_CREDENTIAL_USERNAME")
|
||||
os.Unsetenv("REGISTRY_CREDENTIAL_PASSWORD")
|
||||
}()
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://127.0.0.1", nil)
|
||||
a := DefaultBasicAuthorizer()
|
||||
err := a.Modify(req)
|
||||
assert.Nil(t, err)
|
||||
u, p, ok := req.BasicAuth()
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "testuser", u)
|
||||
assert.Equal(t, "testpassword", p)
|
||||
}
|
@ -29,7 +29,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/common/utils/registry/auth"
|
||||
"github.com/goharbor/harbor/src/core/config"
|
||||
"github.com/goharbor/harbor/src/core/promgr"
|
||||
"github.com/goharbor/harbor/src/core/service/token"
|
||||
coreutils "github.com/goharbor/harbor/src/core/utils"
|
||||
)
|
||||
|
||||
@ -248,7 +247,7 @@ func initRegistryClient() (r *registry.Registry, err error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authorizer := auth.NewRawTokenAuthorizer("harbor-core", token.Registry)
|
||||
authorizer := auth.DefaultBasicAuthorizer()
|
||||
return registry.NewRegistry(endpoint, &http.Client{
|
||||
Transport: registry.NewTransport(registry.GetHTTPTransport(), authorizer),
|
||||
})
|
||||
|
@ -24,7 +24,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/common/utils/registry"
|
||||
"github.com/goharbor/harbor/src/common/utils/registry/auth"
|
||||
"github.com/goharbor/harbor/src/core/config"
|
||||
"github.com/goharbor/harbor/src/core/service/token"
|
||||
)
|
||||
|
||||
// NewRepositoryClientForUI creates a repository client that can only be used to
|
||||
@ -51,7 +50,7 @@ func newRepositoryClient(endpoint, username, repository string) (*registry.Repos
|
||||
uam := &auth.UserAgentModifier{
|
||||
UserAgent: "harbor-registry-client",
|
||||
}
|
||||
authorizer := auth.NewRawTokenAuthorizer(username, token.Registry)
|
||||
authorizer := auth.DefaultBasicAuthorizer()
|
||||
transport := registry.NewTransport(http.DefaultTransport, authorizer, uam)
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
|
@ -20,53 +20,13 @@ import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/distribution/registry/auth/token"
|
||||
httpauth "github.com/goharbor/harbor/src/common/http/modifier/auth"
|
||||
"github.com/goharbor/harbor/src/common/utils/registry"
|
||||
"github.com/goharbor/harbor/src/common/utils/registry/auth"
|
||||
)
|
||||
|
||||
var coreClient *http.Client
|
||||
var mutex = &sync.Mutex{}
|
||||
|
||||
// NewRepositoryClient creates a repository client with standard token authorizer
|
||||
func NewRepositoryClient(endpoint string, insecure bool, credential auth.Credential,
|
||||
tokenServiceEndpoint, repository string) (*registry.Repository, error) {
|
||||
|
||||
transport := registry.GetHTTPTransport(insecure)
|
||||
|
||||
authorizer := auth.NewStandardTokenAuthorizer(&http.Client{
|
||||
Transport: transport,
|
||||
}, credential, tokenServiceEndpoint)
|
||||
|
||||
uam := &UserAgentModifier{
|
||||
UserAgent: "harbor-registry-client",
|
||||
}
|
||||
|
||||
return registry.NewRepository(repository, endpoint, &http.Client{
|
||||
Transport: registry.NewTransport(transport, authorizer, uam),
|
||||
})
|
||||
}
|
||||
|
||||
// NewRepositoryClientForJobservice creates a repository client that can only be used to
|
||||
// access the internal registry
|
||||
func NewRepositoryClientForJobservice(repository, internalRegistryURL, secret, internalTokenServiceURL string) (*registry.Repository, error) {
|
||||
transport := registry.GetHTTPTransport()
|
||||
credential := httpauth.NewSecretAuthorizer(secret)
|
||||
|
||||
authorizer := auth.NewStandardTokenAuthorizer(&http.Client{
|
||||
Transport: transport,
|
||||
}, credential, internalTokenServiceURL)
|
||||
|
||||
uam := &UserAgentModifier{
|
||||
UserAgent: "harbor-registry-client",
|
||||
}
|
||||
|
||||
return registry.NewRepository(repository, internalRegistryURL, &http.Client{
|
||||
Transport: registry.NewTransport(transport, authorizer, uam),
|
||||
})
|
||||
}
|
||||
|
||||
// UserAgentModifier adds the "User-Agent" header to the request
|
||||
type UserAgentModifier struct {
|
||||
UserAgent string
|
||||
@ -78,27 +38,6 @@ func (u *UserAgentModifier) Modify(req *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuildBlobURL ...
|
||||
func BuildBlobURL(endpoint, repository, digest string) string {
|
||||
return fmt.Sprintf("%s/v2/%s/blobs/%s", endpoint, repository, digest)
|
||||
}
|
||||
|
||||
// GetTokenForRepo is used for job handler to get a token for clair.
|
||||
func GetTokenForRepo(repository, secret, internalTokenServiceURL string) (string, error) {
|
||||
credential := httpauth.NewSecretAuthorizer(secret)
|
||||
t, err := auth.GetToken(internalTokenServiceURL, false, credential,
|
||||
[]*token.ResourceActions{{
|
||||
Type: "repository",
|
||||
Name: repository,
|
||||
Actions: []string{"pull"},
|
||||
}})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return t.Token, nil
|
||||
}
|
||||
|
||||
// GetClient returns the HTTP client that will attach jobservce secret to the request, which can be used for
|
||||
// accessing Harbor's Core Service.
|
||||
// This function returns error if the secret of Job service is not set.
|
||||
|
@ -23,6 +23,8 @@ const (
|
||||
manifestInfoKey = contextKey("ManifestInfo")
|
||||
// ScannerPullCtxKey the context key for robot account to bypass the pull policy check.
|
||||
ScannerPullCtxKey = contextKey("ScannerPullCheck")
|
||||
// SkipInjectRegistryCredKey is the context key telling registry proxy to skip adding credentials
|
||||
SkipInjectRegistryCredKey = contextKey("SkipInjectRegistryCredential")
|
||||
)
|
||||
|
||||
var (
|
||||
@ -63,6 +65,12 @@ func ArtifactInfoFromContext(ctx context.Context) (*ArtifactInfo, bool) {
|
||||
return info, ok
|
||||
}
|
||||
|
||||
// SkipInjectRegistryCred reflects whether the inject credentials should be skipped
|
||||
func SkipInjectRegistryCred(ctx context.Context) bool {
|
||||
res, ok := ctx.Value(SkipInjectRegistryCredKey).(bool)
|
||||
return ok && res
|
||||
}
|
||||
|
||||
// NewManifestInfoContext returns context with manifest info
|
||||
func NewManifestInfoContext(ctx context.Context, info *ManifestInfo) context.Context {
|
||||
return context.WithValue(ctx, manifestInfoKey, info)
|
||||
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package authz
|
||||
package v2auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -24,7 +24,9 @@ import (
|
||||
ierror "github.com/goharbor/harbor/src/internal/error"
|
||||
"github.com/goharbor/harbor/src/server/middleware"
|
||||
reg_err "github.com/goharbor/harbor/src/server/registry/error"
|
||||
"golang.org/x/net/context"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type reqChecker struct {
|
||||
@ -32,6 +34,10 @@ type reqChecker struct {
|
||||
}
|
||||
|
||||
func (rc *reqChecker) check(req *http.Request) error {
|
||||
if rc.hasRegistryCred(req) {
|
||||
// TODO: May consider implement a local authorizer for registry, more details see #10602
|
||||
return nil
|
||||
}
|
||||
securityCtx, err := filter.GetSecurityContext(req)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -62,6 +68,9 @@ func (rc *reqChecker) check(req *http.Request) error {
|
||||
}
|
||||
} else if len(middleware.V2CatalogURLRe.FindStringSubmatch(req.URL.Path)) == 1 && !securityCtx.IsSysAdmin() {
|
||||
return fmt.Errorf("unauthorized to list catalog")
|
||||
} else if req.URL.Path == "/v2/" && !securityCtx.IsAuthenticated() {
|
||||
ctx := context.WithValue(req.Context(), middleware.SkipInjectRegistryCredKey, true)
|
||||
*req = *(req.WithContext(ctx))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -77,6 +86,12 @@ func (rc *reqChecker) projectID(name string) (int64, error) {
|
||||
return p.ProjectID, nil
|
||||
}
|
||||
|
||||
func (rc *reqChecker) hasRegistryCred(req *http.Request) bool {
|
||||
u, p, ok := req.BasicAuth()
|
||||
regUser, regPass := config.RegistryCredential()
|
||||
return ok && u == regUser && p == regPass
|
||||
}
|
||||
|
||||
func getAction(req *http.Request) rbac.Action {
|
||||
pushActions := map[string]struct{}{
|
||||
http.MethodPost: {},
|
||||
@ -98,16 +113,24 @@ func getAction(req *http.Request) rbac.Action {
|
||||
|
||||
}
|
||||
|
||||
var checker = reqChecker{
|
||||
pm: config.GlobalProjectMgr,
|
||||
}
|
||||
var (
|
||||
once sync.Once
|
||||
checker reqChecker
|
||||
)
|
||||
|
||||
// Middleware checks the permission of the request to access the artifact
|
||||
func Middleware() func(http.Handler) http.Handler {
|
||||
once.Do(func() {
|
||||
if checker.pm == nil { // for UT, where pm has been set to a mock value
|
||||
checker = reqChecker{
|
||||
pm: config.GlobalProjectMgr,
|
||||
}
|
||||
}
|
||||
})
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
if err := checker.check(req); err != nil {
|
||||
reg_err.Handle(rw, req, ierror.UnauthorizedError(err))
|
||||
reg_err.Handle(rw, req, ierror.UnauthorizedError(err).WithMessage(err.Error()))
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(rw, req)
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package authz
|
||||
package v2auth
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
@ -165,15 +165,26 @@ func TestMiddleware(t *testing.T) {
|
||||
}
|
||||
ctx1 := context.WithValue(baseCtx, middleware.ArtifactInfoKey, ar1)
|
||||
ctx2 := context.WithValue(baseCtx, middleware.ArtifactInfoKey, ar2)
|
||||
ctx2x := context.WithValue(context.Background(), middleware.ArtifactInfoKey, ar2) // no securityCtx
|
||||
ctx3 := context.WithValue(baseCtx, middleware.ArtifactInfoKey, ar3)
|
||||
ctx4 := context.WithValue(baseCtx, middleware.ArtifactInfoKey, ar4)
|
||||
req1a, _ := http.NewRequest(http.MethodGet, "/v2/project_1/hello-world/manifest/v1", nil)
|
||||
req1b, _ := http.NewRequest(http.MethodDelete, "/v2/project_1/hello-world/manifest/v1", nil)
|
||||
req2, _ := http.NewRequest(http.MethodGet, "/v2/library/ubuntu/manifest/14.04", nil)
|
||||
req2x, _ := http.NewRequest(http.MethodGet, "/v2/library/ubuntu/manifest/14.04", nil)
|
||||
req3, _ := http.NewRequest(http.MethodGet, "/v2/_catalog", nil)
|
||||
req4, _ := http.NewRequest(http.MethodPost, "/v2/project_1/ubuntu/blobs/uploads/mount=?mount=sha256:08e4a417ff4e3913d8723a05cc34055db01c2fd165b588e049c5bad16ce6094f&from=project_2/ubuntu", nil)
|
||||
req5, _ := http.NewRequest(http.MethodPost, "/v2/project_1/ubuntu/blobs/uploads/mount=?mount=sha256:08e4a417ff4e3913d8723a05cc34055db01c2fd165b588e049c5bad16ce6094f&from=project_3/ubuntu", nil)
|
||||
|
||||
os.Setenv("REGISTRY_CREDENTIAL_USERNAME", "testuser")
|
||||
os.Setenv("REGISTRY_CREDENTIAL_PASSWORD", "testpassword")
|
||||
defer func() {
|
||||
os.Unsetenv("REGISTRY_CREDENTIAL_USERNAME")
|
||||
os.Unsetenv("REGISTRY_CREDENTIAL_PASSWORD")
|
||||
}()
|
||||
|
||||
req2x.SetBasicAuth("testuser", "testpassword")
|
||||
|
||||
cases := []struct {
|
||||
input *http.Request
|
||||
status int
|
||||
@ -190,6 +201,10 @@ func TestMiddleware(t *testing.T) {
|
||||
input: req2.WithContext(ctx2),
|
||||
status: http.StatusUnauthorized,
|
||||
},
|
||||
{
|
||||
input: req2x.WithContext(ctx2x),
|
||||
status: http.StatusOK,
|
||||
},
|
||||
{
|
||||
input: req3.WithContext(baseCtx),
|
||||
status: http.StatusUnauthorized,
|
@ -15,6 +15,7 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/core/config"
|
||||
pkg_repo "github.com/goharbor/harbor/src/pkg/repository"
|
||||
pkg_tag "github.com/goharbor/harbor/src/pkg/tag"
|
||||
"github.com/goharbor/harbor/src/server/middleware"
|
||||
@ -34,9 +35,9 @@ import (
|
||||
|
||||
// New return the registry instance to handle the registry APIs
|
||||
func New(url *url.URL) http.Handler {
|
||||
// TODO add a director to add the basic auth for docker registry
|
||||
// TODO customize the reverse proxy to improve the performance?
|
||||
proxy := httputil.NewSingleHostReverseProxy(url)
|
||||
proxy.Director = basicAuthDirector(proxy.Director)
|
||||
|
||||
// create the root rooter
|
||||
rootRouter := mux.NewRouter()
|
||||
@ -75,3 +76,13 @@ func New(url *url.URL) http.Handler {
|
||||
|
||||
return rootRouter
|
||||
}
|
||||
|
||||
func basicAuthDirector(d func(*http.Request)) func(*http.Request) {
|
||||
return func(r *http.Request) {
|
||||
d(r)
|
||||
if r != nil && !middleware.SkipInjectRegistryCred(r.Context()) {
|
||||
u, p := config.RegistryCredential()
|
||||
r.SetBasicAuth(u, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
44
src/server/registry/handler_test.go
Normal file
44
src/server/registry/handler_test.go
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 registry
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func direct(req *http.Request) {
|
||||
req.Header.Add("test-key", "test-value")
|
||||
}
|
||||
|
||||
func TestBasicAuthDirector(t *testing.T) {
|
||||
req, _ := http.NewRequest(http.MethodGet, "127.0.0.1", nil)
|
||||
os.Setenv("REGISTRY_CREDENTIAL_USERNAME", "testuser")
|
||||
os.Setenv("REGISTRY_CREDENTIAL_PASSWORD", "testpassword")
|
||||
defer func() {
|
||||
os.Unsetenv("REGISTRY_CREDENTIAL_USERNAME")
|
||||
os.Unsetenv("REGISTRY_CREDENTIAL_PASSWORD")
|
||||
}()
|
||||
|
||||
d := basicAuthDirector(direct)
|
||||
d(req)
|
||||
assert.Equal(t, "test-value", req.Header.Get("test-key"))
|
||||
user, pass, ok := req.BasicAuth()
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "testuser", user)
|
||||
assert.Equal(t, "testpassword", pass)
|
||||
}
|
@ -17,8 +17,11 @@ package registry
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/core/config"
|
||||
"github.com/goharbor/harbor/src/server/middleware/immutable"
|
||||
|
||||
"github.com/goharbor/harbor/src/server/middleware/artifactinfo"
|
||||
"github.com/goharbor/harbor/src/server/middleware/manifestinfo"
|
||||
"github.com/goharbor/harbor/src/server/middleware/readonly"
|
||||
"github.com/goharbor/harbor/src/server/middleware/v2auth"
|
||||
"github.com/goharbor/harbor/src/server/registry/manifest"
|
||||
"github.com/goharbor/harbor/src/server/router"
|
||||
"net/http"
|
||||
@ -32,11 +35,17 @@ func RegisterRoutes() {
|
||||
regURL, _ := config.RegistryURL()
|
||||
url, _ := url.Parse(regURL)
|
||||
proxy := httputil.NewSingleHostReverseProxy(url)
|
||||
proxy.Director = basicAuthDirector(proxy.Director)
|
||||
|
||||
router.NewRoute().Path("/v2/*").Handler(New(url))
|
||||
router.NewRoute().Path("/v2/*").
|
||||
Middleware(artifactinfo.Middleware()).
|
||||
Middleware(v2auth.Middleware()).
|
||||
Handler(New(url))
|
||||
router.NewRoute().
|
||||
Method(http.MethodPut).
|
||||
Path("/v2/*/manifests/:reference").
|
||||
Middleware(artifactinfo.Middleware()).
|
||||
Middleware(v2auth.Middleware()).
|
||||
Middleware(readonly.Middleware()).
|
||||
Middleware(manifestinfo.Middleware()).
|
||||
Middleware(immutable.MiddlewarePush()).
|
||||
|
@ -49,7 +49,7 @@ class DockerAPI(object):
|
||||
if caught_err == False:
|
||||
if expected_error_message is not None:
|
||||
if str(ret).lower().find(expected_error_message.lower()) < 0:
|
||||
raise Exception(r" Failed to catch error [{}] when pull image {}".format (expected_error_message, image))
|
||||
raise Exception(r" Failed to catch error [{}] when pull image {}, return message: {}".format (expected_error_message, image, str(ret)))
|
||||
else:
|
||||
if str(ret).lower().find("error".lower()) >= 0:
|
||||
raise Exception(r" It's was not suppose to catch error when pull image {}, return message is [{}]".format (image, ret))
|
||||
@ -82,10 +82,12 @@ class DockerAPI(object):
|
||||
if caught_err == False:
|
||||
if expected_error_message is not None:
|
||||
if str(ret).lower().find(expected_error_message.lower()) < 0:
|
||||
raise Exception(r" Failed to catch error [{}] when push image {}".format (expected_error_message, harbor_registry))
|
||||
raise Exception(r" Failed to catch error [{}] when push image {}, return message: {}".
|
||||
format (expected_error_message, harbor_registry, str(ret)))
|
||||
else:
|
||||
if str(ret).lower().find("errorDetail".lower()) >= 0:
|
||||
raise Exception(r" It's was not suppose to catch error when push image {}, return message is [{}]".format (harbor_registry, ret))
|
||||
raise Exception(r" It's was not suppose to catch error when push image {}, return message is [{}]".
|
||||
format (harbor_registry, ret))
|
||||
|
||||
def docker_image_build(self, harbor_registry, tags=None, size=1, expected_error_message = None):
|
||||
caught_err = False
|
||||
@ -122,7 +124,8 @@ class DockerAPI(object):
|
||||
if caught_err == False:
|
||||
if expected_error_message is not None:
|
||||
if str(ret).lower().find(expected_error_message.lower()) < 0:
|
||||
raise Exception(r" Failed to catch error [{}] when push image {}".format (expected_error_message, harbor_registry))
|
||||
raise Exception(r" Failed to catch error [{}] when build image {}, return message: {}".
|
||||
format (expected_error_message, harbor_registry, str(ret)))
|
||||
else:
|
||||
if str(ret).lower().find("errorDetail".lower()) >= 0:
|
||||
raise Exception(r" It's was not suppose to catch error when push image {}, return message is [{}]".format (harbor_registry, ret))
|
||||
|
@ -109,25 +109,25 @@ class TestProjects(unittest.TestCase):
|
||||
TestProjects.repo_name_pa, _ = push_image_to_project(project_ra_name_a, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag)
|
||||
|
||||
print "#8. Push image(ImageRA) to project(PB) by robot account(RA), it must be not successful;"
|
||||
push_image_to_project(project_ra_name_b, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag, expected_error_message = "denied: requested access to the resource is denied")
|
||||
push_image_to_project(project_ra_name_b, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag, expected_error_message = "unauthorized to access repository")
|
||||
|
||||
print "#9. Pull image(ImagePB) from project(PB) by robot account(RA), it must be not successful;"
|
||||
pull_harbor_image(harbor_server, robot_account.name, robot_account.token, TestProjects.repo_name_in_project_b, tag_b, expected_error_message = r"pull access denied for " + harbor_server + "/" + TestProjects.repo_name_in_project_b)
|
||||
pull_harbor_image(harbor_server, robot_account.name, robot_account.token, TestProjects.repo_name_in_project_b, tag_b, expected_error_message = "unauthorized to access repository")
|
||||
|
||||
print "#10. Pull image from project(PC), it must be successful;"
|
||||
pull_harbor_image(harbor_server, robot_account.name, robot_account.token, TestProjects.repo_name_in_project_c, tag_c)
|
||||
|
||||
print "#11. Push image(ImageRA) to project(PC) by robot account(RA), it must be not successful;"
|
||||
push_image_to_project(project_ra_name_c, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag, expected_error_message = "denied: requested access to the resource is denied")
|
||||
push_image_to_project(project_ra_name_c, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag, expected_error_message = "unauthorized to access repository")
|
||||
|
||||
print "#12. Update action property of robot account(RA);"
|
||||
self.project.disable_project_robot_account(TestProjects.project_ra_id_a, robot_id, True, **TestProjects.USER_RA_CLIENT)
|
||||
|
||||
print "#13. Pull image(ImagePA) from project(PA) by robot account(RA), it must be not successful;"
|
||||
pull_harbor_image(harbor_server, robot_account.name, robot_account.token, TestProjects.repo_name_in_project_a, tag_a, expected_login_error_message = "401 Client Error: Unauthorized")
|
||||
pull_harbor_image(harbor_server, robot_account.name, robot_account.token, TestProjects.repo_name_in_project_a, tag_a, expected_login_error_message = "401 Unauthorized")
|
||||
|
||||
print "#14. Push image(ImageRA) to project(PA) by robot account(RA), it must be not successful;"
|
||||
push_image_to_project(project_ra_name_a, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag, expected_login_error_message = "401 Client Error: Unauthorized")
|
||||
push_image_to_project(project_ra_name_a, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag, expected_login_error_message = "401 Unauthorized")
|
||||
|
||||
print "#15. Delete robot account(RA), it must be not successful;"
|
||||
self.project.delete_project_robot_account(TestProjects.project_ra_id_a, robot_id, **TestProjects.USER_RA_CLIENT)
|
||||
|
@ -26,19 +26,23 @@ Test Case - Delete a Repository of a Certain Project Created by Normal User
|
||||
Harbor API Test ./tests/apitests/python/test_del_repo.py
|
||||
Test Case - Add a System Global Label to a Certain Tag
|
||||
Harbor API Test ./tests/apitests/python/test_add_sys_label_to_tag.py
|
||||
Test Case - Add Replication Rule
|
||||
Harbor API Test ./tests/apitests/python/test_add_replication_rule.py
|
||||
# TODO uncomment this after replication works with basic auth - #10509
|
||||
# Test Case - Add Replication Rule
|
||||
# Harbor API Test ./tests/apitests/python/test_add_replication_rule.py
|
||||
Test Case - Edit Project Creation
|
||||
Harbor API Test ./tests/apitests/python/test_edit_project_creation.py
|
||||
Test Case - Scan Image
|
||||
Harbor API Test ./tests/apitests/python/test_scan_image.py
|
||||
# TODO uncomment this after image scan work with basic auth - #10277
|
||||
#Test Case - Scan Image
|
||||
# Harbor API Test ./tests/apitests/python/test_scan_image.py
|
||||
Test Case - Manage Project Member
|
||||
Harbor API Test ./tests/apitests/python/test_manage_project_member.py
|
||||
# TODO uncomment this after enable content trust middleware
|
||||
# Test Case - Project Level Policy Content Trust
|
||||
# Harbor API Test ./tests/apitests/python/test_project_level_policy_content_trust.py
|
||||
Test Case - User View Logs
|
||||
Harbor API Test ./tests/apitests/python/test_user_view_logs.py
|
||||
# TODO uncomment this after we move the accesslog away from registry notificaiton
|
||||
# TODO potentially #10602 may also fix this.
|
||||
# Test Case - User View Logs
|
||||
# Harbor API Test ./tests/apitests/python/test_user_view_logs.py
|
||||
# TODO uncomment this after making scan all work with OCI registry
|
||||
# Test Case - Scan All Images
|
||||
# Harbor API Test ./tests/apitests/python/test_scan_all_images.py
|
||||
|
Loading…
Reference in New Issue
Block a user