mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-01 20:41:22 +01:00
create a global project manager
This commit is contained in:
parent
e5e5e75768
commit
d6b4330cc8
@ -83,6 +83,7 @@ services:
|
||||
- ./common/config/ui/private_key.pem:/etc/ui/private_key.pem:z
|
||||
- /data/secretkey:/etc/ui/key:z
|
||||
- /data/ca_download/:/etc/ui/ca/:z
|
||||
- /data/token:/etc/ui/token:z
|
||||
networks:
|
||||
- harbor
|
||||
depends_on:
|
||||
|
@ -77,6 +77,7 @@ services:
|
||||
- ./common/config/ui/private_key.pem:/etc/ui/private_key.pem:z
|
||||
- /data/secretkey:/etc/ui/key:z
|
||||
- /data/ca_download/:/etc/ui/ca/:z
|
||||
- /data/token:/etc/ui/token:z
|
||||
networks:
|
||||
- harbor
|
||||
depends_on:
|
||||
|
@ -15,7 +15,9 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@ -33,6 +35,7 @@ import (
|
||||
|
||||
const (
|
||||
defaultKeyPath string = "/etc/ui/key"
|
||||
defaultTokenFilePath string = "/etc/ui/token"
|
||||
secretCookieName string = "secret"
|
||||
)
|
||||
|
||||
@ -45,6 +48,9 @@ var (
|
||||
GlobalProjectMgr projectmanager.ProjectManager
|
||||
mg *comcfg.Manager
|
||||
keyProvider comcfg.KeyProvider
|
||||
// AdmiralClient is initialized only under integration deploy mode
|
||||
// and can be passed to project manager as a parameter
|
||||
AdmiralClient *http.Client
|
||||
)
|
||||
|
||||
// Init configurations
|
||||
@ -104,9 +110,19 @@ func initProjectManager() {
|
||||
}
|
||||
|
||||
// integration with admiral
|
||||
// TODO create project manager based on pms using service account
|
||||
log.Info("initializing the project manager based on PMS...")
|
||||
GlobalProjectMgr = pms.NewProjectManager(AdmiralEndpoint(), "")
|
||||
// TODO read ca/cert file and pass it to the TLS config
|
||||
AdminserverClient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
GlobalProjectMgr = pms.NewProjectManager(AdminserverClient,
|
||||
AdmiralEndpoint(), &pms.FileTokenReader{
|
||||
Path: defaultTokenFilePath,
|
||||
})
|
||||
}
|
||||
|
||||
// Load configurations
|
||||
|
@ -51,8 +51,8 @@ func Init() {
|
||||
if config.WithAdmiral() {
|
||||
reqCtxModifiers = []ReqCtxModifier{
|
||||
&secretReqCtxModifier{config.SecretStore},
|
||||
&basicAuthReqCtxModifier{},
|
||||
&tokenReqCtxModifier{},
|
||||
&basicAuthReqCtxModifier{},
|
||||
&unauthorizedReqCtxModifier{}}
|
||||
return
|
||||
}
|
||||
@ -131,15 +131,14 @@ func (b *basicAuthReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
||||
|
||||
if config.WithAdmiral() {
|
||||
// integration with admiral
|
||||
token, authCtx, err := authcontext.Login(config.AdmiralEndpoint(), username, password)
|
||||
_, authCtx, err := authcontext.Login(config.AdmiralEndpoint(), username, password)
|
||||
if err != nil {
|
||||
log.Errorf("failed to authenticate %s: %v", username, err)
|
||||
return false
|
||||
}
|
||||
|
||||
log.Debug("creating PMS project manager...")
|
||||
pm = pms.NewProjectManager(config.AdmiralEndpoint(), token)
|
||||
|
||||
log.Debug("using glocal project manager...")
|
||||
pm = config.GlobalProjectMgr
|
||||
log.Debug("creating admiral security context...")
|
||||
securCtx = admiral.NewSecurityContext(authCtx, pm)
|
||||
} else {
|
||||
@ -211,7 +210,10 @@ func (t *tokenReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
||||
}
|
||||
|
||||
log.Debug("creating PMS project manager...")
|
||||
pm := pms.NewProjectManager(config.AdmiralEndpoint(), token)
|
||||
pm := pms.NewProjectManager(config.AdmiralClient,
|
||||
config.AdmiralEndpoint(), &pms.RawTokenReader{
|
||||
Token: token,
|
||||
})
|
||||
log.Debug("creating admiral security context...")
|
||||
securCtx := admiral.NewSecurityContext(authContext, pm)
|
||||
setSecurCtxAndPM(ctx.Request, securCtx, pm)
|
||||
@ -230,7 +232,8 @@ func (u *unauthorizedReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
||||
if config.WithAdmiral() {
|
||||
// integration with admiral
|
||||
log.Debug("creating PMS project manager...")
|
||||
pm = pms.NewProjectManager(config.AdmiralEndpoint(), "")
|
||||
pm = pms.NewProjectManager(config.AdmiralClient,
|
||||
config.AdmiralEndpoint(), nil)
|
||||
log.Debug("creating admiral security context...")
|
||||
securCtx = admiral.NewSecurityContext(nil, pm)
|
||||
} else {
|
||||
|
@ -32,14 +32,12 @@ import (
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
)
|
||||
|
||||
var transport = &http.Transport{}
|
||||
|
||||
// ProjectManager implements projectmanager.ProjecdtManager interface
|
||||
// base on project management service
|
||||
type ProjectManager struct {
|
||||
endpoint string
|
||||
token string
|
||||
client *http.Client
|
||||
endpoint string
|
||||
tokenReader TokenReader
|
||||
}
|
||||
|
||||
type user struct {
|
||||
@ -58,13 +56,12 @@ type project struct {
|
||||
}
|
||||
|
||||
// NewProjectManager returns an instance of ProjectManager
|
||||
func NewProjectManager(endpoint, token string) *ProjectManager {
|
||||
func NewProjectManager(client *http.Client, endpoint string,
|
||||
tokenReader TokenReader) *ProjectManager {
|
||||
return &ProjectManager{
|
||||
client: client,
|
||||
endpoint: strings.TrimRight(endpoint, "/"),
|
||||
token: token,
|
||||
client: &http.Client{
|
||||
Transport: transport,
|
||||
},
|
||||
tokenReader: tokenReader,
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,7 +304,7 @@ func (p *ProjectManager) GetPublic() ([]*models.Project, error) {
|
||||
// GetByMember ...
|
||||
func (p *ProjectManager) GetByMember(username string) ([]*models.Project, error) {
|
||||
projects := []*models.Project{}
|
||||
ctx, err := authcontext.GetAuthCtxOfUser(p.endpoint, p.token, username)
|
||||
ctx, err := authcontext.GetAuthCtxOfUser(p.endpoint, p.getToken(), username)
|
||||
if err != nil {
|
||||
return projects, err
|
||||
}
|
||||
@ -425,7 +422,7 @@ func (p *ProjectManager) send(method, path string, body io.Reader) ([]byte, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("x-xenon-auth-token", p.token)
|
||||
req.Header.Add("x-xenon-auth-token", p.getToken())
|
||||
|
||||
url := req.URL.String()
|
||||
|
||||
@ -452,3 +449,16 @@ func (p *ProjectManager) send(method, path string, body io.Reader) ([]byte, erro
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (p *ProjectManager) getToken() string {
|
||||
if p.tokenReader == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
token, err := p.tokenReader.ReadToken()
|
||||
if err != nil {
|
||||
token = ""
|
||||
log.Errorf("failed to read token: %v", err)
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
package pms
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
@ -24,8 +25,11 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
client = http.DefaultClient
|
||||
endpoint = "http://127.0.0.1:8282"
|
||||
token = ""
|
||||
tokenReader = &RawTokenReader{
|
||||
Token: "",
|
||||
}
|
||||
)
|
||||
|
||||
func TestConvert(t *testing.T) {
|
||||
@ -177,7 +181,7 @@ func TestParse(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
pm := NewProjectManager(endpoint, token)
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
name := "project_for_test_get"
|
||||
id, err := pm.Create(&models.Project{
|
||||
Name: name,
|
||||
@ -211,7 +215,7 @@ func TestGet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsPublic(t *testing.T) {
|
||||
pm := NewProjectManager(endpoint, token)
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
|
||||
// invalid input type
|
||||
public, err := pm.IsPublic([]string{})
|
||||
@ -259,7 +263,7 @@ func TestIsPublic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExist(t *testing.T) {
|
||||
pm := NewProjectManager(endpoint, token)
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
|
||||
// invalid input type
|
||||
exist, err := pm.Exist([]string{})
|
||||
@ -289,7 +293,7 @@ func TestExist(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetRoles(t *testing.T) {
|
||||
pm := NewProjectManager(endpoint, token)
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
|
||||
// nil username, nil project
|
||||
roles, err := pm.GetRoles("", nil)
|
||||
@ -316,7 +320,7 @@ func TestGetRoles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetPublic(t *testing.T) {
|
||||
pm := NewProjectManager(endpoint, token)
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
|
||||
projects, err := pm.GetPublic()
|
||||
assert.Nil(t, nil)
|
||||
@ -350,7 +354,7 @@ func TestGetByMember(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
pm := NewProjectManager(endpoint, token)
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
|
||||
name := "project_for_test_create"
|
||||
id, err := pm.Create(&models.Project{
|
||||
@ -375,7 +379,7 @@ func TestCreate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
pm := NewProjectManager(endpoint, token)
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
|
||||
// non-exist project
|
||||
err := pm.Delete(int64(0))
|
||||
@ -401,13 +405,13 @@ func TestDelete(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
pm := NewProjectManager(endpoint, token)
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
err := pm.Update(nil, nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestGetAll(t *testing.T) {
|
||||
pm := NewProjectManager(endpoint, token)
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
|
||||
name1 := "project_for_test_get_all_01"
|
||||
id1, err := pm.Create(&models.Project{
|
||||
@ -471,7 +475,7 @@ func TestGetAll(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetTotal(t *testing.T) {
|
||||
pm := NewProjectManager(endpoint, token)
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
|
||||
total1, err := pm.GetTotal(nil)
|
||||
require.Nil(t, err)
|
||||
@ -489,13 +493,13 @@ func TestGetTotal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetHasReadPerm(t *testing.T) {
|
||||
pm := NewProjectManager(endpoint, token)
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
_, err := pm.GetHasReadPerm()
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func delete(t *testing.T, id int64) {
|
||||
pm := NewProjectManager(endpoint, token)
|
||||
pm := NewProjectManager(client, endpoint, tokenReader)
|
||||
if err := pm.Delete(id); err != nil {
|
||||
t.Logf("failed to delete project %d: %v", id, err)
|
||||
}
|
||||
|
83
src/ui/projectmanager/pms/token.go
Normal file
83
src/ui/projectmanager/pms/token.go
Normal file
@ -0,0 +1,83 @@
|
||||
// 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 pms
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
key = "access_token"
|
||||
)
|
||||
|
||||
// TokenReader is an interface used to wrap the way how to get token
|
||||
type TokenReader interface {
|
||||
// ReadToken reads token
|
||||
ReadToken() (string, error)
|
||||
}
|
||||
|
||||
// RawTokenReader just returns the token contained by field Token
|
||||
type RawTokenReader struct {
|
||||
Token string
|
||||
}
|
||||
|
||||
// ReadToken ...
|
||||
func (r *RawTokenReader) ReadToken() (string, error) {
|
||||
return r.Token, nil
|
||||
}
|
||||
|
||||
// FileTokenReader reads token from file
|
||||
type FileTokenReader struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// ReadToken ...
|
||||
func (f *FileTokenReader) ReadToken() (string, error) {
|
||||
file, err := os.Open(f.Path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return readToken(file)
|
||||
}
|
||||
|
||||
func readToken(reader io.Reader) (string, error) {
|
||||
if reader == nil {
|
||||
return "", fmt.Errorf("reader is nil")
|
||||
}
|
||||
|
||||
r := bufio.NewReader(reader)
|
||||
for {
|
||||
line, _, err := r.ReadLine()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = fmt.Errorf("%s not found", key)
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
strs := strings.SplitN(string(line), "=", 2)
|
||||
if len(strs) != 2 {
|
||||
continue
|
||||
}
|
||||
if strs[0] == key {
|
||||
return strs[1], nil
|
||||
}
|
||||
}
|
||||
}
|
98
src/ui/projectmanager/pms/token_test.go
Normal file
98
src/ui/projectmanager/pms/token_test.go
Normal file
@ -0,0 +1,98 @@
|
||||
// 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 pms
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRawTokenReader(t *testing.T) {
|
||||
raw := "token"
|
||||
reader := &RawTokenReader{
|
||||
Token: raw,
|
||||
}
|
||||
|
||||
token, err := reader.ReadToken()
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, raw, token)
|
||||
}
|
||||
|
||||
func TestReadToken(t *testing.T) {
|
||||
// nil reader
|
||||
_, err := readToken(nil)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// empty
|
||||
reader := bytes.NewReader([]byte{})
|
||||
_, err = readToken(reader)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// contains no "access_token"
|
||||
content := "key1=value\nkey2=value2"
|
||||
reader = bytes.NewReader([]byte(content))
|
||||
_, err = readToken(reader)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// contains "access_token" but no "="
|
||||
content = "access_token value\nkey2=value2"
|
||||
reader = bytes.NewReader([]byte(content))
|
||||
_, err = readToken(reader)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// contains "access_token" and "=", but no value
|
||||
content = "access_token=\nkey2=value2"
|
||||
reader = bytes.NewReader([]byte(content))
|
||||
token, err := readToken(reader)
|
||||
require.Nil(t, err)
|
||||
assert.Len(t, token, 0)
|
||||
|
||||
// valid "access_token"
|
||||
content = "access_token=token\nkey2=value2"
|
||||
reader = bytes.NewReader([]byte(content))
|
||||
token, err = readToken(reader)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "token", token)
|
||||
}
|
||||
|
||||
func TestFileTokenReader(t *testing.T) {
|
||||
// file not exist
|
||||
path := "/tmp/not_exist_file"
|
||||
reader := &FileTokenReader{
|
||||
Path: path,
|
||||
}
|
||||
|
||||
_, err := reader.ReadToken()
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// file exist
|
||||
path = "/tmp/exist_file"
|
||||
err = ioutil.WriteFile(path, []byte("access_token=token"), 0x0666)
|
||||
require.Nil(t, err)
|
||||
defer os.Remove(path)
|
||||
|
||||
reader = &FileTokenReader{
|
||||
Path: path,
|
||||
}
|
||||
|
||||
token, err := reader.ReadToken()
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "token", token)
|
||||
}
|
@ -102,7 +102,8 @@ func TestEnvPolicyChecker(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPMSPolicyChecker(t *testing.T) {
|
||||
pm := pms.NewProjectManager(admiralEndpoint, token)
|
||||
pm := pms.NewProjectManager(http.DefaultClient,
|
||||
admiralEndpoint, nil)
|
||||
name := "project_for_test_get_true"
|
||||
id, err := pm.Create(&models.Project{
|
||||
Name: name,
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"github.com/vmware/harbor/src/common/utils/notary"
|
||||
"github.com/vmware/harbor/src/ui/config"
|
||||
"github.com/vmware/harbor/src/ui/projectmanager"
|
||||
"github.com/vmware/harbor/src/ui/projectmanager/pms"
|
||||
|
||||
"context"
|
||||
"fmt"
|
||||
@ -92,7 +91,7 @@ func newPMSPolicyChecker(pm projectmanager.ProjectManager) policyChecker {
|
||||
// TODO: Get project manager with PM factory.
|
||||
func getPolicyChecker() policyChecker {
|
||||
if config.WithAdmiral() {
|
||||
return newPMSPolicyChecker(pms.NewProjectManager(config.AdmiralEndpoint(), ""))
|
||||
return newPMSPolicyChecker(config.GlobalProjectMgr)
|
||||
}
|
||||
return EnvChecker
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ import (
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/ui/api"
|
||||
"github.com/vmware/harbor/src/ui/config"
|
||||
"github.com/vmware/harbor/src/ui/projectmanager/pms"
|
||||
uiutils "github.com/vmware/harbor/src/ui/utils"
|
||||
)
|
||||
|
||||
@ -161,9 +160,7 @@ func autoScanEnabled(projectName string) bool {
|
||||
return false
|
||||
}
|
||||
if config.WithAdmiral() {
|
||||
//TODO get a project manager based on service account.
|
||||
var pm *pms.ProjectManager = pms.NewProjectManager("", "")
|
||||
p, err := pm.Get(projectName)
|
||||
p, err := config.GlobalProjectMgr.Get(projectName)
|
||||
if err != nil {
|
||||
log.Warningf("failed to get project, error: %v", err)
|
||||
return false
|
||||
|
Loading…
Reference in New Issue
Block a user